Version in base suite: 2.0.8-3 Base version: fwupd_2.0.8-3 Target version: fwupd_2.0.20-1~bpo13+1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/f/fwupd/fwupd_2.0.8-3.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/f/fwupd/fwupd_2.0.20-1~bpo13+1.dsc /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/contrib/ci/ubuntu-x86_64-test.sh |binary /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/plugins/ch341a/ch341a-vmod.png |binary /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/plugins/optionrom/fuzzing/header-data-payload.rom |binary /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/plugins/optionrom/fuzzing/header-no-data.rom |binary /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/plugins/optionrom/fuzzing/ifr-header-data-payload.rom |binary /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/plugins/optionrom/fuzzing/naked-ifr.rom |binary /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/plugins/wch-ch341a/wch-ch341a-vmod.png |binary /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/descriptors |binary /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/descriptors |binary fwupd-2.0.20/.clang-format | 16 fwupd-2.0.20/.clang-tidy | 4 fwupd-2.0.20/.clangd | 5 fwupd-2.0.20/.editorconfig | 2 fwupd-2.0.20/.github/CODEOWNERS | 81 fwupd-2.0.20/.github/ISSUE_TEMPLATE/bug-report-general.md | 1 fwupd-2.0.20/.github/ISSUE_TEMPLATE/bug-report-uefi.md | 1 fwupd-2.0.20/.github/copilot-instructions.md | 230 fwupd-2.0.20/.github/workflows/ci.yml | 9 fwupd-2.0.20/.github/workflows/codeql-analysis.yml | 10 fwupd-2.0.20/.github/workflows/create_containers.yml | 10 fwupd-2.0.20/.github/workflows/dependency-review.yml | 6 fwupd-2.0.20/.github/workflows/matrix.yml | 128 fwupd-2.0.20/.github/workflows/pull-request-reviews.yml | 13 fwupd-2.0.20/.github/workflows/scorecard.yml | 73 fwupd-2.0.20/.github/workflows/snap.yml | 12 fwupd-2.0.20/.gitignore | 5 fwupd-2.0.20/.pre-commit-config.yaml | 41 fwupd-2.0.20/.tx/config | 9 fwupd-2.0.20/README.md | 20 fwupd-2.0.20/RELEASE | 31 fwupd-2.0.20/contrib/build-openbmc.sh | 5 fwupd-2.0.20/contrib/build-venv.sh | 20 fwupd-2.0.20/contrib/build-windows.sh | 72 fwupd-2.0.20/contrib/ci/Dockerfile-centos.in | 17 fwupd-2.0.20/contrib/ci/Dockerfile-precommit.in | 1 fwupd-2.0.20/contrib/ci/arch-test.sh | 13 fwupd-2.0.20/contrib/ci/build_freebsd_package.sh | 40 fwupd-2.0.20/contrib/ci/build_macos.sh | 2 fwupd-2.0.20/contrib/ci/build_windows.sh | 166 fwupd-2.0.20/contrib/ci/centos-test.sh | 15 fwupd-2.0.20/contrib/ci/centos.sh | 29 fwupd-2.0.20/contrib/ci/check-cli-actions.py | 42 fwupd-2.0.20/contrib/ci/check-cpu.py | 58 fwupd-2.0.20/contrib/ci/check-finalizers.py | 60 fwupd-2.0.20/contrib/ci/check-headers.py | 12 fwupd-2.0.20/contrib/ci/check-meson-install-tags.py | 174 fwupd-2.0.20/contrib/ci/check-null-false-returns.py | 240 fwupd-2.0.20/contrib/ci/check-rss.py | 58 fwupd-2.0.20/contrib/ci/check-source.py | 1503 ++- fwupd-2.0.20/contrib/ci/check-unused.py | 4 fwupd-2.0.20/contrib/ci/check_missing_translations.sh | 2 fwupd-2.0.20/contrib/ci/coverage.sh | 16 fwupd-2.0.20/contrib/ci/ctokenizer.py | 443 fwupd-2.0.20/contrib/ci/ctokenizer_test.py | 319 fwupd-2.0.20/contrib/ci/debian-i386-test.sh | 2 fwupd-2.0.20/contrib/ci/debian-x86_64-test.sh | 2 fwupd-2.0.20/contrib/ci/debian.sh | 41 fwupd-2.0.20/contrib/ci/debian_s390x.sh | 17 fwupd-2.0.20/contrib/ci/dependencies.xml | 180 fwupd-2.0.20/contrib/ci/fedora-test.sh | 2 fwupd-2.0.20/contrib/ci/fedora.sh | 6 fwupd-2.0.20/contrib/ci/fwupd_setup_helpers.py | 8 fwupd-2.0.20/contrib/ci/generate_dependencies.py | 8 fwupd-2.0.20/contrib/ci/generate_docker.py | 2 fwupd-2.0.20/contrib/ci/generate_news.py | 25 fwupd-2.0.20/contrib/ci/get_test_firmware.sh | 22 fwupd-2.0.20/contrib/ci/oss-fuzz.py | 52 fwupd-2.0.20/contrib/ci/precommit.sh | 2 fwupd-2.0.20/contrib/ci/tests/fu-blocked-bitset.c | 16 fwupd-2.0.20/contrib/ci/tests/fu-blocked-funcs.c | 13 fwupd-2.0.20/contrib/ci/tests/fu-blocked-goto.c | 18 fwupd-2.0.20/contrib/ci/tests/fu-blocked-rustgen.c | 15 fwupd-2.0.20/contrib/ci/tests/fu-comments.c | 15 fwupd-2.0.20/contrib/ci/tests/fu-device-convert-version.c | 14 fwupd-2.0.20/contrib/ci/tests/fu-device-display.c | 13 fwupd-2.0.20/contrib/ci/tests/fu-enum-typedef.c | 11 fwupd-2.0.20/contrib/ci/tests/fu-enum.c | 22 fwupd-2.0.20/contrib/ci/tests/fu-equals-true.c | 17 fwupd-2.0.20/contrib/ci/tests/fu-firmware-convert-version.c | 19 fwupd-2.0.20/contrib/ci/tests/fu-function-length-switch.c | 30 fwupd-2.0.20/contrib/ci/tests/fu-function-name.c | 40 fwupd-2.0.20/contrib/ci/tests/fu-gerror-deref.c | 13 fwupd-2.0.20/contrib/ci/tests/fu-gerror-domain.c | 14 fwupd-2.0.20/contrib/ci/tests/fu-gerror-false-returns.c | 15 fwupd-2.0.20/contrib/ci/tests/fu-gerror-literal.c | 15 fwupd-2.0.20/contrib/ci/tests/fu-gerror-missing-suffix.c | 17 fwupd-2.0.20/contrib/ci/tests/fu-gerror-no-return.c | 16 fwupd-2.0.20/contrib/ci/tests/fu-gerror-not-set.c | 17 fwupd-2.0.20/contrib/ci/tests/fu-gerror-void-return.c | 13 fwupd-2.0.20/contrib/ci/tests/fu-gobject-derivable.h | 22 fwupd-2.0.20/contrib/ci/tests/fu-gobject-final.h | 12 fwupd-2.0.20/contrib/ci/tests/fu-gobject-finalize.c | 17 fwupd-2.0.20/contrib/ci/tests/fu-gprint.c | 24 fwupd-2.0.20/contrib/ci/tests/fu-gtask.c | 13 fwupd-2.0.20/contrib/ci/tests/fu-magic-numbers-defined.c | 24 fwupd-2.0.20/contrib/ci/tests/fu-magic-numbers-inline.c | 96 fwupd-2.0.20/contrib/ci/tests/fu-memread.c | 30 fwupd-2.0.20/contrib/ci/tests/fu-nesting-depth.c | 24 fwupd-2.0.20/contrib/ci/tests/fu-null-false-returns.c | 19 fwupd-2.0.20/contrib/ci/tests/fu-param-self-device.c | 12 fwupd-2.0.20/contrib/ci/tests/fu-param-self-firmware.c | 12 fwupd-2.0.20/contrib/ci/tests/fu-param-self-native.c | 32 fwupd-2.0.20/contrib/ci/tests/fu-rustgen-bitshifts.c | 13 fwupd-2.0.20/contrib/ci/tests/fu-rustgen-vars.c | 13 fwupd-2.0.20/contrib/ci/tests/fu-small-conditionals-with-braces.c | 32 fwupd-2.0.20/contrib/ci/tests/fu-static-vars.c | 14 fwupd-2.0.20/contrib/ci/tests/fu-struct-typedef.c | 20 fwupd-2.0.20/contrib/ci/tests/fu-struct.c | 22 fwupd-2.0.20/contrib/ci/tests/fu-variable-lowercase.c | 19 fwupd-2.0.20/contrib/ci/tests/fu-zero-init.c | 13 fwupd-2.0.20/contrib/ci/ubuntu.sh | 26 fwupd-2.0.20/contrib/codespell.cfg | 6 fwupd-2.0.20/contrib/create-plugin.py | 4 fwupd-2.0.20/contrib/debian/control.in | 6 fwupd-2.0.20/contrib/debian/fwupd-qubes-vm-whonix.postinst | 2 fwupd-2.0.20/contrib/debian/fwupd-qubes-vm-whonix.postrm | 2 fwupd-2.0.20/contrib/debian/fwupd-tests.install | 1 fwupd-2.0.20/contrib/debian/fwupd-tests.postinst | 10 fwupd-2.0.20/contrib/debian/fwupd-tests.postrm | 10 fwupd-2.0.20/contrib/debian/fwupd.install | 1 fwupd-2.0.20/contrib/debian/fwupd.maintscript | 1 fwupd-2.0.20/contrib/debian/fwupd.postrm | 4 fwupd-2.0.20/contrib/debian/fwupd.preinst | 2 fwupd-2.0.20/contrib/debian/lintian/fwupd | 2 fwupd-2.0.20/contrib/debian/lintian/fwupd-tests | 2 fwupd-2.0.20/contrib/debian/tests/ci | 8 fwupd-2.0.20/contrib/debian/tests/libfwupd-dev | 2 fwupd-2.0.20/contrib/firmware_packager/README.md | 2 fwupd-2.0.20/contrib/firmware_packager/firmware_packager.py | 191 fwupd-2.0.20/contrib/firmware_packager/meson.build | 22 fwupd-2.0.20/contrib/fwupd.spec.in | 90 fwupd-2.0.20/contrib/fwupd.wxs.in | 7 fwupd-2.0.20/contrib/generate-ds20.py | 2 fwupd-2.0.20/contrib/launch-venv.sh | 72 fwupd-2.0.20/contrib/meson.build | 16 fwupd-2.0.20/contrib/mingw64.cross | 1 fwupd-2.0.20/contrib/pcap2emulation.py | 6 fwupd-2.0.20/contrib/qubes/README.md | 3 fwupd-2.0.20/contrib/qubes/meson.build | 48 fwupd-2.0.20/contrib/qubes/src/fwupd_receive_updates.py | 4 fwupd-2.0.20/contrib/qubes/src/qubes_fwupd_common.py | 1 fwupd-2.0.20/contrib/qubes/src/qubes_fwupdmgr.py | 4 fwupd-2.0.20/contrib/setup | 101 fwupd-2.0.20/contrib/snap/fwupd-command | 76 fwupd-2.0.20/contrib/snap/snapcraft.yaml | 2 fwupd-2.0.20/contrib/tartan.sh | 16 fwupd-2.0.20/contrib/test-venv.sh | 7 fwupd-2.0.20/data/bash-completion/fwupdmgr | 8 fwupd-2.0.20/data/bash-completion/fwupdtool | 6 fwupd-2.0.20/data/bash-completion/meson.build | 12 fwupd-2.0.20/data/bios-settings.d/README.md | 2 fwupd-2.0.20/data/bios-settings.d/meson.build | 8 fwupd-2.0.20/data/device-tests/caldigit-element.json | 4 fwupd-2.0.20/data/device-tests/dell-wd19tb.json | 2 fwupd-2.0.20/data/device-tests/meson.build | 17 fwupd-2.0.20/data/fish-completion/fwupdmgr.fish | 128 fwupd-2.0.20/data/fish-completion/meson.build | 4 fwupd-2.0.20/data/fwupd-i2c.conf | 1 fwupd-2.0.20/data/fwupd.shutdown.in | 6 fwupd-2.0.20/data/icons/128x128/meson.build | 6 fwupd-2.0.20/data/icons/64x64/meson.build | 6 fwupd-2.0.20/data/meson.build | 122 fwupd-2.0.20/data/motd/85-fwupd.motd.in | 2 fwupd-2.0.20/data/motd/meson.build | 56 fwupd-2.0.20/data/org.freedesktop.fwupd.metainfo.xml | 439 fwupd-2.0.20/data/org.freedesktop.fwupd.service.in | 2 fwupd-2.0.20/data/pki/LVFS-CA-2025PQ.pem | 162 fwupd-2.0.20/data/pki/meson.build | 52 fwupd-2.0.20/data/power.quirk | 4 fwupd-2.0.20/data/remotes.d/lvfs-testing.conf | 1 fwupd-2.0.20/data/remotes.d/lvfs.conf | 1 fwupd-2.0.20/data/remotes.d/meson.build | 16 fwupd-2.0.20/data/tests/build-certs.py | 2 fwupd-2.0.20/data/tests/fwupd.sh | 58 fwupd-2.0.20/data/tests/fwupd.test.in | 2 fwupd-2.0.20/data/tests/fwupdmgr-online.sh | 240 fwupd-2.0.20/data/tests/fwupdmgr-online.test.in | 3 fwupd-2.0.20/data/tests/fwupdmgr-p2p.sh | 29 fwupd-2.0.20/data/tests/fwupdmgr-p2p.test.in | 2 fwupd-2.0.20/data/tests/fwupdmgr.sh | 297 fwupd-2.0.20/data/tests/fwupdmgr.test.in | 2 fwupd-2.0.20/data/tests/fwupdtool-efiboot.sh | 116 fwupd-2.0.20/data/tests/fwupdtool-efiboot.test.in | 2 fwupd-2.0.20/data/tests/fwupdtool.sh | 497 - fwupd-2.0.20/data/tests/fwupdtool.test.in | 2 fwupd-2.0.20/data/tests/meson.build | 84 fwupd-2.0.20/data/vendors.quirk | 69 fwupd-2.0.20/debian/changelog | 143 fwupd-2.0.20/debian/control | 20 fwupd-2.0.20/debian/control.in | 2 fwupd-2.0.20/debian/copyright | 20 fwupd-2.0.20/debian/fwupd-tests.install | 1 fwupd-2.0.20/debian/fwupd-tests.postinst | 10 fwupd-2.0.20/debian/fwupd-tests.postrm | 10 fwupd-2.0.20/debian/fwupd.install | 1 fwupd-2.0.20/debian/fwupd.postrm | 4 fwupd-2.0.20/debian/fwupd.preinst | 2 fwupd-2.0.20/debian/libfwupd3.symbols | 19 fwupd-2.0.20/debian/patches/0001-Always-build-the-D-Bus-service.patch | 109 fwupd-2.0.20/debian/patches/0001-Fix-PK-and-KEK-enumeration-failure-on-some-systems.patch | 97 fwupd-2.0.20/debian/patches/0001-Fix-a-crash-when-paring-uevents-that-are-not-KEY-VAL.patch | 101 fwupd-2.0.20/debian/patches/0001-Fix-the-parents-and-grandparents-for-KEK-and-DBX-dev.patch | 68 fwupd-2.0.20/debian/patches/0001-trivial-Fix-the-debian-i686-build.patch | 35 fwupd-2.0.20/debian/patches/0001-trivial-only-use-google-pixel-fastboot-device-on-uns.patch | 59 fwupd-2.0.20/debian/patches/0001-trivial-require-libusb-1.0.27.patch | 28 fwupd-2.0.20/debian/patches/flaky-p2p.patch | 8 fwupd-2.0.20/debian/patches/series | 7 fwupd-2.0.20/debian/rules | 7 fwupd-2.0.20/debian/tests/ci | 8 fwupd-2.0.20/debian/tests/ci-flaky | 8 fwupd-2.0.20/debian/tests/control | 4 fwupd-2.0.20/debian/tests/libfwupd-dev | 2 fwupd-2.0.20/docs/bios-settings.md | 2 fwupd-2.0.20/docs/building.md | 16 fwupd-2.0.20/docs/env.md | 5 fwupd-2.0.20/docs/fwupd-remotes.d.md | 16 fwupd-2.0.20/docs/fwupd.conf.md | 16 fwupd-2.0.20/docs/fwupdplugin.toml.in | 1 fwupd-2.0.20/docs/hsi-tests.d/meson.build | 16 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Amd.RollbackProtection.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Amd.SmmLocked.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Amd.SpiReplayProtection.json | 7 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Amd.SpiWriteProtection.json | 7 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Bios.CapsuleUpdates.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Bios.RollbackProtection.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Cet.Active.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Cet.Enabled.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.EncryptedRam.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Acm.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Enabled.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Otp.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Policy.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Verified.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelGds.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Kernel.Lockdown.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Kernel.Tainted.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Mei.KeyManifest.json | 3 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Mei.ManufacturingMode.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Mei.OverrideStrap.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.PlatformDebugEnabled.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.PlatformDebugLocked.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.PlatformFused.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.PrebootDma.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Smap.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Spi.Bioswe.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Spi.Ble.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Spi.Descriptor.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Spi.SmmBwp.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.SupportedCpu.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.SuspendToIdle.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.SuspendToRam.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Tpm.EmptyPcr.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Tpm.ReconstructionPcr0.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Tpm.Version20.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Uefi.BootserviceVars.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Uefi.Db.json | 2 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Uefi.MemoryProtection.json | 1 fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Uefi.Pk.json | 1 fwupd-2.0.20/docs/hwids.md | 2 fwupd-2.0.20/docs/meson.build | 254 fwupd-2.0.20/docs/nda.md | 2 fwupd-2.0.20/docs/tutorial.md | 2 fwupd-2.0.20/docs/uefi-db.md | 191 fwupd-2.0.20/generate-build/generate-man.py | 15 fwupd-2.0.20/generate-build/meson.build | 7 fwupd-2.0.20/libfwupd/README.md | 7 fwupd-2.0.20/libfwupd/fwupd-bios-setting.c | 84 fwupd-2.0.20/libfwupd/fwupd-bios-setting.h | 6 fwupd-2.0.20/libfwupd/fwupd-build.h | 18 fwupd-2.0.20/libfwupd/fwupd-client-sync.c | 104 fwupd-2.0.20/libfwupd/fwupd-client-sync.h | 10 fwupd-2.0.20/libfwupd/fwupd-client.c | 361 fwupd-2.0.20/libfwupd/fwupd-client.h | 27 fwupd-2.0.20/libfwupd/fwupd-codec.c | 39 fwupd-2.0.20/libfwupd/fwupd-codec.h | 5 fwupd-2.0.20/libfwupd/fwupd-common.c | 22 fwupd-2.0.20/libfwupd/fwupd-common.h | 2 fwupd-2.0.20/libfwupd/fwupd-context-test.c | 6 fwupd-2.0.20/libfwupd/fwupd-device.c | 44 fwupd-2.0.20/libfwupd/fwupd-device.h | 1 fwupd-2.0.20/libfwupd/fwupd-enums-private.h | 8 fwupd-2.0.20/libfwupd/fwupd-enums.c | 38 fwupd-2.0.20/libfwupd/fwupd-enums.h | 106 fwupd-2.0.20/libfwupd/fwupd-error.c | 23 fwupd-2.0.20/libfwupd/fwupd-error.h | 2 fwupd-2.0.20/libfwupd/fwupd-plugin.c | 1 fwupd-2.0.20/libfwupd/fwupd-remote-private.h | 6 fwupd-2.0.20/libfwupd/fwupd-remote.c | 167 fwupd-2.0.20/libfwupd/fwupd-remote.h | 12 fwupd-2.0.20/libfwupd/fwupd-report.c | 1 fwupd-2.0.20/libfwupd/fwupd-request.c | 7 fwupd-2.0.20/libfwupd/fwupd-request.h | 2 fwupd-2.0.20/libfwupd/fwupd-security-attr.c | 4 fwupd-2.0.20/libfwupd/fwupd-security-attr.h | 3 fwupd-2.0.20/libfwupd/fwupd-self-test.c | 48 fwupd-2.0.20/libfwupd/fwupd-thread-test.c | 6 fwupd-2.0.20/libfwupd/fwupd.map | 39 fwupd-2.0.20/libfwupd/meson.build | 152 fwupd-2.0.20/libfwupdplugin/README.md | 31 fwupd-2.0.20/libfwupdplugin/fu-acpi-table.c | 9 fwupd-2.0.20/libfwupdplugin/fu-archive-firmware.c | 4 fwupd-2.0.20/libfwupdplugin/fu-archive.c | 2 fwupd-2.0.20/libfwupdplugin/fu-archive.h | 2 fwupd-2.0.20/libfwupdplugin/fu-backend.c | 37 fwupd-2.0.20/libfwupdplugin/fu-backend.h | 2 fwupd-2.0.20/libfwupdplugin/fu-bios-setting.c | 5 fwupd-2.0.20/libfwupdplugin/fu-bios-settings.c | 52 fwupd-2.0.20/libfwupdplugin/fu-bios-settings.h | 3 fwupd-2.0.20/libfwupdplugin/fu-block-device.c | 1 fwupd-2.0.20/libfwupdplugin/fu-block-partition.c | 33 fwupd-2.0.20/libfwupdplugin/fu-bluez-device.c | 28 fwupd-2.0.20/libfwupdplugin/fu-byte-array.c | 60 fwupd-2.0.20/libfwupdplugin/fu-byte-array.h | 9 fwupd-2.0.20/libfwupdplugin/fu-bytes.c | 4 fwupd-2.0.20/libfwupdplugin/fu-cab-firmware.c | 114 fwupd-2.0.20/libfwupdplugin/fu-cab-image.c | 1 fwupd-2.0.20/libfwupdplugin/fu-cfi-device.c | 28 fwupd-2.0.20/libfwupdplugin/fu-cfi-device.h | 2 fwupd-2.0.20/libfwupdplugin/fu-cfu-offer.c | 9 fwupd-2.0.20/libfwupdplugin/fu-cfu-payload.c | 13 fwupd-2.0.20/libfwupdplugin/fu-chunk-array.c | 1 fwupd-2.0.20/libfwupdplugin/fu-chunk.c | 29 fwupd-2.0.20/libfwupdplugin/fu-common-freebsd.c | 14 fwupd-2.0.20/libfwupdplugin/fu-common-guid.c | 8 fwupd-2.0.20/libfwupdplugin/fu-common-linux.c | 97 fwupd-2.0.20/libfwupdplugin/fu-common-windows.c | 2 fwupd-2.0.20/libfwupdplugin/fu-common.c | 110 fwupd-2.0.20/libfwupdplugin/fu-common.h | 59 fwupd-2.0.20/libfwupdplugin/fu-common.rs | 18 fwupd-2.0.20/libfwupdplugin/fu-composite-input-stream.c | 3 fwupd-2.0.20/libfwupdplugin/fu-config-private.h | 11 fwupd-2.0.20/libfwupdplugin/fu-config.c | 182 fwupd-2.0.20/libfwupdplugin/fu-context-private.h | 14 fwupd-2.0.20/libfwupdplugin/fu-context.c | 243 fwupd-2.0.20/libfwupdplugin/fu-context.h | 168 fwupd-2.0.20/libfwupdplugin/fu-context.rs | 46 fwupd-2.0.20/libfwupdplugin/fu-coswid-firmware.c | 351 fwupd-2.0.20/libfwupdplugin/fu-coswid-firmware.h | 6 fwupd-2.0.20/libfwupdplugin/fu-coswid.rs | 6 fwupd-2.0.20/libfwupdplugin/fu-crc.c | 34 fwupd-2.0.20/libfwupdplugin/fu-crc.h | 49 fwupd-2.0.20/libfwupdplugin/fu-crc.rs | 43 fwupd-2.0.20/libfwupdplugin/fu-csv-entry.c | 5 fwupd-2.0.20/libfwupdplugin/fu-csv-firmware.c | 6 fwupd-2.0.20/libfwupdplugin/fu-device-event-private.h | 3 fwupd-2.0.20/libfwupdplugin/fu-device-event.c | 5 fwupd-2.0.20/libfwupdplugin/fu-device-event.h | 4 fwupd-2.0.20/libfwupdplugin/fu-device-locker.c | 34 fwupd-2.0.20/libfwupdplugin/fu-device-locker.h | 8 fwupd-2.0.20/libfwupdplugin/fu-device-poll-locker.h | 13 fwupd-2.0.20/libfwupdplugin/fu-device-private.h | 26 fwupd-2.0.20/libfwupdplugin/fu-device.c | 1463 ++- fwupd-2.0.20/libfwupdplugin/fu-device.h | 357 fwupd-2.0.20/libfwupdplugin/fu-device.rs | 5 fwupd-2.0.20/libfwupdplugin/fu-dfu-firmware-private.h | 2 fwupd-2.0.20/libfwupdplugin/fu-dfu-firmware.c | 21 fwupd-2.0.20/libfwupdplugin/fu-dfu-firmware.rs | 6 fwupd-2.0.20/libfwupdplugin/fu-dfuse-firmware.c | 41 fwupd-2.0.20/libfwupdplugin/fu-dpaux-device.c | 6 fwupd-2.0.20/libfwupdplugin/fu-drm-device-private.h | 12 fwupd-2.0.20/libfwupdplugin/fu-drm-device.c | 33 fwupd-2.0.20/libfwupdplugin/fu-dummy-efivars.c | 20 fwupd-2.0.20/libfwupdplugin/fu-dump.h | 2 fwupd-2.0.20/libfwupdplugin/fu-edid.c | 80 fwupd-2.0.20/libfwupdplugin/fu-edid.h | 4 fwupd-2.0.20/libfwupdplugin/fu-efi-common.c | 73 fwupd-2.0.20/libfwupdplugin/fu-efi-common.h | 7 fwupd-2.0.20/libfwupdplugin/fu-efi-device-path-list.c | 10 fwupd-2.0.20/libfwupdplugin/fu-efi-device-path.c | 23 fwupd-2.0.20/libfwupdplugin/fu-efi-file.c | 46 fwupd-2.0.20/libfwupdplugin/fu-efi-filesystem.c | 17 fwupd-2.0.20/libfwupdplugin/fu-efi-ftw-store.c | 160 fwupd-2.0.20/libfwupdplugin/fu-efi-ftw-store.h | 12 fwupd-2.0.20/libfwupdplugin/fu-efi-hard-drive-device-path.c | 12 fwupd-2.0.20/libfwupdplugin/fu-efi-load-option.c | 43 fwupd-2.0.20/libfwupdplugin/fu-efi-lz77-decompressor.c | 45 fwupd-2.0.20/libfwupdplugin/fu-efi-section.c | 114 fwupd-2.0.20/libfwupdplugin/fu-efi-signature-list.c | 160 fwupd-2.0.20/libfwupdplugin/fu-efi-signature-list.h | 10 fwupd-2.0.20/libfwupdplugin/fu-efi-signature.c | 6 fwupd-2.0.20/libfwupdplugin/fu-efi-signature.h | 1 fwupd-2.0.20/libfwupdplugin/fu-efi-variable-authentication2.c | 263 fwupd-2.0.20/libfwupdplugin/fu-efi-variable-authentication2.h | 19 fwupd-2.0.20/libfwupdplugin/fu-efi-volume.c | 217 fwupd-2.0.20/libfwupdplugin/fu-efi-vss-auth-variable.c | 319 fwupd-2.0.20/libfwupdplugin/fu-efi-vss-auth-variable.h | 22 fwupd-2.0.20/libfwupdplugin/fu-efi-vss2-variable-store.c | 183 fwupd-2.0.20/libfwupdplugin/fu-efi-vss2-variable-store.h | 19 fwupd-2.0.20/libfwupdplugin/fu-efi-x509-device.c | 119 fwupd-2.0.20/libfwupdplugin/fu-efi-x509-signature-private.h | 2 fwupd-2.0.20/libfwupdplugin/fu-efi-x509-signature.c | 125 fwupd-2.0.20/libfwupdplugin/fu-efi.rs | 159 fwupd-2.0.20/libfwupdplugin/fu-efivars.c | 80 fwupd-2.0.20/libfwupdplugin/fu-efivars.h | 25 fwupd-2.0.20/libfwupdplugin/fu-elf-firmware.c | 38 fwupd-2.0.20/libfwupdplugin/fu-elf.rs | 2 fwupd-2.0.20/libfwupdplugin/fu-fdt-firmware.c | 63 fwupd-2.0.20/libfwupdplugin/fu-fdt-image.c | 3 fwupd-2.0.20/libfwupdplugin/fu-firmware-common.c | 3 fwupd-2.0.20/libfwupdplugin/fu-firmware.c | 511 - fwupd-2.0.20/libfwupdplugin/fu-firmware.h | 230 fwupd-2.0.20/libfwupdplugin/fu-firmware.rs | 72 fwupd-2.0.20/libfwupdplugin/fu-fit-firmware.c | 49 fwupd-2.0.20/libfwupdplugin/fu-fmap-firmware.c | 180 fwupd-2.0.20/libfwupdplugin/fu-freebsd-efivars.c | 69 fwupd-2.0.20/libfwupdplugin/fu-fuzzer-firmware.c.in | 8 fwupd-2.0.20/libfwupdplugin/fu-fuzzer-main.c | 4 fwupd-2.0.20/libfwupdplugin/fu-gcab.c | 61 fwupd-2.0.20/libfwupdplugin/fu-heci-device.c | 288 fwupd-2.0.20/libfwupdplugin/fu-heci-device.h | 62 fwupd-2.0.20/libfwupdplugin/fu-heci.rs | 153 fwupd-2.0.20/libfwupdplugin/fu-hid-descriptor.c | 26 fwupd-2.0.20/libfwupdplugin/fu-hid-device.c | 22 fwupd-2.0.20/libfwupdplugin/fu-hid-device.h | 2 fwupd-2.0.20/libfwupdplugin/fu-hid-report-item.c | 6 fwupd-2.0.20/libfwupdplugin/fu-hidraw-device.c | 306 fwupd-2.0.20/libfwupdplugin/fu-hidraw-device.h | 26 fwupd-2.0.20/libfwupdplugin/fu-hidraw.rs | 31 fwupd-2.0.20/libfwupdplugin/fu-hwids-config.c | 1 fwupd-2.0.20/libfwupdplugin/fu-hwids-darwin.c | 2 fwupd-2.0.20/libfwupdplugin/fu-hwids-kenv.c | 6 fwupd-2.0.20/libfwupdplugin/fu-hwids-private.h | 1 fwupd-2.0.20/libfwupdplugin/fu-hwids.c | 4 fwupd-2.0.20/libfwupdplugin/fu-i2c-device.c | 31 fwupd-2.0.20/libfwupdplugin/fu-ifd-bios.c | 7 fwupd-2.0.20/libfwupdplugin/fu-ifd-common.c | 113 fwupd-2.0.20/libfwupdplugin/fu-ifd-common.h | 33 fwupd-2.0.20/libfwupdplugin/fu-ifd-firmware.c | 98 fwupd-2.0.20/libfwupdplugin/fu-ifd-image.h | 2 fwupd-2.0.20/libfwupdplugin/fu-ifd.rs | 10 fwupd-2.0.20/libfwupdplugin/fu-ifwi-cpd-firmware.c | 89 fwupd-2.0.20/libfwupdplugin/fu-ifwi-fpt-firmware.c | 27 fwupd-2.0.20/libfwupdplugin/fu-ifwi.rs | 5 fwupd-2.0.20/libfwupdplugin/fu-ihex-firmware.c | 16 fwupd-2.0.20/libfwupdplugin/fu-input-stream.c | 43 fwupd-2.0.20/libfwupdplugin/fu-input-stream.h | 9 fwupd-2.0.20/libfwupdplugin/fu-intel-thunderbolt-firmware.c | 5 fwupd-2.0.20/libfwupdplugin/fu-intel-thunderbolt-nvm.c | 56 fwupd-2.0.20/libfwupdplugin/fu-intel-thunderbolt.rs | 2 fwupd-2.0.20/libfwupdplugin/fu-io-channel.c | 24 fwupd-2.0.20/libfwupdplugin/fu-io-channel.h | 44 fwupd-2.0.20/libfwupdplugin/fu-io-channel.rs | 4 fwupd-2.0.20/libfwupdplugin/fu-ioctl.c | 30 fwupd-2.0.20/libfwupdplugin/fu-ioctl.h | 4 fwupd-2.0.20/libfwupdplugin/fu-json-firmware.c | 43 fwupd-2.0.20/libfwupdplugin/fu-json-firmware.h | 16 fwupd-2.0.20/libfwupdplugin/fu-kernel.c | 21 fwupd-2.0.20/libfwupdplugin/fu-linear-firmware.c | 28 fwupd-2.0.20/libfwupdplugin/fu-linux-efivars.c | 81 fwupd-2.0.20/libfwupdplugin/fu-lzma-common.c | 3 fwupd-2.0.20/libfwupdplugin/fu-mei-device.c | 315 fwupd-2.0.20/libfwupdplugin/fu-mei-device.h | 8 fwupd-2.0.20/libfwupdplugin/fu-mem.c | 19 fwupd-2.0.20/libfwupdplugin/fu-msgpack-item.c | 16 fwupd-2.0.20/libfwupdplugin/fu-oprom-device.c | 25 fwupd-2.0.20/libfwupdplugin/fu-oprom-firmware.c | 64 fwupd-2.0.20/libfwupdplugin/fu-oprom-firmware.h | 34 fwupd-2.0.20/libfwupdplugin/fu-oprom.rs | 29 fwupd-2.0.20/libfwupdplugin/fu-output-stream.c | 100 fwupd-2.0.20/libfwupdplugin/fu-output-stream.h | 20 fwupd-2.0.20/libfwupdplugin/fu-partial-input-stream.c | 6 fwupd-2.0.20/libfwupdplugin/fu-path.c | 119 fwupd-2.0.20/libfwupdplugin/fu-path.h | 6 fwupd-2.0.20/libfwupdplugin/fu-pci-device.c | 24 fwupd-2.0.20/libfwupdplugin/fu-pefile-firmware.c | 87 fwupd-2.0.20/libfwupdplugin/fu-pkcs7.c | 156 fwupd-2.0.20/libfwupdplugin/fu-pkcs7.h | 15 fwupd-2.0.20/libfwupdplugin/fu-plugin-private.h | 8 fwupd-2.0.20/libfwupdplugin/fu-plugin.c | 93 fwupd-2.0.20/libfwupdplugin/fu-plugin.h | 22 fwupd-2.0.20/libfwupdplugin/fu-progress.c | 39 fwupd-2.0.20/libfwupdplugin/fu-progress.h | 7 fwupd-2.0.20/libfwupdplugin/fu-progress.rs | 2 fwupd-2.0.20/libfwupdplugin/fu-quirks.c | 21 fwupd-2.0.20/libfwupdplugin/fu-quirks.h | 2 fwupd-2.0.20/libfwupdplugin/fu-rustgen-enum.c.in | 82 fwupd-2.0.20/libfwupdplugin/fu-rustgen-enum.h.in | 32 fwupd-2.0.20/libfwupdplugin/fu-rustgen-struct.c.in | 227 fwupd-2.0.20/libfwupdplugin/fu-rustgen-struct.h.in | 17 fwupd-2.0.20/libfwupdplugin/fu-rustgen.c.in | 7 fwupd-2.0.20/libfwupdplugin/fu-rustgen.h.in | 8 fwupd-2.0.20/libfwupdplugin/fu-sbatlevel-section.c | 44 fwupd-2.0.20/libfwupdplugin/fu-security-attrs-private.h | 2 fwupd-2.0.20/libfwupdplugin/fu-security-attrs.c | 4 fwupd-2.0.20/libfwupdplugin/fu-self-test-device.c | 25 fwupd-2.0.20/libfwupdplugin/fu-self-test-device.h | 12 fwupd-2.0.20/libfwupdplugin/fu-self-test.c | 905 + fwupd-2.0.20/libfwupdplugin/fu-self-test.rs | 3 fwupd-2.0.20/libfwupdplugin/fu-smbios.c | 27 fwupd-2.0.20/libfwupdplugin/fu-smbios.rs | 57 fwupd-2.0.20/libfwupdplugin/fu-srec-firmware.c | 9 fwupd-2.0.20/libfwupdplugin/fu-string.c | 42 fwupd-2.0.20/libfwupdplugin/fu-string.h | 4 fwupd-2.0.20/libfwupdplugin/fu-test-device.c | 25 fwupd-2.0.20/libfwupdplugin/fu-test-device.h | 12 fwupd-2.0.20/libfwupdplugin/fu-udev-device.c | 390 fwupd-2.0.20/libfwupdplugin/fu-udev-device.h | 29 fwupd-2.0.20/libfwupdplugin/fu-uefi-device.c | 74 fwupd-2.0.20/libfwupdplugin/fu-uefi-device.h | 4 fwupd-2.0.20/libfwupdplugin/fu-usb-bos-descriptor.c | 40 fwupd-2.0.20/libfwupdplugin/fu-usb-config-descriptor.c | 4 fwupd-2.0.20/libfwupdplugin/fu-usb-descriptor.c | 16 fwupd-2.0.20/libfwupdplugin/fu-usb-device-ds20.c | 10 fwupd-2.0.20/libfwupdplugin/fu-usb-device.c | 299 fwupd-2.0.20/libfwupdplugin/fu-usb-device.h | 3 fwupd-2.0.20/libfwupdplugin/fu-usb-endpoint.c | 2 fwupd-2.0.20/libfwupdplugin/fu-usb-hid-descriptor.c | 2 fwupd-2.0.20/libfwupdplugin/fu-usb-interface.c | 14 fwupd-2.0.20/libfwupdplugin/fu-usb.rs | 6 fwupd-2.0.20/libfwupdplugin/fu-uswid-firmware.c | 44 fwupd-2.0.20/libfwupdplugin/fu-uswid.rs | 4 fwupd-2.0.20/libfwupdplugin/fu-v4l-device.c | 3 fwupd-2.0.20/libfwupdplugin/fu-v4l.rs | 2 fwupd-2.0.20/libfwupdplugin/fu-version-common.c | 28 fwupd-2.0.20/libfwupdplugin/fu-volume-locker.c | 135 fwupd-2.0.20/libfwupdplugin/fu-volume-locker.h | 19 fwupd-2.0.20/libfwupdplugin/fu-volume.c | 43 fwupd-2.0.20/libfwupdplugin/fu-volume.h | 4 fwupd-2.0.20/libfwupdplugin/fu-windows-efivars.c | 20 fwupd-2.0.20/libfwupdplugin/fu-windows-efivars.h | 4 fwupd-2.0.20/libfwupdplugin/fu-x509-certificate.c | 282 fwupd-2.0.20/libfwupdplugin/fu-x509-certificate.h | 21 fwupd-2.0.20/libfwupdplugin/fwupdplugin.h | 13 fwupd-2.0.20/libfwupdplugin/meson.build | 190 fwupd-2.0.20/libfwupdplugin/rustgen.py | 233 fwupd-2.0.20/libfwupdplugin/tests/colorhug/meson.build | 12 fwupd-2.0.20/libfwupdplugin/tests/coswid.builder.xml | 2 fwupd-2.0.20/libfwupdplugin/tests/efi-ftw-store.builder.xml | 4 fwupd-2.0.20/libfwupdplugin/tests/efi-variable-authentication2.builder.xml | 12 fwupd-2.0.20/libfwupdplugin/tests/efi-volume-sized.builder.xml | 5 fwupd-2.0.20/libfwupdplugin/tests/efi-volume.builder.xml | 20 fwupd-2.0.20/libfwupdplugin/tests/efi-vss-auth-variable.builder.xml | 6 fwupd-2.0.20/libfwupdplugin/tests/efi-vss2-variable-store.builder.xml | 10 fwupd-2.0.20/libfwupdplugin/tests/efi/efivars/meson.build | 48 fwupd-2.0.20/libfwupdplugin/tests/fmap-offset.builder.xml | 2 fwupd-2.0.20/libfwupdplugin/tests/fmap.builder.xml | 10 fwupd-2.0.20/libfwupdplugin/tests/fwupd/fwupd.conf | 2 fwupd-2.0.20/libfwupdplugin/tests/meson.build | 113 fwupd-2.0.20/libfwupdplugin/tests/quirks.d/tests.quirk | 6 fwupd-2.0.20/libfwupdplugin/tests/uevent | 3 fwupd-2.0.20/meson.build | 667 - fwupd-2.0.20/meson.format | 6 fwupd-2.0.20/meson_options.txt | 177 fwupd-2.0.20/plugins/README.md | 5 fwupd-2.0.20/plugins/acpi-dmar/fu-acpi-dmar-plugin.c | 6 fwupd-2.0.20/plugins/acpi-dmar/fu-acpi-dmar.c | 6 fwupd-2.0.20/plugins/acpi-dmar/fu-self-test.c | 4 fwupd-2.0.20/plugins/acpi-dmar/meson.build | 6 fwupd-2.0.20/plugins/acpi-facp/fu-acpi-facp-plugin.c | 4 fwupd-2.0.20/plugins/acpi-facp/fu-acpi-facp.c | 2 fwupd-2.0.20/plugins/acpi-facp/meson.build | 6 fwupd-2.0.20/plugins/acpi-ivrs/fu-acpi-ivrs-plugin.c | 6 fwupd-2.0.20/plugins/acpi-ivrs/fu-acpi-ivrs.c | 6 fwupd-2.0.20/plugins/acpi-ivrs/fu-self-test.c | 4 fwupd-2.0.20/plugins/acpi-ivrs/meson.build | 6 fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat-health-record.c | 12 fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat-plugin.c | 6 fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat-version-element.c | 10 fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat-version-record.c | 20 fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat.c | 12 fwupd-2.0.20/plugins/acpi-phat/fu-self-test.c | 2 fwupd-2.0.20/plugins/acpi-phat/meson.build | 5 fwupd-2.0.20/plugins/algoltek-usb/README.md | 7 fwupd-2.0.20/plugins/algoltek-usb/fu-algoltek-usb-device.c | 150 fwupd-2.0.20/plugins/algoltek-usb/fu-algoltek-usb-firmware.c | 10 fwupd-2.0.20/plugins/algoltek-usb/fu-algoltek-usb-plugin.c | 1 fwupd-2.0.20/plugins/algoltek-usb/tests/algoltek-ag9421.json | 8 fwupd-2.0.20/plugins/algoltek-usbcr/fu-algoltek-usbcr-device.c | 61 fwupd-2.0.20/plugins/algoltek-usbcr/fu-algoltek-usbcr-device.h | 2 fwupd-2.0.20/plugins/algoltek-usbcr/fu-algoltek-usbcr-firmware.c | 2 fwupd-2.0.20/plugins/amd-gpu/README.md | 7 fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-atom-firmware.c | 96 fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-atom-firmware.h | 2 fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-atom.rs | 2 fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-device.c | 82 fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-plugin.c | 5 fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-psp-firmware.c | 105 fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-uma.c | 250 fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-uma.h | 27 fwupd-2.0.20/plugins/amd-gpu/meson.build | 14 fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-rembrandt-setup.json | 125 fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-rembrandt.json | 17 fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-setup.json | 119 fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-strix-setup.json | 119 fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-strix.json | 17 fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu.json | 17 fwupd-2.0.20/plugins/amd-gpu/tests/amd-dgpu-navi3x.json | 4 fwupd-2.0.20/plugins/amd-kria/README.md | 14 fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-device.c | 23 fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-image-firmware.c | 4 fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-persistent-firmware.c | 12 fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-plugin.c | 21 fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-som-eeprom.c | 53 fwupd-2.0.20/plugins/amd-kria/meson.build | 5 fwupd-2.0.20/plugins/amd-pmc/README.md | 7 fwupd-2.0.20/plugins/amd-pmc/fu-amd-pmc-device.c | 4 fwupd-2.0.20/plugins/amd-pmc/meson.build | 5 fwupd-2.0.20/plugins/analogix/README.md | 7 fwupd-2.0.20/plugins/analogix/fu-analogix-common.h | 19 fwupd-2.0.20/plugins/analogix/fu-analogix-device.c | 72 fwupd-2.0.20/plugins/analogix/fu-analogix-firmware.c | 16 fwupd-2.0.20/plugins/analogix/fu-analogix-plugin.c | 1 fwupd-2.0.20/plugins/analogix/fu-analogix.rs | 19 fwupd-2.0.20/plugins/analogix/tests/analogix-anx7518.json | 8 fwupd-2.0.20/plugins/android-boot/README.md | 7 fwupd-2.0.20/plugins/android-boot/android-boot.quirk | 8 fwupd-2.0.20/plugins/android-boot/fu-android-boot-device.c | 7 fwupd-2.0.20/plugins/android-boot/meson.build | 4 fwupd-2.0.20/plugins/asus-hid/README.md | 7 fwupd-2.0.20/plugins/asus-hid/asus-hid.quirk | 12 fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-child-device.c | 105 fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-device.c | 197 fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-device.h | 2 fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-firmware.c | 19 fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-plugin.c | 1 fwupd-2.0.20/plugins/asus-hid/fu-asus-hid.rs | 2 fwupd-2.0.20/plugins/asus-hid/tests/asus-hid-setup.json | 243 fwupd-2.0.20/plugins/asus-hid/tests/asus-hid.json | 11 fwupd-2.0.20/plugins/ata/fu-ata-device.c | 28 fwupd-2.0.20/plugins/ata/meson.build | 5 fwupd-2.0.20/plugins/aver-hid/README.md | 7 fwupd-2.0.20/plugins/aver-hid/fu-aver-hid-device.c | 186 fwupd-2.0.20/plugins/aver-hid/fu-aver-hid-firmware.c | 2 fwupd-2.0.20/plugins/aver-hid/fu-aver-hid-plugin.c | 2 fwupd-2.0.20/plugins/aver-hid/fu-aver-hid.rs | 52 fwupd-2.0.20/plugins/aver-hid/fu-aver-safeisp-device.c | 133 fwupd-2.0.20/plugins/aver-hid/tests/aver-fone540.json | 8 fwupd-2.0.20/plugins/bcm57xx/README.md | 11 fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-common.c | 6 fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-common.h | 25 fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-device.c | 59 fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-dict-image.c | 4 fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-firmware.c | 165 fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-firmware.h | 2 fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-plugin.c | 31 fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-plugin.h | 2 fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-recovery-device.c | 902 - fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-recovery-device.h | 16 fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-stage1-image.c | 6 fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-stage2-image.c | 4 fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx.rs | 29 fwupd-2.0.20/plugins/bcm57xx/fu-self-test.c | 13 fwupd-2.0.20/plugins/bcm57xx/meson.build | 9 fwupd-2.0.20/plugins/bcm57xx/tests/dell-kh08p.json | 4 fwupd-2.0.20/plugins/bios/fu-bios-plugin.c | 26 fwupd-2.0.20/plugins/bios/meson.build | 5 fwupd-2.0.20/plugins/bnr-dp/README.md | 9 fwupd-2.0.20/plugins/bnr-dp/fu-bnr-dp-device.c | 50 fwupd-2.0.20/plugins/bnr-dp/fu-bnr-dp-firmware.c | 35 fwupd-2.0.20/plugins/bnr-dp/fu-bnr-dp-firmware.h | 2 fwupd-2.0.20/plugins/bnr-dp/fu-bnr-dp.rs | 2 fwupd-2.0.20/plugins/bnr-dp/fu-self-test.c | 2 fwupd-2.0.20/plugins/bnr-dp/meson.build | 3 fwupd-2.0.20/plugins/bnr-dp/tests/bnr-dp.json | 4 fwupd-2.0.20/plugins/ccgx-dmc/README.md | 7 fwupd-2.0.20/plugins/ccgx-dmc/ccgx-dmc.quirk | 13 fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc-device.c | 87 fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc-devx-device.c | 15 fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc-firmware.c | 54 fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc-plugin.c | 1 fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc.rs | 2 fwupd-2.0.20/plugins/ccgx-dmc/fu-self-test.c | 2 fwupd-2.0.20/plugins/ccgx-dmc/meson.build | 1 fwupd-2.0.20/plugins/ccgx-dmc/tests/hp-dock-g5.json | 8 fwupd-2.0.20/plugins/ccgx/README.md | 7 fwupd-2.0.20/plugins/ccgx/fu-ccgx-firmware.c | 26 fwupd-2.0.20/plugins/ccgx/fu-ccgx-hid-device.c | 4 fwupd-2.0.20/plugins/ccgx/fu-ccgx-hpi-common.h | 237 fwupd-2.0.20/plugins/ccgx/fu-ccgx-hpi-device.c | 213 fwupd-2.0.20/plugins/ccgx/fu-ccgx-plugin.c | 2 fwupd-2.0.20/plugins/ccgx/fu-ccgx-pure-hid-device.c | 51 fwupd-2.0.20/plugins/ccgx/fu-ccgx.rs | 172 fwupd-2.0.20/plugins/ccgx/fu-self-test.c | 2 fwupd-2.0.20/plugins/ccgx/meson.build | 1 fwupd-2.0.20/plugins/cfu/cfu.quirk | 1 fwupd-2.0.20/plugins/cfu/fu-cfu-device.c | 43 fwupd-2.0.20/plugins/cfu/fu-cfu-module.c | 32 fwupd-2.0.20/plugins/cfu/fu-cfu-plugin.c | 2 fwupd-2.0.20/plugins/cfu/fu-cfu.rs | 4 fwupd-2.0.20/plugins/cfu/meson.build | 6 fwupd-2.0.20/plugins/ch341a/README.md | 62 fwupd-2.0.20/plugins/ch341a/ch341a.quirk | 2 fwupd-2.0.20/plugins/ch341a/fu-ch341a-cfi-device.c | 429 fwupd-2.0.20/plugins/ch341a/fu-ch341a-cfi-device.h | 12 fwupd-2.0.20/plugins/ch341a/fu-ch341a-device.c | 290 fwupd-2.0.20/plugins/ch341a/fu-ch341a-device.h | 17 fwupd-2.0.20/plugins/ch341a/fu-ch341a-plugin.c | 44 fwupd-2.0.20/plugins/ch341a/fu-ch341a-plugin.h | 11 fwupd-2.0.20/plugins/ch341a/lsusb.txt | 68 fwupd-2.0.20/plugins/ch341a/meson.build | 18 fwupd-2.0.20/plugins/ch341a/tests/ch341a-setup.json | 81 fwupd-2.0.20/plugins/ch341a/tests/ch341a.json | 17 fwupd-2.0.20/plugins/ch347/README.md | 40 fwupd-2.0.20/plugins/ch347/ch347.quirk | 2 fwupd-2.0.20/plugins/ch347/fu-ch347-cfi-device.c | 49 fwupd-2.0.20/plugins/ch347/fu-ch347-cfi-device.h | 12 fwupd-2.0.20/plugins/ch347/fu-ch347-device.c | 310 fwupd-2.0.20/plugins/ch347/fu-ch347-device.h | 23 fwupd-2.0.20/plugins/ch347/fu-ch347-plugin.c | 35 fwupd-2.0.20/plugins/ch347/fu-ch347-plugin.h | 11 fwupd-2.0.20/plugins/ch347/fu-ch347.rs | 19 fwupd-2.0.20/plugins/ch347/meson.build | 19 fwupd-2.0.20/plugins/ch347/tests/ch347-setup.json | 124 fwupd-2.0.20/plugins/ch347/tests/ch347.json | 17 fwupd-2.0.20/plugins/corsair/README.md | 7 fwupd-2.0.20/plugins/corsair/fu-corsair-bp.c | 44 fwupd-2.0.20/plugins/corsair/fu-corsair-device.c | 122 fwupd-2.0.20/plugins/corsair/fu-corsair-plugin.c | 1 fwupd-2.0.20/plugins/corsair/tests/corsair-katar-pro-xt.json | 2 fwupd-2.0.20/plugins/corsair/tests/corsair-sabre-pro.json | 2 fwupd-2.0.20/plugins/corsair/tests/corsair-sabre-rgb-pro.json | 2 fwupd-2.0.20/plugins/cpu/cpu.quirk | 2 fwupd-2.0.20/plugins/cpu/fu-cpu-device.c | 90 fwupd-2.0.20/plugins/cpu/fu-cpu.rs | 11 fwupd-2.0.20/plugins/cpu/meson.build | 5 fwupd-2.0.20/plugins/cros-ec/README.md | 7 fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-common.h | 2 fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-firmware.c | 2 fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-plugin.c | 1 fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-usb-device.c | 242 fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-usb-device.h | 19 fwupd-2.0.20/plugins/cros-ec/fu-cros-ec.rs | 2 fwupd-2.0.20/plugins/cros-ec/tests/acer-d501.json | 2 fwupd-2.0.20/plugins/cros-ec/tests/google-servo-micro.json | 8 fwupd-2.0.20/plugins/dell-dock/README.md | 8 fwupd-2.0.20/plugins/dell-dock/dell-dock.quirk | 30 fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-common.c | 6 fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-ec.c | 271 fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-ec.h | 22 fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-hid.c | 126 fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-hid.h | 8 fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-hub.c | 14 fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-hub.h | 2 fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-mst.c | 459 fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-plugin.c | 63 fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-status.c | 37 fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-tbt.c | 64 fwupd-2.0.20/plugins/dell-dock/fu-dell-dock.rs | 34 fwupd-2.0.20/plugins/dell-dock/meson.build | 1 fwupd-2.0.20/plugins/dell-kestrel/README.md | 37 fwupd-2.0.20/plugins/dell-kestrel/dell-kestrel.quirk | 4 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-common.h | 3 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-dpmux.c | 22 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-ec.c | 265 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-ec.h | 6 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-ec.rs | 7 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-hid-device.c | 131 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-hid.rs | 6 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-ilan.c | 19 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-package.c | 25 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-pd.c | 19 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-plugin.c | 92 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rmm.c | 32 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rmm.h | 4 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rtshub-firmware.c | 5 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rtshub.c | 180 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rtshub.h | 6 fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-wtpd.c | 20 fwupd-2.0.20/plugins/dell/README.md | 7 fwupd-2.0.20/plugins/dell/dell.quirk | 2 fwupd-2.0.20/plugins/dell/fu-dell-plugin.c | 75 fwupd-2.0.20/plugins/dell/fu-dell.rs | 13 fwupd-2.0.20/plugins/dell/meson.build | 6 fwupd-2.0.20/plugins/dell/tests/build-fwupdx64-efi-signed.py | 11 fwupd-2.0.20/plugins/dell/tests/build-uefi-insyde.py | 35 fwupd-2.0.20/plugins/dell/tests/efi-framebuffer/efi-framebuffer.0/height | 2 fwupd-2.0.20/plugins/dell/tests/efi-framebuffer/efi-framebuffer.0/width | 2 fwupd-2.0.20/plugins/dell/tests/example/build.sh | 2 fwupd-2.0.20/plugins/dell/tests/grub2-mkconfig | 1 fwupd-2.0.20/plugins/dell/tests/grub2-reboot | 1 fwupd-2.0.20/plugins/dell/tests/grub2/grub.cfg | 1 fwupd-2.0.20/plugins/dell/tests/meson.build | 29 fwupd-2.0.20/plugins/dell/tests/test.quirk | 2 fwupd-2.0.20/plugins/dell/tests/uefi-update-info.builder.xml | 7 fwupd-2.0.20/plugins/devlink/README.md | 146 fwupd-2.0.20/plugins/devlink/devlink.quirk | 29 fwupd-2.0.20/plugins/devlink/fu-devlink-backend.c | 196 fwupd-2.0.20/plugins/devlink/fu-devlink-backend.h | 26 fwupd-2.0.20/plugins/devlink/fu-devlink-component.c | 223 fwupd-2.0.20/plugins/devlink/fu-devlink-component.h | 19 fwupd-2.0.20/plugins/devlink/fu-devlink-device.c | 1007 ++ fwupd-2.0.20/plugins/devlink/fu-devlink-device.h | 30 fwupd-2.0.20/plugins/devlink/fu-devlink-netlink.c | 653 + fwupd-2.0.20/plugins/devlink/fu-devlink-netlink.h | 79 fwupd-2.0.20/plugins/devlink/fu-devlink-plugin.c | 374 fwupd-2.0.20/plugins/devlink/fu-devlink-plugin.h | 11 fwupd-2.0.20/plugins/devlink/fu-self-test.c | 203 fwupd-2.0.20/plugins/devlink/meson.build | 61 fwupd-2.0.20/plugins/devlink/tests/devlink-netdevsim.json | 20 fwupd-2.0.20/plugins/dfu/contrib/parse-avrdude-conf.py | 2 fwupd-2.0.20/plugins/dfu/dfu.quirk | 33 fwupd-2.0.20/plugins/dfu/fu-dfu-device.c | 163 fwupd-2.0.20/plugins/dfu/fu-dfu-device.h | 2 fwupd-2.0.20/plugins/dfu/fu-dfu-plugin.c | 1 fwupd-2.0.20/plugins/dfu/fu-dfu-self-test.c | 18 fwupd-2.0.20/plugins/dfu/fu-dfu-target-avr.c | 132 fwupd-2.0.20/plugins/dfu/fu-dfu-target-stm.c | 24 fwupd-2.0.20/plugins/dfu/fu-dfu-target.c | 281 fwupd-2.0.20/plugins/dfu/fu-dfu-target.h | 21 fwupd-2.0.20/plugins/dfu/fu-dfu.rs | 37 fwupd-2.0.20/plugins/dfu/meson.build | 5 fwupd-2.0.20/plugins/dfu/tests/fwupd-a3bu-xplained.json | 8 fwupd-2.0.20/plugins/dfu/tests/fwupd-at90usbkey.json | 8 fwupd-2.0.20/plugins/dfu/tests/realtek-rts5855.json | 6 fwupd-2.0.20/plugins/ebitdo/fu-ebitdo-device.c | 64 fwupd-2.0.20/plugins/ebitdo/fu-ebitdo-firmware.c | 21 fwupd-2.0.20/plugins/ebitdo/fu-ebitdo-plugin.c | 1 fwupd-2.0.20/plugins/ebitdo/tests/8bitdo-nes30pro.json | 2 fwupd-2.0.20/plugins/ebitdo/tests/8bitdo-sf30pro.json | 2 fwupd-2.0.20/plugins/ebitdo/tests/8bitdo-sfc30.json | 2 fwupd-2.0.20/plugins/egis-moc/README.md | 39 fwupd-2.0.20/plugins/egis-moc/egis-moc.quirk | 4 fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-common.c | 36 fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-common.h | 14 fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-device.c | 536 + fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-device.h | 12 fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-plugin.c | 36 fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-plugin.h | 11 fwupd-2.0.20/plugins/egis-moc/fu-egis-moc.rs | 54 fwupd-2.0.20/plugins/egis-moc/fu-self-test.c | 50 fwupd-2.0.20/plugins/egis-moc/meson.build | 37 fwupd-2.0.20/plugins/egis-moc/tests/egis-moc.json | 18 fwupd-2.0.20/plugins/elan-kbd/README.md | 7 fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-debug-device.c | 7 fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-device.c | 61 fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-firmware.c | 11 fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-plugin.c | 2 fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-runtime.c | 5 fwupd-2.0.20/plugins/elan-kbd/tests/elan-kbd.json | 4 fwupd-2.0.20/plugins/elanfp/README.md | 7 fwupd-2.0.20/plugins/elanfp/elanfp.quirk | 3 fwupd-2.0.20/plugins/elanfp/fu-elanfp-device.c | 26 fwupd-2.0.20/plugins/elanfp/fu-elanfp-firmware.c | 8 fwupd-2.0.20/plugins/elanfp/fu-elanfp-plugin.c | 1 fwupd-2.0.20/plugins/elanfp/tests/elan-p1515e.json | 4 fwupd-2.0.20/plugins/elantp/README.md | 8 fwupd-2.0.20/plugins/elantp/fu-elantp-common.h | 34 fwupd-2.0.20/plugins/elantp/fu-elantp-firmware.c | 4 fwupd-2.0.20/plugins/elantp/fu-elantp-haptic-firmware.c | 4 fwupd-2.0.20/plugins/elantp/fu-elantp-hid-device.c | 190 fwupd-2.0.20/plugins/elantp/fu-elantp-hid-haptic-device.c | 378 fwupd-2.0.20/plugins/elantp/fu-elantp-hid-haptic-device.h | 2 fwupd-2.0.20/plugins/elantp/fu-elantp-i2c-device.c | 129 fwupd-2.0.20/plugins/elantp/fu-elantp.rs | 35 fwupd-2.0.20/plugins/elantp/fu-self-test.c | 2 fwupd-2.0.20/plugins/elantp/meson.build | 5 fwupd-2.0.20/plugins/emmc/emmc.quirk | 2 fwupd-2.0.20/plugins/emmc/fu-emmc-device.c | 35 fwupd-2.0.20/plugins/emmc/meson.build | 4 fwupd-2.0.20/plugins/ep963x/fu-ep963x-common.h | 40 fwupd-2.0.20/plugins/ep963x/fu-ep963x-device.c | 2 fwupd-2.0.20/plugins/ep963x/fu-ep963x-firmware.c | 2 fwupd-2.0.20/plugins/ep963x/fu-ep963x-plugin.c | 10 fwupd-2.0.20/plugins/ep963x/fu-ep963x-plugin.h | 2 fwupd-2.0.20/plugins/ep963x/fu-ep963x.rs | 48 fwupd-2.0.20/plugins/ep963x/meson.build | 4 fwupd-2.0.20/plugins/fastboot/ci.quirk | 3 fwupd-2.0.20/plugins/fastboot/fastboot.quirk | 7 fwupd-2.0.20/plugins/fastboot/fu-fastboot-device.c | 109 fwupd-2.0.20/plugins/fastboot/fu-fastboot-plugin.c | 1 fwupd-2.0.20/plugins/fastboot/meson.build | 7 fwupd-2.0.20/plugins/flashrom/README.md | 6 fwupd-2.0.20/plugins/flashrom/flashrom.quirk | 1 fwupd-2.0.20/plugins/flashrom/fu-flashrom-cmos.c | 12 fwupd-2.0.20/plugins/flashrom/fu-flashrom-device.c | 140 fwupd-2.0.20/plugins/flashrom/fu-flashrom-plugin.c | 48 fwupd-2.0.20/plugins/flashrom/meson.build | 4 fwupd-2.0.20/plugins/focalfp/README.md | 7 fwupd-2.0.20/plugins/focalfp/fu-focalfp-firmware.c | 4 fwupd-2.0.20/plugins/focalfp/fu-focalfp-hid-device.c | 74 fwupd-2.0.20/plugins/focalfp/fu-focalfp.rs | 19 fwupd-2.0.20/plugins/focalfp/meson.build | 5 fwupd-2.0.20/plugins/fpc/README.md | 7 fwupd-2.0.20/plugins/fpc/fu-fpc-device.c | 45 fwupd-2.0.20/plugins/fpc/fu-fpc-ff2-firmware.c | 4 fwupd-2.0.20/plugins/fpc/fu-fpc-plugin.c | 1 fwupd-2.0.20/plugins/fpc/tests/fpc-lenfy-moh.json | 4 fwupd-2.0.20/plugins/framework-qmk/README.md | 23 fwupd-2.0.20/plugins/framework-qmk/framework-qmk.quirk | 29 fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk-device.c | 130 fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk-device.h | 16 fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk-plugin.c | 36 fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk-plugin.h | 15 fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk.rs | 15 fwupd-2.0.20/plugins/framework-qmk/meson.build | 20 fwupd-2.0.20/plugins/framework-qmk/tests/framework-qmk.json | 26 fwupd-2.0.20/plugins/framework-qmk/tests/framework16-ansi-setup.json | 267 fwupd-2.0.20/plugins/framework-qmk/tests/framework16-macropad-setup.json | 267 fwupd-2.0.20/plugins/fresco-pd/fu-fresco-pd-device.c | 14 fwupd-2.0.20/plugins/fresco-pd/fu-fresco-pd-firmware.c | 4 fwupd-2.0.20/plugins/fresco-pd/fu-fresco-pd-plugin.c | 1 fwupd-2.0.20/plugins/fresco-pd/tests/ugreen-cm260.json | 4 fwupd-2.0.20/plugins/genesys-gl32xx/fu-genesys-gl32xx-device.c | 50 fwupd-2.0.20/plugins/genesys-gl32xx/fu-genesys-gl32xx-firmware.c | 4 fwupd-2.0.20/plugins/genesys-gl32xx/meson.build | 4 fwupd-2.0.20/plugins/genesys/README.md | 92 fwupd-2.0.20/plugins/genesys/fu-genesys-common.h | 16 fwupd-2.0.20/plugins/genesys/fu-genesys-hubhid-device.c | 8 fwupd-2.0.20/plugins/genesys/fu-genesys-plugin.c | 19 fwupd-2.0.20/plugins/genesys/fu-genesys-scaler-device.c | 265 fwupd-2.0.20/plugins/genesys/fu-genesys-scaler-firmware.c | 10 fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-codesign-firmware.c | 8 fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-dev-firmware.c | 12 fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-device.c | 584 - fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-device.h | 2 fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-firmware.c | 56 fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-pd-firmware.c | 12 fwupd-2.0.20/plugins/genesys/genesys-usbhub-fwbank.quirk | 73 fwupd-2.0.20/plugins/genesys/meson.build | 9 fwupd-2.0.20/plugins/genesys/tests/genesys-evb-failure.json | 34 fwupd-2.0.20/plugins/genesys/tests/genesys-evb.json | 19 fwupd-2.0.20/plugins/genesys/tests/genesys-servo-dock.json | 32 fwupd-2.0.20/plugins/genesys/tests/genesys-zdn-chromebook.json | 20 fwupd-2.0.20/plugins/goodix-moc/README.md | 7 fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-common.h | 66 fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-device.c | 462 fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-device.h | 13 fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-plugin.c | 37 fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-plugin.h | 11 fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-common.h | 66 fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-device.c | 463 fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-device.h | 13 fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-plugin.c | 45 fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-plugin.h | 11 fwupd-2.0.20/plugins/goodix-moc/goodix-moc.quirk | 26 fwupd-2.0.20/plugins/goodix-moc/goodixmoc.quirk | 26 fwupd-2.0.20/plugins/goodix-moc/meson.build | 8 fwupd-2.0.20/plugins/goodix-tp/README.md | 7 fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-brlb-device.c | 32 fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-brlb-firmware.c | 15 fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-firmware.c | 15 fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-firmware.h | 5 fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-gtx8-device.c | 37 fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-gtx8-firmware.c | 45 fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-hid-device.c | 10 fwupd-2.0.20/plugins/gpio/fu-gpio-device.c | 8 fwupd-2.0.20/plugins/gpio/meson.build | 5 fwupd-2.0.20/plugins/hailuck/README.md | 48 fwupd-2.0.20/plugins/hailuck/data/lspci-bl.txt | 47 fwupd-2.0.20/plugins/hailuck/data/lspci.txt | 89 fwupd-2.0.20/plugins/hailuck/fu-hailuck-bl-device.c | 315 fwupd-2.0.20/plugins/hailuck/fu-hailuck-bl-device.h | 12 fwupd-2.0.20/plugins/hailuck/fu-hailuck-common.h | 12 fwupd-2.0.20/plugins/hailuck/fu-hailuck-kbd-device.c | 92 fwupd-2.0.20/plugins/hailuck/fu-hailuck-kbd-device.h | 12 fwupd-2.0.20/plugins/hailuck/fu-hailuck-kbd-firmware.c | 95 fwupd-2.0.20/plugins/hailuck/fu-hailuck-kbd-firmware.h | 16 fwupd-2.0.20/plugins/hailuck/fu-hailuck-plugin.c | 39 fwupd-2.0.20/plugins/hailuck/fu-hailuck-plugin.h | 11 fwupd-2.0.20/plugins/hailuck/fu-hailuck-tp-device.c | 255 fwupd-2.0.20/plugins/hailuck/fu-hailuck-tp-device.h | 15 fwupd-2.0.20/plugins/hailuck/fu-hailuck.rs | 23 fwupd-2.0.20/plugins/hailuck/hailuck.quirk | 25 fwupd-2.0.20/plugins/hailuck/meson.build | 21 fwupd-2.0.20/plugins/hailuck/tests/hailuck-setup.json | 98 fwupd-2.0.20/plugins/hailuck/tests/hailuck.json | 17 fwupd-2.0.20/plugins/hpi-cfu/README.md | 11 fwupd-2.0.20/plugins/hpi-cfu/fu-hpi-cfu-device.c | 282 fwupd-2.0.20/plugins/hpi-cfu/fu-hpi-cfu-plugin.c | 2 fwupd-2.0.20/plugins/hpi-cfu/hpi-cfu.quirk | 9 fwupd-2.0.20/plugins/hpi-cfu/tests/hp-dock-g6-ultra.json | 8 fwupd-2.0.20/plugins/huddly-usb/README.md | 9 fwupd-2.0.20/plugins/huddly-usb/fu-huddly-usb-common.c | 4 fwupd-2.0.20/plugins/huddly-usb/fu-huddly-usb-device.c | 114 fwupd-2.0.20/plugins/huddly-usb/fu-huddly-usb-plugin.c | 1 fwupd-2.0.20/plugins/huddly-usb/huddly-usb.quirk | 4 fwupd-2.0.20/plugins/huddly-usb/tests/huddly-s1.json | 4 fwupd-2.0.20/plugins/hughski-colorhug/README.md | 7 fwupd-2.0.20/plugins/hughski-colorhug/fu-hughski-colorhug-device.c | 38 fwupd-2.0.20/plugins/hughski-colorhug/fu-hughski-colorhug-plugin.c | 1 fwupd-2.0.20/plugins/hughski-colorhug/fu-hughski-colorhug.rs | 10 fwupd-2.0.20/plugins/hughski-colorhug/tests/hughski-colorhug-plus.json | 8 fwupd-2.0.20/plugins/hughski-colorhug/tests/hughski-colorhug.json | 8 fwupd-2.0.20/plugins/hughski-colorhug/tests/hughski-colorhug2.json | 8 fwupd-2.0.20/plugins/ilitek-its/README.md | 62 fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-block.c | 74 fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-block.h | 17 fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-common.c | 23 fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-common.h | 12 fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-device.c | 1049 ++ fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-device.h | 17 fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-firmware.c | 228 fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-firmware.h | 22 fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-plugin.c | 93 fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-plugin.h | 11 fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its.rs | 115 fwupd-2.0.20/plugins/ilitek-its/ilitek-its.quirk | 20 fwupd-2.0.20/plugins/ilitek-its/meson.build | 24 fwupd-2.0.20/plugins/ilitek-its/tests/ilitek-touchscreen.json | 18 fwupd-2.0.20/plugins/intel-amt/README.md | 34 fwupd-2.0.20/plugins/intel-amt/fu-intel-amt-device.c | 252 fwupd-2.0.20/plugins/intel-amt/fu-intel-amt-device.h | 12 fwupd-2.0.20/plugins/intel-amt/fu-intel-amt-plugin.c | 36 fwupd-2.0.20/plugins/intel-amt/fu-intel-amt-plugin.h | 11 fwupd-2.0.20/plugins/intel-amt/fu-intel-amt.rs | 83 fwupd-2.0.20/plugins/intel-amt/intel-amt.quirk | 3 fwupd-2.0.20/plugins/intel-amt/meson.build | 22 fwupd-2.0.20/plugins/intel-amt/tests/intel-amt-setup.json | 428 fwupd-2.0.20/plugins/intel-amt/tests/intel-amt.json | 17 fwupd-2.0.20/plugins/intel-cvs/README.md | 7 fwupd-2.0.20/plugins/intel-cvs/fu-intel-cvs-device.c | 14 fwupd-2.0.20/plugins/intel-cvs/fu-intel-cvs-firmware.c | 4 fwupd-2.0.20/plugins/intel-cvs/fu-intel-cvs.rs | 6 fwupd-2.0.20/plugins/intel-gsc/README.md | 195 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-aux-device.c | 102 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-aux-device.h | 2 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-aux-firmware.c | 156 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-code-firmware.c | 32 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-code-firmware.h | 2 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-common.c | 136 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-common.h | 20 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-device.c | 716 - fwupd-2.0.20/plugins/intel-gsc/fu-igsc-device.h | 19 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-heci.h | 130 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-oprom-device.c | 207 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-oprom-device.h | 4 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-oprom-firmware.c | 122 fwupd-2.0.20/plugins/intel-gsc/fu-igsc-plugin.c | 58 fwupd-2.0.20/plugins/intel-gsc/fu-igsc.rs | 254 fwupd-2.0.20/plugins/intel-gsc/igsc.quirk | 109 fwupd-2.0.20/plugins/intel-gsc/meson.build | 6 fwupd-2.0.20/plugins/intel-gsc/tests/intel-gsc.json | 44 fwupd-2.0.20/plugins/intel-mchi/README.md | 43 fwupd-2.0.20/plugins/intel-mchi/fu-intel-mchi-device.c | 159 fwupd-2.0.20/plugins/intel-mchi/fu-intel-mchi-device.h | 12 fwupd-2.0.20/plugins/intel-mchi/fu-intel-mchi-plugin.c | 36 fwupd-2.0.20/plugins/intel-mchi/fu-intel-mchi-plugin.h | 11 fwupd-2.0.20/plugins/intel-mchi/intel-mchi.quirk | 2 fwupd-2.0.20/plugins/intel-mchi/meson.build | 18 fwupd-2.0.20/plugins/intel-mchi/tests/intel-mchi-setup.json | 435 fwupd-2.0.20/plugins/intel-mchi/tests/intel-mchi.json | 16 fwupd-2.0.20/plugins/intel-me/README.md | 52 fwupd-2.0.20/plugins/intel-me/fu-intel-me-amt-device.c | 253 fwupd-2.0.20/plugins/intel-me/fu-intel-me-amt-device.h | 16 fwupd-2.0.20/plugins/intel-me/fu-intel-me-amt.rs | 83 fwupd-2.0.20/plugins/intel-me/fu-intel-me-common.c | 73 fwupd-2.0.20/plugins/intel-me/fu-intel-me-common.h | 14 fwupd-2.0.20/plugins/intel-me/fu-intel-me-heci-device.c | 160 fwupd-2.0.20/plugins/intel-me/fu-intel-me-heci-device.h | 31 fwupd-2.0.20/plugins/intel-me/fu-intel-me-mca-device.c | 133 fwupd-2.0.20/plugins/intel-me/fu-intel-me-mca-device.h | 18 fwupd-2.0.20/plugins/intel-me/fu-intel-me-mkhi-device.c | 90 fwupd-2.0.20/plugins/intel-me/fu-intel-me-mkhi-device.h | 18 fwupd-2.0.20/plugins/intel-me/fu-intel-me-mkhi.rs | 95 fwupd-2.0.20/plugins/intel-me/fu-intel-me-plugin.c | 42 fwupd-2.0.20/plugins/intel-me/fu-intel-me-plugin.h | 11 fwupd-2.0.20/plugins/intel-me/intel-me.quirk | 14 fwupd-2.0.20/plugins/intel-me/meson.build | 24 fwupd-2.0.20/plugins/intel-mkhi/README.md | 42 fwupd-2.0.20/plugins/intel-mkhi/fu-intel-mkhi-device.c | 107 fwupd-2.0.20/plugins/intel-mkhi/fu-intel-mkhi-device.h | 12 fwupd-2.0.20/plugins/intel-mkhi/fu-intel-mkhi-plugin.c | 36 fwupd-2.0.20/plugins/intel-mkhi/fu-intel-mkhi-plugin.h | 11 fwupd-2.0.20/plugins/intel-mkhi/intel-mkhi.quirk | 2 fwupd-2.0.20/plugins/intel-mkhi/meson.build | 16 fwupd-2.0.20/plugins/intel-usb4/fu-intel-usb4-device.c | 54 fwupd-2.0.20/plugins/intel-usb4/fu-intel-usb4-plugin.c | 1 fwupd-2.0.20/plugins/intel-usb4/fu-intel-usb4.rs | 10 fwupd-2.0.20/plugins/iommu/meson.build | 5 fwupd-2.0.20/plugins/jabra-file/README.md | 11 fwupd-2.0.20/plugins/jabra-file/fu-jabra-file-device.c | 111 fwupd-2.0.20/plugins/jabra-file/fu-jabra-file-firmware.c | 6 fwupd-2.0.20/plugins/jabra-file/fu-jabra-file-plugin.c | 2 fwupd-2.0.20/plugins/jabra-file/tests/jabra-panacast-50.json | 4 fwupd-2.0.20/plugins/jabra-gnp/README.md | 11 fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-child-device.c | 422 fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-child-device.h | 32 fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-common.c | 815 + fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-common.h | 102 fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-device.c | 882 - fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-device.h | 15 fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-firmware.c | 10 fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-firmware.h | 13 fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-image.c | 24 fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-plugin.c | 1 fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp.rs | 15 fwupd-2.0.20/plugins/jabra-gnp/jabra-gnp.quirk | 9 fwupd-2.0.20/plugins/jabra-gnp/meson.build | 2 fwupd-2.0.20/plugins/jabra-gnp/tests/jabra-evolve2-75.json | 9 fwupd-2.0.20/plugins/jabra/fu-jabra-device.c | 9 fwupd-2.0.20/plugins/jabra/fu-jabra-plugin.c | 1 fwupd-2.0.20/plugins/jabra/tests/jabra-speak-410.json | 4 fwupd-2.0.20/plugins/jabra/tests/jabra-speak-510.json | 4 fwupd-2.0.20/plugins/jabra/tests/jabra-speak-710.json | 4 fwupd-2.0.20/plugins/kinetic-dp/README.md | 7 fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-device.c | 14 fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-plugin.c | 2 fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-puma-device.c | 73 fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-puma-firmware.c | 34 fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-secure-device.c | 58 fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-secure-firmware.c | 12 fwupd-2.0.20/plugins/kinetic-dp/meson.build | 4 fwupd-2.0.20/plugins/legion-hid/README.md | 47 fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-child.c | 129 fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-child.h | 17 fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-device.c | 786 + fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-device.h | 22 fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-firmware.c | 107 fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-firmware.h | 20 fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-plugin.c | 40 fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-plugin.h | 11 fwupd-2.0.20/plugins/legion-hid/fu-legion-hid.rs | 120 fwupd-2.0.20/plugins/legion-hid/legion-hid.quirk | 11 fwupd-2.0.20/plugins/legion-hid/meson.build | 21 fwupd-2.0.20/plugins/legion-hid/tests/legion-hid.json | 30 fwupd-2.0.20/plugins/legion-hid2/README.md | 7 fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-bl-device.c | 81 fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-bl-device.h | 19 fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-device.c | 556 - fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-device.h | 6 fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-firmware.c | 70 fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-firmware.h | 3 fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-iap-device.c | 374 fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-iap-device.h | 16 fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-plugin.c | 8 fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-sipo-device.c | 81 fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-sipo-device.h | 19 fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2.rs | 41 fwupd-2.0.20/plugins/legion-hid2/legion-hid2.quirk | 14 fwupd-2.0.20/plugins/legion-hid2/meson.build | 3 fwupd-2.0.20/plugins/legion-hid2/tests/legion-hid2.json | 18 fwupd-2.0.20/plugins/lenovo-thinklmi/fu-self-test.c | 32 fwupd-2.0.20/plugins/lenovo-thinklmi/meson.build | 4 fwupd-2.0.20/plugins/linux-display/fu-linux-display-plugin.c | 5 fwupd-2.0.20/plugins/linux-display/meson.build | 4 fwupd-2.0.20/plugins/linux-lockdown/fu-linux-lockdown-plugin.c | 4 fwupd-2.0.20/plugins/linux-lockdown/meson.build | 5 fwupd-2.0.20/plugins/linux-sleep/fu-linux-sleep-plugin.c | 3 fwupd-2.0.20/plugins/linux-sleep/meson.build | 6 fwupd-2.0.20/plugins/linux-swap/fu-linux-swap-plugin.c | 4 fwupd-2.0.20/plugins/linux-swap/fu-linux-swap.c | 4 fwupd-2.0.20/plugins/linux-swap/meson.build | 6 fwupd-2.0.20/plugins/linux-tainted/fu-linux-tainted-plugin.c | 4 fwupd-2.0.20/plugins/linux-tainted/meson.build | 5 fwupd-2.0.20/plugins/logind/fu-logind-plugin.c | 2 fwupd-2.0.20/plugins/logind/meson.build | 4 fwupd-2.0.20/plugins/logitech-bulkcontroller/README.md | 7 fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-child.c | 17 fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-child.h | 2 fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.c | 188 fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.h | 15 fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.c | 281 fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-plugin.c | 1 fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller.rs | 2 fwupd-2.0.20/plugins/logitech-bulkcontroller/logitech-bulkcontroller.quirk | 5 fwupd-2.0.20/plugins/logitech-bulkcontroller/meson.build | 7 fwupd-2.0.20/plugins/logitech-bulkcontroller/tests/logi-rally-bar.json | 7 fwupd-2.0.20/plugins/logitech-bulkcontroller/tests/logi-sight-setup.json | 306 fwupd-2.0.20/plugins/logitech-bulkcontroller/tests/logi-sight.json | 17 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-nordic.c | 4 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-texas.c | 4 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader.c | 37 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-common.h | 6 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-device.c | 1094 +- fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-device.h | 4 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.c | 98 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.h | 4 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp.c | 21 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-plugin.c | 10 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-radio.c | 24 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-radio.h | 6 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-rdfu.rs | 165 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.c | 80 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.h | 4 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.c | 35 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.h | 4 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.c | 25 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.h | 4 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp.rs | 1 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-rdfu-firmware.c | 417 fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-rdfu-firmware.h | 29 fwupd-2.0.20/plugins/logitech-hidpp/logitech-hidpp.quirk | 8 fwupd-2.0.20/plugins/logitech-hidpp/meson.build | 7 fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-bolt-receiver.json | 4 fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-k780.json | 4 fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-m650.json | 4 fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-m750.json | 2 fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-mr0077.json | 4 fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-rqr12-signed.json | 4 fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-rqr12.json | 8 fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-rqr24-signed.json | 4 fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-rqr24.json | 8 fwupd-2.0.20/plugins/logitech-rallysystem/README.md | 7 fwupd-2.0.20/plugins/logitech-rallysystem/fu-logitech-rallysystem-audio-device.c | 18 fwupd-2.0.20/plugins/logitech-rallysystem/fu-logitech-rallysystem-tablehub-device.c | 67 fwupd-2.0.20/plugins/logitech-rallysystem/meson.build | 4 fwupd-2.0.20/plugins/logitech-scribe/README.md | 7 fwupd-2.0.20/plugins/logitech-scribe/fu-logitech-scribe-device.c | 25 fwupd-2.0.20/plugins/logitech-scribe/logitech-scribe.quirk | 2 fwupd-2.0.20/plugins/logitech-scribe/meson.build | 11 fwupd-2.0.20/plugins/logitech-scribe/tests/logi-scribe-setup.json | 94 fwupd-2.0.20/plugins/logitech-scribe/tests/logi-scribe.json | 20 fwupd-2.0.20/plugins/logitech-tap/README.md | 7 fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-hdmi-device.c | 8 fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-plugin.c | 8 fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-sensor-device.c | 114 fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-touch-device.c | 94 fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-touch-firmware.c | 11 fwupd-2.0.20/plugins/logitech-tap/meson.build | 4 fwupd-2.0.20/plugins/mediatek-scaler/README.md | 8 fwupd-2.0.20/plugins/mediatek-scaler/fu-mediatek-scaler-device.c | 168 fwupd-2.0.20/plugins/mediatek-scaler/fu-mediatek-scaler-firmware.c | 5 fwupd-2.0.20/plugins/mediatek-scaler/mediatek-scaler.quirk | 6 fwupd-2.0.20/plugins/mediatek-scaler/meson.build | 4 fwupd-2.0.20/plugins/meson.build | 50 fwupd-2.0.20/plugins/modem-manager/README.md | 26 fwupd-2.0.20/plugins/modem-manager/fu-cinterion-fdl-updater.c | 357 fwupd-2.0.20/plugins/modem-manager/fu-cinterion-fdl-updater.h | 31 fwupd-2.0.20/plugins/modem-manager/fu-cinterion-fdl-updater.rs | 13 fwupd-2.0.20/plugins/modem-manager/fu-dfota-updater.c | 404 fwupd-2.0.20/plugins/modem-manager/fu-dfota-updater.h | 29 fwupd-2.0.20/plugins/modem-manager/fu-firehose-updater.c | 925 - fwupd-2.0.20/plugins/modem-manager/fu-firehose-updater.h | 40 fwupd-2.0.20/plugins/modem-manager/fu-mbim-qdu-updater.c | 527 - fwupd-2.0.20/plugins/modem-manager/fu-mbim-qdu-updater.h | 32 fwupd-2.0.20/plugins/modem-manager/fu-mm-backend.c | 528 + fwupd-2.0.20/plugins/modem-manager/fu-mm-backend.h | 15 fwupd-2.0.20/plugins/modem-manager/fu-mm-common.c | 49 fwupd-2.0.20/plugins/modem-manager/fu-mm-common.h | 16 fwupd-2.0.20/plugins/modem-manager/fu-mm-device.c | 2608 +---- fwupd-2.0.20/plugins/modem-manager/fu-mm-device.h | 56 fwupd-2.0.20/plugins/modem-manager/fu-mm-dfota-device.c | 488 + fwupd-2.0.20/plugins/modem-manager/fu-mm-dfota-device.h | 13 fwupd-2.0.20/plugins/modem-manager/fu-mm-fastboot-device.c | 147 fwupd-2.0.20/plugins/modem-manager/fu-mm-fastboot-device.h | 16 fwupd-2.0.20/plugins/modem-manager/fu-mm-fdl-device.c | 370 fwupd-2.0.20/plugins/modem-manager/fu-mm-fdl-device.h | 13 fwupd-2.0.20/plugins/modem-manager/fu-mm-fdl.rs | 13 fwupd-2.0.20/plugins/modem-manager/fu-mm-firehose-device.c | 94 fwupd-2.0.20/plugins/modem-manager/fu-mm-firehose-device.h | 14 fwupd-2.0.20/plugins/modem-manager/fu-mm-mbim-device.c | 584 + fwupd-2.0.20/plugins/modem-manager/fu-mm-mbim-device.h | 27 fwupd-2.0.20/plugins/modem-manager/fu-mm-mhi-qcdm-device.c | 209 fwupd-2.0.20/plugins/modem-manager/fu-mm-mhi-qcdm-device.h | 16 fwupd-2.0.20/plugins/modem-manager/fu-mm-plugin.c | 538 - fwupd-2.0.20/plugins/modem-manager/fu-mm-qcdm-device.c | 134 fwupd-2.0.20/plugins/modem-manager/fu-mm-qcdm-device.h | 18 fwupd-2.0.20/plugins/modem-manager/fu-mm-qdu-mbim-device.c | 322 fwupd-2.0.20/plugins/modem-manager/fu-mm-qdu-mbim-device.h | 17 fwupd-2.0.20/plugins/modem-manager/fu-mm-qmi-device.c | 964 ++ fwupd-2.0.20/plugins/modem-manager/fu-mm-qmi-device.h | 13 fwupd-2.0.20/plugins/modem-manager/fu-qmi-pdc-updater.c | 773 - fwupd-2.0.20/plugins/modem-manager/fu-qmi-pdc-updater.h | 29 fwupd-2.0.20/plugins/modem-manager/fu-sahara-loader.c | 422 fwupd-2.0.20/plugins/modem-manager/fu-sahara-loader.h | 32 fwupd-2.0.20/plugins/modem-manager/fu-sahara.rs | 112 fwupd-2.0.20/plugins/modem-manager/fu-self-test.c | 109 fwupd-2.0.20/plugins/modem-manager/meson.build | 72 fwupd-2.0.20/plugins/modem-manager/modem-manager.quirk | 129 fwupd-2.0.20/plugins/modem-manager/tests/foxconn-t99w373-mbim-setup.json | 33 fwupd-2.0.20/plugins/modem-manager/tests/foxconn-t99w373-mbim.json | 18 fwupd-2.0.20/plugins/modem-manager/tests/lenovo-em061kgl-mbim.json | 19 fwupd-2.0.20/plugins/modem-manager/tests/qc-eg25ggc-fastboot-setup.json | 85 fwupd-2.0.20/plugins/modem-manager/tests/qc-eg25ggc-fastboot.json | 18 fwupd-2.0.20/plugins/msr/fu-msr-plugin.c | 16 fwupd-2.0.20/plugins/msr/meson.build | 12 fwupd-2.0.20/plugins/mtd/README.md | 36 fwupd-2.0.20/plugins/mtd/ci.quirk | 5 fwupd-2.0.20/plugins/mtd/fu-mtd-device.c | 816 + fwupd-2.0.20/plugins/mtd/fu-mtd-device.h | 12 fwupd-2.0.20/plugins/mtd/fu-mtd-ifd-device.c | 170 fwupd-2.0.20/plugins/mtd/fu-mtd-plugin.c | 2 fwupd-2.0.20/plugins/mtd/fu-self-test.c | 323 fwupd-2.0.20/plugins/mtd/meson.build | 19 fwupd-2.0.20/plugins/mtd/mtd.quirk | 15 fwupd-2.0.20/plugins/mtd/tests/.gitignore | 2 fwupd-2.0.20/plugins/mtd/tests/bnr-mtd.json | 18 fwupd-2.0.20/plugins/mtd/tests/build.sh | 4 fwupd-2.0.20/plugins/mtd/tests/mtd-fmap.builder.xml | 12 fwupd-2.0.20/plugins/mtd/tests/mtd-fwupd.conf | 4 fwupd-2.0.20/plugins/mtd/tests/mtd-ifd.builder.xml | 17 fwupd-2.0.20/plugins/mtd/tests/mtd-uswid.builder.xml | 6 fwupd-2.0.20/plugins/mtd/tests/mtdram.json | 18 fwupd-2.0.20/plugins/mtd/tests/org.fwupd.mtdram.metainfo.xml | 33 fwupd-2.0.20/plugins/mtd/tests/uswid.builder.xml | 8 fwupd-2.0.20/plugins/nordic-hid/README.md | 6 fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-archive.c | 14 fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-cfg-channel.c | 67 fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-firmware-b0.c | 13 fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-firmware-mcuboot.c | 13 fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-firmware.c | 2 fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid.rs | 13 fwupd-2.0.20/plugins/nordic-hid/meson.build | 5 fwupd-2.0.20/plugins/nordic-hid/nordic-hid.quirk | 8 fwupd-2.0.20/plugins/nordic-hid/tests/nordic-hid-nrf52840-b0.json | 4 fwupd-2.0.20/plugins/nordic-hid/tests/nordic-hid-nrf52840-mcuboot.json | 4 fwupd-2.0.20/plugins/nvme/README.md | 15 fwupd-2.0.20/plugins/nvme/fu-nvme-common.c | 140 fwupd-2.0.20/plugins/nvme/fu-nvme-common.h | 123 fwupd-2.0.20/plugins/nvme/fu-nvme-device.c | 213 fwupd-2.0.20/plugins/nvme/fu-nvme-device.h | 2 fwupd-2.0.20/plugins/nvme/fu-nvme-plugin.c | 1 fwupd-2.0.20/plugins/nvme/fu-nvme.rs | 163 fwupd-2.0.20/plugins/nvme/fu-self-test.c | 38 fwupd-2.0.20/plugins/nvme/meson.build | 8 fwupd-2.0.20/plugins/nvme/nvme.quirk | 15 fwupd-2.0.20/plugins/nvme/tests/lenovo-pcsn720.json | 4 fwupd-2.0.20/plugins/optionrom/README.md | 26 fwupd-2.0.20/plugins/optionrom/fu-optionrom-plugin.c | 68 fwupd-2.0.20/plugins/optionrom/fu-optionrom-plugin.h | 11 fwupd-2.0.20/plugins/optionrom/meson.build | 16 fwupd-2.0.20/plugins/optionrom/optionrom.quirk | 3 fwupd-2.0.20/plugins/parade-lspcon/README.md | 7 fwupd-2.0.20/plugins/parade-lspcon/fu-parade-lspcon-device.c | 8 fwupd-2.0.20/plugins/parade-lspcon/meson.build | 4 fwupd-2.0.20/plugins/parade-usbhub/README.md | 11 fwupd-2.0.20/plugins/parade-usbhub/fu-parade-usbhub-device.c | 42 fwupd-2.0.20/plugins/parade-usbhub/fu-parade-usbhub-firmware.c | 2 fwupd-2.0.20/plugins/parade-usbhub/fu-parade-usbhub-plugin.c | 1 fwupd-2.0.20/plugins/parade-usbhub/fu-parade-usbhub.rs | 2 fwupd-2.0.20/plugins/parade-usbhub/meson.build | 2 fwupd-2.0.20/plugins/parade-usbhub/parade-usbhub.quirk | 12 fwupd-2.0.20/plugins/parade-usbhub/tests/parade-ps5512evb.json | 8 fwupd-2.0.20/plugins/pci-bcr/fu-pci-bcr-plugin.c | 2 fwupd-2.0.20/plugins/pci-bcr/meson.build | 4 fwupd-2.0.20/plugins/pci-mei/fu-pci-mei-plugin.c | 10 fwupd-2.0.20/plugins/pci-mei/meson.build | 4 fwupd-2.0.20/plugins/pci-psp/README.md | 7 fwupd-2.0.20/plugins/pci-psp/fu-pci-psp-device.c | 106 fwupd-2.0.20/plugins/pci-psp/meson.build | 11 fwupd-2.0.20/plugins/pci-psp/tests/pci-psp-strix-enumerate.json | 17 fwupd-2.0.20/plugins/pci-psp/tests/pci-psp-strix.json | 112 fwupd-2.0.20/plugins/pixart-rf/README.md | 7 fwupd-2.0.20/plugins/pixart-rf/fu-pxi-ble-device.c | 128 fwupd-2.0.20/plugins/pixart-rf/fu-pxi-common.c | 56 fwupd-2.0.20/plugins/pixart-rf/fu-pxi-common.h | 16 fwupd-2.0.20/plugins/pixart-rf/fu-pxi-firmware.c | 16 fwupd-2.0.20/plugins/pixart-rf/fu-pxi-receiver-device.c | 72 fwupd-2.0.20/plugins/pixart-rf/fu-pxi-wireless-device.c | 126 fwupd-2.0.20/plugins/pixart-rf/fu-pxi-wireless-device.h | 2 fwupd-2.0.20/plugins/pixart-rf/fu-pxi.rs | 13 fwupd-2.0.20/plugins/pixart-rf/fu-self-test.c | 4 fwupd-2.0.20/plugins/pixart-rf/meson.build | 5 fwupd-2.0.20/plugins/pixart-rf/pixart-rf.quirk | 5 fwupd-2.0.20/plugins/pixart-rf/tests/hp-910-bt-keyboard.json | 2 fwupd-2.0.20/plugins/pixart-rf/tests/hp-910-bt-mouse.json | 2 fwupd-2.0.20/plugins/pixart-rf/tests/pixart-rf-2862-dongle.json | 8 fwupd-2.0.20/plugins/pixart-tp/README.md | 88 fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-device.c | 1499 +++ fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-device.h | 22 fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-firmware.c | 300 fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-firmware.h | 21 fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-haptic-device.c | 843 + fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-haptic-device.h | 20 fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-plugin.c | 50 fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-plugin.h | 12 fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-section.c | 245 fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-section.h | 32 fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp.rs | 327 fwupd-2.0.20/plugins/pixart-tp/fu-self-test.c | 58 fwupd-2.0.20/plugins/pixart-tp/meson.build | 57 fwupd-2.0.20/plugins/pixart-tp/pixart-tp.quirk | 14 fwupd-2.0.20/plugins/pixart-tp/tests/pixart-tp.builder.xml | 31 fwupd-2.0.20/plugins/pixart-tp/tests/pixart-tp.json | 18 fwupd-2.0.20/plugins/powerd/fu-powerd-plugin.c | 68 fwupd-2.0.20/plugins/qc-firehose/README.md | 7 fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-impl-common.c | 2 fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-impl.c | 78 fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-raw-device.c | 6 fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-sahara-impl.c | 10 fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-usb-device.c | 26 fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose.rs | 2 fwupd-2.0.20/plugins/qc-firehose/fu-self-test.c | 14 fwupd-2.0.20/plugins/qc-firehose/meson.build | 3 fwupd-2.0.20/plugins/qc-firehose/tests/qc-eg25ggc.json | 4 fwupd-2.0.20/plugins/qc-s5gen2/README.md | 7 fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-ble-device.c | 143 fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-device.c | 228 fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-firmware.c | 18 fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-hid-device.c | 54 fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-plugin.c | 4 fwupd-2.0.20/plugins/qc-s5gen2/meson.build | 10 fwupd-2.0.20/plugins/qc-s5gen2/qc-s5gen2.quirk | 4 fwupd-2.0.20/plugins/qc-s5gen2/tests/google-gid8.json | 18 fwupd-2.0.20/plugins/qc-s5gen2/tests/qualcomm-qcc5171.json | 2 fwupd-2.0.20/plugins/qc-s5gen2/tests/ugreen-15765a-setup.json | 287 fwupd-2.0.20/plugins/qc-s5gen2/tests/ugreen-15765a.json | 17 fwupd-2.0.20/plugins/qsi-dock/README.md | 7 fwupd-2.0.20/plugins/qsi-dock/fu-qsi-dock-child-device.c | 14 fwupd-2.0.20/plugins/qsi-dock/fu-qsi-dock-mcu-device.c | 19 fwupd-2.0.20/plugins/qsi-dock/fu-qsi-dock-plugin.c | 2 fwupd-2.0.20/plugins/realtek-mst/README.md | 12 fwupd-2.0.20/plugins/realtek-mst/fu-realtek-mst-device.c | 19 fwupd-2.0.20/plugins/realtek-mst/meson.build | 4 fwupd-2.0.20/plugins/redfish/fu-ipmi-device.c | 20 fwupd-2.0.20/plugins/redfish/fu-redfish-backend.c | 14 fwupd-2.0.20/plugins/redfish/fu-redfish-common.c | 28 fwupd-2.0.20/plugins/redfish/fu-redfish-device.c | 76 fwupd-2.0.20/plugins/redfish/fu-redfish-device.h | 2 fwupd-2.0.20/plugins/redfish/fu-redfish-hpe-device.c | 27 fwupd-2.0.20/plugins/redfish/fu-redfish-legacy-device.c | 23 fwupd-2.0.20/plugins/redfish/fu-redfish-multipart-device.c | 99 fwupd-2.0.20/plugins/redfish/fu-redfish-multipart-device.h | 4 fwupd-2.0.20/plugins/redfish/fu-redfish-plugin.c | 15 fwupd-2.0.20/plugins/redfish/fu-redfish-request.c | 9 fwupd-2.0.20/plugins/redfish/fu-redfish-request.h | 9 fwupd-2.0.20/plugins/redfish/fu-redfish-smbios.c | 18 fwupd-2.0.20/plugins/redfish/fu-redfish-smc-device.c | 23 fwupd-2.0.20/plugins/redfish/fu-redfish.rs | 7 fwupd-2.0.20/plugins/redfish/fu-self-test.c | 19 fwupd-2.0.20/plugins/redfish/meson.build | 8 fwupd-2.0.20/plugins/redfish/tests/fwupd.conf | 4 fwupd-2.0.20/plugins/redfish/tests/redfish-fwupd.conf | 4 fwupd-2.0.20/plugins/redfish/tests/redfish.py | 12 fwupd-2.0.20/plugins/robots.json | 9 fwupd-2.0.20/plugins/rp-pico/README.md | 7 fwupd-2.0.20/plugins/rp-pico/fu-rp-pico-device.c | 2 fwupd-2.0.20/plugins/rp-pico/fu-rp-pico-plugin.c | 2 fwupd-2.0.20/plugins/rp-pico/tests/mnt-pocket-reform-sysctl.json | 4 fwupd-2.0.20/plugins/rts54hid/README.md | 76 fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-common.h | 17 fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-device.c | 363 fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-device.h | 14 fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-module.c | 266 fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-module.h | 12 fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-plugin.c | 50 fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-plugin.h | 11 fwupd-2.0.20/plugins/rts54hid/fu-rts54hid.rs | 41 fwupd-2.0.20/plugins/rts54hid/meson.build | 16 fwupd-2.0.20/plugins/rts54hid/rts54hid.quirk | 18 fwupd-2.0.20/plugins/rts54hub/README.md | 11 fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-device.c | 152 fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-device.h | 26 fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-plugin.c | 14 fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-plugin.h | 2 fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-background.c | 76 fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-device.c | 147 fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-device.h | 34 fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-foreground.c | 103 fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-mergeinfo.c | 493 + fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-mergeinfo.h | 20 fwupd-2.0.20/plugins/rts54hub/fu-rts54hub.rs | 76 fwupd-2.0.20/plugins/rts54hub/meson.build | 5 fwupd-2.0.20/plugins/rts54hub/rts54hub.quirk | 87 fwupd-2.0.20/plugins/rts54hub/tests/realtek-br1u3aa.json | 18 fwupd-2.0.20/plugins/rts54hub/tests/realtek-rts5423.json | 8 fwupd-2.0.20/plugins/scsi/fu-scsi-device.c | 16 fwupd-2.0.20/plugins/scsi/meson.build | 4 fwupd-2.0.20/plugins/scsi/scsi.quirk | 2 fwupd-2.0.20/plugins/snapd-uefi/README.md | 16 fwupd-2.0.20/plugins/snapd-uefi/fu-self-test.c | 732 + fwupd-2.0.20/plugins/snapd-uefi/fu-snapd-uefi-plugin.c | 319 fwupd-2.0.20/plugins/snapd-uefi/fu-snapd-uefi-plugin.h | 13 fwupd-2.0.20/plugins/snapd-uefi/meson.build | 50 fwupd-2.0.20/plugins/snapd-uefi/tests/snapd.py | 272 fwupd-2.0.20/plugins/steelseries/README.md | 32 fwupd-2.0.20/plugins/steelseries/fu-steelseries-device.c | 79 fwupd-2.0.20/plugins/steelseries/fu-steelseries-device.h | 2 fwupd-2.0.20/plugins/steelseries/fu-steelseries-firmware.c | 6 fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-gen1.c | 10 fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-gen2.c | 167 fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-hid.c | 15 fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-hid.h | 2 fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-tunnel.c | 126 fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz.c | 84 fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz.h | 3 fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz.rs | 28 fwupd-2.0.20/plugins/steelseries/fu-steelseries-gamepad.c | 24 fwupd-2.0.20/plugins/steelseries/fu-steelseries-mouse.c | 7 fwupd-2.0.20/plugins/steelseries/fu-steelseries-plugin.c | 4 fwupd-2.0.20/plugins/steelseries/fu-steelseries-sonic.c | 49 fwupd-2.0.20/plugins/steelseries/meson.build | 1 fwupd-2.0.20/plugins/steelseries/steelseries.quirk | 67 fwupd-2.0.20/plugins/steelseries/tests/steelseries-aerox-3-wireless.json | 12 fwupd-2.0.20/plugins/steelseries/tests/steelseries-nova3p.json | 18 fwupd-2.0.20/plugins/steelseries/tests/steelseries-nova5.json | 10 fwupd-2.0.20/plugins/steelseries/tests/steelseries-rival-3-wireless.json | 4 fwupd-2.0.20/plugins/steelseries/tests/steelseries-stratus-duo-rx.json | 4 fwupd-2.0.20/plugins/steelseries/tests/steelseries-stratus-duo.json | 4 fwupd-2.0.20/plugins/synaptics-cape/README.md | 7 fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape-device.c | 210 fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape-hid-firmware.c | 32 fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape-plugin.c | 2 fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape-sngl-firmware.c | 14 fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape.rs | 3 fwupd-2.0.20/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c | 49 fwupd-2.0.20/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.c | 10 fwupd-2.0.20/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-plugin.c | 2 fwupd-2.0.20/plugins/synaptics-cxaudio/tests/lenovo-03x7609-cxaudio.json | 4 fwupd-2.0.20/plugins/synaptics-mst/README.md | 7 fwupd-2.0.20/plugins/synaptics-mst/fu-self-test.c | 5 fwupd-2.0.20/plugins/synaptics-mst/fu-synaptics-mst-common.c | 5 fwupd-2.0.20/plugins/synaptics-mst/fu-synaptics-mst-device.c | 153 fwupd-2.0.20/plugins/synaptics-mst/fu-synaptics-mst-firmware.c | 14 fwupd-2.0.20/plugins/synaptics-mst/meson.build | 5 fwupd-2.0.20/plugins/synaptics-prometheus/README.md | 7 fwupd-2.0.20/plugins/synaptics-prometheus/fu-self-test.c | 11 fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-common.c | 57 fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-config.c | 57 fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-device.c | 24 fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-device.h | 2 fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-firmware.c | 22 fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-plugin.c | 1 fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom.rs | 1 fwupd-2.0.20/plugins/synaptics-prometheus/meson.build | 1 fwupd-2.0.20/plugins/synaptics-prometheus/synaptics-prometheus.quirk | 4 fwupd-2.0.20/plugins/synaptics-prometheus/tests/synaptics-prometheus.json | 4 fwupd-2.0.20/plugins/synaptics-rmi/README.md | 8 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-common.c | 15 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-common.h | 2 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-device.c | 103 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-device.h | 21 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c | 256 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.c | 121 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-ps2-device.c | 118 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.c | 81 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.h | 4 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v6-device.c | 2 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.c | 456 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.h | 4 fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi.rs | 79 fwupd-2.0.20/plugins/synaptics-rmi/meson.build | 10 fwupd-2.0.20/plugins/synaptics-rmi/synaptics-rmi.quirk | 18 fwupd-2.0.20/plugins/synaptics-rmi/tests/synaptics-rmi-tm4066.json | 18 fwupd-2.0.20/plugins/synaptics-vmm9/README.md | 7 fwupd-2.0.20/plugins/synaptics-vmm9/fu-synaptics-vmm9-device.c | 79 fwupd-2.0.20/plugins/synaptics-vmm9/fu-synaptics-vmm9-firmware.c | 4 fwupd-2.0.20/plugins/synaptics-vmm9/fu-synaptics-vmm9-plugin.c | 2 fwupd-2.0.20/plugins/synaptics-vmm9/fu-synaptics-vmm9.rs | 14 fwupd-2.0.20/plugins/synaptics-vmm9/tests/synaptics-vmm9430evb.json | 8 fwupd-2.0.20/plugins/system76-launch/README.md | 7 fwupd-2.0.20/plugins/system76-launch/fu-system76-launch-device.c | 43 fwupd-2.0.20/plugins/system76-launch/fu-system76-launch-plugin.c | 1 fwupd-2.0.20/plugins/telink-dfu/README.md | 7 fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu-archive.c | 9 fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu-ble-device.c | 16 fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu-hid-device.c | 59 fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu-plugin.c | 1 fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu.rs | 4 fwupd-2.0.20/plugins/telink-dfu/tests/telink-ryder-dongle.json | 4 fwupd-2.0.20/plugins/test/fu-test-device.c | 69 fwupd-2.0.20/plugins/test/fu-test-device.h | 15 fwupd-2.0.20/plugins/test/fu-test-plugin.c | 56 fwupd-2.0.20/plugins/test/meson.build | 1 fwupd-2.0.20/plugins/thelio-io/README.md | 7 fwupd-2.0.20/plugins/thelio-io/fu-thelio-io-device.c | 27 fwupd-2.0.20/plugins/thelio-io/fu-thelio-io-plugin.c | 1 fwupd-2.0.20/plugins/thelio-io/meson.build | 4 fwupd-2.0.20/plugins/thunderbolt/README.md | 4 fwupd-2.0.20/plugins/thunderbolt/fu-self-test.c | 305 fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-common.c | 12 fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-controller.c | 47 fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-device.c | 146 fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-device.h | 2 fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-plugin.c | 10 fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-retimer.c | 14 fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-retimer.h | 4 fwupd-2.0.20/plugins/thunderbolt/meson.build | 6 fwupd-2.0.20/plugins/thunderbolt/tests/caldigit-ts4-tbt.json | 2 fwupd-2.0.20/plugins/thunderbolt/tests/lenovo-x280-tbt.json | 18 fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-common.h | 20 fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-device.c | 48 fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-firmware.c | 11 fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-pd-device.c | 31 fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-plugin.c | 1 fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x.rs | 21 fwupd-2.0.20/plugins/ti-tps6598x/tests/caldigit-ts4.json | 8 fwupd-2.0.20/plugins/tpm/README.md | 7 fwupd-2.0.20/plugins/tpm/fu-self-test.c | 71 fwupd-2.0.20/plugins/tpm/fu-tpm-device.c | 2 fwupd-2.0.20/plugins/tpm/fu-tpm-eventlog-common.c | 4 fwupd-2.0.20/plugins/tpm/fu-tpm-eventlog-parser.c | 64 fwupd-2.0.20/plugins/tpm/fu-tpm-eventlog-parser.h | 2 fwupd-2.0.20/plugins/tpm/fu-tpm-eventlog.c | 85 fwupd-2.0.20/plugins/tpm/fu-tpm-plugin.c | 57 fwupd-2.0.20/plugins/tpm/fu-tpm-v2-device.c | 96 fwupd-2.0.20/plugins/tpm/fu-tpm.rs | 65 fwupd-2.0.20/plugins/tpm/meson.build | 7 fwupd-2.0.20/plugins/tpm/tpm.quirk | 58 fwupd-2.0.20/plugins/uefi-capsule/README.md | 38 fwupd-2.0.20/plugins/uefi-capsule/fu-acpi-uefi.c | 10 fwupd-2.0.20/plugins/uefi-capsule/fu-bitmap-image.c | 8 fwupd-2.0.20/plugins/uefi-capsule/fu-self-test.c | 733 + fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-bgrt.c | 11 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-bootmgr.c | 40 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-bootmgr.h | 2 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-backend-freebsd.c | 20 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-backend-linux.c | 74 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-device.c | 139 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-device.h | 1 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-plugin.c | 211 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-plugin.h | 4 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-cod-device.c | 121 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-common.c | 17 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-common.h | 4 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-grub-device.c | 71 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-nvram-device.c | 9 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-update-info.c | 17 fwupd-2.0.20/plugins/uefi-capsule/fu-uefi.rs | 18 fwupd-2.0.20/plugins/uefi-capsule/fwupd.grub.conf.in | 18 fwupd-2.0.20/plugins/uefi-capsule/meson.build | 14 fwupd-2.0.20/plugins/uefi-capsule/tests/build-fwupdx64-efi-signed.py | 11 fwupd-2.0.20/plugins/uefi-capsule/tests/build-uefi-insyde.py | 35 fwupd-2.0.20/plugins/uefi-capsule/tests/efi-framebuffer/efi-framebuffer.0/height | 2 fwupd-2.0.20/plugins/uefi-capsule/tests/efi-framebuffer/efi-framebuffer.0/width | 2 fwupd-2.0.20/plugins/uefi-capsule/tests/example/build.sh | 2 fwupd-2.0.20/plugins/uefi-capsule/tests/grub2-mkconfig | 1 fwupd-2.0.20/plugins/uefi-capsule/tests/grub2-reboot | 1 fwupd-2.0.20/plugins/uefi-capsule/tests/grub2/grub.cfg | 1 fwupd-2.0.20/plugins/uefi-capsule/tests/meson.build | 29 fwupd-2.0.20/plugins/uefi-capsule/tests/test.quirk | 2 fwupd-2.0.20/plugins/uefi-capsule/tests/uefi-update-info.builder.xml | 7 fwupd-2.0.20/plugins/uefi-capsule/uefi-capsule.quirk | 56 fwupd-2.0.20/plugins/uefi-db/README.md | 7 fwupd-2.0.20/plugins/uefi-db/fu-uefi-db-device.c | 34 fwupd-2.0.20/plugins/uefi-db/fu-uefi-db-plugin.c | 1 fwupd-2.0.20/plugins/uefi-db/meson.build | 2 fwupd-2.0.20/plugins/uefi-dbx/README.md | 5 fwupd-2.0.20/plugins/uefi-dbx/fu-dbxtool.c | 19 fwupd-2.0.20/plugins/uefi-dbx/fu-self-test.c | 783 - fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-common.c | 11 fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-common.h | 2 fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-device.c | 146 fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-device.h | 5 fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-plugin.c | 71 fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-snapd-notifier.c | 236 fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-snapd-notifier.h | 30 fwupd-2.0.20/plugins/uefi-dbx/meson.build | 9 fwupd-2.0.20/plugins/uefi-dbx/tests/snapd.py | 272 fwupd-2.0.20/plugins/uefi-dbx/tests/uefi-dbx.json | 4 fwupd-2.0.20/plugins/uefi-dbx/uefi-dbx.quirk | 121 fwupd-2.0.20/plugins/uefi-esrt/fu-uefi-esrt-plugin.c | 4 fwupd-2.0.20/plugins/uefi-esrt/meson.build | 4 fwupd-2.0.20/plugins/uefi-kek/README.md | 7 fwupd-2.0.20/plugins/uefi-kek/fu-uefi-kek-device.c | 32 fwupd-2.0.20/plugins/uefi-kek/fu-uefi-kek-plugin.c | 1 fwupd-2.0.20/plugins/uefi-kek/meson.build | 2 fwupd-2.0.20/plugins/uefi-mok/README.md | 7 fwupd-2.0.20/plugins/uefi-mok/fu-uefi-mok-plugin.c | 3 fwupd-2.0.20/plugins/uefi-mok/meson.build | 3 fwupd-2.0.20/plugins/uefi-pk/fu-uefi-pk-device.c | 72 fwupd-2.0.20/plugins/uefi-pk/fu-uefi-pk-device.h | 3 fwupd-2.0.20/plugins/uefi-pk/fu-uefi-pk-plugin.c | 12 fwupd-2.0.20/plugins/uefi-pk/meson.build | 2 fwupd-2.0.20/plugins/uefi-pk/tests/uefi-pk.json | 2 fwupd-2.0.20/plugins/uefi-recovery/fu-uefi-recovery-plugin.c | 2 fwupd-2.0.20/plugins/uefi-recovery/meson.build | 4 fwupd-2.0.20/plugins/uefi-sbat/README.md | 9 fwupd-2.0.20/plugins/uefi-sbat/fu-uefi-sbat-device.c | 14 fwupd-2.0.20/plugins/uefi-sbat/fu-uefi-sbat-firmware.c | 4 fwupd-2.0.20/plugins/uefi-sbat/fu-uefi-sbat-plugin.c | 2 fwupd-2.0.20/plugins/uefi-sbat/meson.build | 2 fwupd-2.0.20/plugins/uf2/README.md | 11 fwupd-2.0.20/plugins/uf2/fu-self-test.c | 15 fwupd-2.0.20/plugins/uf2/fu-uf2-device.c | 28 fwupd-2.0.20/plugins/uf2/fu-uf2-firmware.c | 196 fwupd-2.0.20/plugins/uf2/fu-uf2.rs | 29 fwupd-2.0.20/plugins/uf2/meson.build | 5 fwupd-2.0.20/plugins/uf2/tests/uf2.builder.xml | 2 fwupd-2.0.20/plugins/upower/fu-upower-plugin.c | 66 fwupd-2.0.20/plugins/upower/meson.build | 5 fwupd-2.0.20/plugins/usi-dock/README.md | 7 fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-child-device.c | 15 fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-dmc-device.c | 14 fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-mcu-device.c | 229 fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-mcu-device.h | 6 fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-plugin.c | 60 fwupd-2.0.20/plugins/usi-dock/fu-usi-dock.rs | 4 fwupd-2.0.20/plugins/usi-dock/usi-dock.quirk | 5 fwupd-2.0.20/plugins/vbe/README.md | 9 fwupd-2.0.20/plugins/vbe/fu-vbe-device.c | 2 fwupd-2.0.20/plugins/vbe/fu-vbe-plugin.c | 10 fwupd-2.0.20/plugins/vbe/fu-vbe-simple-device.c | 17 fwupd-2.0.20/plugins/vendor-example/fu-vendor-example-device.c.in | 4 fwupd-2.0.20/plugins/vendor-example/fu-vendor-example-firmware.c.in | 3 fwupd-2.0.20/plugins/vendor-example/fu-vendor-example-plugin.c.in | 6 fwupd-2.0.20/plugins/vendor-example/meson.build.in | 5 fwupd-2.0.20/plugins/vendor-example/tests/vendor-example.json | 8 fwupd-2.0.20/plugins/vli/README.md | 7 fwupd-2.0.20/plugins/vli/fu-vli-device.c | 34 fwupd-2.0.20/plugins/vli/fu-vli-pd-common.h | 19 fwupd-2.0.20/plugins/vli/fu-vli-pd-device.c | 35 fwupd-2.0.20/plugins/vli/fu-vli-pd-device.h | 5 fwupd-2.0.20/plugins/vli/fu-vli-pd-firmware.c | 12 fwupd-2.0.20/plugins/vli/fu-vli-pd-parade-device.c | 58 fwupd-2.0.20/plugins/vli/fu-vli-plugin.c | 1 fwupd-2.0.20/plugins/vli/fu-vli-usbhub-common.h | 8 fwupd-2.0.20/plugins/vli/fu-vli-usbhub-device.c | 234 fwupd-2.0.20/plugins/vli/fu-vli-usbhub-firmware.c | 89 fwupd-2.0.20/plugins/vli/fu-vli-usbhub-i2c-common.c | 48 fwupd-2.0.20/plugins/vli/fu-vli-usbhub-msp430-device.c | 36 fwupd-2.0.20/plugins/vli/fu-vli-usbhub-pd-device.c | 65 fwupd-2.0.20/plugins/vli/fu-vli-usbhub-rtd21xx-device.c | 116 fwupd-2.0.20/plugins/vli/fu-vli.rs | 71 fwupd-2.0.20/plugins/vli/meson.build | 1 fwupd-2.0.20/plugins/vli/tests/bizlink-no-sku-vli.json | 4 fwupd-2.0.20/plugins/vli/tests/hyper-no-sku-vli.json | 4 fwupd-2.0.20/plugins/vli/tests/lenovo-03x7168.json | 4 fwupd-2.0.20/plugins/vli/tests/lenovo-03x7605.json | 4 fwupd-2.0.20/plugins/vli/tests/lenovo-03x7608-vli.json | 2 fwupd-2.0.20/plugins/vli/tests/lenovo-40au0065-vli.json | 4 fwupd-2.0.20/plugins/vli/tests/lenovo-GX90T33021-vli.json | 8 fwupd-2.0.20/plugins/wacom-raw/README.md | 7 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-aes-device.c | 291 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-aes-device.h | 12 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-common.c | 101 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-common.h | 27 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-device.c | 401 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-device.h | 50 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-emr-device.c | 303 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-emr-device.h | 12 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-aes-device.c | 306 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-aes-device.h | 16 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-common.c | 74 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-common.h | 25 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-device.c | 407 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-device.h | 52 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-emr-device.c | 308 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-emr-device.h | 16 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-plugin.c | 10 fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw.rs | 6 fwupd-2.0.20/plugins/wacom-raw/meson.build | 12 fwupd-2.0.20/plugins/wacom-raw/tests/wacom-g14t.json | 4 fwupd-2.0.20/plugins/wacom-raw/wacom-raw.quirk | 4 fwupd-2.0.20/plugins/wacom-usb/README.md | 7 fwupd-2.0.20/plugins/wacom-usb/fu-self-test.c | 2 fwupd-2.0.20/plugins/wacom-usb/fu-wac-android-device.c | 2 fwupd-2.0.20/plugins/wacom-usb/fu-wac-device.c | 57 fwupd-2.0.20/plugins/wacom-usb/fu-wac-firmware.c | 10 fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-bluetooth-id6.c | 8 fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-bluetooth-id9.c | 36 fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-bluetooth.c | 18 fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-scaler.c | 12 fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-sub-cpu.c | 26 fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-touch-id7.c | 36 fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-touch.c | 8 fwupd-2.0.20/plugins/wacom-usb/fu-wac-module.c | 59 fwupd-2.0.20/plugins/wacom-usb/fu-wac-plugin.c | 13 fwupd-2.0.20/plugins/wacom-usb/fu-wac.rs | 4 fwupd-2.0.20/plugins/wacom-usb/meson.build | 1 fwupd-2.0.20/plugins/wacom-usb/tests/wacom-intuos-bt-m.json | 6 fwupd-2.0.20/plugins/wacom-usb/tests/wacom-intuos-bt-s.json | 4 fwupd-2.0.20/plugins/wch-ch341a/README.md | 61 fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-cfi-device.c | 462 fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-cfi-device.h | 16 fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-device.c | 264 fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-device.h | 20 fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-plugin.c | 36 fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-plugin.h | 11 fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a.rs | 39 fwupd-2.0.20/plugins/wch-ch341a/lsusb.txt | 68 fwupd-2.0.20/plugins/wch-ch341a/meson.build | 18 fwupd-2.0.20/plugins/wch-ch341a/tests/wch-ch341a.json | 17 fwupd-2.0.20/plugins/wch-ch341a/wch-ch341a.quirk | 2 fwupd-2.0.20/plugins/wch-ch347/README.md | 40 fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-cfi-device.c | 56 fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-cfi-device.h | 16 fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-device.c | 317 fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-device.h | 23 fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-plugin.c | 36 fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-plugin.h | 11 fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347.rs | 19 fwupd-2.0.20/plugins/wch-ch347/meson.build | 19 fwupd-2.0.20/plugins/wch-ch347/tests/wch-ch347-setup.json | 124 fwupd-2.0.20/plugins/wch-ch347/tests/wch-ch347.json | 17 fwupd-2.0.20/plugins/wch-ch347/wch-ch347.quirk | 2 fwupd-2.0.20/plugins/wistron-dock/README.md | 7 fwupd-2.0.20/plugins/wistron-dock/fu-wistron-dock-common.h | 26 fwupd-2.0.20/plugins/wistron-dock/fu-wistron-dock-device.c | 50 fwupd-2.0.20/plugins/wistron-dock/fu-wistron-dock-plugin.c | 2 fwupd-2.0.20/plugins/wistron-dock/fu-wistron-dock.rs | 30 fwupd-2.0.20/plugins/wistron-dock/tests/wistron-dock-40b7.json | 8 fwupd-2.0.20/po/.gitignore | 1 fwupd-2.0.20/po/LINGUAS | 2 fwupd-2.0.20/po/af.po | 9 fwupd-2.0.20/po/ar.po | 3508 +++++++ fwupd-2.0.20/po/ast.po | 9 fwupd-2.0.20/po/bg.po | 3442 +++++++ fwupd-2.0.20/po/ca.po | 205 fwupd-2.0.20/po/cs.po | 167 fwupd-2.0.20/po/da.po | 29 fwupd-2.0.20/po/de.po | 130 fwupd-2.0.20/po/en_GB.po | 87 fwupd-2.0.20/po/eo.po | 9 fwupd-2.0.20/po/es.po | 143 fwupd-2.0.20/po/et.po | 4605 +++++++++ fwupd-2.0.20/po/eu.po | 17 fwupd-2.0.20/po/fi.po | 318 fwupd-2.0.20/po/fr.po | 142 fwupd-2.0.20/po/fur.po | 31 fwupd-2.0.20/po/fwupd.pot | 4664 ++++++++++ fwupd-2.0.20/po/gl.po | 24 fwupd-2.0.20/po/he.po | 33 fwupd-2.0.20/po/hi.po | 34 fwupd-2.0.20/po/hr.po | 689 + fwupd-2.0.20/po/hu.po | 30 fwupd-2.0.20/po/id.po | 37 fwupd-2.0.20/po/it.po | 33 fwupd-2.0.20/po/ja.po | 3434 +++++++ fwupd-2.0.20/po/ka.po | 16 fwupd-2.0.20/po/kk.po | 7 fwupd-2.0.20/po/ko.po | 51 fwupd-2.0.20/po/ky.po | 12 fwupd-2.0.20/po/lt.po | 30 fwupd-2.0.20/po/meson.build | 15 fwupd-2.0.20/po/nl.po | 39 fwupd-2.0.20/po/oc.po | 12 fwupd-2.0.20/po/pa.po | 422 fwupd-2.0.20/po/pl.po | 107 fwupd-2.0.20/po/pt.po | 29 fwupd-2.0.20/po/pt_BR.po | 149 fwupd-2.0.20/po/ro.po | 96 fwupd-2.0.20/po/ru.po | 93 fwupd-2.0.20/po/si.po | 29 fwupd-2.0.20/po/sk.po | 14 fwupd-2.0.20/po/sl.po | 203 fwupd-2.0.20/po/sr.po | 11 fwupd-2.0.20/po/sv.po | 96 fwupd-2.0.20/po/test-deps | 2 fwupd-2.0.20/po/tr.po | 33 fwupd-2.0.20/po/uk.po | 87 fwupd-2.0.20/po/zh_CN.po | 46 fwupd-2.0.20/po/zh_TW.po | 18 fwupd-2.0.20/policy/meson.build | 19 fwupd-2.0.20/policy/org.freedesktop.fwupd.policy.in | 12 fwupd-2.0.20/src/fu-cabinet.c | 137 fwupd-2.0.20/src/fu-cabinet.h | 5 fwupd-2.0.20/src/fu-client.c | 10 fwupd-2.0.20/src/fu-client.h | 4 fwupd-2.0.20/src/fu-console.c | 96 fwupd-2.0.20/src/fu-console.h | 4 fwupd-2.0.20/src/fu-daemon.c | 32 fwupd-2.0.20/src/fu-daemon.h | 5 fwupd-2.0.20/src/fu-daemon.rs | 10 fwupd-2.0.20/src/fu-dbus-daemon.c | 352 fwupd-2.0.20/src/fu-debug.c | 6 fwupd-2.0.20/src/fu-device-list.c | 61 fwupd-2.0.20/src/fu-engine-config.c | 33 fwupd-2.0.20/src/fu-engine-config.h | 6 fwupd-2.0.20/src/fu-engine-emulator.c | 63 fwupd-2.0.20/src/fu-engine-emulator.h | 5 fwupd-2.0.20/src/fu-engine-helper.c | 116 fwupd-2.0.20/src/fu-engine-helper.h | 3 fwupd-2.0.20/src/fu-engine-request.c | 10 fwupd-2.0.20/src/fu-engine-request.h | 5 fwupd-2.0.20/src/fu-engine-requirements.c | 579 - fwupd-2.0.20/src/fu-engine.c | 1313 +- fwupd-2.0.20/src/fu-engine.h | 45 fwupd-2.0.20/src/fu-engine.rs | 29 fwupd-2.0.20/src/fu-history.c | 19 fwupd-2.0.20/src/fu-main-windows.c | 7 fwupd-2.0.20/src/fu-main.c | 38 fwupd-2.0.20/src/fu-polkit-agent.c | 7 fwupd-2.0.20/src/fu-polkit-authority.c | 18 fwupd-2.0.20/src/fu-polkit-authority.h | 2 fwupd-2.0.20/src/fu-release.c | 172 fwupd-2.0.20/src/fu-release.h | 9 fwupd-2.0.20/src/fu-remote-list.c | 84 fwupd-2.0.20/src/fu-remote-list.h | 5 fwupd-2.0.20/src/fu-remote.c | 51 fwupd-2.0.20/src/fu-remote.h | 2 fwupd-2.0.20/src/fu-security-attr-common.c | 1 fwupd-2.0.20/src/fu-security-attr-common.h | 2 fwupd-2.0.20/src/fu-self-test.c | 1440 ++- fwupd-2.0.20/src/fu-systemd.c | 2 fwupd-2.0.20/src/fu-tool.c | 2566 +++-- fwupd-2.0.20/src/fu-udev-backend.c | 146 fwupd-2.0.20/src/fu-unix-seekable-input-stream.c | 6 fwupd-2.0.20/src/fu-usb-backend.c | 72 fwupd-2.0.20/src/fu-util-common.c | 67 fwupd-2.0.20/src/fu-util-common.h | 23 fwupd-2.0.20/src/fu-util.c | 2281 ++-- fwupd-2.0.20/src/fwupdmgr.md | 178 fwupd-2.0.20/src/meson.build | 335 fwupd-2.0.20/src/org.freedesktop.fwupd.xml | 58 fwupd-2.0.20/src/tests/auth/meson.build | 43 fwupd-2.0.20/src/tests/host-emulate/meson.build | 8 fwupd-2.0.20/src/tests/missing-hwid/meson.build | 24 fwupd-2.0.20/src/tests/multiple-rels/meson.build | 13 fwupd-2.0.20/src/tests/remotes2.d/lvfs-testing.conf | 1 fwupd-2.0.20/src/tests/remotes2.d/lvfs.conf | 1 fwupd-2.0.20/src/tests/remotes2.d/meson.build | 16 fwupd-2.0.20/src/tests/sys/class/mei/mei0/uevent | 4 fwupd-2.0.20/src/tests/sys/class/mei/mei0/uuid | 1 fwupd-2.0.20/src/tests/sys/class/mei/mei0/version | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/uevent | 4 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/uuid | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/version | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/class | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/device | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/dev | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/fw_status | 6 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/fw_ver | 3 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/kind | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/uevent | 4 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/uuid | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/version | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/uevent | 3 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/revision | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/class | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/device | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/revision | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/rom | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/subsystem_device | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/subsystem_vendor | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/uevent | 6 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/dev | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/uevent | 3 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/modalias | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/uevent | 6 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceClass | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceNumber | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceProtocol | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceSubClass | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/modalias | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/uevent | 6 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/dev | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/dev_debug | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/index | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/name | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/uevent | 3 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceClass | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceProtocol | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceSubClass | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bcdDevice | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/busnum | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/dev | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/devnum | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/idProduct | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/idVendor | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/manufacturer | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/product | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/removable | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/uevent | 9 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/version | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceClass | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceProtocol | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceSubClass | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bcdDevice | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/busnum | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/dev | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/devnum | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/devpath | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/idProduct | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/idVendor | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/manufacturer | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/dev | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/uevent | 3 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/modalias | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/uevent | 6 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceClass | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceNumber | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceProtocol | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceSubClass | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/modalias | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/uevent | 6 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/dev | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/dev_debug | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/index | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/name | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/uevent | 3 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceClass | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceProtocol | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceSubClass | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bcdDevice | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/busnum | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/dev | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/devnum | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/idProduct | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/idVendor | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/manufacturer | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/product | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/removable | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/uevent | 9 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/version | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/uevent | 9 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/version | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/vendor | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem_device | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem_vendor | 1 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/uevent | 6 fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/vendor | 1 fwupd-2.0.20/src/tests/sys/fs/selinux/enforce | 1 fwupd-2.0.20/subprojects/.gitignore | 1 fwupd-2.0.20/subprojects/flashrom.wrap | 2 1975 files changed, 95119 insertions(+), 41436 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpt_cj31ce/fwupd_2.0.8-3.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpt_cj31ce/fwupd_2.0.20-1~bpo13+1.dsc: no acceptable signature found diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/pci/devices/0000:00:14.0/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/pci/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/pci/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/pci/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/pci/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/pci/devices/0000:00:14.0/usb1/1-1/subsystem/devices/1-1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/pci/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/pci/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/pci/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/pci/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/pci/devices/0000:00:14.0/usb1/subsystem/devices/1-1/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/scsi/devices/17:0:3:0/block/sde/sde5/subsystem/nvme1/device/nvme/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/scsi/devices/17:0:3:0/block/sde/sde5/subsystem/nvme1/subsystem/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/scsi/devices/17:0:3:0/block/sde/sde5/subsystem/nvme1/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/scsi/devices/17:0:3:0/block/sde/sde5/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/scsi/devices/17:0:3:0/block/sde/sde5/subsystem/sde5: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/scsi/devices/17:0:3:0/block/sde/subsystem/nvme1/device/nvme/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/scsi/devices/17:0:3:0/block/sde/subsystem/nvme1/subsystem/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/scsi/devices/17:0:3:0/block/sde/subsystem/nvme1/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/scsi/devices/17:0:3:0/block/sde/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/scsi/devices/17:0:3:0/block/sde/subsystem/sde5/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/scsi/devices/17:0:3:0/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/serio/devices/serio1/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/usb/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/usb/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/usb/devices/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/usb/devices/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/bus/usb/devices/1-1/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/block/nvme1/device/nvme/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/block/nvme1/subsystem/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/block/nvme1/subsystem/sde/sde5/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/block/nvme1/subsystem/sde/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/block/sde/sde5/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/block/sde/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/block/sde5/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/hidraw/hidraw1/device/hidraw/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/hidraw/hidraw1/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/nvme/nvme1/device/nvme/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/nvme/nvme1/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/nvme/sde/sde5/subsystem/nvme1/device/nvme/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/nvme/sde/sde5/subsystem/nvme1/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/nvme/sde/sde5/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/nvme/sde/sde5/subsystem/sde5: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/nvme/sde/subsystem/nvme1/device/nvme/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/nvme/sde/subsystem/nvme1/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/nvme/sde/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/nvme/sde/subsystem/sde5/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/tpm/tpm0/device/tpm/tpm0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/tpm/tpm0/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/video4linux/video0/device/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/video4linux/video0/device/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/video4linux/video0/device/video4linux/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/class/video4linux/video0/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:14.0/subsystem/devices/0000:00:14.0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:14.0/usb1/1-1/subsystem/devices/1-1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:14.0/usb1/subsystem/devices/1-1/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/subsystem/devices/1-1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/0000:02:00.0/nvme/nvme1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/0000:02:00.0/nvme/nvme1/subsystem/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/0000:02:00.0/nvme/nvme1/subsystem/sde/sde5/subsystem/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/0000:02:00.0/nvme/nvme1/subsystem/sde/sde5/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/0000:02:00.0/nvme/nvme1/subsystem/sde/sde5/subsystem/sde5: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/0000:02:00.0/nvme/nvme1/subsystem/sde/subsystem/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/0000:02:00.0/nvme/nvme1/subsystem/sde/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/0000:02:00.0/nvme/nvme1/subsystem/sde/subsystem/sde5/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/subsystem/devices/0000:00:14.0/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/subsystem/devices/0000:00:14.0/usb1/1-1/subsystem/devices/1-1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:1b.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/17:0:3:0/block/sde/sde5/subsystem/nvme1/device/nvme/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/17:0:3:0/block/sde/sde5/subsystem/nvme1/subsystem/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/17:0:3:0/block/sde/sde5/subsystem/nvme1/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/17:0:3:0/block/sde/sde5/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/17:0:3:0/block/sde/sde5/subsystem/sde5: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/17:0:3:0/block/sde/subsystem/nvme1/device/nvme/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/17:0:3:0/block/sde/subsystem/nvme1/subsystem/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/17:0:3:0/block/sde/subsystem/nvme1/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/17:0:3:0/block/sde/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/17:0:3:0/block/sde/subsystem/sde5/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/17:0:3:0/subsystem/devices/17:0:3:0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/subsystem/devices/17:0:3:0/block/sde/sde5/subsystem/nvme1/device/nvme/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/subsystem/devices/17:0:3:0/block/sde/sde5/subsystem/nvme1/subsystem/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/subsystem/devices/17:0:3:0/block/sde/sde5/subsystem/nvme1/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/subsystem/devices/17:0:3:0/block/sde/sde5/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/subsystem/devices/17:0:3:0/block/sde/sde5/subsystem/sde5: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/subsystem/devices/17:0:3:0/block/sde/subsystem/nvme1/device/nvme/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/subsystem/devices/17:0:3:0/block/sde/subsystem/nvme1/subsystem/nvme1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/subsystem/devices/17:0:3:0/block/sde/subsystem/nvme1/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/subsystem/devices/17:0:3:0/block/sde/subsystem/sde: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/subsystem/devices/17:0:3:0/block/sde/subsystem/sde5/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/host17/port-17:3/end_device-17:3/target17:0:3/subsystem/devices/17:0:3:0/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/subsystem/devices/0000:00:14.0/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/subsystem/devices/0000:00:14.0/usb1/1-1/subsystem/devices/1-1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/0000:51:00.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/subsystem/devices/0000:00:14.0/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/subsystem/devices/0000:00:14.0/usb1/1-1/subsystem/devices/1-1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/subsystem/hidraw1: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/subsystem/video0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:50/0000:50:02.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/subsystem: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/platform/STM0125:00/tpm/tpm0/device: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/platform/STM0125:00/tpm/tpm0/subsystem/tpm0: recursive directory loop diff: /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/platform/i8042/serio1/subsystem/devices/serio1: recursive directory loop diff -Nru fwupd-2.0.8/.clang-format fwupd-2.0.20/.clang-format --- fwupd-2.0.8/.clang-format 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.clang-format 2026-02-26 11:36:18.000000000 +0000 @@ -33,19 +33,17 @@ UseTab: 'Always' PenaltyBreakAssignment: '3' PenaltyBreakBeforeFirstCallParameter: '15' ---- -Language: 'Proto' ---- -Language: 'Cpp' IncludeCategories: - Regex: '^"config.h"$' - Priority: '0' + Priority: 0 - Regex: '' - Priority: '1' + Priority: 1 - Regex: '^<' - Priority: '2' + Priority: 2 - Regex: 'fwupd' - Priority: '3' + Priority: 3 - Regex: '.*' - Priority: '4' + Priority: 4 +--- +Language: 'Proto' ... diff -Nru fwupd-2.0.8/.clang-tidy fwupd-2.0.20/.clang-tidy --- fwupd-2.0.8/.clang-tidy 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.clang-tidy 2026-02-26 11:36:18.000000000 +0000 @@ -2,6 +2,7 @@ Checks: "-*,\ bugprone-*,\ -bugprone-assignment-in-if-condition,\ +-bugprone-casting-through-void,\ -bugprone-easily-swappable-parameters,\ -bugprone-implicit-widening-of-multiplication-result,\ -bugprone-macro-parentheses,\ @@ -10,6 +11,8 @@ -bugprone-reserved-identifier,\ -bugprone-too-small-loop-variable,\ -bugprone-unchecked-optional-access,\ +-bugprone-multi-level-pointer-conversion,\ +-bugprone-multi-level-implicit-pointer-conversion,\ misc-*,\ -misc-confusable-identifiers,\ -misc-const-correctness,\ @@ -38,5 +41,6 @@ -readability-redundant-declaration,\ -readability-suspicious-call-argument,\ -readability-uppercase-literal-suffix,\ +-header-filter=.*\ " ... diff -Nru fwupd-2.0.8/.clangd fwupd-2.0.20/.clangd --- fwupd-2.0.8/.clangd 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/.clangd 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,5 @@ +CompileFlags: + CompilationDatabase: venv/build +Diagnostics: + Includes: + IgnoreHeader: config\.h diff -Nru fwupd-2.0.8/.editorconfig fwupd-2.0.20/.editorconfig --- fwupd-2.0.8/.editorconfig 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.editorconfig 2026-02-26 11:36:18.000000000 +0000 @@ -25,7 +25,7 @@ [*.py] indent_size = 4 -[*.sh] +[*.{sh,{pre,post}{inst,rm}}] indent_size = 4 [*.rs] diff -Nru fwupd-2.0.8/.github/CODEOWNERS fwupd-2.0.20/.github/CODEOWNERS --- fwupd-2.0.8/.github/CODEOWNERS 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/.github/CODEOWNERS 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,81 @@ +# This file defines code owners for fwupd repository +# Format: [path pattern] [space-separated list of owners] +# Order is important: last matching pattern takes precedence + +# Default owners for everything +* @superm1 @hughsie + +# Plugin-specific owners +plugins/algoltek-usb/ @MasonLyu +plugins/amd-gpu/ @superm1 +plugins/amd-kria/ @superm1 @michalsimek +plugins/amd-pmc/ @superm1 +plugins/analogix/ @xtcui +plugins/android-boot/ @DylanVanAssche +plugins/aver-hid/ @AVer-V001598 +plugins/bcm57xx/ @meklort +plugins/bnr-dp/ @tmuehlbacher-bnr +plugins/ccgx/ @IfxBrent +plugins/ccgx-dmc/ @IfxBrent +plugins/corsair/ @dushko-devx +plugins/cros-ec/ @bleungatchromium +plugins/dell/ @CragW +plugins/dell-dock/ @CragW +plugins/dell-kestrel/ @CragW +plugins/devlink/ @jpirko +plugins/egis-moc/ @jasonhouang +plugins/elan-kbd/ @hughsie +plugins/elanfp/ @MichaelCheng04 +plugins/elantp/ @jinglewu @josh-chen-elan +plugins/focalfp/ @waynehuang2022 +plugins/fpc/ @jimzhang2 +plugins/framework-qmk/ @JohnAZoidberg +plugins/genesys/ @adamgene +plugins/goodix-moc/ @boger047 +plugins/goodix-tp/ @xulinkun +plugins/huddly-usb/ @LarsStensen +plugins/hughski-colorhug/ @hughsie +plugins/ilitek-its/ @ILITEK-JoeHung +plugins/intel-cvs/ @hughsie +plugins/intel-gsc/ @Fscarbr +plugins/jabra-file/ @gdpcastro +plugins/jabra-gnp/ @gdpcastro +plugins/kinetic-dp/ @FrancisLeeKinetic +plugins/legion-hid/ @gzmarshall +plugins/logitech-bulkcontroller/ @vcdmp +plugins/logitech-rallysystem/ @vcdmp +plugins/logitech-scribe/ @vcdmp +plugins/logitech-tap/ @vcdmp +plugins/mediatek-scaler/ @CragW @GregLo007 +plugins/modem-manager/ @aleksander0m +plugins/nordic-hid/ @MarekPieta +plugins/parade-lspcon/ @Pingyao5115 +plugins/parade-usbhub/ @hublin2024 @jimmytu5167 @andychu5168 +plugins/pci-psp/ @superm1 +plugins/pixart-rf/ @sam412081go +plugins/pixart-tp/ @PixartHarris +plugins/qc-firehose/ @hughsie +plugins/qc-s5gen2/ @d4s +plugins/qsi-dock/ @hssinf +plugins/realtek-mst/ @tari +plugins/rp-pico/ @mntmn +plugins/rts54hub/ @AnyProblem +plugins/snapd-uefi/ @BAMF0 @bboozzoo +plugins/synaptics-cape/ @CNXT-Simon +plugins/synaptics-mst/ @ApolloLing +plugins/synaptics-prometheus/ @synavincent +plugins/synaptics-rmi/ @blueue @vhuag +plugins/synaptics-vmm9/ @ApolloLing +plugins/system76-launch/ @jackpot51 +plugins/telink-dfu/ @liangjiazhi-telink +plugins/thelio-io/ @jackpot51 +plugins/uefi-db/ @hughsie +plugins/uefi-kek/ @hughsie +plugins/uefi-mok/ @hughsie +plugins/uefi-sbat/ @vathpela +plugins/usi-dock/ @victor-cheng +plugins/vbe/ @sjg20 +plugins/vli/ @memily +plugins/wacom-raw/ @flying-elephant +plugins/wacom-usb/ @jigpu +plugins/wistron-dock/ @a999153 diff -Nru fwupd-2.0.8/.github/ISSUE_TEMPLATE/bug-report-general.md fwupd-2.0.20/.github/ISSUE_TEMPLATE/bug-report-general.md --- fwupd-2.0.8/.github/ISSUE_TEMPLATE/bug-report-general.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.github/ISSUE_TEMPLATE/bug-report-general.md 2026-02-26 11:36:18.000000000 +0000 @@ -21,6 +21,7 @@ ```shell fwupdtool get-report-metadata +fwupdtool hwids ``` Please note how you installed it (`apt`, `dnf`, `pacman`, source, etc): diff -Nru fwupd-2.0.8/.github/ISSUE_TEMPLATE/bug-report-uefi.md fwupd-2.0.20/.github/ISSUE_TEMPLATE/bug-report-uefi.md --- fwupd-2.0.8/.github/ISSUE_TEMPLATE/bug-report-uefi.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.github/ISSUE_TEMPLATE/bug-report-uefi.md 2026-02-26 11:36:18.000000000 +0000 @@ -21,6 +21,7 @@ ```shell fwupdtool get-report-metadata +fwupdtool hwids ``` Please note how you installed it (`apt`, `dnf`, `pacman`, source, etc): diff -Nru fwupd-2.0.8/.github/copilot-instructions.md fwupd-2.0.20/.github/copilot-instructions.md --- fwupd-2.0.8/.github/copilot-instructions.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/.github/copilot-instructions.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,230 @@ +# fwupd Firmware Update Daemon + +Always reference these instructions first and fallback to search or bash commands only when you encounter unexpected information that does not match the info here. + +fwupd is a Linux firmware update daemon built with Meson, written in C with GLib, and includes 130+ plugins for various hardware vendors. It provides both a daemon (`fwupd`) and command-line tools (`fwupdmgr`, `fwupdtool`) for firmware management. + +## Working Effectively + +### CRITICAL: Setup and Build Process +- **CONTAINER REQUIRED**: Use pre-built container `ghcr.io/fwupd/fwupd/fwupd-debian-x86_64:latest` for consistent development environment +- Setup script: `./contrib/ci/fwupd_setup_helpers.py install-dependencies -o debian --yes` (inside container) -- takes 30+ minutes for dependency installation. NEVER CANCEL. +- Build command: `build-fwupd` (in venv) -- takes 45+ minutes. NEVER CANCEL. Set timeout to 60+ minutes. +- Test command: `test-fwupd` (in venv) -- takes 15+ minutes. NEVER CANCEL. Set timeout to 30+ minutes. + +### Bootstrap the Development Environment + +**Container-based development (REQUIRED for automation)** +```bash +# Use the pre-built fwupd container for consistent Debian environment +# Container: https://github.com/fwupd/fwupd/pkgs/container/fwupd%2Ffwupd-debian-x86_64 +docker pull ghcr.io/fwupd/fwupd/fwupd-debian-x86_64:latest + +# Run container with source mounted +docker run -it --privileged -v $(pwd):/workspace ghcr.io/fwupd/fwupd/fwupd-debian-x86_64:latest + +# Inside container - install dependencies first +cd /workspace +./contrib/ci/fwupd_setup_helpers.py install-dependencies -o debian --yes + +# Create virtual environment +python3 -m venv venv --system-site-packages --prompt fwupd +source venv/bin/activate + +# Setup development wrappers +BASE=../contrib/ +BIN=venv/bin/ +TEMPLATE=${BASE}/launch-venv.sh +for F in fwupdtool fwupdmgr fwupd; do + rm -f ${BIN}/${F} + ln -s $TEMPLATE ${BIN}/${F} +done +rm -f ${BIN}/build-fwupd +ln -s ${BASE}/build-venv.sh ${BIN}/build-fwupd +rm -f ${BIN}/test-fwupd +ln -s ${BASE}/test-venv.sh ${BIN}/test-fwupd + +# Build the project - takes 45+ minutes, NEVER CANCEL, set timeout 60+ minutes +build-fwupd + +# Run tests - takes 15+ minutes, NEVER CANCEL, set timeout 30+ minutes +test-fwupd +``` +## Running Applications + +### Development Environment Tools +All tools run with elevated privileges automatically in the venv: +- `fwupdtool` - Debugging tool for developers, runs standalone without daemon +- `fwupdmgr` - End-user client tool, requires daemon running +- `fwupd` - Background daemon, run manually in development + +### Basic Usage Examples +```bash +# Get devices from specific plugin (fastest for development) +fwupdtool --plugins vli get-devices --verbose + +# Get all devices (comprehensive) +fwupdtool get-devices + +# Parse firmware blob +fwupdtool firmware-parse /path/to/firmware.bin + +# Install firmware blob to device +fwupdtool --verbose --plugins PLUGIN install-blob /path/to/firmware.bin DEVICE_ID +``` + +### End-to-End Testing (Two Terminals Required) +Terminal 1 - Run daemon: +```bash +source venv/bin/activate +fwupd --verbose +``` + +Terminal 2 - Run client: +```bash +source venv/bin/activate +fwupdmgr install ~/firmware.cab +``` + +## Validation + +### ALWAYS run these validation steps after making changes: +```bash +# Format code +./contrib/reformat-code.py + +# Run linting +pre-commit run --all-files + +# Build and test (with proper timeouts) +build-fwupd # 45+ minutes, NEVER CANCEL +test-fwupd # 15+ minutes, NEVER CANCEL +``` + +### Manual Testing Scenarios +Always test at least one complete end-to-end scenario: +1. **Plugin Development**: Test with `fwupdtool --plugins YOURPLUGIN get-devices --verbose` +2. **Daemon Changes**: Run daemon in terminal 1, use fwupdmgr in terminal 2 +3. **Firmware Installation**: Use `fwupdtool install-blob` for quick testing + +## Repository Structure + +### Key Directories +- `src/` - Main daemon and tool source code +- `libfwupd/` - Client library source +- `libfwupdplugin/` - Plugin framework library +- `plugins/` - 130+ hardware vendor plugins +- `contrib/` - Build scripts and CI helpers +- `data/` - Configuration files and resources +- `docs/` - Documentation and building guides + +### Important Files +- `meson.build` - Main build configuration +- `meson_options.txt` - Build options (117 configurable options) +- `contrib/setup` - Development environment setup script +- `contrib/ci/fwupd_setup_helpers.py` - Dependency installation helper +- `.github/workflows/matrix.yml` - CI build matrix (Fedora, Debian, Arch, etc.) + +### Build Dependencies (Ubuntu) +Over 80 packages including: meson, libglib2.0-dev, libxmlb-dev, libjcat-dev, libarchive-dev, libcbor-dev, libcurl4-gnutls-dev, valgrind, clang-tools, python3-gi-cairo, and many more. + +## Common Tasks + +### Plugin Development +```bash +# Test specific plugin only +fwupdtool --plugins YOUR_PLUGIN get-devices --verbose + +# Parse plugin-specific firmware +fwupdtool firmware-parse firmware.bin +# Choose the appropriate parser from the list + +# Install plugin firmware +fwupdtool --verbose --plugins YOUR_PLUGIN install-blob firmware.bin DEVICE_ID +``` + +### Code Quality +```bash +# Auto-format current patch +./contrib/reformat-code.py + +# Format specific commits +./contrib/reformat-code.py HEAD~5 + +# Run all pre-commit hooks +pre-commit run --all-files + +# Check specific files +shellcheck contrib/ci/*.sh +``` + +### CI Validation (runs on multiple platforms) +The CI tests on: Fedora, CentOS, Debian x86_64, Debian i386, Ubuntu x86_64, Arch Linux +- All builds include AddressSanitizer and -Werror +- Full test suite with LVFS integration testing +- Package installation and removal testing + +## Debugging with Visual Studio Code + +### Available Tasks +- Build task: `Ctrl+Shift+B` - runs `build-fwupd` +- Test task: `Ctrl+Shift+P` -> "Run test task" +- Debug task: `gdbserver-fwupd` for daemon debugging + +### Debugging Tools +```bash +# Debug any tool with gdbserver +DEBUG=1 fwupdtool get-devices +DEBUG=1 fwupdmgr get-devices +``` + +## Build Options + +### Common Meson Options +- `-Dbuild=all|standalone|library` - What to build +- `-Dtests=true|false` - Enable/disable tests +- `-Dplugin_*=enabled|disabled|auto` - Individual plugin control +- `-Dsystemd=enabled|disabled|auto` - systemd integration +- `-Dintrospection=enabled|disabled|auto` - GObject introspection + +### Quick Build Variants +```bash +# Library only (faster) +meson setup build -Dbuild=library + +# Without tests (faster) +meson setup build -Dtests=false + +# Specific plugins only +meson setup build -Dplugin_uefi_capsule=enabled # enable specific plugin +``` + +## Time Expectations +- **Container setup**: 5-10 minutes (container pull and start) +- **Dependency installation**: 30+ minutes (NEVER CANCEL) - inside container +- **Full build**: 45-60 minutes (NEVER CANCEL) +- **Test suite**: 15-20 minutes (NEVER CANCEL) +- **Individual plugin test**: 1-5 minutes +- **Code formatting**: 1-2 minutes + +## Troubleshooting + +### Build Issues +- **CONTAINER REQUIRED**: Use `ghcr.io/fwupd/fwupd/fwupd-debian-x86_64:latest` for consistent Debian environment +- **Dependency Installation**: Run `./contrib/ci/fwupd_setup_helpers.py install-dependencies -o debian --yes` inside container +- **Git Ownership**: If you see "detected dubious ownership", run `git config --global --add safe.directory /workspace` inside container +- Check Meson version: `./contrib/ci/fwupd_setup_helpers.py test-meson` +- Verify venv setup: `source venv/bin/activate` should show `(fwupd)` prompt +- **Python venv**: Use `python3 -m venv venv --system-site-packages` after dependency installation + +### Runtime Issues +- For daemon testing: ensure polkit/dbus running +- For plugin testing: use `fwupdtool` instead of full daemon +- For firmware parsing: check plugin supports your hardware format + +### Common Validation Failures +- **Pre-commit failures**: Run `./contrib/reformat-code.py` first +- **Test failures**: Check if related to your changes or pre-existing +- **Build failures**: Verify dependencies and increase timeout + + diff -Nru fwupd-2.0.8/.github/workflows/ci.yml fwupd-2.0.20/.github/workflows/ci.yml --- fwupd-2.0.8/.github/workflows/ci.yml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.github/workflows/ci.yml 2026-02-26 11:36:18.000000000 +0000 @@ -1,13 +1,12 @@ name: Continuous Integration on: push: - branches: [ main ] - -permissions: - contents: read + branches: [ 2_0_X ] jobs: snap: + permissions: + contents: read uses: ./.github/workflows/snap.yml with: deploy: true @@ -40,7 +39,7 @@ fuzz-seconds: 150 dry-run: false - name: Upload Crash - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 if: failure() && steps.build.outcome == 'success' with: name: artifacts diff -Nru fwupd-2.0.8/.github/workflows/codeql-analysis.yml fwupd-2.0.20/.github/workflows/codeql-analysis.yml --- fwupd-2.0.8/.github/workflows/codeql-analysis.yml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.github/workflows/codeql-analysis.yml 2026-02-26 11:36:18.000000000 +0000 @@ -2,9 +2,9 @@ on: push: - branches: [ main ] + branches: [ 2_0_X ] pull_request: - branches: [ main ] + branches: [ 2_0_X ] permissions: contents: read @@ -25,10 +25,10 @@ steps: - name: Checkout repository - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Initialize CodeQL - uses: github/codeql-action/init@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15 + uses: github/codeql-action/init@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v3.29.5 with: languages: ${{ matrix.language }} @@ -47,4 +47,4 @@ ninja - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15 + uses: github/codeql-action/analyze@cf1bb45a277cb3c205638b2cd5c984db1c46a412 # v3.29.5 diff -Nru fwupd-2.0.8/.github/workflows/create_containers.yml fwupd-2.0.20/.github/workflows/create_containers.yml --- fwupd-2.0.8/.github/workflows/create_containers.yml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.github/workflows/create_containers.yml 2026-02-26 11:36:18.000000000 +0000 @@ -15,26 +15,26 @@ strategy: fail-fast: false matrix: - os: [precommit, fedora, debian-x86_64, arch, debian-i386, ubuntu-x86_64] + os: [precommit, fedora, debian-x86_64, arch, debian-i386, ubuntu-x86_64, centos] steps: - name: Check out the repo - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: "Generate Dockerfile" env: OS: ${{ matrix.os }} run: ./contrib/ci/generate_docker.py - name: Set up Docker Buildx - uses: docker/setup-buildx-action@b5ca514318bd6ebac0fb2aedd5d36ec1b5c232a2 # v3.10.0 + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1 - name: Login to GitHub Container Registry - uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0 + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3.6.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Push to GitHub Packages - uses: docker/build-push-action@471d1dc4e07e5cdedd4c2171150001c434f0b7a4 # v6.15.0 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0 with: context: . push: true diff -Nru fwupd-2.0.8/.github/workflows/dependency-review.yml fwupd-2.0.20/.github/workflows/dependency-review.yml --- fwupd-2.0.8/.github/workflows/dependency-review.yml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.github/workflows/dependency-review.yml 2026-02-26 11:36:18.000000000 +0000 @@ -17,11 +17,11 @@ runs-on: ubuntu-latest steps: - name: Harden Runner - uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1 + uses: step-security/harden-runner@df199fb7be9f65074067a9eb93f12bb4c5547cf2 # v2.13.3 with: egress-policy: audit - name: 'Checkout Repository' - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: 'Dependency Review' - uses: actions/dependency-review-action@ce3cf9537a52e8119d91fd484ab5b8a807627bf8 # v4.6.0 + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 diff -Nru fwupd-2.0.8/.github/workflows/matrix.yml fwupd-2.0.20/.github/workflows/matrix.yml --- fwupd-2.0.8/.github/workflows/matrix.yml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.github/workflows/matrix.yml 2026-02-26 11:36:18.000000000 +0000 @@ -5,26 +5,25 @@ required: true type: boolean -permissions: - contents: read - jobs: build: + permissions: + contents: read runs-on: ubuntu-latest needs: library strategy: fail-fast: false matrix: - os: [fedora, debian-x86_64, arch, debian-i386, ubuntu-x86_64] + os: [fedora, centos, debian-x86_64, arch, debian-i386, ubuntu-x86_64] steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Docker login - run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p $GITHUB_TOKEN + run: docker login ghcr.io -u $GITHUB_ACTOR -p $GITHUB_TOKEN env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Download tarball - if: matrix.os == 'fedora' - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + if: matrix.os == 'fedora' || matrix.os == 'centos' + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 id: download with: name: tarball @@ -36,24 +35,33 @@ run: | docker run --privileged -e CI_NETWORK=$CI_NETWORK -e CI=$CI -e VERSION=$VERSION -t \ -v $GITHUB_WORKSPACE:/github/workspace \ - docker.pkg.github.com/fwupd/fwupd/fwupd-${{matrix.os}}:latest + ghcr.io/fwupd/fwupd/fwupd-${{matrix.os}}:latest - name: Save any applicable artifacts - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: ${{ matrix.os }} path: ${{ github.workspace }}/dist/* if-no-files-found: ignore + - name: Check if tests supported + id: supported + run: | + if [ -f contrib/ci/${{matrix.os}}-test.sh ]; then + echo "supported=true" >> $GITHUB_OUTPUT + else + echo "supported=false" >> $GITHUB_OUTPUT + fi - name: Test in container + if: steps.supported.outputs.supported == 'true' env: CI_NETWORK: true CI: true run: | docker run --privileged -e CI_NETWORK=$CI_NETWORK -e CI=$CI -t \ -v $GITHUB_WORKSPACE:/github/workspace \ - docker.pkg.github.com/fwupd/fwupd/fwupd-${{matrix.os}}:latest \ + ghcr.io/fwupd/fwupd/fwupd-${{matrix.os}}:latest \ contrib/ci/${{matrix.os}}-test.sh - name: Save any coverage data - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: coverage-${{ join(matrix.*, '-') }} path: ${{ github.workspace }}/coverage.xml @@ -64,10 +72,12 @@ CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} openbmc: + permissions: + contents: read runs-on: ubuntu-22.04 if: ${{ !inputs.publish }} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Refresh dependencies run: | sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list @@ -81,11 +91,13 @@ ./contrib/build-openbmc.sh --prefix=/home/runner/.root library: + permissions: + contents: read runs-on: ubuntu-latest outputs: version: ${{ steps.version.outputs.version }} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Refresh dependencies run: | sudo rm -f /etc/apt/sources.list.d/microsoft-prod.list @@ -108,101 +120,89 @@ run: | echo "version=$(meson introspect build --projectinfo | jq -r .version)" >> $GITHUB_OUTPUT - name: Save tarball - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: tarball path: ${{ github.workspace }}/build/meson-dist/*xz macos: + permissions: + contents: read runs-on: macos-latest if: ${{ !inputs.publish }} steps: - name: install dependencies run: | - brew uninstall --ignore-dependencies --force pkg-config@0.29.2 brew install meson usb.ids gobject-introspection libarchive json-glib protobuf-c vala gi-docgen python3 -m pip install --user jinja2 --break-system-packages - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: configure run: ./contrib/ci/build_macos.sh - name: build run: ninja -C build-macos build-windows: + permissions: + contents: read runs-on: ubuntu-latest container: - image: fedora:latest + image: fedora:42 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: configure run: ./contrib/ci/build_windows.sh - name: upload-artifact - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: windows path: | ${{ github.workspace }}/dist/setup/*.msi - ${{ github.workspace }}/dist/news.txt - publish-docs: - name: Publish docs - if: ${{ inputs.publish }} + build-freebsd: + permissions: + contents: read runs-on: ubuntu-latest - needs: build + if: ${{ !inputs.publish }} steps: - - uses: actions/checkout@v4 - - name: Download artifact - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 - id: download - with: - name: ubuntu-x86_64 - - name: Install SSH key - uses: shimataro/ssh-key-action@v2 - with: - key: ${{ secrets.FWUPD_GITHUB_IO_SSH_KEY }} - name: id_rsa # optional - known_hosts: unnecessary - if_key_exists: fail # replace / ignore / fail; optional (defaults to fail) - - name: Clone docs - run: | - cd share/doc/fwupd - git clone --depth 1 git@github.com:fwupd/fwupd.github.io.git - - name: Trigger docs deployment - run: | - cd share/doc/fwupd/fwupd.github.io - git config credential.helper 'cache --timeout=120' - git config user.email "info@fwupd.org" - git config user.name "Documentation deployment Bot" - rm -rf * - cp ../../libfwupd* ../*html . -R - git add . - git commit -a --allow-empty -m "Trigger deployment" - git push git@github.com:fwupd/fwupd.github.io.git + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + - name: Build + id: test + uses: vmactions/freebsd-vm@v1 + env: + CI: true + CC: gcc + with: + usesh: true + mem: 8192 + sync: rsync + envs: 'CI CC' + prepare: | + pkg install -y bash python3 git cmake gcc + run: | + ./contrib/ci/fwupd_setup_helpers.py install-dependencies -o freebsd --yes + ./contrib/setup + bash -c "source ./venv/bin/activate && build-fwupd" publish-windows: + permissions: + contents: write name: Publish Windows binaries runs-on: ubuntu-latest if: ${{ inputs.publish }} needs: build-windows steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 + with: + fetch-depth: 0 - name: Download artifact - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 id: download with: name: windows - - name: Populate release body - id: read_release - shell: bash - run: | - r=$(cat dist/news.txt) - echo "RELEASE_BODY=$r" >> $GITHUB_OUTPUT - name: Upload Binaries to Release uses: svenstaro/upload-release-action@v2 with: repo_token: ${{ secrets.GITHUB_TOKEN }} tag: ${{ github.ref }} file_glob: true - file: dist/setup/*.msi - body: | - ${{ steps.read_release.outputs.RELEASE_BODY }} # <--- Use environment variables that was created earlier + file: fwupd*.msi diff -Nru fwupd-2.0.8/.github/workflows/pull-request-reviews.yml fwupd-2.0.20/.github/workflows/pull-request-reviews.yml --- fwupd-2.0.8/.github/workflows/pull-request-reviews.yml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.github/workflows/pull-request-reviews.yml 2026-02-26 11:36:18.000000000 +0000 @@ -1,23 +1,22 @@ name: Pull Request reviews on: pull_request: - branches: [ main ] - -permissions: - contents: read # to fetch code (actions/checkout) + branches: [ 2_0_X ] jobs: pre-commit: + permissions: + contents: read runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 - name: Docker login - run: docker login docker.pkg.github.com -u $GITHUB_ACTOR -p $GITHUB_TOKEN + run: docker login ghcr.io -u $GITHUB_ACTOR -p $GITHUB_TOKEN env: GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} - name: Run pre-commit hooks run: | - docker run --privileged -t -v $GITHUB_WORKSPACE:/github/workspace docker.pkg.github.com/fwupd/fwupd/fwupd-precommit:latest + docker run --privileged -t -v $GITHUB_WORKSPACE:/github/workspace ghcr.io/fwupd/fwupd/fwupd-precommit:latest snap: needs: pre-commit diff -Nru fwupd-2.0.8/.github/workflows/scorecard.yml fwupd-2.0.20/.github/workflows/scorecard.yml --- fwupd-2.0.8/.github/workflows/scorecard.yml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.github/workflows/scorecard.yml 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -# This workflow uses actions that are not certified by GitHub. They are provided -# by a third-party and are governed by separate terms of service, privacy -# policy, and support documentation. - -name: Scorecard supply-chain security -on: - # For Branch-Protection check. Only the default branch is supported. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection - branch_protection_rule: - # To guarantee Maintained check is occasionally updated. See - # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained - schedule: - - cron: '25 11 * * 1' - push: - branches: [ "main" ] - -# Declare default permissions as read only. -permissions: read-all - -jobs: - analysis: - name: Scorecard analysis - runs-on: ubuntu-latest - permissions: - # Needed to upload the results to code-scanning dashboard. - security-events: write - # Needed to publish results and get a badge (see publish_results below). - id-token: write - # Uncomment the permissions below if installing in a private repository. - # contents: read - # actions: read - - steps: - - name: "Checkout code" - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - with: - persist-credentials: false - - - name: "Run analysis" - uses: ossf/scorecard-action@f49aabe0b5af0936a0987cfb85d86b75731b0186 # v2.4.1 - with: - results_file: results.sarif - results_format: sarif - # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: - # - you want to enable the Branch-Protection check on a *public* repository, or - # - you are installing Scorecard on a *private* repository - # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action?tab=readme-ov-file#authentication-with-fine-grained-pat-optional. - # repo_token: ${{ secrets.SCORECARD_TOKEN }} - - # Public repositories: - # - Publish results to OpenSSF REST API for easy access by consumers - # - Allows the repository to include the Scorecard badge. - # - See https://github.com/ossf/scorecard-action#publishing-results. - # For private repositories: - # - `publish_results` will always be set to `false`, regardless - # of the value entered here. - publish_results: true - - # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF - # format to the repository Actions tab. - - name: "Upload artifact" - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: SARIF file - path: results.sarif - retention-days: 5 - - # Upload the results to GitHub's code scanning dashboard (optional). - # Commenting out will disable upload of results to your repo's Code Scanning dashboard - - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@45775bd8235c68ba998cffa5171334d58593da47 # v3.28.15 - with: - sarif_file: results.sarif diff -Nru fwupd-2.0.8/.github/workflows/snap.yml fwupd-2.0.20/.github/workflows/snap.yml --- fwupd-2.0.8/.github/workflows/snap.yml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.github/workflows/snap.yml 2026-02-26 11:36:18.000000000 +0000 @@ -16,15 +16,15 @@ snap_name: ${{ steps.snapcraft.outputs.snap }} channel: ${{ steps.channel.outputs.channel }} steps: - - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + - uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 with: fetch-depth: 0 - id: channel run: | if git describe --exact-match; then - echo "channel=candidate" >> $GITHUB_OUTPUT + echo "name=channel::2.0.x/candidate" >> $GITHUB_OUTPUT else - echo "channel=edge" >> $GITHUB_OUTPUT + echo "name=channel::2.0.x/edge" >> $GITHUB_OUTPUT fi - id: prep run: | @@ -32,7 +32,7 @@ ln -s ../contrib/snap/snapcraft.yaml snap/snapcraft.yaml - uses: snapcore/action-build@3bdaa03e1ba6bf59a65f84a751d943d549a54e79 # v1.3.0 id: snapcraft - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + - uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 with: name: snap path: ${{ steps.snapcraft.outputs.snap }} @@ -41,7 +41,7 @@ needs: build-snap runs-on: ubuntu-latest steps: - - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 id: download with: name: snap @@ -80,7 +80,7 @@ runs-on: ubuntu-latest if: ${{ inputs.deploy }} steps: - - uses: actions/download-artifact@95815c38cf2ff2164869cbab79da8d1f422bc89e # v4.2.1 + - uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 id: download with: name: snap diff -Nru fwupd-2.0.8/.gitignore fwupd-2.0.20/.gitignore --- fwupd-2.0.8/.gitignore 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.gitignore 2026-02-26 11:36:18.000000000 +0000 @@ -37,3 +37,8 @@ fwupdtool.txt *.efi *.zip +/.cache/ +/compile_commands.json +*.nix +/.direnv +/.envrc diff -Nru fwupd-2.0.8/.pre-commit-config.yaml fwupd-2.0.20/.pre-commit-config.yaml --- fwupd-2.0.8/.pre-commit-config.yaml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.pre-commit-config.yaml 2026-02-26 11:36:18.000000000 +0000 @@ -1,12 +1,11 @@ -default_stages: [commit] +default_stages: [pre-commit] repos: - repo: https://github.com/pre-commit/pre-commit-hooks - rev: v4.5.0 + rev: v6.0.0 hooks: - id: no-commit-to-branch args: [--branch, main, --pattern, 1_.*_X] - id: check-added-large-files - - id: check-byte-order-marker - id: check-executables-have-shebangs - id: forbid-new-submodules - id: check-yaml @@ -25,12 +24,12 @@ - id: mixed-line-ending args: [--fix=lf] - repo: https://github.com/codespell-project/codespell - rev: v2.2.2 + rev: v2.4.1 hooks: - id: codespell args: ['--config', './contrib/codespell.cfg', --write-changes] - repo: https://github.com/ambv/black - rev: 23.12.1 + rev: 25.12.0 hooks: - id: black - repo: local @@ -55,11 +54,10 @@ BootOrder-8be4df61-93ca-11d2-aa0d-00e098032b8c| OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c| fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416)| - plugins/ch341a/ch341a-vmod\.png| + plugins/wch-ch341a/wch-ch341a-vmod\.png| plugins/lenovo-thinklmi/tests/efi/efivars/(fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416| OsIndicationsSupported-8be4df61-93ca-11d2-aa0d-00e098032b8c)| plugins/logitech-hidpp/data/dump\.(csv\.gz|tdc)| - plugins/optionrom/fuzzing/(header-data-payload|header-no-data|ifr-header-data-payload|naked-ifr)\.rom| plugins/pci-bcr/config| plugins/redfish/tests/efi/efivars/(RedfishIndications-16faa37e-4b6a-4891-9028-242de65a3b70| RedfishOSCredentials-16faa37e-4b6a-4891-9028-242de65a3b70)| @@ -68,18 +66,10 @@ src/tests/history_v[12]\.db| src/tests/sys/devices/pci0000:00/0000:00:14\.0/usb1/1-1/descriptors )$ - - id: check-null-false-returns - name: check for null / false return mismatch - language: script - entry: ./contrib/ci/check-null-false-returns.py - id: check-potfiles name: check for missing translated files from potfiles language: script entry: ./contrib/ci/check-potfiles.py - - id: check-finalizers - name: check for missing GObject parent finalize - language: script - entry: ./contrib/ci/check-finalizers.py - id: check-headers name: check for superfluous includes language: script @@ -97,27 +87,42 @@ language: system entry: shellcheck --severity=warning -e SC2068 types: [shell] + exclude: ^contrib/PKGBUILD$ + - id: shfmt + name: format shell scripts with shfmt + language: system + entry: shfmt --write --indent 4 + types: [shell] + exclude: ^contrib/PKGBUILD$ - id: run-tests name: run tests before pushing language: system entry: "test-fwupd" - stages: [push] + stages: [pre-push] - id: clang-format name: clang-format language: script entry: ./contrib/reformat-code.py types: [c] + - id: check-cli-actions + name: check cli actions + language: script + entry: ./contrib/ci/check-cli-actions.py - id: check-license name: Check license header types_or: [shell, c, python] language: script entry: ./contrib/ci/check-license.py + - id: check-meson-install-tags + name: Check meson install tags + language: script + entry: ./contrib/ci/check-meson-install-tags.py - repo: https://github.com/igorshubovych/markdownlint-cli - rev: v0.38.0 + rev: v0.45.0 hooks: - id: markdownlint args: ['--fix', '--ignore', '.github'] - repo: https://github.com/gitleaks/gitleaks - rev: v8.16.3 + rev: v8.28.0 hooks: - id: gitleaks diff -Nru fwupd-2.0.8/.tx/config fwupd-2.0.20/.tx/config --- fwupd-2.0.8/.tx/config 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/.tx/config 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -[main] -host = https://www.transifex.com - -[o:freedesktop:p:fwupd:r:main] -file_filter = po/.po -source_file = po/fwupd.pot -source_lang = en -type = PO - diff -Nru fwupd-2.0.8/README.md fwupd-2.0.20/README.md --- fwupd-2.0.8/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,7 @@ # fwupd [![Build Status](https://github.com/fwupd/fwupd/actions/workflows/ci.yml/badge.svg)](https://github.com/fwupd/fwupd/actions/workflows/ci.yml) +[![Translation Status](https://hosted.weblate.org/widget/fwupd/svg-badge.svg)](https://hosted.weblate.org/engage/fwupd/) [![CodeQL](https://github.com/fwupd/fwupd/actions/workflows/codeql-analysis.yml/badge.svg)](https://github.com/fwupd/fwupd/actions/workflows/codeql-analysis.yml) [![Coverity Scan Build Status](https://scan.coverity.com/projects/10744/badge.svg)](https://scan.coverity.com/projects/10744) [![Fuzzing Status](https://oss-fuzz-build-logs.storage.googleapis.com/badges/fwupd.svg)](https://bugs.chromium.org/p/oss-fuzz/issues/list?sort=-opened&can=1&q=proj:fwupd) @@ -20,9 +21,10 @@ See [Building and Debugging](docs/building.md) for how to build the fwupd development environment. -**NOTE:** In most cases, end users should not compile fwupd from scratch; it's a -complicated project with dozens of dependencies (and as many configuration options) -and there's just too many things that can go wrong. +> [!TIP] +> In most cases, end users should not compile fwupd from scratch; it's a +> complicated project with dozens of dependencies (and as many configuration options) +> and there's just too many things that can go wrong. Users should just have fwupd installed and updated by their distro, managed and tested by the package maintainer. @@ -41,7 +43,7 @@ analysis plugin built to analyze GLib code. It can be installed and then run using: mkdir build-tartan - CC=clang-17 meson ../ + CC=clang-18 meson ../ SCANBUILD=../contrib/tartan.sh ninja scan-build ## LVFS @@ -77,7 +79,7 @@ This will download and apply all updates for your system. - Updates that can be applied live will be done immediately. -- Updates that run at bootup will be staged for the next reboot. +- Updates that run at boot-up will be staged for the next reboot. You can find more information about the update workflow in the end users section of the [fwupd website](https://fwupd.org). @@ -138,3 +140,11 @@ - [Coverity](https://scan.coverity.com/) - static analyzer for Java, C/C++, C#, JavaScript, Ruby, and Python code. - [PVS-Studio](https://pvs-studio.com/en/pvs-studio/?utm_source=website&utm_medium=github&utm_campaign=open_source) - static analyzer for C, C++, C#, and Java code. + +## Packaging notes + +If you are working or maintaining a package of fwupd downstream, please consider the following notes. + +- The Meson build option `systemd_unit_user` should be used carefully, and the specified user (or resulting group) should be inaccessible to + unprivileged system users. Otherwise, this may pose a risk for privilege escalation. The default value for this setting (`DynamicUser=true`) is + secure and should be used in the general case. diff -Nru fwupd-2.0.8/RELEASE fwupd-2.0.20/RELEASE --- fwupd-2.0.8/RELEASE 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/RELEASE 2026-02-26 11:36:18.000000000 +0000 @@ -1,38 +1,15 @@ fwupd Release Notes -Forking stable branch: - -When forking main into a stable 2_1_X, be sure to disable the following CI jobs: - * publish-docs (`.circleci/config.yml`) - * deploy-store (`snap.yaml`) - -Also update `SECURITY.md`, removing the oldest branch and add the new branch at the top. -To make sure it's done right, you can reference commit 433e809318c68c9ab6d4ae50ee9c4312503185d8 - -Check IFD parsing (if the files are available): - - ../../contrib/check-ifd-firmware.py ../../fwupd-test-roms/ - Write release entries: - * ../../contrib/generate-release.py - * copy into ../../data/org.freedesktop.fwupd.metainfo.xml - * appstream-util appdata-to-news ../../data/org.freedesktop.fwupd.metainfo.xml > NEWS - -Update translations: - -ninja-build fwupd-pot -cd ../.. -tx push --source -tx pull --all --force --minimum-perc=5 -cd venv/build -ninja-build fix-translations -git add ../../po/*.po + ../../contrib/generate-release.py + # copy into ../../data/org.freedesktop.fwupd.metainfo.xml + appstream-util appdata-to-news ../../data/org.freedesktop.fwupd.metainfo.xml > NEWS 2. Commit changes to git: # MAKE SURE THIS IS CORRECT -export release_ver="2.0.8" +export release_ver="2.0.20" git commit -a -m "Release fwupd ${release_ver}" --no-verify git tag -s -f -m "Release fwupd ${release_ver}" "${release_ver}" diff -Nru fwupd-2.0.8/contrib/build-openbmc.sh fwupd-2.0.20/contrib/build-openbmc.sh --- fwupd-2.0.8/contrib/build-openbmc.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/build-openbmc.sh 2026-02-26 11:36:18.000000000 +0000 @@ -5,15 +5,18 @@ meson setup build-openbmc \ -Dauto_features=disabled \ -Ddocs=disabled \ + -Dbuildtype=minsize \ + -Dstrip=true \ -Dpolkit=disabled \ -Dbash_completion=false \ -Dfish_completion=false \ -Dfirmware-packager=false \ + -Dplugin_uefi_capsule_splash=false \ -Dhsi=disabled \ -Dman=false \ -Dmetainfo=false \ -Dtests=true \ - -Dsystemd_root_prefix=/tmp \ + -Dsystemd=disabled \ -Dlibxmlb:gtkdoc=false \ $@ diff -Nru fwupd-2.0.8/contrib/build-venv.sh fwupd-2.0.20/contrib/build-venv.sh --- fwupd-2.0.8/contrib/build-venv.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/build-venv.sh 2026-02-26 11:36:18.000000000 +0000 @@ -7,23 +7,23 @@ #build and install if [ -d /opt/homebrew/opt/libarchive/lib/pkgconfig ]; then - EXTRA_ARGS="${EXTRA_ARGS} -Dpkg_config_path=/opt/homebrew/opt/libarchive/lib/pkgconfig" + EXTRA_ARGS="${EXTRA_ARGS} -Dpkg_config_path=/opt/homebrew/opt/libarchive/lib/pkgconfig" fi if [ ! -d ${BUILD} ] || ! [ -e ${BUILD}/build.ninja ]; then - meson setup ${BUILD} --prefix=${DIST} ${EXTRA_ARGS} $@ + meson setup ${BUILD} --prefix=${DIST} ${EXTRA_ARGS} $@ fi ninja -C ${BUILD} install # check whether we have an existing fwupd EFI binary in the host system to use EFI_PREFIX=$(pkg-config fwupd-efi --variable=prefix 2>/dev/null || echo "/usr") EFI_DIR=libexec/fwupd/efi -BINARIES=$(find "${EFI_PREFIX}/${EFI_DIR}" -name "*.efi*" -type f -print) +BINARIES=$(find "${EFI_PREFIX}/${EFI_DIR}" -name "*.efi*" -type f -print || true) if [ -n "${BINARIES}" ]; then - mkdir -p ${DIST}/${EFI_DIR} - for i in ${BINARIES}; do - if [ -f "${DIST}/${EFI_DIR}/$(basename $i)" ]; then - continue - fi - ln -s $i "${DIST}/${EFI_DIR}/$(basename $i)" - done + mkdir -p ${DIST}/${EFI_DIR} + for i in ${BINARIES}; do + if [ -f "${DIST}/${EFI_DIR}/$(basename $i)" ]; then + continue + fi + ln -s $i "${DIST}/${EFI_DIR}/$(basename $i)" + done fi diff -Nru fwupd-2.0.8/contrib/build-windows.sh fwupd-2.0.20/contrib/build-windows.sh --- fwupd-2.0.8/contrib/build-windows.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/build-windows.sh 2026-02-26 11:36:18.000000000 +0000 @@ -45,39 +45,39 @@ # copy deps cp -f -v \ - $MINGW32BINDIR/gspawn-win64-helper-console.exe \ - $MINGW32BINDIR/gspawn-win64-helper.exe \ - $MINGW32BINDIR/iconv.dll \ - $MINGW32BINDIR/libarchive-13.dll \ - $MINGW32BINDIR/libbrotlicommon.dll \ - $MINGW32BINDIR/libbrotlidec.dll \ - $MINGW32BINDIR/libbz2-1.dll \ - $MINGW32BINDIR/libcrypto-3-x64.dll \ - $MINGW32BINDIR/libcurl-4.dll \ - $MINGW32BINDIR/libffi-*.dll \ - $MINGW32BINDIR/libgcc_s_seh-1.dll \ - $MINGW32BINDIR/libgio-2.0-0.dll \ - $MINGW32BINDIR/libglib-2.0-0.dll \ - $MINGW32BINDIR/libgmodule-2.0-0.dll \ - $MINGW32BINDIR/libgmp-10.dll \ - $MINGW32BINDIR/libgnutls-30.dll \ - $MINGW32BINDIR/libgobject-2.0-0.dll \ - $MINGW32BINDIR/libhogweed-*.dll \ - $MINGW32BINDIR/libidn2-0.dll \ - $MINGW32BINDIR/libintl-8.dll \ - $MINGW32BINDIR/libjson-glib-1.0-0.dll \ - $MINGW32BINDIR/liblzma-5.dll \ - $MINGW32BINDIR/libnettle-*.dll \ - $MINGW32BINDIR/libp11-kit-0.dll \ - $MINGW32BINDIR/libpcre2-8-0.dll \ - $MINGW32BINDIR/libsqlite3-0.dll \ - $MINGW32BINDIR/libssh2-1.dll \ - $MINGW32BINDIR/libssl-3-x64.dll \ - $MINGW32BINDIR/libssp-0.dll \ - $MINGW32BINDIR/libtasn1-6.dll \ - $MINGW32BINDIR/libusb-1.0.dll \ - $MINGW32BINDIR/libwinpthread-1.dll \ - $MINGW32BINDIR/libxml2-2.dll \ - $MINGW32BINDIR/libzstd.dll \ - $MINGW32BINDIR/zlib1.dll \ - "$DESTDIR/bin/" + $MINGW32BINDIR/gspawn-win64-helper-console.exe \ + $MINGW32BINDIR/gspawn-win64-helper.exe \ + $MINGW32BINDIR/iconv.dll \ + $MINGW32BINDIR/libarchive-13.dll \ + $MINGW32BINDIR/libbrotlicommon.dll \ + $MINGW32BINDIR/libbrotlidec.dll \ + $MINGW32BINDIR/libbz2-1.dll \ + $MINGW32BINDIR/libcrypto-3-x64.dll \ + $MINGW32BINDIR/libcurl-4.dll \ + $MINGW32BINDIR/libffi-*.dll \ + $MINGW32BINDIR/libgcc_s_seh-1.dll \ + $MINGW32BINDIR/libgio-2.0-0.dll \ + $MINGW32BINDIR/libglib-2.0-0.dll \ + $MINGW32BINDIR/libgmodule-2.0-0.dll \ + $MINGW32BINDIR/libgmp-10.dll \ + $MINGW32BINDIR/libgnutls-30.dll \ + $MINGW32BINDIR/libgobject-2.0-0.dll \ + $MINGW32BINDIR/libhogweed-*.dll \ + $MINGW32BINDIR/libidn2-0.dll \ + $MINGW32BINDIR/libintl-8.dll \ + $MINGW32BINDIR/libjson-glib-1.0-0.dll \ + $MINGW32BINDIR/liblzma-5.dll \ + $MINGW32BINDIR/libnettle-*.dll \ + $MINGW32BINDIR/libp11-kit-0.dll \ + $MINGW32BINDIR/libpcre2-8-0.dll \ + $MINGW32BINDIR/libsqlite3-0.dll \ + $MINGW32BINDIR/libssh2-1.dll \ + $MINGW32BINDIR/libssl-3-x64.dll \ + $MINGW32BINDIR/libssp-0.dll \ + $MINGW32BINDIR/libtasn1-6.dll \ + $MINGW32BINDIR/libusb-1.0.dll \ + $MINGW32BINDIR/libwinpthread-1.dll \ + $MINGW32BINDIR/libxml2-2.dll \ + $MINGW32BINDIR/libzstd.dll \ + $MINGW32BINDIR/zlib1.dll \ + "$DESTDIR/bin/" diff -Nru fwupd-2.0.8/contrib/ci/Dockerfile-centos.in fwupd-2.0.20/contrib/ci/Dockerfile-centos.in --- fwupd-2.0.8/contrib/ci/Dockerfile-centos.in 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/Dockerfile-centos.in 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +FROM quay.io/centos/centos:stream9 +%%%OS%%% +ENV LANG=en_US.UTF-8 +ENV LANGUAGE=en_US:en +ENV LC_ALL=en_US.UTF-8 +RUN echo fubar > /etc/machine-id +RUN dnf -y update +RUN echo fubar > /etc/machine-id +RUN dnf copr enable rhughes/fwupd-epel-9 -y +RUN dnf -y install https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm +RUN crb enable +%%%INSTALL_DEPENDENCIES_COMMAND%%% +RUN ls -R /var/cache +RUN wget -P /var/cache https://github.com/fwupd/fwupd/releases/download/1.9.31/fwupd-1.9.31.tar.xz +RUN wget -P /var/cache https://github.com/fwupd/fwupd-efi/archive/refs/tags/1.7.tar.gz +WORKDIR /github/workspace +CMD ["./contrib/ci/centos.sh"] diff -Nru fwupd-2.0.8/contrib/ci/Dockerfile-precommit.in fwupd-2.0.20/contrib/ci/Dockerfile-precommit.in --- fwupd-2.0.8/contrib/ci/Dockerfile-precommit.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/Dockerfile-precommit.in 2026-02-26 11:36:18.000000000 +0000 @@ -5,6 +5,7 @@ patch \ pre-commit \ shellcheck \ + shfmt \ clang-format RUN apt install wget -yq RUN mkdir -p /tmp/repo && \ diff -Nru fwupd-2.0.8/contrib/ci/arch-test.sh fwupd-2.0.20/contrib/ci/arch-test.sh --- fwupd-2.0.8/contrib/ci/arch-test.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/arch-test.sh 2026-02-26 11:36:18.000000000 +0000 @@ -10,14 +10,13 @@ plugins/uefi-dbx/tests/snapd.py --datadir /usr/share/installed-tests/fwupd/tests & # run TPM simulator -#swtpm socket --tpm2 --server port=2321 --ctrl type=tcp,port=2322 --flags not-need-init --tpmstate "dir=$PWD" & -#trap 'kill $!' EXIT +export TPM2TOOLS_TCTI=swtpm:host=127.0.0.1,port=2321 +swtpm socket --tpm2 --server port=2321 --ctrl type=tcp,port=2322 --flags not-need-init,startup-clear --tpmstate "dir=$PWD" & +SWTPM_PID=$! +trap 'kill $SWTPM_PID' EXIT # extend a PCR0 value for test suite -#sleep 2 -#tpm2_startup -c -#tpm2_pcrextend 0:sha1=f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 -# mark as disabled until it is fixed -#export TPM_SERVER_RUNNING=1 +sleep 2 +tpm2_pcrextend 0:sha1=f1d2d2f924e986ac86fdf7b36c94bcdf32beec15 0:sha256=722961af9796ab090ace25e9c341aa3177bf2fd7b411c65c661599a84e5feef8 0:sha384=6b38548127fa865ff6fa81cbee64f3b18c7fa01490c700a66e4c8acc4a197db30d83dbb4d4dbb61099baf14490c7681d 0:sha512=d636e21e2eb4b1effd3cc6e3bb40ddf01bb1ecef7e43c8b0e480ba887a12780f70f0e0b7342f1d3c7e4e87849ecd5810930822560ad8e02d7cad8a206df12e1d #run the CI tests for Qt5 meson qt5-thread-test contrib/ci/qt5-thread-test --werror -Db_coverage=true diff -Nru fwupd-2.0.8/contrib/ci/build_freebsd_package.sh fwupd-2.0.20/contrib/ci/build_freebsd_package.sh --- fwupd-2.0.8/contrib/ci/build_freebsd_package.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/build_freebsd_package.sh 2026-02-26 11:36:18.000000000 +0000 @@ -7,27 +7,27 @@ while [ -n "$1" ]; do case $1 in - --GITHUB_SHA=*) - GITHUB_SHA=${1#--GITHUB_SHA=} - ;; - --GITHUB_REPOSITORY=*) - GITHUB_REPOSITORY=${1#--GITHUB_REPOSITORY=} - ;; - --GITHUB_REPOSITORY_OWNER=*) - GITHUB_REPOSITORY_OWNER=${1#--GITHUB_REPOSITORY_OWNER=} - ;; - --GITHUB_TAG=*) - GITHUB_TAG=${1#--GITHUB_TAG=} - ;; - *) - echo "Command $1 unknown. exiting..." - exit 1 - ;; + --GITHUB_SHA=*) + GITHUB_SHA=${1#--GITHUB_SHA=} + ;; + --GITHUB_REPOSITORY=*) + GITHUB_REPOSITORY=${1#--GITHUB_REPOSITORY=} + ;; + --GITHUB_REPOSITORY_OWNER=*) + GITHUB_REPOSITORY_OWNER=${1#--GITHUB_REPOSITORY_OWNER=} + ;; + --GITHUB_TAG=*) + GITHUB_TAG=${1#--GITHUB_TAG=} + ;; + *) + echo "Command $1 unknown. exiting..." + exit 1 + ;; esac shift done -if [ -z "$GITHUB_SHA" ] || [ -z "$GITHUB_REPOSITORY" ] || \ +if [ -z "$GITHUB_SHA" ] || [ -z "$GITHUB_REPOSITORY" ] || [ -z "$GITHUB_REPOSITORY_OWNER" ] || [ -z "$GITHUB_TAG" ]; then exit 1 fi @@ -41,7 +41,7 @@ cp /etc/pkg/FreeBSD.conf /usr/local/etc/pkg/repos/FreeBSD.conf # Use latest pkg repo instead of quarterly https://wiki.freebsd.org/Ports/QuarterlyBranch sed -i .old 's|url: "pkg+http://pkg.FreeBSD.org/${ABI}/quarterly"|url: "pkg+http://pkg.FreeBSD.org/${ABI}/latest"|' \ -/usr/local/etc/pkg/repos/FreeBSD.conf + /usr/local/etc/pkg/repos/FreeBSD.conf pkg install -y meson efivar pkg upgrade -y meson cd /usr @@ -58,7 +58,7 @@ make clean make # Generate current list of files in the pkg-plist -make makeplist > pkg-plist +make makeplist >pkg-plist sed -i "" "1d" pkg-plist sed -i "" "s/%%PORTDOCS%%%%DOCSDIR%%/%%DOCSDIR%%/g" pkg-plist # Build artifact @@ -66,4 +66,4 @@ make package make install cp /usr/ports/sysutils/fwupd/work/pkg/fwupd*.pkg \ -~/work/fwupd/fwupd/fwupd-freebsd-${GITHUB_TAG}-${GITHUB_SHA}.pkg || exit 1 + ~/work/fwupd/fwupd/fwupd-freebsd-${GITHUB_TAG}-${GITHUB_SHA}.pkg || exit 1 diff -Nru fwupd-2.0.8/contrib/ci/build_macos.sh fwupd-2.0.20/contrib/ci/build_macos.sh --- fwupd-2.0.8/contrib/ci/build_macos.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/build_macos.sh 2026-02-26 11:36:18.000000000 +0000 @@ -2,6 +2,8 @@ set -e set -x +export PKG_CONFIG_PATH="/opt/homebrew/Cellar/readline/8.2.13/lib/pkgconfig:$PKG_CONFIG_PATH" + mkdir -p build-macos && cd build-macos meson setup .. \ -Dbuild=all \ diff -Nru fwupd-2.0.8/contrib/ci/build_windows.sh fwupd-2.0.20/contrib/ci/build_windows.sh --- fwupd-2.0.8/contrib/ci/build_windows.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/build_windows.sh 2026-02-26 11:36:18.000000000 +0000 @@ -23,6 +23,11 @@ rm -rf $DESTDIR $build mkdir -p $build $DESTDIR && cd $build +# Hack for Fedora bug +if [ "$(id -u)" -eq 0 ]; then + sed -i '/^Requires.private: termcap/d' /usr/x86_64-w64-mingw32/sys-root/mingw/lib/pkgconfig/readline.pc +fi + # run before using meson export WINEPREFIX=$build/.wine @@ -69,80 +74,85 @@ # deps find $MINGW32BINDIR \ - -name gspawn-win64-helper-console.exe \ - -o -name gspawn-win64-helper.exe \ - -o -name iconv.dll \ - -o -name libarchive-13.dll \ - -o -name libbrotlicommon.dll \ - -o -name libbrotlidec.dll \ - -o -name libbz2-1.dll \ - -o -name libcrypto-3-x64.dll \ - -o -name libcurl-4.dll \ - -o -name "libffi-*.dll" \ - -o -name libgcc_s_seh-1.dll \ - -o -name libgio-2.0-0.dll \ - -o -name libglib-2.0-0.dll \ - -o -name libgmodule-2.0-0.dll \ - -o -name libgmp-10.dll \ - -o -name libgnutls-30.dll \ - -o -name libgobject-2.0-0.dll \ - -o -name "libhogweed-*.dll" \ - -o -name libidn2-0.dll \ - -o -name libintl-8.dll \ - -o -name libjson-glib-1.0-0.dll \ - -o -name liblzma-5.dll \ - -o -name "libnettle-*.dll" \ - -o -name libp11-kit-0.dll \ - -o -name libpcre2-8-0.dll \ - -o -name libsqlite3-0.dll \ - -o -name libssh2-1.dll \ - -o -name libssl-3-x64.dll \ - -o -name libssp-0.dll \ - -o -name libtasn1-6.dll \ - -o -name libusb-1.0.dll \ - -o -name libwinpthread-1.dll \ - -o -name libxml2-2.dll \ - -o -name libxmlb-2.dll \ - -o -name libzstd.dll \ - -o -name zlib1.dll \ - | wixl-heat \ - -p $MINGW32BINDIR/ \ - --win64 \ - --directory-ref BINDIR \ - --var "var.MINGW32BINDIR" \ - --component-group "CG.fwupd-deps" | \ - tee $build/contrib/fwupd-deps.wxs - -echo $CERTDIR/ca-bundle.crt \ - | wixl-heat \ - -p $CERTDIR/ \ - --win64 \ - --directory-ref BINDIR \ - --var "var.CERTDIR" \ - --component-group "CG.fwupd-crts" | \ - tee $build/contrib/fwupd-crts.wxs + -name gspawn-win64-helper-console.exe \ + -o -name gspawn-win64-helper.exe \ + -o -name iconv.dll \ + -o -name libarchive-13.dll \ + -o -name libbrotlicommon.dll \ + -o -name libbrotlidec.dll \ + -o -name libbz2-1.dll \ + -o -name libcrypto-3-x64.dll \ + -o -name libcurl-4.dll \ + -o -name "libffi-*.dll" \ + -o -name libgcc_s_seh-1.dll \ + -o -name libgio-2.0-0.dll \ + -o -name libglib-2.0-0.dll \ + -o -name libgmodule-2.0-0.dll \ + -o -name libgmp-10.dll \ + -o -name libgnutls-30.dll \ + -o -name libgobject-2.0-0.dll \ + -o -name "libhogweed-*.dll" \ + -o -name libidn2-0.dll \ + -o -name libintl-8.dll \ + -o -name libjson-glib-1.0-0.dll \ + -o -name liblzma-5.dll \ + -o -name "libnettle-*.dll" \ + -o -name libp11-kit-0.dll \ + -o -name libpcre2-8-0.dll \ + -o -name libpsl-5.dll \ + -o -name libsqlite3-0.dll \ + -o -name libssh2-1.dll \ + -o -name libssl-3-x64.dll \ + -o -name libssp-0.dll \ + -o -name libtermcap-0.dll \ + -o -name libreadline8.dll \ + -o -name libtasn1-6.dll \ + -o -name libunistring-2.dll \ + -o -name libusb-1.0.dll \ + -o -name libwinpthread-1.dll \ + -o -name libxml2-2.dll \ + -o -name libxmlb-2.dll \ + -o -name libzstd.dll \ + -o -name wldap32.dll \ + -o -name zlib1.dll | + wixl-heat \ + -p $MINGW32BINDIR/ \ + --win64 \ + --directory-ref BINDIR \ + --var "var.MINGW32BINDIR" \ + --component-group "CG.fwupd-deps" | + tee $build/contrib/fwupd-deps.wxs + +echo $CERTDIR/ca-bundle.crt | + wixl-heat \ + -p $CERTDIR/ \ + --win64 \ + --directory-ref BINDIR \ + --var "var.CERTDIR" \ + --component-group "CG.fwupd-crts" | + tee $build/contrib/fwupd-crts.wxs # no static libraries find "$DESTDIR/" -type f -name "*.a" -print0 | xargs rm -f # our files -find "$DESTDIR" | \ - wixl-heat \ - -p "$DESTDIR/" \ - -x include/ \ - -x share/fwupd/device-tests/ \ - -x share/tests/ \ - -x share/man/ \ - -x share/doc/ \ - -x lib/pkgconfig/ \ - --win64 \ - --directory-ref INSTALLDIR \ - --var "var.DESTDIR" \ - --component-group "CG.fwupd-files" | \ - tee "$build/contrib/fwupd-files.wxs" +find "$DESTDIR" | + wixl-heat \ + -p "$DESTDIR/" \ + -x include/ \ + -x share/fwupd/device-tests/ \ + -x share/tests/ \ + -x share/man/ \ + -x share/doc/ \ + -x lib/pkgconfig/ \ + --win64 \ + --directory-ref INSTALLDIR \ + --var "var.DESTDIR" \ + --component-group "CG.fwupd-files" | + tee "$build/contrib/fwupd-files.wxs" #add service install key -sed -i "$build/contrib/fwupd-files.wxs" -f - << EOF +sed -i "$build/contrib/fwupd-files.wxs" -f - <,fwupd.exe"/>\\ , EOF @@ -150,19 +160,15 @@ MSI_FILENAME="$DESTDIR/setup/fwupd-$VERSION-setup-x86_64.msi" mkdir -p "$DESTDIR/setup" wixl -v \ - "$build/contrib/fwupd.wxs" \ - "$build/contrib/fwupd-crts.wxs" \ - "$build/contrib/fwupd-deps.wxs" \ - "$build/contrib/fwupd-files.wxs" \ - -D CERTDIR=$CERTDIR \ - -D MINGW32BINDIR=$MINGW32BINDIR \ - -D Win64="yes" \ - -D DESTDIR="$DESTDIR" \ - -o "${MSI_FILENAME}" - -#generate news release -echo "Generating news for version $VERSION" -contrib/ci/generate_news.py $VERSION | tee -a $DESTDIR/news.txt + "$build/contrib/fwupd.wxs" \ + "$build/contrib/fwupd-crts.wxs" \ + "$build/contrib/fwupd-deps.wxs" \ + "$build/contrib/fwupd-files.wxs" \ + -D CERTDIR=$CERTDIR \ + -D MINGW32BINDIR=$MINGW32BINDIR \ + -D Win64="yes" \ + -D DESTDIR="$DESTDIR" \ + -o "${MSI_FILENAME}" # check the msi archive can be installed and removed (use "wine uninstaller" to do manually) wine msiexec /i "${MSI_FILENAME}" diff -Nru fwupd-2.0.8/contrib/ci/centos-test.sh fwupd-2.0.20/contrib/ci/centos-test.sh --- fwupd-2.0.8/contrib/ci/centos-test.sh 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/centos-test.sh 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,15 @@ +#!/bin/sh -e + +#install RPM packages +dnf install -y dist/*.rpm + +# set up enough PolicyKit and D-Bus to run the daemon +mkdir -p /run/dbus +mkdir -p /var +ln -s /var/run /run +dbus-daemon --system --fork +/usr/lib/polkit-1/polkitd & +sleep 5 + +# run the daemon startup to check it can start +/usr/libexec/fwupd/fwupd --immediate-exit --verbose diff -Nru fwupd-2.0.8/contrib/ci/centos.sh fwupd-2.0.20/contrib/ci/centos.sh --- fwupd-2.0.8/contrib/ci/centos.sh 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/centos.sh 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,29 @@ +#!/bin/bash +set -e +set -x + +# get any missing deps from the container +./contrib/ci/fwupd_setup_helpers.py install-dependencies --yes -o centos + +# disable the safe directory feature +git config --global safe.directory "*" + +# copy current tarball, old fwupd and fwupd-efi +RPMVERSION=${VERSION//-/.} +mkdir -p $HOME/rpmbuild/SOURCES/ +mv fwupd-$VERSION.tar.xz $HOME/rpmbuild/SOURCES/ +cp /var/cache/*.tar.* $HOME/rpmbuild/SOURCES/ + +# generate a spec file and build RPM packages +mkdir -p build +sed "s,#VERSION#,$RPMVERSION,; + s,#BUILD#,1,; + s,#LONGDATE#,$(date '+%a %b %d %Y'),; + s,#ALPHATAG#,alpha,; + s,Source0.*,Source0:\tfwupd-$VERSION.tar.xz," \ + contrib/fwupd.spec.in >build/fwupd.spec +rpmbuild -ba build/fwupd.spec --with=libfwupdcompat + +# copy as artifact +mkdir -p dist +cp $HOME/rpmbuild/RPMS/*/*.rpm dist diff -Nru fwupd-2.0.8/contrib/ci/check-cli-actions.py fwupd-2.0.20/contrib/ci/check-cli-actions.py --- fwupd-2.0.8/contrib/ci/check-cli-actions.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/check-cli-actions.py 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 +# pylint: disable=invalid-name,missing-module-docstring,missing-function-docstring +# +# Copyright 2025 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +import subprocess +import sys + +if __name__ == "__main__": + rc: int = 0 + + # load the manpage, bash-completion, etc + data: dict[str, str] = {} + for fn in [ + "src/fwupdmgr.md", + "data/bash-completion/fwupdmgr", + "data/fish-completion/fwupdmgr.fish", + ]: + with open(fn, "rb") as f: + data[fn] = f.read().decode() + + # if we can't run the binary for some reason, assume everything is okay + try: + pr = subprocess.run( + ["venv/build/src/fwupdmgr", "get-actions", "--force"], + cwd=".", + # stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + encoding="utf-8", + ) + except (FileNotFoundError, subprocess.CalledProcessError): + sys.exit(0) + + # check each action is at least mentioned in each file + for fn, txt in data.items(): + for action in pr.stdout.split("\n"): + if txt.find(action) == -1: + print(f"* CLI action {action} not documented in {fn}") + rc = 1 + sys.exit(rc) diff -Nru fwupd-2.0.8/contrib/ci/check-cpu.py fwupd-2.0.20/contrib/ci/check-cpu.py --- fwupd-2.0.8/contrib/ci/check-cpu.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/check-cpu.py 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# pylint: disable=invalid-name,missing-module-docstring,missing-function-docstring +# +# Copyright 2025 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1-or-later + + +import argparse +import os +import sys +import subprocess + + +def _check(cwd: str, argv: list[str], limit: int = 0) -> int: + # prime cache + try: + subprocess.check_output(argv, cwd=cwd) + except subprocess.CalledProcessError as e: + print(e) + return 1 + try: + rc = subprocess.run( + ["valgrind", "--tool=callgrind"] + argv, + cwd=cwd, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + encoding="utf-8", + check=True, + ) + except subprocess.CalledProcessError as e: + print(e) + return 1 + value: int = 0 + for line in rc.stderr.split("\n"): + if line.find("Collected : ") != -1: + value = int(line.split(" ")[3]) + if limit and value > limit * 1_000_000: + print(f"CPU usage was {value//1_000_000}Mcycles (limit of {limit}Mcycles)") + return 1 + print(f"CPU usage was {value//1_000_000}Mcycles") + return 0 + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Check CPU usage") + parser.add_argument("--limit", type=int, help="CPU limit in Mcycles") + parser.add_argument("command", nargs="+", help="Command to run and measure") + args = parser.parse_args() + try: + sys.exit( + _check( + cwd=os.path.dirname(sys.argv[0]), argv=args.command, limit=args.limit + ) + ) + except IndexError: + parser.print_help() + sys.exit(1) diff -Nru fwupd-2.0.8/contrib/ci/check-finalizers.py fwupd-2.0.20/contrib/ci/check-finalizers.py --- fwupd-2.0.8/contrib/ci/check-finalizers.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/check-finalizers.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,60 +0,0 @@ -#!/usr/bin/env python3 -# pylint: disable=invalid-name,missing-docstring,consider-using-f-string -# pylint: disable=too-few-public-methods -# -# Copyright 2022 Richard Hughes -# -# SPDX-License-Identifier: LGPL-2.1-or-later - -import glob -import sys -from typing import List - - -class ReturnValidator: - def __init__(self): - self.warnings: List[str] = [] - - def parse(self, fn: str) -> None: - with open(fn, "rb") as f: - infunc = False - has_parent_finalize = False - for line in f.read().decode().split("\n"): - # found the function, but ignore the prototype - if line.find("_finalize(") != -1: - if line.endswith(";"): - continue - infunc = True - continue - - # got it - if line.find("->finalize(") != -1: - has_parent_finalize = True - continue - - # finalize is done - if infunc and line.startswith("}"): - if not has_parent_finalize: - self.warnings.append(f"{fn} did not have parent ->finalize()") - break - - -def test_files(): - # test all C source files - validator = ReturnValidator() - for fn in ( - glob.glob("libfwupd/*.c") - + glob.glob("libfwupdplugin/*.c") - + glob.glob("plugins/*/*.c") - + glob.glob("src/*.c") - ): - validator.parse(fn) - for warning in validator.warnings: - print(warning) - - return 1 if validator.warnings else 0 - - -if __name__ == "__main__": - # all done! - sys.exit(test_files()) diff -Nru fwupd-2.0.8/contrib/ci/check-headers.py fwupd-2.0.20/contrib/ci/check-headers.py --- fwupd-2.0.8/contrib/ci/check-headers.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/check-headers.py 2026-02-26 11:36:18.000000000 +0000 @@ -25,7 +25,7 @@ for char in ["\t"]: line = line.replace(char, " ") includes.append(line.split(" ")[-1]) - return includes + return sorted(includes) def test_files() -> int: @@ -104,6 +104,16 @@ print(f"{fn} does not include config.h") rc = 1 + # check for headers including themselves + if fn.endswith(".h") and os.path.basename(fn) in includes: + print(f"{fn} includes itself") + rc = 1 + + # check for duplicate includes + if sorted(set(includes)) != includes: + print(f"{fn} contains duplicate includes") + rc = 1 + # check for one header implying the other implied_headers = { "fu-common.h": ["xmlb.h"], diff -Nru fwupd-2.0.8/contrib/ci/check-meson-install-tags.py fwupd-2.0.20/contrib/ci/check-meson-install-tags.py --- fwupd-2.0.8/contrib/ci/check-meson-install-tags.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/check-meson-install-tags.py 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,174 @@ +#!/usr/bin/env python3 +# +# Copyright 2025 Thomas Mühlbacher +# +# SPDX-License-Identifier: LGPL-2.1-or-later + + +import argparse +import json +import logging +import os +import subprocess +import sys + +from typing import Iterator + + +def parse_version(ver): + return tuple(map(int, ver.split("."))) + + +# see https://github.com/mesonbuild/meson/pull/14890 +def old_meson_missing_vapi_deps_tag() -> bool: + version_str = subprocess.check_output( + ["meson", "--version"], + text=True, + ) + return parse_version(version_str.strip()) <= parse_version("1.9.0") + + +def objects_with_tag(obj) -> Iterator[dict]: + match obj: + case dict(d): + if "tag" in d: + yield d + for v in d.values(): + yield from objects_with_tag(v) + case list(l): + for i in l: + yield from objects_with_tag(i) + + +def collect_tags(install_plan) -> list[str]: + tags = set() + + for obj in objects_with_tag(install_plan): + tags.add(obj["tag"]) + + return sorted(["null" if t is None else t for t in tags]) + + +def collect_files(install_plan, tag) -> list[str]: + files = list() + + if tag == "null": + tag = None + + for obj in objects_with_tag(install_plan): + if obj["tag"] == tag: + files.append(obj["destination"]) + + return sorted(files) + + +def print_tags(install_plan): + for tag in collect_tags(install_plan): + print(tag) + + +def print_files(install_plan, tag): + for file in collect_files(install_plan, tag): + print(file) + + +def print_all(install_plan): + for tag in collect_tags(install_plan): + for file in collect_files(install_plan, tag): + print(f"{tag}: {file}") + + +def check(install_plan) -> int: + exit_code = 0 + tags = collect_tags(install_plan) + + # check for files missing install tag + ignore_vapi_deps = old_meson_missing_vapi_deps_tag() + for null_file in collect_files(install_plan, None): + if ignore_vapi_deps and null_file.endswith("fwupd.deps"): + logging.warning(f"missing meson install tag: {null_file}") + else: + logging.error(f"missing meson install tag: {null_file}") + exit_code = 1 + + # check for incorrect test file tags + for tag in [t for t in tags if t != "tests"]: + files = collect_files(install_plan, tag) + files = [f for f in files if "installed-tests" in f] + for f in files: + logging.error(f"file should have 'tests' tag: {f}") + exit_code = 1 + + if exit_code != 0: + logging.warning( + "meson build directory may be outdated which requires full wipe and new `meson setup`" + ) + return exit_code + + +def main() -> int: + parser = argparse.ArgumentParser(description="Meson install tag helper") + parser.add_argument( + "-C", dest="working_directory", default=".", help="Meson build directory" + ) + parser.add_argument( + "command", + choices=[ + "check", + "list", + "list-files", + "list-tags", + ], + ) + parser.add_argument("-t", "--tag", help="Optional tag for list-files command") + + args = parser.parse_args() + + install_plan = json.loads( + subprocess.check_output( + ["meson", "introspect", "--install-plan"], + cwd=args.working_directory, + text=True, + ) + ) + + exit_code = 0 + match args.command: + case "check": + exit_code = check(install_plan) + case "list": + print_all(install_plan) + case "list-files": + print_files(install_plan, args.tag) + case "list-tags": + print_tags(install_plan) + + return exit_code + + +if __name__ == "__main__": + logging.basicConfig( + level=logging.INFO, format="%(levelname)s: %(message)s", stream=sys.stderr + ) + + if "PRE_COMMIT" not in os.environ: + sys.exit(main()) + + if not os.path.exists("venv/build"): + logging.info("no configured build directory, skipping check-meson-install-tag") + sys.exit(0) + + install_plan = json.loads( + subprocess.check_output( + ["meson", "introspect", "--install-plan"], + cwd="venv/build", + text=True, + ) + ) + + if len(collect_files(install_plan, None)) > 10: + logging.info("build dir is likely outdated, skipping check-meson-install-tag") + sys.exit(0) + + check(install_plan) + sys.exit(0) diff -Nru fwupd-2.0.8/contrib/ci/check-null-false-returns.py fwupd-2.0.20/contrib/ci/check-null-false-returns.py --- fwupd-2.0.8/contrib/ci/check-null-false-returns.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/check-null-false-returns.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,240 +0,0 @@ -#!/usr/bin/env python3 -# pylint: disable=invalid-name,missing-docstring,too-many-branches -# pylint: disable=too-many-statements,too-many-return-statements,too-few-public-methods -# -# Copyright 2021 Richard Hughes -# -# SPDX-License-Identifier: LGPL-2.1-or-later - -import glob -import os -import sys -from typing import List - - -def _tokenize(line: str) -> List[str]: - # remove whitespace - line = line.strip() - line = line.replace("\t", "") - line = line.replace(";", "") - - # find value - line = line.replace(" ", "|") - line = line.replace(",", "|") - line = line.replace(")", "|") - line = line.replace("(", "|") - - # return empty tokens - tokens = [] - for token in line.rsplit("|"): - if token: - tokens.append(token) - return tokens - - -class ReturnValidator: - def __init__(self): - self.warnings: List[str] = [] - - # internal state - self._fn = None - self._line_num = None - self._value = None - self._nret = None - self._rvif = None - self._line = None - - @property - def _tokens(self) -> List[str]: - return _tokenize(self._line) - - @property - def _value_relaxed(self) -> str: - if self._value in ["0x0", "0x00", "0x0000"]: - return "0" - if self._value in ["0xffffffff"]: - return "G_MAXUINT32" - if self._value in ["0xffff"]: - return "G_MAXUINT16" - if self._value in ["0xff"]: - return "G_MAXUINT8" - if self._value in ["G_SOURCE_REMOVE"]: - return "FALSE" - if self._value in ["G_SOURCE_CONTINUE"]: - return "TRUE" - return self._value - - def _test_rvif(self) -> None: - # parse "g_return_val_if_fail (SOMETHING (foo), NULL);" - self._value = self._tokens[-1] - - # enumerated enum, so ignore - if self._value.find("_") != -1: - return - - # is invalid - if self._rvif and self._value_relaxed not in self._rvif: - self.warnings.append( - "{} line {} got {}, expected {}".format( - self._fn, self._line_num, self._value, ", ".join(self._rvif) - ) - ) - - def _test_return(self) -> None: - # parse "return 0x0;" - self._value = self._tokens[-1] - - # is invalid - if self._nret and self._value_relaxed in self._nret: - self.warnings.append( - "{} line {} got {}, which is not valid -- expected {}".format( - self._fn, self._line_num, self._value, "|".join(self._rvif) - ) - ) - - def parse(self, fn: str) -> None: - self._fn = fn - with open(fn) as f: - self._rvif = None - self._nret = None - self._line_num = 0 - for line in f.readlines(): - self._line_num += 1 - line = line.replace("LIBUSB_CALL", "") - line = line.rstrip() - if not line: - continue - if line.endswith("\\"): - continue - if line.endswith("&&"): - continue - self._line = line - idx = line.find("g_return_val_if_fail") - if idx != -1: - self._test_rvif() - continue - idx = line.find("return") - if idx != -1: - # continue - if len(self._tokens) == 2: - self._test_return() - continue - - # not a function header - if line[0] in ["#", " ", "\t", "{", "}", "/"]: - continue - - # label - if line.endswith(":"): - continue - - # remove prefixes - if line.startswith("static"): - line = line[7:] - if line.startswith("inline"): - line = line[7:] - - # a pointer - if line.endswith("*"): - self._rvif = ["NULL"] - self._nret = ["FALSE"] - continue - - # not a leading line - if line.find(" ") != -1: - continue - - # a type we know - if line in ["void"]: - self._rvif = [] - self._nret = [] - continue - if line in ["gpointer"]: - self._rvif = ["NULL"] - self._nret = ["FALSE"] - continue - if line in ["gboolean"]: - self._rvif = ["TRUE", "FALSE"] - self._nret = ["NULL", "0"] - continue - if line in ["guint32"]: - self._rvif = ["0", "G_MAXUINT32"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - if line in ["GQuark", "GType"]: - self._rvif = ["0"] - self._nret = ["NULL", "0", "TRUE", "FALSE"] - continue - if line in ["guint64"]: - self._rvif = ["0", "G_MAXUINT64"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - if line in ["guint16"]: - self._rvif = ["0", "G_MAXUINT16"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - if line in ["guint8"]: - self._rvif = ["0", "G_MAXUINT8"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - if line in ["gint64"]: - self._rvif = ["0", "-1", "G_MAXINT64"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - if line in ["gint32"]: - self._rvif = ["0", "-1", "G_MAXINT32"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - if line in ["gint16"]: - self._rvif = ["0", "-1", "G_MAXINT16"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - if line in ["gint8"]: - self._rvif = ["0", "-1", "G_MAXINT8"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - if line in ["gint", "int"]: - self._rvif = ["0", "-1", "G_MAXINT"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - if line in ["guint"]: - self._rvif = ["0", "G_MAXUINT"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - if line in ["gulong"]: - self._rvif = ["0", "G_MAXLONG"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - if line in ["gsize", "size_t"]: - self._rvif = ["0", "G_MAXSIZE"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - if line in ["gssize", "ssize_t"]: - self._rvif = ["0", "-1", "G_MAXSSIZE"] - self._nret = ["NULL", "TRUE", "FALSE"] - continue - # print('unknown return type {}'.format(line)) - self._rvif = None - self._nret = None - - -def test_files(): - # test all C source files - validator = ReturnValidator() - - for fn in ( - glob.glob("libfwupd/*.c") - + glob.glob("libfwupdplugin/*.c") - + glob.glob("plugins/*/*.c") - + glob.glob("src/*.c") - ): - validator.parse(fn) - for warning in validator.warnings: - print(warning) - - return 1 if validator.warnings else 0 - - -if __name__ == "__main__": - # all done! - sys.exit(test_files()) diff -Nru fwupd-2.0.8/contrib/ci/check-rss.py fwupd-2.0.20/contrib/ci/check-rss.py --- fwupd-2.0.8/contrib/ci/check-rss.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/check-rss.py 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,58 @@ +#!/usr/bin/env python3 +# pylint: disable=invalid-name,missing-module-docstring,missing-function-docstring +# +# Copyright 2025 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1-or-later + + +import argparse +import os +import sys +import subprocess + + +def _check(cwd: str, argv: list[str], limit: int = 0) -> int: + # prime cache + try: + subprocess.check_output(argv, cwd=cwd) + except subprocess.CalledProcessError as e: + print(e) + return 1 + try: + rc = subprocess.run( + ["valgrind"] + argv, + cwd=cwd, + stderr=subprocess.PIPE, + stdout=subprocess.PIPE, + encoding="utf-8", + check=True, + ) + except subprocess.CalledProcessError as e: + print(e) + return 1 + value: int = 0 + for line in rc.stderr.split("\n"): + if line.find("in use at exit: ") != -1: + value = int(line.split(" ")[9].strip().replace(",", "")) + if limit and value > limit * 1024: + print(f"RSS usage was {value//1024}kB (limit of {limit}kB)") + return 1 + print(f"RSS usage was {value//1024}kB") + return 0 + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Check RSS usage") + parser.add_argument("--limit", type=int, help="RSS limit in kB") + parser.add_argument("command", nargs="+", help="Command to run and measure") + args = parser.parse_args() + try: + sys.exit( + _check( + cwd=os.path.dirname(sys.argv[0]), argv=args.command, limit=args.limit + ) + ) + except IndexError: + parser.print_help() + sys.exit(1) diff -Nru fwupd-2.0.8/contrib/ci/check-source.py fwupd-2.0.20/contrib/ci/check-source.py --- fwupd-2.0.8/contrib/ci/check-source.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/check-source.py 2026-02-26 11:36:18.000000000 +0000 @@ -1,75 +1,119 @@ #!/usr/bin/env python3 -# pylint: disable=invalid-name,missing-module-docstring,missing-function-docstring # # Copyright 2023 Richard Hughes # # SPDX-License-Identifier: LGPL-2.1-or-later +# +# pylint: disable=invalid-name,missing-module-docstring,missing-function-docstring +# pylint: disable=too-many-lines,too-many-return-statements,missing-class-docstring +# pylint: disable=too-many-arguments,too-many-positional-arguments,too-many-statements +# pylint: disable=too-few-public-methods,too-many-branches,protected-access import glob import sys import os +import argparse from typing import List, Optional +from ctokenizer import Tokenizer, Node, NodeHint, Token, TokenHint -def _find_func_name(line: str) -> Optional[str]: - # these are not functions - for prefix in ["\t", " ", "typedef", "__attribute__", "G_DEFINE_"]: - if line.startswith(prefix): - return None - - # ignore prototypes - if line.endswith(";"): - return None - - # strip to function name then test for validity - idx: int = line.find("(") - if idx == -1: - return None - func_name = line[:idx] - if func_name.find(" ") != -1: - return None +# convert a CamelCase name into snake_case +def _camel_to_snake(name: str) -> str: + name_snake: str = "" + for char in name: + if char.islower() or char.isnumeric(): + name_snake += char + continue + if char == "_": + name_snake += char + continue + if name_snake: # and not char.isnumeric(): + name_snake += "_" + name_snake += char.lower() + return name_snake + - # success! - return func_name +def _value_relaxed(data: str) -> str: + if data in ["0x0", "0x00", "0x0000"]: + return "0" + if data in ["G_SOURCE_REMOVE"]: + return "FALSE" + if data in ["G_SOURCE_CONTINUE"]: + return "TRUE" + return data class SourceFailure: - def __init__(self, fn=None, linecnt=None, message=None, nocheck=None): + def __init__( + self, fn=None, linecnt=None, message=None, nocheck=None, expected=False + ) -> None: self.fn: Optional[str] = fn self.linecnt: Optional[int] = linecnt self.message: Optional[str] = message self.nocheck: Optional[str] = nocheck + self.expected: bool = expected class Checker: - MAX_FUNCTION_LINES: int = 400 - def __init__(self): + def __init__(self, verbose: bool = False) -> None: + self.verbose: bool = verbose self.failures: List[SourceFailure] = [] self._current_fn: Optional[str] = None - self._current_linecnt: Optional[int] = None self._current_nocheck: Optional[str] = None + self._gtype_parents: dict[str, str] = {} + self._klass_funcs: list[str] = [] + self._expected_failure_prefixes: list[str] = [] + + def add_expected_failure(self, message_prefix: str) -> None: + self._expected_failure_prefixes.append(message_prefix) + + def _should_process_node(self, node: Node) -> bool: + + if self._current_nocheck: + if node.tokens_pre.count_fuzzy([f"~/* {self._current_nocheck} */"]) > 0: + return False + if node.tokens.count_fuzzy([f"~/* {self._current_nocheck} */"]) > 0: + return False + return True + + def add_failure(self, message=None, linecnt: Optional[int] = None) -> None: + + # we were expecting this + expected: bool = False + for message_prefix in self._expected_failure_prefixes: + if message.startswith(message_prefix): + expected = True + break - def add_failure(self, message=None): + # add self.failures.append( SourceFailure( + expected=expected, fn=self._current_fn, - linecnt=self._current_linecnt, + linecnt=linecnt, message=message, nocheck=self._current_nocheck, ) ) - def _test_line_function_names_private(self, func_name: str) -> None: + def has_failure(self, message_prefix: str) -> bool: + for failure in self.failures: + if failure.message and failure.message.startswith(message_prefix): + return True + return False + + def _test_function_names_prefix_private(self, func_name: str, token: Token) -> None: valid_prefixes = ["_fwupd_", "_fu_", "_g_", "_xb_"] for prefix in valid_prefixes: if func_name.startswith(prefix): return self.add_failure( - f"invalid function name {func_name} should have {'|'.join(valid_prefixes)} prefix" + f"invalid function name {func_name} should have {'|'.join(valid_prefixes)} prefix", + linecnt=token.linecnt, ) - def _test_line_function_names_valid(self, func_name: str) -> None: + def _test_function_names_prefix_valid(self, func_name: str, token: Token) -> None: # sanity check if not self._current_fn: return @@ -87,7 +131,7 @@ return # doh - if func_name in ["main", "fu_plugin_init_vfuncs"]: + if func_name in ["main", "fu_plugin_init_vfuncs", "fu_plugin_get_data"]: return # this is stuff that should move to GLib @@ -113,7 +157,7 @@ valid_prefixes = [] valid_prefixes.append(prefix) for key, value in { - "fu_crc": "fu_misr", # FIXME: split out to fu-misr.[c|h] + "fu_crc": "fu_misr", # split out to fu-misr.[c|h]? "fu_darwin_efivars": "fu_efivars", "fu_dbus_daemon": "fu_daemon", "fu_dbxtool": "fu_util", @@ -133,55 +177,500 @@ return self.add_failure( - f"invalid function name {func_name} should have {'|'.join(valid_prefixes)} prefix" + f"invalid function name {func_name} should have {'|'.join(valid_prefixes)} prefix", + linecnt=token.linecnt, ) - def _test_line_function_names(self, line: str) -> None: - # empty line - if not line: + def _discover_klass_functions(self, node: Node) -> None: + """discover all the device_class-> functions""" + + if node.depth != 0: + return + idx = node.tokens_pre.find_fuzzy(["~fu_*_class_init@FUNCTION"]) + if idx == -1: return - # skip! - self._current_nocheck = "nocheck:name" - if line.find(self._current_nocheck) != -1: + idx = 0 + while True: + idx = node.tokens.find_fuzzy(["-", ">", "~*", "="], offset=idx + 1) + if idx == -1: + break + self._klass_funcs.append(node.tokens[idx + 4].data) + + def _test_variable_case(self, node: Node) -> None: + """disallow non-lowercase variables""" + + if node.hint == NodeHint.ENUM: + return + idx = node.tokens.find_fuzzy(["="]) + if idx == -1: + return + token = node.tokens[idx - 1] + if token.data.find(".") != -1: + return + if token.data.lower() != token.data: + self.add_failure( + f"mixed case variable {token.data}", + linecnt=token.linecnt, + ) + + def _test_struct_member_case(self, node: Node) -> None: + """disallow non-lowercase struct members""" + + if node.hint not in [NodeHint.STRUCT, NodeHint.STRUCT_TYPEDEF]: + return + idx: int = 0 + while True: + idx = node.tokens.find_fuzzy([";"], offset=idx + 1) + if idx == -1: + return + token = node.tokens[idx - 1] + if token.data.lower() != token.data: + self.add_failure( + f"mixed case struct member {token.data}", + linecnt=token.linecnt, + ) + + def _test_param_self_native_device(self, node: Node) -> None: + """use @self for native internal functions, not the basetype""" + + if self._current_fn and os.path.basename(self._current_fn) in [ + "fu-device.c", + "fu-device-locker.c", + "fu-device-progress.c", + ]: + return + if self._current_fn and os.path.basename(self._current_fn).endswith(".h"): + return + if node.depth != 0: + return + idx = node.tokens_pre.find_fuzzy(["@FUNCTION", "(", "FuDevice", "~*", "device"]) + if idx == -1: + return + token = node.tokens_pre[idx] + if token.data.endswith("_cb"): + return + if token.data in self._klass_funcs: + return + self.add_failure( + "native device functions should use self as the first parameter not device", + linecnt=token.linecnt, + ) + + def _test_param_self_native_firmware(self, node: Node) -> None: + """use @self for native internal functions, not the basetype""" + + if self._current_fn and os.path.basename(self._current_fn) in [ + "fu-firmware-progress.c", + "fu-self-test.c", + ]: + return + if self._current_fn and os.path.basename(self._current_fn).endswith(".h"): + return + if self._current_fn and os.path.basename(self._current_fn).endswith( + "-common.c" + ): + return + if node.depth != 0: + return + idx = node.tokens_pre.find_fuzzy( + ["@FUNCTION", "(", "FuFirmware", "~*", "firmware"] + ) + if idx == -1: + return + token = node.tokens_pre[idx] + if token.data in self._klass_funcs: + return + self.add_failure( + "native firmware functions should use self as the first parameter not firmware", + linecnt=token.linecnt, + ) + + def _test_param_self_device(self, node: Node) -> None: + """only use @self for device GTypes, not the basetype""" + + if self._current_fn and os.path.basename(self._current_fn) in [ + "fu-device.c", + "fu-device.h", + "fu-device-poll-locker.h", + "fu-device-private.h", + ]: + return + if node.depth != 0: + return + idx = node.tokens_pre.find_fuzzy(["@FUNCTION", "(", "FuDevice", "*", "self"]) + if idx == -1: + return + token = node.tokens_pre[idx + 4] + self.add_failure( + f"invalid parameter name {token.data} should be called 'device'", + linecnt=token.linecnt, + ) + + def _test_param_self_firmware(self, node: Node) -> None: + """only use @self for firmware GTypes, not the basetype""" + + if self._current_fn and os.path.basename(self._current_fn) in [ + "fu-firmware.c", + ]: + return + if node.depth != 0: + return + idx = node.tokens_pre.find_fuzzy(["@FUNCTION", "(", "FuFirmware", "*", "self"]) + if idx == -1: + return + token = node.tokens_pre[idx + 4] + self.add_failure( + f"invalid parameter name {token.data} should be called 'firmware'", + linecnt=token.linecnt, + ) + + def _test_function_names_ensure(self, node: Node) -> None: + """setting internal state should be 'ensuring' the data, not setting it""" + + if node.depth != 0: return - # parse - func_name: str = _find_func_name(line) - if not func_name: + idx = node.tokens_pre.find_fuzzy( + [ + "gboolean", + "~fu_*_set_*@FUNCTION", + "(", + "~*", + "*", + "self", + ",", + "GError", + "*", + "*", + "error", + ")", + ] + ) + if idx != -1: + token = node.tokens_pre[idx] + self.add_failure( + "function should be called ensure, not set", + linecnt=token.linecnt, + ) + + def _test_function_names_prefix(self, node: Node) -> None: + + if node.depth != 0: return - if func_name.startswith("_"): - self._test_line_function_names_private(func_name) + + idx: int = 0 + func_name: str = "" + while True: + idx = node.tokens_pre.find_fuzzy(["@FUNCTION", "("], offset=idx + 1) + if idx == -1: + return + + # sanity check + token = node.tokens_pre[idx] + func_name = token.data + if len(func_name) < 10: + continue + if func_name in ["__attribute__"]: + continue + if self.verbose: + print("func_name", func_name) + if func_name.upper() != func_name and func_name.lower() != func_name: + self.add_failure( + "mixed case function name", + linecnt=token.linecnt, + ) + if func_name.startswith("_"): + self._test_function_names_prefix_private(func_name, token) + else: + self._test_function_names_prefix_valid(func_name, token) + + def _test_missing_literal(self, node: Node) -> None: + """test for missing literals""" + idx = node.tokens.find_fuzzy( + ["g_task_return_new_error", "(", "~*", ",", "~*", ",", "~*", ",", "~*", ")"] + ) + if idx != -1: + token = node.tokens[idx] + self.add_failure( + "missing literal, use g_task_return_new_error_literal() instead", + linecnt=token.linecnt, + ) + idx = node.tokens.find_fuzzy(["g_prefix_error", "(", "~*", ",", "~*", ")"]) + if idx != -1: + token = node.tokens[idx] + self.add_failure( + "missing literal, use g_prefix_error_literal() instead", + linecnt=token.linecnt, + ) + + idx = node.tokens.find_fuzzy( + ["g_set_error", "(", "~*", ",", "~*", ",", "~*", ",", "~*", ")"] + ) + if idx != -1: + if node.tokens[idx + 8].data.find("%m") == -1: + self.add_failure( + "missing literal, use g_set_error_literal() instead", + linecnt=node.linecnt, + ) + + def _test_missing_error_suffixes(self, node: Node) -> None: + """test for missing : suffixes""" + idx = node.tokens.find_fuzzy(["~g_prefix_error*", "(", "~*", ","]) + if idx == -1: + return + token = node.tokens[idx + 4] + if not token.data.endswith(': "'): + self.add_failure("missing ': ' suffix", linecnt=token.linecnt) + + def _test_extra_error_suffixes(self, node: Node) -> None: + """test for extra : suffixes""" + idx = node.tokens.find_fuzzy(["~g_set_error*", "(", "~*[", ","]) + if idx == -1: return - self._test_line_function_names_valid(func_name) + token = node.tokens[idx + 8] + if token.data.endswith(': "'): + self.add_failure("extraneous ': ' suffix", linecnt=token.linecnt) - def _test_line_enums(self, line: str) -> None: - # skip! - self._current_nocheck = "nocheck:prefix" - if line.find(self._current_nocheck) != -1: + def _test_enums(self, node: Node) -> None: + if node.depth != 0: return + # only consider the last token + idx = node.tokens_pre.find_fuzzy(["@ENUM"], offset=len(node.tokens_pre) - 1) + if idx == -1: + return + name = node.tokens_pre[idx].data + if self.verbose: + print("enum_name", name) + # needs Fu prefix - enum_name = None valid_prefixes = ["Fwupd", "Fu"] - if line.startswith("enum ") and line.endswith("{"): - enum_name = line[5:-2] - elif line.startswith("typedef enum ") and line.endswith("{"): - enum_name = line[13:-2] - if not enum_name: + for prefix in valid_prefixes: + if name.startswith(prefix): + return + self.add_failure( + f"invalid enum name {name} should have {'|'.join(valid_prefixes)} prefix", + linecnt=node.linecnt, + ) + + def _test_struct(self, node: Node) -> None: + + if node.depth != 0: + return + + # no limit on name + if node.hint == NodeHint.STRUCT_CONST: return + + # only consider the last token + name: Optional[str] = None + if node.hint == NodeHint.STRUCT_TYPEDEF: + name = node.tokens_pre[-1].data + else: + idx = node.tokens_pre.find_fuzzy( + ["@STRUCT"], offset=len(node.tokens_pre) - 1 + ) + if idx != -1: + name = node.tokens_pre[idx].data + if not name: + return + if name.startswith("_"): + name = name[1:] + if self.verbose: + print("struct_name", name) + + # only for Rust code + if name.startswith("FuStruct"): + self.add_failure( + f"incorrect struct name {name} -- FuStruct is reserved for Rust code", + linecnt=node.linecnt, + ) + + # needs Fu prefix + valid_prefixes = ["Fwupd", "Fu"] for prefix in valid_prefixes: - if enum_name.startswith(prefix): + if name.startswith(prefix): return self.add_failure( - f"invalid enum name {enum_name} should have {'|'.join(valid_prefixes)} prefix" + f"invalid struct name {name} should have {'|'.join(valid_prefixes)} prefix", + linecnt=node.linecnt, ) - def _test_line_debug_fns(self, line: str) -> None: - # no console output expected - self._current_nocheck = "nocheck:print" - if line.find(self._current_nocheck) != -1: + def _test_static_vars(self, node: Node) -> None: + + if node.depth != 0: + return + + idx = node.tokens_pre.find_fuzzy(["static", "~*", "~*", "="]) + if idx == -1: + return + + token = node.tokens_pre[idx + 2] + if token.data in ["signals", "quarks"]: + return + self.add_failure( + f"static variable {token.data} not allowed", linecnt=token.linecnt + ) + + def _test_rustgen_bitshifts(self, node: Node) -> None: + + if node.hint == NodeHint.ENUM: + return + idx = node.tokens.find_fuzzy(["]", "<", "<", "16"]) + if idx != -1 and node.tokens.find_fuzzy(["]", "<", "<", "8"]) != -1: + token = node.tokens[idx + 3] + self.add_failure( + "endian unsafe construction; perhaps use fu_memread_uint32 or rustgen", + linecnt=token.linecnt, + ) + + def _test_rustgen_vars(self, node: Node) -> None: + + idx = node.tokens.find_fuzzy(["g_autoptr", "(", "~FuStruct*", ")", "~*", "="]) + if idx == -1: + return + token = node.tokens[idx + 4] + if not token.data.startswith("st"): + self.add_failure( + f"rustgen structure '{token.data}' has to have 'st' prefix", + linecnt=token.linecnt, + ) + + def _test_zero_init(self, node: Node) -> None: + if node.hint in [ + NodeHint.UNION, + NodeHint.STRUCT, + NodeHint.STRUCT_CONST, + NodeHint.STRUCT_TYPEDEF, + ]: + return + if node.tokens_pre.count_fuzzy(["struct"]) > 0: return + idx = node.tokens.find_fuzzy(["~guint*", "~*", "[", "~*", "]", ";"]) + if idx != -1: + token = node.tokens[idx] + self.add_failure( + "buffer not zero init, use ` = {0}`", linecnt=token.linecnt + ) + + def _test_debug_newlines(self, node: Node) -> None: + + for func in ["g_info", "g_debug", "g_message"]: + idx: int = 0 + while True: + idx = node.tokens.find_fuzzy([func, "(", "~*\\n*"], offset=idx + 1) + if idx == -1: + break + token = node.tokens[idx] + self.add_failure( + f"{func} should not contain newlines", linecnt=token.linecnt + ) + + def _test_debug_fullstops(self, node: Node) -> None: + + for func in ["g_error", "g_info", "g_debug", "g_message"]: + idx: int = 0 + while True: + idx = node.tokens.find_fuzzy([func, "(", '~*."'], offset=idx + 1) + if idx == -1: + break + token = node.tokens[idx] + self.add_failure( + f"{func} should not end with a full stop", linecnt=token.linecnt + ) + + def _test_debug_sentence_case(self, node: Node) -> None: + + for func in ["g_error", "g_info", "g_debug", "g_message"]: + idx: int = 0 + while True: + idx = node.tokens.find_fuzzy([func], offset=idx + 1) + if idx == -1: + break + token = node.tokens[idx + 2] + first_word = token.data[1:].split(" ")[0] + if first_word and first_word[0].isupper() and first_word[1:].islower(): + self.add_failure( + f"{func} should not use sentence case", linecnt=token.linecnt + ) + + def _test_comment_cpp(self, node: Node) -> None: + """/* C comments please */""" + idx = node.tokens.find_fuzzy(["//"]) + if idx != -1: + token = node.tokens[idx] + self.add_failure( + f"use C style comments, not C++, e.g. /* this */", + linecnt=token.linecnt, + ) + + def _test_comment_lower_case(self, node: Node) -> None: + """single line comments are supposed to be lowercase""" + idx: int = 0 + while True: + idx = node.tokens.find_fuzzy(["@COMMENT"], offset=idx + 1) + if idx == -1: + break + token = node.tokens[idx] + first_word = token.data[2:-2].strip().split(" ")[0] + if ( + first_word not in ["Windows", "Microsoft", "Thunderbolt", "Dell"] + and token.linecnt == token.linecnt_end + and first_word + and first_word[0].isupper() + and first_word[1:].islower() + ): + self.add_failure( + f"single line comments should not use sentence case", + linecnt=token.linecnt, + ) + + def _test_equals_true(self, node: Node) -> None: + """do not allow `if (foo == TRUE)`""" + + idx = node.tokens_pre.find_fuzzy(["==", "TRUE", ")"]) + if idx != -1: + token = node.tokens_pre[idx] + self.add_failure( + f"do not compare a boolean to TRUE", + linecnt=token.linecnt, + ) + + def _test_blocked_goto(self, node: Node) -> None: + idx = node.tokens.find_fuzzy(["goto"]) + if idx != -1: + token = node.tokens_pre[idx] + self.add_failure( + f"do not use goto, refactor into a new block", + linecnt=token.linecnt, + ) + + def _test_device_display(self, node: Node) -> None: + """use fu_device_get_id_display rather than the two different commands""" + + idx1 = node.tokens.find_fuzzy(["fu_device_get_name", "(", "~*", ")"]) + idx2 = node.tokens.find_fuzzy(["fu_device_get_id", "(", "~*", ")"]) + if idx1 != -1 and idx2 != -1: + + # check this isn't an assert + if node.tokens[idx1 - 2].data == "g_assert_cmpstr": + return + + # same FuDevice within a limited number of lines? + token1 = node.tokens[idx1 + 2] + token2 = node.tokens[idx2 + 2] + limit: int = 3 + if token1.data == token2.data: + if abs(token1.linecnt - token2.linecnt) < limit: + self.add_failure( + "use fu_device_get_id_display() rather than " + "fu_device_get_name()+fu_device_get_id()", + linecnt=token1.linecnt, + ) + + def _test_debug_fns(self, node: Node) -> None: + # no console output expected if self._current_fn and os.path.basename(self._current_fn) in [ "fu-console.c", "fu-daemon.c", @@ -197,264 +686,729 @@ ]: return for token, msg in { - "g_print(": "Use g_debug() instead", - "g_printerr(": "Use g_debug() instead", + "~g_print*": "Use g_debug() instead", }.items(): - if line.find(token) != -1: - self.add_failure(f"contains blocked token {token}: {msg}") + idx = node.tokens.find_fuzzy([token]) + if idx != -1: + token = node.tokens[idx] + self.add_failure( + f"contains blocked token {token.data}: {msg}", linecnt=token.linecnt + ) + + def _test_gobject_finalize(self, node: Node) -> None: + + if node.tokens_pre.endswith_fuzzy( + ["void", "~*_finalize", "(", "GObject", "*", "~*", ")"] + ): + token = node.tokens_pre[-1] + idx = node.tokens.find_fuzzy( + ["G_OBJECT_CLASS", "(", "~*_parent_class", ")", "-", ">", "finalize"] + ) + if idx == -1: + self.add_failure( + "did not have parent ->finalize()", linecnt=token.linecnt + ) + + def _test_blocked_funcs(self, node: Node) -> None: - def _test_line_blocked_fns(self, line: str) -> None: - self._current_nocheck = "nocheck:blocked" - if line.find(self._current_nocheck) != -1: - return for token, msg in { - "cbor_get_uint8(": "Use cbor_get_int() instead", - "cbor_get_uint16(": "Use cbor_get_int() instead", - "cbor_get_uint32(": "Use cbor_get_int() instead", - "g_error(": "Use GError instead", - "g_byte_array_free_to_bytes(": "Use g_bytes_new() instead", - "g_usb_device_bulk_transfer(": "Use fu_usb_device_bulk_transfer() instead", - "g_usb_device_claim_interface(": "Use fu_usb_device_claim_interface() instead", - "g_usb_device_control_transfer(": "Use fu_usb_device_control_transfer() instead", - "g_usb_device_get_configuration_index(": "Use fu_usb_device_get_configuration_index() instead", - "g_usb_device_get_custom_index(": "Use fu_usb_device_get_custom_index() instead", - "g_usb_device_get_device_class(": "Use fu_usb_device_get_class() instead", - "g_usb_device_get_interface(": "Use fu_usb_device_get_interface() instead", - "g_usb_device_get_interfaces(": "Use fu_usb_device_get_interfaces() instead", - "g_usb_device_get_release(": "Use fu_usb_device_get_release() instead", - "g_usb_device_get_serial_number_index(": "Use fu_usb_device_get_serial_number_index() instead", - "g_usb_device_get_string_descriptor_bytes_full(": "Use fu_usb_device_get_string_descriptor_bytes_full() instead", - "g_usb_device_get_string_descriptor desc_index(": "Use fu_usb_device_get_string_descriptor desc_index() instead", - "g_usb_device_interrupt_transfer(": "Use fu_usb_device_interrupt_transfer() instead", - "g_usb_device_release_interface(": "Use fu_usb_device_release_interface() instead", - "g_usb_device_reset error(": "Use fu_usb_device_reset error() instead", - "g_usb_device_set_interface_alt(": "Use fu_usb_device_set_interface_alt() instead", - "g_ascii_strtoull(": "Use fu_strtoull() instead", - "g_ascii_strtoll(": "Use fu_strtoll() instead", - "g_random_int_range(": "Use a predicatable token instead", - "g_assert(": "Use g_set_error() or g_return_val_if_fail() instead", - "g_udev_device_get_sysfs_attr(": "Use fu_udev_device_read_sysfs() instead", - "g_udev_device_get_property(": "Use fu_udev_device_read_property() instead", - "g_udev_client_new(": "Use fu_backend_create_device() instead", + "~cbor_get_uint?": "Use cbor_get_int() instead", + "~cbor_get_uint??": "Use cbor_get_int() instead", + "g_error": "Use GError instead", + "g_byte_array_free_to_bytes": "Use g_bytes_new() instead", + "g_ascii_strtoull": "Use fu_strtoull() instead", + "g_ascii_strtoll": "Use fu_strtoll() instead", + "g_strerror": "Use fwupd_strerror() instead", + "g_random_int_range": "Use a predicatable token instead", + "g_assert": "Use g_set_error() or g_return_val_if_fail() instead", "HIDIOCSFEATURE": "Use fu_hidraw_device_set_feature() instead", "HIDIOCGFEATURE": "Use fu_hidraw_device_get_feature() instead", - "|= 1 <<": "Use FU_BIT_SET() instead", - "|= 1u <<": "Use FU_BIT_SET() instead", - "|= 1ull <<": "Use FU_BIT_SET() instead", - "|= (1 <<": "Use FU_BIT_SET() instead", - "|= (1u <<": "Use FU_BIT_SET() instead", - "|= (1ull <<": "Use FU_BIT_SET() instead", - "&= ~(1 <<": "Use FU_BIT_CLEAR() instead", - "&= ~(1u <<": "Use FU_BIT_CLEAR() instead", - "&= ~(1ull <<": "Use FU_BIT_CLEAR() instead", - "__attribute__((packed))": "Use rustgen instead", - "memcpy(": "Use fu_memcpy_safe or rustgen instead", - "GUINT16_FROM_BE(": "Use fu_memread_uint16_safe() or rustgen instead", - "GUINT16_FROM_LE(": "Use fu_memread_uint16_safe() or rustgen instead", - "GUINT16_TO_BE(": "Use fu_memwrite_uint16_safe() or rustgen instead", - "GUINT16_TO_LE(": "Use fu_memwrite_uint16_safe() or rustgen instead", - "GUINT32_FROM_BE(": "Use fu_memread_uint32_safe() or rustgen instead", - "GUINT32_FROM_LE(": "Use fu_memread_uint32_safe() or rustgen instead", - "GUINT32_TO_BE(": "Use fu_memwrite_uint32_safe() or rustgen instead", - "GUINT32_TO_LE(": "Use fu_memwrite_uint32_safe() or rustgen instead", - "GUINT64_FROM_BE(": "Use fu_memread_uint64_safe() or rustgen instead", - "GUINT64_FROM_LE(": "Use fu_memread_uint64_safe() or rustgen instead", - "GUINT64_TO_BE(": "Use fu_memwrite_uint64_safe() or rustgen instead", - "GUINT64_TO_LE(": "Use fu_memwrite_uint64_safe() or rustgen instead", - " ioctl(": "Use fu_udev_device_ioctl() instead", + "memcpy": "Use fu_memcpy_safe or rustgen instead", + "~GUINT??_FROM_?E": "Use fu_memread_uintXX_safe() or rustgen instead", + "~GUINT??_TO_?E": "Use fu_memwrite_uintXX_safe() or rustgen instead", + "ioctl": "Use fu_udev_device_ioctl() instead", }.items(): - if line.find(token) != -1: - self.add_failure(f"contains blocked token {token}: {msg}") + idx = node.tokens.find_fuzzy([token, "("]) + if idx != -1: + token = node.tokens[idx] + self.add_failure( + f"contains blocked token {token.data}: {msg}", linecnt=token.linecnt + ) - def _test_lines_gerror(self, lines: List[str]) -> None: - self._current_nocheck = "nocheck:error" - linecnt_g_set_error: int = 0 - for linecnt, line in enumerate(lines): - if line.find(self._current_nocheck) != -1: - continue - self._current_linecnt = linecnt + 1 - - # do not use G_IO_ERROR internally - if line.find("g_set_error") != -1: - linecnt_g_set_error = linecnt - if linecnt - linecnt_g_set_error < 5: - for error_domain in ["G_IO_ERROR", "G_FILE_ERROR"]: - if line.find(error_domain) != -1: - self.add_failure("uses g_set_error() without using FWUPD_ERROR") + idx = node.tokens.find_fuzzy(["|=", "~1*", "<", "<"]) + if idx != -1: + token = node.tokens[idx] + self.add_failure("Use FU_BIT_SET() instead", linecnt=token.linecnt) + idx = node.tokens.find_fuzzy(["|=", "(", "~1*", "<", "<"]) + if idx != -1: + token = node.tokens[idx] + self.add_failure("Use FU_BIT_SET() instead", linecnt=token.linecnt) + idx = node.tokens.find_fuzzy(["&", "=", "~", "(", "~1*", "<", "<"]) + if idx != -1: + token = node.tokens[idx] + self.add_failure("Use FU_BIT_CLEAR() instead", linecnt=token.linecnt) + idx = node.tokens_pre.find_fuzzy( + ["__attribute__", "(", "(", "packed", ")", ")"] + ) + if idx != -1: + token = node.tokens_pre[idx] + self.add_failure("use rustgen instead", linecnt=token.linecnt) + + def _test_magic_numbers_defined(self, nodes: list[Node]) -> None: + + cnt: int = 0 + limit: int = 15 + linecnt: int = 0 + for node in nodes: + if node.depth != 0: + continue + + # overridden per-file + idx = node.tokens_pre.find_fuzzy([f"~*{self._current_nocheck}=*"]) + if idx != -1: + limit = int(node.tokens_pre[idx].data.split("=")[1].split(" ")[0]) + cnt_tmp = node.tokens_pre.count_fuzzy(["#define", "~*", "~0x*"]) + if cnt_tmp: + linecnt = node.linecnt + cnt += cnt_tmp + if cnt > limit: + self.add_failure( + f"file has too many #defined magic values ({cnt}), limit of {limit}", + linecnt=linecnt, + ) + + def _test_magic_numbers_inline(self, nodes: list[Node]) -> None: + + cnt: int = 0 + limit: int = 80 + linecnt: int = 0 + for node in nodes: + # overridden per-file + idx = node.tokens_pre.find_fuzzy([f"~*{self._current_nocheck}=*"]) + if idx != -1: + for word in node.tokens_pre[idx].data.split(" "): + if word.startswith("nocheck:magic-inlines="): + limit = int(word[22:]) break + for token in node.tokens: + if token.data in ["0x0", "0x00", "0x0000"]: + continue + if len(token.data) >= 3 and token.data.startswith("0x"): + linecnt = node.linecnt + cnt += 1 + if cnt > limit: + self.add_failure( + f"file has too many inline magic values ({cnt}), limit of {limit}", + linecnt=linecnt, + ) - def _test_lines_function_length(self, lines: List[str]) -> None: - self._current_nocheck = "nocheck:lines" - func_begin: int = 0 - func_name: Optional[str] = None - for linecnt, line in enumerate(lines): - if line.find(self._current_nocheck) != -1: - func_begin = 0 - continue - if line == "{": - func_begin = linecnt - continue - if func_begin > 0 and line == "}": - self._current_linecnt = func_begin - if linecnt - func_begin > self.MAX_FUNCTION_LINES: - if func_name: - self.add_failure( - f"{func_name} is too long, was {linecnt - func_begin} of {self.MAX_FUNCTION_LINES}" - ) - else: + def _test_gerror_false_returns(self, nodes: list[Node]) -> None: + + for node in nodes: + if node.depth == 0: + continue + if not self._should_process_node(node): + return + idx = node.tokens.find_fuzzy(["~g_set_error*", "("]) + if idx != -1: + token = node.tokens[idx] + idx = node.tokens.find_fuzzy(["return", "~*", ";"], offset=idx) + if idx == -1: + idx = node.tokens.find_fuzzy(["break", ";"], offset=idx) + if idx == -1: + self.add_failure( + "uses g_set_error() without returning a value", + linecnt=token.linecnt, + ) + break + + def _test_gerror_not_set(self, nodes: list[Node]) -> None: + + limit: int = 10 + for node in nodes: + if node.depth == 0: + continue + if not self._should_process_node(node): + continue + idx = node.tokens.find_fuzzy(["~g_prefix_error*", "("]) + if idx != -1: + linecnt = node.tokens[idx].linecnt + if self.verbose: + print(f"GError required @{linecnt}") + + found_linecnt: list[int] = [] + + # set error inner + idx_found = node.tokens.find_fuzzy( + ["~error*", ")"], reverse=True, offset=idx + ) + if idx_found != -1: + found_linecnt.append(node.tokens[idx_found].linecnt_end) + idx_found = node.tokens.find_fuzzy( + ["~g_set_error*", "(", "~error*"], reverse=True, offset=idx + ) + if idx_found != -1: + found_linecnt.append(node.tokens[idx_found].linecnt_end) + + # set error prior + idx_found = node.tokens_pre.find_fuzzy(["~error*", ")"], reverse=True) + if idx_found != -1: + found_linecnt.append(node.tokens_pre[idx_found].linecnt_end) + idx_found = node.tokens_pre.find_fuzzy( + ["~g_set_error*", "(", "~error*"], reverse=True + ) + if idx_found != -1: + found_linecnt.append(node.tokens_pre[idx_found].linecnt_end) + idx_found = node.tokens_pre.find_fuzzy(["~error*", ","], reverse=True) + if idx_found != -1: + found_linecnt.append(node.tokens_pre[idx_found].linecnt_end) + + # find the closest error set + linecnt_closest: int = 0 + if found_linecnt: + linecnt_closest = max(found_linecnt) + if self.verbose: + print(f"GError set @{linecnt_closest}") + + linecnt_delta = linecnt - linecnt_closest + if linecnt_delta > limit: + self.add_failure( + "uses g_prefix_error() without setting GError for " + f"{linecnt_delta}/{limit} previous lines", + linecnt=linecnt, + ) + break + + def _test_gerror_domain(self, node: Node) -> None: + """must use FUWPD_ERROR domains""" + idx = node.tokens.find_fuzzy( + ["~g_set_error*", "(", "~*", ",", "~G_*", ",", "~G_*"] + ) + if idx == -1: + return + token = node.tokens[idx] + self.add_failure( + "uses g_set_error() without using FWUPD_ERROR", linecnt=token.linecnt + ) + + def _test_gerror_deref(self, node: Node) -> None: + """using (*error)->message""" + idx = node.tokens.find_fuzzy(["(", "*", "~*error*", ")", "-", ">"]) + if idx == -1: + return + token = node.tokens[idx] + self.add_failure( + "dereferences GError; use error_local instead", linecnt=token.linecnt + ) + + def _test_switch(self, nodes: list[Node]) -> None: + + limit: int = 2 + cnt: int = 0 + for node in nodes: + # restrict only per-top-level=function, not per-file + if node.depth == 0: + cnt = 0 + idx = node.tokens_pre.find_fuzzy(["switch", "(", "~*", ")"]) + if idx != -1: + cnt += 1 + if cnt > limit: + self.add_failure( + f"has too many switches ({cnt}), limit of {limit}", + linecnt=node.linecnt, + ) + break + + def _test_null_false_returns(self, nodes: list[Node]) -> None: + + # allowed values from g_return_val_if_fail() + types_rvif = { + "*": ["NULL"], + "GQuark": ["0"], + "GType": ["G_TYPE_INVALID"], + "gpointer": ["NULL"], + "gboolean": ["TRUE", "FALSE"], + "guint32": ["0", "G_MAXUINT32"], + "guint64": ["0", "G_MAXUINT64"], + "guint16": ["0", "G_MAXUINT16"], + "guint8": ["0", "G_MAXUINT8"], + "gint64": ["0", "-1", "G_MAXINT64"], + "gint32": ["0", "-1", "G_MAXINT32"], + "gint16": ["0", "-1", "G_MAXINT16"], + "gint8": ["0", "-1", "G_MAXINT8"], + "gint": ["0", "-1", "G_MAXINT"], + "guint": ["0", "G_MAXUINT"], + "gulong": ["0", "G_MAXLONG"], + "gsize": ["0", "G_MAXSIZE"], + "gssize": ["0", "-1", "G_MAXSSIZE"], + } + + # disallowed values from return + types_nret = { + "*": ["FALSE"], + "GQuark": ["NULL", "TRUE", "FALSE"], + "GType": ["NULL", "TRUE", "FALSE"], + "gpointer": ["FALSE"], + "gboolean": ["NULL", "0"], + "guint32": ["NULL", "TRUE", "FALSE"], + "guint64": ["NULL", "TRUE", "FALSE"], + "guint16": ["NULL", "TRUE", "FALSE"], + "guint8": ["NULL", "TRUE", "FALSE"], + "gint64": ["NULL", "TRUE", "FALSE"], + "gint32": ["NULL", "TRUE", "FALSE"], + "gint16": ["NULL", "TRUE", "FALSE"], + "gint8": ["NULL", "TRUE", "FALSE"], + "gint": ["NULL", "TRUE", "FALSE"], + "guint": ["NULL", "TRUE", "FALSE"], + "gulong": ["NULL", "TRUE", "FALSE"], + "gsize": ["NULL", "TRUE", "FALSE"], + "gssize": ["NULL", "TRUE", "FALSE"], + } + + current_type: str = "" + for node in nodes: + if not self._should_process_node(node): + continue + + # new function + if node.depth == 0: + idx = node.tokens_pre.find_fuzzy(["~*", "("]) + if idx == -1: + continue + token_type = node.tokens_pre[idx - 1] + token_func = node.tokens_pre[idx] + if self.verbose: + print("TYPE", token_func.data, token_type.data) + current_type = token_type.data + + # look for g_return_val_if_fail + idx = node.tokens.find_fuzzy(["g_return_val_if_fail", "("]) + if idx != -1: + # advance to the return + idx = node.tokens.find_fuzzy([",", "~*", ")", ";"], offset=idx) + if idx != -1: + token = node.tokens[idx + 1] + try: + rvif = types_rvif[current_type] + except KeyError: + if self.verbose: + print(f"missing type {current_type}") + continue + if _value_relaxed(token.data) not in rvif: self.add_failure( - f"function is too long, was {linecnt - func_begin} of {self.MAX_FUNCTION_LINES}" + "g_return_val_if_fail() return type invalid, " + f"expected {'|'.join(rvif)} for {current_type}, not {token.data}", + linecnt=token.linecnt, ) - if func_name and linecnt - func_begin < 3: - if func_name.endswith("_finalize"): - self.add_failure(f"{func_name} is redundant and can be removed") - func_begin = 0 - func_name = None + + # look for return values + idx = node.tokens.find_fuzzy(["return", "~*", ";"]) + if idx == -1: continue + token = node.tokens[idx + 1] + if token.hint == TokenHint.FUNCTION: + continue + try: + nret = types_nret[current_type] + except KeyError: + continue + if _value_relaxed(token.data) in nret: + self.add_failure( + f"return type invalid for {current_type}: {token.data}", + linecnt=token.linecnt, + ) - # is a function? - func_name_tmp: Optional[str] = _find_func_name(line) - if func_name_tmp: - func_name = func_name_tmp + def _test_gerror_void_return(self, node: Node) -> None: + """takes GError but returns void""" - def _test_lines_firmware_convert_version(self, lines: List[str]) -> None: - self._current_nocheck = "nocheck:set-version" + if node.depth != 0: + return - if self._current_fn and os.path.basename(self._current_fn) in [ - "fu-firmware.c", - "fu-firmware.h", - ]: + idx_start = node.tokens_pre.find_fuzzy(["static", "void", "@FUNCTION"]) + if idx_start == -1: + return + idx = node.tokens_pre.find_fuzzy( + ["GError", "*", "*", "error", ")"], offset=idx_start + ) + if idx == -1: + return + if idx - idx_start < 10: + token = node.tokens_pre[idx] + self.add_failure( + "void return type not expected for GError", + linecnt=token.linecnt, + ) + + def _test_function_length(self, node: Node) -> None: + + if node.depth != 0: return + limit: int = 400 + if node.linecnt_end - node.linecnt > limit: + self.add_failure( + f"function is too long, was {node.linecnt_end - node.linecnt} of {limit}", + linecnt=node.linecnt, + ) + + def _test_firmware_convert_version(self, nodes: list[Node]) -> None: # contains fu_firmware_set_version_raw() _set_version_raw: bool = False - for line in lines: - if line.find("fu_firmware_set_version_raw(") != -1: + for node in nodes: + idx = node.tokens.find_fuzzy( + ["fu_firmware_set_version_raw", "(", "~*", ",", "~*", ")", ";"] + ) + if idx != -1: _set_version_raw = True - break if not _set_version_raw: return # also contains fu_firmware_set_version() - for linecnt, line in enumerate(lines): - if line.find(self._current_nocheck) != -1: + for node in nodes: + if not self._should_process_node(node): continue - self._current_linecnt = linecnt + 1 - if line.find("fu_firmware_set_version(") != -1: + idx = node.tokens.find_fuzzy( + ["fu_firmware_set_version", "(", "~*", ",", "~*", ")", ";"] + ) + if idx != -1: + token = node.tokens[idx] self.add_failure( - "Use FuFirmwareClass->convert_version rather than fu_firmware_set_version()" + "Use FuFirmwareClass->convert_version rather than fu_firmware_set_version()", + linecnt=token.linecnt, ) - def _test_lines_device_convert_version(self, lines: List[str]) -> None: - self._current_nocheck = "nocheck:set-version" + def _test_device_convert_version(self, nodes: list[Node]) -> None: if self._current_fn and os.path.basename(self._current_fn) in [ - "fu-device.c", - "fu-device.h", "fu-self-test.c", ]: return - # contains fu_firmware_set_version_raw() + # contains fu_device_set_version_raw() _set_version_raw: bool = False - for line in lines: - if line.find("fu_device_set_version_raw(") != -1: + for node in nodes: + idx = node.tokens.find_fuzzy( + ["fu_device_set_version_raw", "(", "~*", ",", "~*", ")", ";"] + ) + if idx != -1: _set_version_raw = True - break if not _set_version_raw: return - # also contains fu_firmware_set_version() - for linecnt, line in enumerate(lines): - if line.find(self._current_nocheck) != -1: + # also contains fu_device_set_version() + for node in nodes: + if not self._should_process_node(node): continue - self._current_linecnt = linecnt + 1 - if line.find("fu_device_set_version(") != -1: + idx = node.tokens.find_fuzzy( + ["fu_device_set_version", "(", "~*", ",", "~*", ")", ";"] + ) + if idx != -1: + token = node.tokens[idx] self.add_failure( - "Use FuDeviceClass->convert_version rather than fu_device_set_version()" + "Use FuDeviceClass->convert_version rather than fu_device_set_version()", + linecnt=token.linecnt, ) - def _test_lines_depth(self, lines: List[str]) -> None: - # check depth - self._current_nocheck = "nocheck:depth" - depth: int = 0 - for linecnt, line in enumerate(lines): - if line.find(self._current_nocheck) != -1: - continue - self._current_linecnt = linecnt + 1 - for char in line: - if char == "{": - depth += 1 - if depth > 5: - self.add_failure("is nested too deep") - success = False + def _test_small_conditionals_with_braces_node(self, node: Node) -> None: + + # has previous conditional + idx = node.tokens_pre.find_fuzzy(["else", "if", "("]) + if idx != -1: + return + + # has conditional + idx = node.tokens_pre.find_fuzzy(["if", "("]) + if idx == -1: + return + + # multiline conditional can have braces + if node.linecnt_end - node.tokens_pre[idx].linecnt >= 3: + return + + # inline struct + idx = node.tokens_pre.find_fuzzy(["@STRUCT"]) + if idx != -1: + return + + # nested + idx = node.tokens.find_fuzzy(["if", "("]) + if idx != -1: + return + + # not one line block + if len(node.tokens) < 2: + return + if node.tokens[1].linecnt != node.tokens[-1].linecnt: + return + limit: int = 70 + if len("".join([token.data for token in node.tokens])) < limit: + token = node.tokens[1] + self.add_failure( + "no {} required for small single-line conditional", + linecnt=token.linecnt, + ) + + def _test_small_conditionals_with_braces(self, nodes: list[Node]) -> None: + + # we need to parse the nodes in order + for idx, node in enumerate(nodes): + next_node_depth: int = 0 + try: + next_node_depth = nodes[idx + 1].depth + except IndexError: + pass + + # followed by else + try: + idx = nodes[idx + 1].tokens_pre.find_fuzzy(["else"]) + if idx != -1: continue - if char == "}": - if depth == 0: - self.add_failure("has unequal nesting") - success = False - continue - depth -= 1 + except IndexError: + pass - # sanity check - self._current_linecnt = None - if depth != 0: - self.add_failure("nesting was weird") - success = False - - def _test_lines(self, lines: List[str]) -> None: - lines_nocheck: List[str] = [] - - # tests we can do line by line - for linecnt, line in enumerate(lines): - self._current_linecnt = linecnt + 1 + # ignore if is nested deeper + if next_node_depth > node.depth: + continue + + # check node + self._test_small_conditionals_with_braces_node(node) + + def _test_nesting_depth(self, node: Node) -> None: + + limit: int = 5 + if node.depth >= limit: + self.add_failure( + f"is nested too deep {node.depth}/{limit}", linecnt=node.linecnt + ) + + def _test_memread(self, node: Node) -> None: + + limit: int = 7 + cnt = node.tokens.count_fuzzy(["~fu_memread_uint*"]) + if cnt >= limit: + self.add_failure( + f"too many calls to fu_memread_uintXX() ({cnt}/{limit}), use rustgen", + linecnt=node.linecnt, + ) + cnt = node.tokens.count_fuzzy(["~fu_memwrite_uint*"]) + if cnt >= limit: + self.add_failure( + f"too many calls to fu_memwrite_uintXX() ({cnt}/{limit}), use rustgen", + linecnt=node.linecnt, + ) + + def _test_gobject_parents(self, nodes: list[Node]) -> None: + + gtype: str = "" + gtypeparent: str = "" + for node in nodes: + if node.depth != 0: + continue + if not self._should_process_node(node): + return + + # .h + idx = node.tokens_pre.find_fuzzy(["~G_DECLARE_*_TYPE", "("]) + if idx != -1: + gtype = node.tokens_pre[idx + 2].data + gtypeparent = node.tokens_pre[idx + 10].data + self._gtype_parents[gtype] = gtypeparent + + # check the class def is correct + if gtype: + idx = node.tokens.find_fuzzy(["~*Class", "parent_class"]) + if idx != -1: + gtypeparentclass_found = node.tokens[idx].data + gtypeparentclass_expected = f"{gtypeparent}Class" + if gtypeparentclass_found != gtypeparentclass_expected: + self.add_failure( + f"wrong parent_class for {gtype}, " + f"got {gtypeparentclass_found} and " + f"expected {gtypeparentclass_expected}", + linecnt=node.linecnt, + ) + + # .c + idx = node.tokens_pre.find_fuzzy(["~G_DEFINE_TYPE*", "("]) + if idx != -1: + gtype = node.tokens_pre[idx + 2].data + if self.verbose: + print("GTYPE", gtype, self._gtype_parents) + if not gtype in self._gtype_parents: + continue + gtypeparent_found: str = node.tokens_pre[idx + 6].data + gtype_snake = _camel_to_snake(self._gtype_parents[gtype]).split( + "_", maxsplit=1 + ) + gtypeparent_expected = f"{gtype_snake[0]}_type_{gtype_snake[1]}".upper() + if gtypeparent_found != gtypeparent_expected: + self.add_failure( + f"wrong parent GType for {gtype}, " + f"got {gtypeparent_found} and " + f"expected {gtypeparent_expected}", + linecnt=node.linecnt, + ) + + def _test_nodes(self, nodes: list[Node]) -> None: + + # preroll + self._klass_funcs.clear() + for node in nodes: + self._discover_klass_functions(node) + if self.verbose: + print("KLASS FUNCS", self._klass_funcs) + + # tests we can do node by node + for node in nodes: + + # test for missing ->finalize() + self._current_nocheck = "nocheck:finalize" + if self._should_process_node(node): + self._test_gobject_finalize(node) # test for blocked functions - self._test_line_blocked_fns(line) + self._current_nocheck = "nocheck:blocked" + if self._should_process_node(node): + self._test_blocked_funcs(node) + self._test_blocked_goto(node) + self._test_device_display(node) + self._test_equals_true(node) # test for debug lines - self._test_line_debug_fns(line) + self._current_nocheck = "nocheck:print" + if self._should_process_node(node): + self._test_debug_fns(node) + self._test_debug_newlines(node) + self._test_debug_fullstops(node) + self._test_debug_sentence_case(node) + self._test_comment_lower_case(node) + self._test_comment_cpp(node) + + # not nesting too deep + self._current_nocheck = "nocheck:depth" + if self._should_process_node(node): + self._test_nesting_depth(node) # test for function names - self._test_line_function_names(line) + self._current_nocheck = "nocheck:name" + if self._should_process_node(node): + self._test_function_names_prefix(node) + self._test_function_names_ensure(node) + self._test_param_self_device(node) + self._test_param_self_firmware(node) + self._test_param_self_native_device(node) + self._test_param_self_native_firmware(node) + self._test_variable_case(node) + self._test_struct_member_case(node) + + # test for invalid struct and enum names + self._current_nocheck = "nocheck:prefix" + if self._should_process_node(node): + self._test_struct(node) + self._test_enums(node) + + # test for static variables + self._current_nocheck = "nocheck:static" + if self._should_process_node(node): + self._test_static_vars(node) + + # test for rustgen variables + self._current_nocheck = "nocheck:rustgen" + if self._should_process_node(node): + self._test_rustgen_vars(node) + + # test for endian safety + self._current_nocheck = "nocheck:endian" + if self._should_process_node(node): + self._test_rustgen_bitshifts(node) + + # GError + self._current_nocheck = "nocheck:error" + if self._should_process_node(node): + self._test_gerror_domain(node) + self._test_gerror_void_return(node) + self._test_extra_error_suffixes(node) + self._test_missing_error_suffixes(node) + self._test_missing_literal(node) + self._test_gerror_deref(node) + + # using too many memory reads/writes + self._current_nocheck = "nocheck:memread" + if self._should_process_node(node): + self._test_memread(node) + + # test for non-zero'd init + self._current_nocheck = "nocheck:zero-init" + if self._should_process_node(node): + self._test_zero_init(node) + + # functions too long + self._current_nocheck = "nocheck:lines" + if self._should_process_node(node): + self._test_function_length(node) - # test for invalid enum names - self._test_line_enums(line) + # NULL != FALSE + self._current_nocheck = "nocheck:lines" + self._test_null_false_returns(nodes) + self._test_small_conditionals_with_braces(nodes) - # using FUWPD_ERROR domains - self._test_lines_gerror(lines) + # using too many switch() + self._current_nocheck = None + self._test_switch(nodes) - # not nesting too deep - self._test_lines_depth(lines) + # should use FuFirmwareClass->convert_version or FuDeviceClass->convert_version + self._current_nocheck = "nocheck:set-version" + self._test_firmware_convert_version(nodes) + self._test_device_convert_version(nodes) - # functions too long - self._test_lines_function_length(lines) + # using too many hardcoded constants + self._current_nocheck = "nocheck:magic-defines" + self._test_magic_numbers_defined(nodes) + self._current_nocheck = "nocheck:magic-inlines" + self._test_magic_numbers_inline(nodes) + + # setting GError, not returning + self._current_nocheck = "nocheck:error-false-return" + self._test_gerror_false_returns(nodes) - # should use FuFirmwareClass->convert_version - self._test_lines_firmware_convert_version(lines) + # prefix with no set + self._current_nocheck = "nocheck:error" + self._test_gerror_not_set(nodes) - # should use FuDeviceClass->convert_version - self._test_lines_device_convert_version(lines) + # test GObject parent types + self._current_nocheck = "nocheck:name" + self._test_gobject_parents(nodes) def test_file(self, fn: str) -> None: self._current_fn = fn + self._current_nocheck = None with open(fn, "rb") as f: try: - self._test_lines(f.read().decode().split("\n")) + data = f.read().decode() except UnicodeDecodeError as e: print(f"failed to read {fn}: {e}") + if data.find("Copyright") == -1: + self.add_failure(f"does not have copyright assigned") + if data.find("Copyright") == -1: + self.add_failure(f"does not have a SPDX-License-Identifier") + tokenizer = Tokenizer(data) + nodes = tokenizer.nodes + if self.verbose: + print(nodes) + self._test_nodes(nodes) -def test_files() -> int: +def test_files(fns_optional: list[str], verbose: bool = False) -> int: # test all C and H files - rc: int = 0 - checker = Checker() + checker = Checker(verbose=verbose) # use any file specified in argv, falling back to scanning the entire tree fns: List[str] = [] - if len(sys.argv) > 1: - for fn in sys.argv[1:]: + if fns_optional: + for fn in fns_optional: + if fn.startswith("contrib/ci/tests"): + continue try: ext: str = fn.rsplit(".", maxsplit=1)[1] except IndexError: @@ -466,9 +1420,10 @@ fns.extend(glob.glob("libfwupdplugin/*.[c|h]")) fns.extend(glob.glob("plugins/*/*.[c|h]")) fns.extend(glob.glob("src/*.[c|h]")) - for fn in fns: + for fn in sorted(fns, reverse=True): if os.path.basename(fn) == "check-source.py": continue + print(f"checking {fn}…", file=sys.stderr) checker.test_file(fn) # show issues @@ -487,6 +1442,56 @@ return 1 if checker.failures else 0 +def unit_test(fn: str, verbose: bool = False) -> int: + + # load test file with any expected failures + rc: int = 0 + checker = Checker(verbose=verbose) + with open(fn, "rb") as f: + lines = f.read().decode().split("\n") + for line in lines: + if line.startswith(" * nocheck:expect:"): + checker.add_expected_failure(line[19:]) + checker.test_file(fn) + + # any unexpected failures + for failure in checker.failures: + if not failure.expected: + print(f"{failure.fn} did not expect to see: {failure.message}") + rc += 1 + + # check we got the ones we wanted + for expect in checker._expected_failure_prefixes: + if not checker.has_failure(expect): + print(f"{fn} expected to see: {expect}") + rc += 1 + + # print what we did get + if rc: + for failure in checker.failures: + line_tmp: str = "" + if failure.fn: + line_tmp += failure.fn + if failure.linecnt: + line_tmp += f":{failure.linecnt}" + line_tmp += f": {failure.message}" + print(line_tmp) + + return rc + + if __name__ == "__main__": + + parser = argparse.ArgumentParser(description="Check source files") + parser.add_argument("--test", type=str, help="Run self tests") + parser.add_argument("--verbose", action="store_true", help="Run in verbose mode") + args, argv = parser.parse_known_args() + + if args.test: + _rc: int = 0 + for _fn in glob.glob(f"{args.test}/*.[c|h]"): + _rc += unit_test(_fn, args.verbose) + sys.exit(_rc) + # all done! - sys.exit(test_files()) + sys.exit(test_files(argv, args.verbose)) diff -Nru fwupd-2.0.8/contrib/ci/check-unused.py fwupd-2.0.20/contrib/ci/check-unused.py --- fwupd-2.0.8/contrib/ci/check-unused.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/check-unused.py 2026-02-26 11:36:18.000000000 +0000 @@ -50,6 +50,10 @@ continue if fnmatch.fnmatch(symb, "fu_struct_*_get_*"): continue + if fnmatch.fnmatch(symb, "fu_*_ref"): + continue + if fnmatch.fnmatch(symb, "fu_*_unref"): + continue if symb.find("__proto__") != -1: continue if symb in ["main", "fu_plugin_init_vfuncs"]: diff -Nru fwupd-2.0.8/contrib/ci/check_missing_translations.sh fwupd-2.0.20/contrib/ci/check_missing_translations.sh --- fwupd-2.0.8/contrib/ci/check_missing_translations.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/check_missing_translations.sh 2026-02-26 11:36:18.000000000 +0000 @@ -4,5 +4,5 @@ cd po intltool-update -m if [ -f missing ]; then - exit 1 + exit 1 fi diff -Nru fwupd-2.0.8/contrib/ci/coverage.sh fwupd-2.0.20/contrib/ci/coverage.sh --- fwupd-2.0.8/contrib/ci/coverage.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/coverage.sh 2026-02-26 11:36:18.000000000 +0000 @@ -7,12 +7,12 @@ fi gcovr -x \ - --filter build/libfwupd \ - --filter build/libfwupdplugin \ - --filter build/plugins \ - --filter build/src \ - --exclude build/libfwupd/fwupd-context-test.c \ - --exclude build/libfwupd/fwupd-thread-test.c \ - --exclude-lines-by-pattern '^.*(G_OBJECT_WARN_INVALID|G_DEFINE_TYPE|JSON_NODE_HOLDS_OBJECT|g_autoptr|g_critical|g_warning|g_assert_cmpfloat_with_epsilon|g_assert_cmpint|g_assert_cmpstr|g_assert_cmpuint|g_assert_error|g_assert_false|g_assert_no_error|g_assert_nonnull|g_assert_not_reached|g_assert_null|g_assert_true|g_return_if_fail|g_return_val_if_fail).*$' \ - -o coverage.xml + --filter build/libfwupd \ + --filter build/libfwupdplugin \ + --filter build/plugins \ + --filter build/src \ + --exclude build/libfwupd/fwupd-context-test.c \ + --exclude build/libfwupd/fwupd-thread-test.c \ + --exclude-lines-by-pattern '^.*(G_OBJECT_WARN_INVALID|G_DEFINE_TYPE|JSON_NODE_HOLDS_OBJECT|g_autoptr|g_critical|g_warning|g_assert_cmpfloat_with_epsilon|g_assert_cmpint|g_assert_cmpstr|g_assert_cmpuint|g_assert_error|g_assert_false|g_assert_no_error|g_assert_nonnull|g_assert_not_reached|g_assert_null|g_assert_true|g_return_if_fail|g_return_val_if_fail).*$' \ + -o coverage.xml sed "s,build/,,g" coverage.xml -i diff -Nru fwupd-2.0.8/contrib/ci/ctokenizer.py fwupd-2.0.20/contrib/ci/ctokenizer.py --- fwupd-2.0.8/contrib/ci/ctokenizer.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/ctokenizer.py 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,443 @@ +#!/usr/bin/python3 +# +# Copyright 2025 Richard Hughes +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# pylint: disable=missing-module-docstring,missing-class-docstring, +# pylint: disable=missing-function-docstring,too-few-public-methods,consider-using-enumerate + +from typing import Optional +from enum import Enum +from fnmatch import fnmatch +import copy + + +class TokenHint(Enum): + + COMMENT = 1 + STRING = 2 + DEFINE = 3 + ENUM = 4 + LBRACKET = 5 + RBRACKET = 6 + FUNCTION = 7 + MACRO = 8 + INTEGER = 9 + STRUCT = 10 + + @classmethod + def value_of(cls, value): + for k, v in cls.__members__.items(): + if k == value: + return v + raise ValueError(f"'{cls.__name__}' enum not found for '{value}'") + + +def _check_int(s: str) -> bool: + if s.startswith("-"): + s = s[1:] + if s.startswith("0x"): + s = s[2:] + return s.isdigit() + + +class Token: + + def __init__(self, data: str, linecnt: int = 0, hint: Optional[TokenHint] = None): + + self.linecnt: int = linecnt + self.linecnt_end: int = linecnt + self.data: str = data + self.hint: Optional[TokenHint] = hint + + # autohint + if not self.hint: + if data.startswith('"') and data.endswith('"'): + self.hint = TokenHint.STRING + elif data.startswith("/*") and data.endswith("*/"): + self.hint = TokenHint.COMMENT + elif data == "(": + self.hint = TokenHint.LBRACKET + elif data == ")": + self.hint = TokenHint.RBRACKET + elif _check_int(data): + self.hint = TokenHint.INTEGER + + def __repr__(self) -> str: + + tmp = [f"linecnt={self.linecnt}"] + if self.linecnt_end != self.linecnt: + tmp.append(f"linecnt_end={self.linecnt_end}") + if self.data: + tmp.append(f"data='{self.data}'") + if self.hint: + tmp.append(f"hint={self.hint}") + return f"Token({', '.join(tmp)})" + + +def _token_fuzzy_match(token: Token, data: str) -> bool: + """data is of the form: + * `literal` + * `~fnmatch` + * `@HINT` + * `literal@HINT` + * `~fnmatch@HINT` + """ + + # hint + query: list[str] = data.split("@") + try: + if token.hint != TokenHint.value_of(query[1]): + return False + except IndexError: + pass + + # fnmatch + if query[0].startswith("~"): + query_fnmatch = query[0][1:] + if not query_fnmatch or query_fnmatch == "*": + return True + return fnmatch(token.data, query_fnmatch) + + # literal + if query[0]: + return token.data == query[0] + + # empty + return True + + +class TokenList(list): + + def __init__(self, tokens: Optional[list[Token]] = None): + + for token in tokens or []: + self.append(token) + + def append(self, token: Token) -> None: + + # autohint + if not token.hint and token.data != ";": + if len(self) >= 1 and self[-1].data == "#define": + token.hint = TokenHint.DEFINE + elif len(self) >= 1 and self[-1].data == "enum": + token.hint = TokenHint.ENUM + elif len(self) >= 1 and self[-1].data == "struct": + token.hint = TokenHint.STRUCT + + # autohint previous token + if len(self) > 0 and token.hint == TokenHint.LBRACKET: + token_prev: Token = self[-1] + if not token_prev.hint: + if token_prev.data.find("_") != -1: + if token_prev.data.upper() == token_prev.data: + token_prev.hint = TokenHint.MACRO + else: + token_prev.hint = TokenHint.FUNCTION + + # add + list.append(self, token) + + def find_fuzzy( + self, + data_fuzzy: list[str], + offset: Optional[int] = None, + reverse: bool = False, + skip_comments: bool = False, + ) -> int: + """ + Look for a fuzzy token sequence and return the position to the + first token. Returns -1 if not found. + """ + if reverse: + pos_range = range(offset or len(self) - 1, 0, -1) + else: + pos_range = range(offset or 0, len(self) - (len(data_fuzzy) - 1)) + for pos in pos_range: + + all_match: bool = True + for datapos in range(len(data_fuzzy)): + comment_offset: int = 0 + if skip_comments and self[pos + datapos].hint == TokenHint.COMMENT: + comment_offset = 1 + token = self[pos + datapos + comment_offset] + if not _token_fuzzy_match(token, data_fuzzy[datapos]): + all_match = False + break + if all_match: + return pos + return -1 + + def endswith_fuzzy(self, data_fuzzy: list[str]) -> bool: + """ + Look for a fuzzy token sequence at the end of the list. + Returns False if not found. + """ + offset = len(self) - len(data_fuzzy) + if offset < 0: + return False + for datapos in range(len(data_fuzzy)): + token = self[offset + datapos] + if not _token_fuzzy_match(token, data_fuzzy[datapos]): + return False + return True + + def count_fuzzy(self, data_fuzzy: list[str]) -> int: + """ + Return the number of fuzzy matches matching all tokens. + """ + cnt: int = 0 + for pos in range(len(self) - (len(data_fuzzy) - 1)): + all_match: bool = True + for datapos in range(len(data_fuzzy)): + if not _token_fuzzy_match(self[pos + datapos], data_fuzzy[datapos]): + all_match = False + break + if all_match: + cnt += 1 + return cnt + + +class NodeHint(Enum): + + UNION = 1 + ENUM = 2 + STRUCT = 3 + STRUCT_TYPEDEF = 4 + STRUCT_CONST = 5 + + +class Node: + + def __init__( + self, depth: int, linecnt: int, tokens_pre: Optional[TokenList] = None + ): + self.depth: int = depth + self.linecnt: int = linecnt + self.linecnt_end: int = linecnt + self.tokens_pre: TokenList = tokens_pre or TokenList() + self.tokens: TokenList = TokenList() + self.hint: Optional[NodeHint] = None + + def __repr__(self) -> str: + + tmp = [f"depth={self.depth}", f"linecnt={self.linecnt}"] + if self.linecnt_end != self.linecnt: + tmp.append(f"linecnt_end={self.linecnt_end}") + if self.hint: + tmp.append(f"hint={self.hint}") + if self.tokens_pre: + tmp.append(f"tokens_pre={[token.data for token in self.tokens_pre]}") + if self.tokens: + tmp.append(f"tokens={[token.data for token in self.tokens]}") + return f"Node({', '.join(tmp)})" + + +class Tokenizer: + def __init__(self, data: str): + self.tokens: TokenList = TokenList() + self._nodes: list[Node] = [] + self._acc: str = "" + self._linecnt: int = 1 + if data: + self._parse(data) + + def _add_token(self, token: Token) -> None: + + # merge into previous token + if self.tokens: + token_prev: Token = self.tokens[-1] + + # STRING -> STRING + if token_prev.hint == TokenHint.STRING and token.hint == TokenHint.STRING: + new = token_prev.data[:-1] + token.data[1:] + token_prev.data = new + token_prev.linecnt_end = token.linecnt + return + + # COMMENT -> COMMENT + if token_prev.hint == TokenHint.COMMENT and token.hint == TokenHint.COMMENT: + new = token_prev.data + " " + token.data + token_prev.data = new + token_prev.linecnt_end = token.linecnt + return + + # "-" -> INTEGER + if token_prev.data == "-" and token.hint == TokenHint.INTEGER: + new = token_prev.data + token.data + token_prev.data = new + return + + # add new + self.tokens.append(token) + + def dump_acc(self, hint: Optional[TokenHint] = None) -> None: + + stripped = self._acc.strip() + if stripped: + if fnmatch(stripped, "G_G*_FORMAT"): + stripped = '"%u"' + self._add_token(Token(stripped, linecnt=self._linecnt, hint=hint)) + self._acc = "" + + def _parse(self, data: str): + + is_quote_mode: bool = False + is_comment_mode: bool = False + is_escape_mode: bool = False + + for pos, char in enumerate(data): + + # newline + if char == "\n": + + if is_comment_mode: + self.dump_acc(hint=TokenHint.COMMENT) + else: + self.dump_acc() + self._linecnt += 1 + continue + + # only valid once + is_escape_mode = data[pos - 1] == "\\" and data[pos - 2] != "\\" + + # string quotes + if ( + char == '"' + and not is_escape_mode + and not (data[pos - 1] == "'" and data[pos + 1] == "'") + ): + is_quote_mode = not is_quote_mode + + # delimiter + dump_char: bool = False + if ( + char + in [ + " ", + "*", + "(", + ")", + "{", + "}", + ",", + "&", + ";", + ">", + "<", + "-", + "[", + "]", + "!", + ] + and not is_comment_mode + and not is_quote_mode + and not (data[pos - 1] == "'" and data[pos + 1] == "'") + ): + self.dump_acc() + dump_char = True + + # comment + if data[pos : pos + 2] == "/*" and not is_quote_mode: + is_comment_mode = True + + self._acc += char + + # end comment + if data[pos - 1 : pos + 1] == "*/" and not is_quote_mode: + self.dump_acc(hint=TokenHint.COMMENT) + is_comment_mode = False + + # delimiter + if dump_char: + self.dump_acc() + + # any left + self.dump_acc() + + def _add_node(self, node: Node): + + # auto-hint node + if not node.hint: + try: + if node.tokens_pre[-1].data == "union": + node.hint = NodeHint.UNION + elif node.tokens_pre[-1].data == "enum": + node.hint = NodeHint.ENUM + elif node.tokens_pre[-1].data == "struct": + node.hint = NodeHint.STRUCT + if node.tokens_pre[-2].data == "typedef": + node.hint = NodeHint.STRUCT_TYPEDEF + elif node.tokens_pre[-2].data == "const": + node.hint = NodeHint.STRUCT_CONST + except IndexError: + pass + + # add + self._nodes.append(node) + + def _ensure_nodes(self) -> None: + + tokens_acc: TokenList = TokenList() + depth: int = 0 + stack: list[Node] = [] + node_parent: Optional[Node] = None + + for token in self.tokens: + + # ignore __attribute__ + if token.data == "G_GNUC_FLAG_ENUM": + continue + + # start, end or continue + if token.data == "{": + if stack: + stack[-1].tokens.extend(tokens_acc) + node = Node( + depth=len(stack), + linecnt=token.linecnt, + tokens_pre=copy.copy(tokens_acc), + ) + stack.append(node) + self._add_node(node) + tokens_acc.clear() + depth += 1 + elif token.data == "}": + node_parent = stack.pop() + node_parent.tokens.extend(tokens_acc) + node_parent.linecnt_end = token.linecnt + tokens_acc.clear() + depth -= 1 + else: + # reposition typedef struct names to be before the { + if ( + node_parent + and node_parent.tokens_pre + and node_parent.tokens_pre[-1].data + in [ + "enum", + "struct", + "union", + ] + ): + node_parent.tokens_pre.append(token) + else: + tokens_acc.append(token) + + # create a fake node + if not self._nodes and tokens_acc: + self._add_node( + Node(depth=0, linecnt=tokens_acc[0].linecnt, tokens_pre=tokens_acc) + ) + + # sanity check + if depth != 0: + raise ValueError("has unequal nesting") + + @property + def nodes(self) -> list[Node]: + + if not self._nodes: + self._ensure_nodes() + return self._nodes diff -Nru fwupd-2.0.8/contrib/ci/ctokenizer_test.py fwupd-2.0.20/contrib/ci/ctokenizer_test.py --- fwupd-2.0.8/contrib/ci/ctokenizer_test.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/ctokenizer_test.py 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,319 @@ +#!/usr/bin/python3 +# +# Copyright 2025 Richard Hughes +# +# SPDX-License-Identifier: GPL-2.0-or-later +# +# pylint: disable=missing-module-docstring,missing-class-docstring,missing-function-docstring + +import unittest + +from ctokenizer import Tokenizer, Token, TokenList + + +class TestCTokenize(unittest.TestCase): + + def _compare_tokens(self, data: str, tokens_wanted: list[str]) -> None: + + tokens = Tokenizer(data).tokens + print(tokens) + tokens_data = [token.data for token in tokens] + self.assertEqual(tokens_data, tokens_wanted) + + def _compare_tokens_full(self, data: str, tokens_wanted: list[str]) -> None: + + tokens = Tokenizer(data).tokens + print(tokens) + tokens_data = [str(token) for token in tokens] + self.assertEqual(tokens_data, tokens_wanted) + + def _compare_nodes(self, data: str, nodes_wanted: list[str]) -> None: + + tokenizer = Tokenizer(data) + nodes = tokenizer.nodes + print(nodes) + nodes_data = [str(node) for node in tokenizer.nodes] + self.assertEqual(nodes_data, nodes_wanted) + + def test_tokens(self): + + self._compare_tokens_full( + " /* comment */ /* comment2 */ ", + [ + "Token(linecnt=1, data='/* comment */ /* comment2 */', hint=TokenHint.COMMENT)", + ], + ) + self._compare_tokens_full( + "/* one\n\ttwo. */", + [ + "Token(linecnt=1, linecnt_end=2, data='/* one two. */', hint=TokenHint.COMMENT)", + ], + ) + self._compare_tokens_full( + "/**\n * comment3\n * comment4\n **/", + [ + "Token(linecnt=1, linecnt_end=4, data='/** * comment3 * comment4 **/', hint=TokenHint.COMMENT)", + ], + ) + self._compare_tokens_full( + "/**\n * comment3\n * comment4\n **/", + [ + "Token(linecnt=1, linecnt_end=4, data='/** * comment3 * comment4 **/', hint=TokenHint.COMMENT)", + ], + ) + self._compare_tokens( + "guint8 *foo_cb(guint16 *buf)\n{\nreturn 42;\n}\n", + [ + "guint8", + "*", + "foo_cb", + "(", + "guint16", + "*", + "buf", + ")", + "{", + "return", + "42", + ";", + "}", + ], + ) + self._compare_tokens( + 'g_print("hello world");', + [ + "g_print", + "(", + '"hello world"', + ")", + ";", + ], + ) + self._compare_tokens( + '1 \\"2\\" 3', + [ + "1", + '\\"2\\"', + "3", + ], + ) + self._compare_tokens( + "if (&char == '\"')", + [ + "if", + "(", + "&", + "char", + "==", + "'\"'", + ")", + ], + ) + self._compare_tokens( + 'xb_node_query_first("component/*");', + [ + "xb_node_query_first", + "(", + '"component/*"', + ")", + ";", + ], + ) + self._compare_tokens( + "g_set_error_literal(error,\n\tFWUPD_ERROR,\n\tFWUPD_ERROR_NOTHING_TO_DO,\n\t" + '"no" G_GSIZE_FORMAT "pe "\n\t" no"\n\t"pe2");\n', + [ + "g_set_error_literal", + "(", + "error", + ",", + "FWUPD_ERROR", + ",", + "FWUPD_ERROR_NOTHING_TO_DO", + ",", + '"no%upe nope2"', + ")", + ";", + ], + ) + self._compare_tokens( + "return -1;\n", + [ + "return", + "-1", + ";", + ], + ) + self._compare_tokens( + "#define FOO_001 0x123\n", + [ + "#define", + "FOO_001", + "0x123", + ], + ) + + def test_token_list(self): + + toklist = TokenList( + [ + Token("/*start*/"), + Token("guint8"), + Token("buf"), + Token("["), + Token("10"), + Token("]"), + Token(";"), + Token("/*end*/"), + ] + ) + print(toklist) + self.assertEqual(toklist.find_fuzzy(["~guint*", "~*", "[", "~*", "]", ";"]), 1) + self.assertTrue(toklist.endswith_fuzzy([";", "/*end*/"])) + self.assertFalse(toklist.endswith_fuzzy(["]", ";"])) + + toklist = TokenList( + [ + Token("one"), + ] + ) + print(toklist) + self.assertFalse(toklist.endswith_fuzzy(["one", "two"])) + + toklist = TokenList( + [ + Token("match1"), # 0 + Token("token"), # 1 + Token("match2"), # 2 + Token("token"), # 3 + Token("match3"), # 4 + ] + ) + print(toklist) + self.assertEqual(toklist.find_fuzzy(["~match*"]), 0) + self.assertEqual(toklist.find_fuzzy(["~match*"], reverse=True), 4) + self.assertEqual(toklist.find_fuzzy(["~match*"], offset=1), 2) + self.assertEqual(toklist.find_fuzzy(["~match*"], offset=3, reverse=True), 2) + + toklist = TokenList( + [ + Token("/*start*/"), + Token("guint8"), + Token("buf"), + Token("["), + Token("10"), + Token("]"), + Token(";"), + Token("/*end*/"), + ] + ) + print(toklist) + self.assertEqual(toklist.find_fuzzy(["/*end*/", "DOESNOTEXIST"]), -1) + + toklist = TokenList( + [ + Token("fu_memread_uint16"), + Token("fu_memread_uint32"), + Token("fu_memread_uint64"), + ] + ) + print(toklist) + self.assertEqual(toklist.count_fuzzy(["~fu_memwrite_uint*"]), 0) + self.assertEqual(toklist.count_fuzzy(["~fu_memread_uint*"]), 3) + + toklist = TokenList( + [ + Token("/* comment1 */"), + Token("func1_cb"), + Token("/* comment2 */"), + Token("func2_cb"), + ] + ) + print(toklist) + self.assertEqual( + toklist.find_fuzzy(["func1_cb", "func2_cb"], skip_comments=True), 1 + ) + + # autohint + toklist = TokenList( + [ + Token("func_cb"), + Token("("), + Token("-1"), + Token(")"), + ] + ) + print(toklist) + self.assertEqual( + str(toklist[0]), "Token(linecnt=0, data='func_cb', hint=TokenHint.FUNCTION)" + ) + self.assertEqual( + str(toklist[1]), "Token(linecnt=0, data='(', hint=TokenHint.LBRACKET)" + ) + self.assertEqual( + str(toklist[2]), "Token(linecnt=0, data='-1', hint=TokenHint.INTEGER)" + ) + self.assertEqual( + str(toklist[3]), "Token(linecnt=0, data=')', hint=TokenHint.RBRACKET)" + ) + self.assertEqual( + toklist.find_fuzzy(["func_cb@MACRO", "("], skip_comments=True), -1 + ) + self.assertEqual( + toklist.find_fuzzy(["func_cb@FUNCTION", "("], skip_comments=True), 0 + ) + self.assertEqual(toklist.find_fuzzy(["@FUNCTION", "("], skip_comments=True), 0) + self.assertEqual(toklist.find_fuzzy(["", "("], skip_comments=True), 0) + + def test_nodes(self): + + self._compare_nodes( + "typedef struct {\n\tgchar *id;\n} FwupdDevicePrivate;\n", + [ + "Node(depth=0, linecnt=1, linecnt_end=3, hint=NodeHint.STRUCT_TYPEDEF, " + "tokens_pre=['typedef', 'struct', 'FwupdDevicePrivate'], " + "tokens=['gchar', '*', 'id', ';'])", + ], + ) + + self._compare_nodes( + "#define FOO_001 0x123\n#define FOO_002 0x123\n", + [ + "Node(depth=0, linecnt=1, tokens_pre=['#define', 'FOO_001', '0x123', " + "'#define', 'FOO_002', '0x123'])", + ], + ) + + self._compare_nodes( + "union { guint8 one; guint two; } name;", + [ + "Node(depth=0, linecnt=1, hint=NodeHint.UNION, tokens_pre=['union', 'name'], " + "tokens=['guint8', 'one', ';', 'guint', 'two', ';'])" + ], + ) + self._compare_nodes( + "void main (void) {" + " gint rc;" + " if (1) {" + " cond1;" + " }" + " middle;" + " if (1) {" + " cond2;" + " }" + " return rc;" + "}", + [ + "Node(depth=0, linecnt=1, tokens_pre=['void', 'main', '(', 'void', ')'], " + "tokens=['gint', 'rc', ';', 'if', '(', '1', ')', 'middle', ';', 'if', " + "'(', '1', ')', 'return', 'rc', ';'])", + "Node(depth=1, linecnt=1, tokens_pre=['gint', 'rc', ';', 'if', '(', '1', " + "')'], tokens=['cond1', ';'])", + "Node(depth=1, linecnt=1, tokens_pre=['middle', ';', 'if', '(', '1', ')'], " + "tokens=['cond2', ';'])", + ], + ) + + +if __name__ == "__main__": + unittest.main() diff -Nru fwupd-2.0.8/contrib/ci/debian-i386-test.sh fwupd-2.0.20/contrib/ci/debian-i386-test.sh --- fwupd-2.0.8/contrib/ci/debian-i386-test.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/debian-i386-test.sh 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,7 @@ # run tests ./contrib/ci/get_test_firmware.sh /usr/share/installed-tests/fwupd/ service dbus restart -gnome-desktop-testing-runner --timeout=600 fwupd +gnome-desktop-testing-runner --timeout=1200 fwupd # generate coverage report ./contrib/ci/coverage.sh diff -Nru fwupd-2.0.8/contrib/ci/debian-x86_64-test.sh fwupd-2.0.20/contrib/ci/debian-x86_64-test.sh --- fwupd-2.0.8/contrib/ci/debian-x86_64-test.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/debian-x86_64-test.sh 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,7 @@ # run tests ./contrib/ci/get_test_firmware.sh /usr/share/installed-tests/fwupd/ service dbus restart -gnome-desktop-testing-runner --timeout=600 fwupd +gnome-desktop-testing-runner --timeout=1200 fwupd # generate coverage report ./contrib/ci/coverage.sh diff -Nru fwupd-2.0.8/contrib/ci/debian.sh fwupd-2.0.20/contrib/ci/debian.sh --- fwupd-2.0.8/contrib/ci/debian.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/debian.sh 2026-02-26 11:36:18.000000000 +0000 @@ -4,23 +4,22 @@ export QUBES_OPTION= - #although it's debian, we don't build packages if [ "$OS" = "debian-s390x" ]; then - ./contrib/ci/debian_s390x.sh - exit 0 + ./contrib/ci/debian_s390x.sh + exit 0 fi # Set Qubes Os vars if -Dqubes=true is parameter if [ "$QUBES" = "true" ]; then - export QUBES_OPTION='-Dqubes=true' + export QUBES_OPTION='-Dqubes=true' fi #prepare export DEBFULLNAME="CI Builder" export DEBEMAIL="ci@travis-ci.org" -VERSION=`git describe | sed 's/-/+r/;s/-/+/'` -[ -z $VERSION ] && VERSION=`head meson.build | grep ' version:' | cut -d \' -f2` +VERSION=$(git describe | sed 's/-/+r/;s/-/+/') +[ -z $VERSION ] && VERSION=$(head meson.build | grep ' version:' | cut -d \' -f2) rm -rf build/ mkdir -p build shopt -s extglob @@ -38,30 +37,28 @@ #disable unit tests if fwupd is already installed (may cause problems) if [ -x /usr/lib/fwupd/fwupd ]; then - export DEB_BUILD_OPTIONS=nocheck + export DEB_BUILD_OPTIONS=nocheck fi #build the package EDITOR=/bin/true dch --create --package fwupd -v $VERSION "CI Build" debuild --no-lintian --preserve-envvar CI --preserve-envvar CC \ - --preserve-envvar QUBES_OPTION + --preserve-envvar QUBES_OPTION #check lintian output #suppress tags that are side effects of building in docker this way lintian ../*changes \ - -IE \ - --pedantic \ - --no-tag-display-limit \ - --suppress-tags missing-build-dependency-for-dh-addon \ - --suppress-tags library-not-linked-against-libc \ - --suppress-tags bad-distribution-in-changes-file \ - --suppress-tags debian-watch-file-in-native-package \ - --suppress-tags source-nmu-has-incorrect-version-number \ - --suppress-tags no-symbols-control-file \ - --suppress-tags gzip-file-is-not-multi-arch-same-safe \ - --suppress-tags missing-dependency-on-libc \ - --suppress-tags arch-dependent-file-not-in-arch-specific-directory \ - --suppress-tags package-installs-ieee-data \ - --allow-root + -IE \ + --pedantic \ + --tag-display-limit 0 \ + --suppress-tags missing-build-dependency-for-dh-addon \ + --suppress-tags bad-distribution-in-changes-file \ + --suppress-tags source-nmu-has-incorrect-version-number \ + --suppress-tags no-symbols-control-file \ + --suppress-tags gzip-file-is-not-multi-arch-same-safe \ + --suppress-tags missing-dependency-on-libc \ + --suppress-tags arch-dependent-file-not-in-arch-specific-directory \ + --suppress-tags package-installs-ieee-data \ + --allow-root #place built packages in dist outside docker mkdir -p ../dist diff -Nru fwupd-2.0.8/contrib/ci/debian_s390x.sh fwupd-2.0.20/contrib/ci/debian_s390x.sh --- fwupd-2.0.8/contrib/ci/debian_s390x.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/debian_s390x.sh 2026-02-26 11:36:18.000000000 +0000 @@ -15,18 +15,17 @@ cp contrib/ci/s390x_cross.txt build/ cd build meson setup .. \ - --cross-file s390x_cross.txt \ - --werror \ - -Dplugin_flashrom=disabled \ - -Dplugin_modem_manager=disabled \ - -Dintrospection=false \ - -Dlibxmlb:introspection=false \ - -Dlibxmlb:gtkdoc=false \ - -Dman=false + --cross-file s390x_cross.txt \ + --werror \ + -Dplugin_flashrom=disabled \ + -Dplugin_modem_manager=disabled \ + -Dintrospection=false \ + -Dlibxmlb:introspection=false \ + -Dlibxmlb:gtkdoc=false \ + -Dman=false ninja -v ninja test -v cd .. - #test for missing translation files ./contrib/ci/check_missing_translations.sh diff -Nru fwupd-2.0.8/contrib/ci/dependencies.xml fwupd-2.0.20/contrib/ci/dependencies.xml --- fwupd-2.0.8/contrib/ci/dependencies.xml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/dependencies.xml 2026-02-26 11:36:18.000000000 +0000 @@ -21,6 +21,9 @@ + + + @@ -213,6 +216,9 @@ + + libdrm + @@ -249,9 +255,6 @@ libftdi - - libftdi-devel - libftdi-devel libftdi-devel @@ -265,6 +268,9 @@ + + libftdi1 + @@ -287,6 +293,9 @@ + + libpci + @@ -309,6 +318,9 @@ + + noto-sans + @@ -409,6 +421,26 @@ + + + readline-devel + + + readline-devel + readline-devel + mingw64-readline + + + + + + + + + + + + @@ -457,6 +489,9 @@ flashrom-devel flashrom-devel + + flashrom-devel + ia64 @@ -498,6 +533,9 @@ + + gettext + @@ -507,6 +545,9 @@ + + gpgme + @@ -592,6 +633,9 @@ + + glib + @@ -626,6 +670,9 @@ + + + @@ -663,6 +710,9 @@ + + gnutls + @@ -704,16 +754,17 @@ + + + libxmlb - libxmlb-devel libxmlb-devel @@ -734,6 +785,9 @@ + + libxmlb + @@ -757,6 +811,9 @@ + + libjcat + @@ -835,6 +892,9 @@ libarchive + + libarchive + @@ -844,6 +904,9 @@ libcbor-devel libcbor-devel + + libcbor-devel + @@ -855,6 +918,9 @@ + + libcbor + @@ -949,11 +1015,6 @@ passim - - - - - @@ -1027,6 +1088,9 @@ + + + @@ -1073,6 +1137,9 @@ + + pango + @@ -1091,6 +1158,9 @@ mingw64-pkg-config + + pkgconf + @@ -1112,6 +1182,9 @@ + + polkit + @@ -1136,6 +1209,28 @@ + + + libmnl + + + libmnl-devel + + + libmnl-devel + libmnl-devel + + + + + + + + + + + + libqmi-devel @@ -1211,6 +1306,9 @@ python-pip + + py311-pip + @@ -1220,9 +1318,6 @@ - - - @@ -1230,6 +1325,9 @@ virtualenv + + py311-virtualenv + @@ -1342,6 +1440,9 @@ + + py311-gi-docgen + @@ -1370,22 +1471,10 @@ - - - - - - - - - - - - @@ -1429,6 +1518,9 @@ + + py311-pygobject + @@ -1520,6 +1612,19 @@ sqlite + + sqlite3 + + + + + py311-sqlite3 + + + + + efivar + @@ -1676,6 +1781,9 @@ valgrind-if-available valgrind-if-available + + valgrind + @@ -1733,13 +1841,21 @@ - - - ShellCheck + + + + + + + + + + + protobuf-c @@ -1764,6 +1880,9 @@ protobuf-c + + protobuf-c + @@ -1830,4 +1949,9 @@ msitools + + + + + diff -Nru fwupd-2.0.8/contrib/ci/fedora-test.sh fwupd-2.0.20/contrib/ci/fedora-test.sh --- fwupd-2.0.8/contrib/ci/fedora-test.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/fedora-test.sh 2026-02-26 11:36:18.000000000 +0000 @@ -26,7 +26,7 @@ # run the installed tests whilst the daemon debugging NO_COLOR=1 G_DEBUG=fatal-criticals /usr/libexec/fwupd/fwupd --verbose --no-timestamp >fwupd.txt 2>&1 & sleep 10 -gnome-desktop-testing-runner --timeout=600 fwupd +gnome-desktop-testing-runner --timeout=1200 fwupd # generate coverage report ./contrib/ci/coverage.sh diff -Nru fwupd-2.0.8/contrib/ci/fedora.sh fwupd-2.0.20/contrib/ci/fedora.sh --- fwupd-2.0.8/contrib/ci/fedora.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/fedora.sh 2026-02-26 11:36:18.000000000 +0000 @@ -21,14 +21,14 @@ mkdir -p build sed "s,#VERSION#,$RPMVERSION,; s,#BUILD#,1,; - s,#LONGDATE#,`date '+%a %b %d %Y'`,; + s,#LONGDATE#,$(date '+%a %b %d %Y'),; s,#ALPHATAG#,alpha,; s,enable_dummy 0,enable_dummy 1,; s,Source0.*,Source0:\tfwupd-$VERSION.tar.xz," \ - contrib/fwupd.spec.in > build/fwupd.spec + contrib/fwupd.spec.in >build/fwupd.spec if [ -n "$CI" ]; then - sed -i "s,enable_ci 0,enable_ci 1,;" build/fwupd.spec + sed -i "s,enable_ci 0,enable_ci 1,;" build/fwupd.spec fi #build RPM packages diff -Nru fwupd-2.0.8/contrib/ci/fwupd_setup_helpers.py fwupd-2.0.20/contrib/ci/fwupd_setup_helpers.py --- fwupd-2.0.8/contrib/ci/fwupd_setup_helpers.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/fwupd_setup_helpers.py 2026-02-26 11:36:18.000000000 +0000 @@ -18,7 +18,7 @@ def get_possible_profiles(): - return ["fedora", "centos", "debian", "ubuntu", "arch", "darwin"] + return ["fedora", "centos", "debian", "ubuntu", "arch", "darwin", "freebsd"] def detect_profile(): @@ -29,6 +29,8 @@ import distro target = distro.id() + if target == "rhel": + return "centos" if target not in get_possible_profiles(): target = distro.like() return target @@ -193,6 +195,8 @@ installer = ["dnf", "install"] elif profile == "arch": installer = ["pacman", "-Syu", "--noconfirm", "--needed"] + elif profile == "freebsd": + installer = ["pkg", "install"] else: print("unable to detect OS profile, use --os= to specify") print(f"\tsupported profiles: {get_possible_profiles()}") @@ -280,6 +284,8 @@ elif command == "install-pip": if args.os == "darwin": install_packages(args.os, args.variant, args.yes, args.debug, ["python"]) + elif args.os == "freebsd": + install_packages(args.os, args.variant, args.yes, args.debug, ["py311-pip"]) else: install_packages( args.os, args.variant, args.yes, args.debug, ["python3-pip"] diff -Nru fwupd-2.0.8/contrib/ci/generate_dependencies.py fwupd-2.0.20/contrib/ci/generate_dependencies.py --- fwupd-2.0.8/contrib/ci/generate_dependencies.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/generate_dependencies.py 2026-02-26 11:36:18.000000000 +0000 @@ -18,7 +18,7 @@ def get_possible_profiles(): - return ["fedora", "centos", "debian", "ubuntu", "arch", "darwin"] + return ["fedora", "centos", "debian", "ubuntu", "arch", "darwin", "freebsd"] def detect_profile(): @@ -29,6 +29,8 @@ import distro target = distro.id() + if target == "rhel": + return "centos" if target not in get_possible_profiles(): target = distro.like() return target @@ -193,6 +195,8 @@ installer = ["dnf", "install"] elif profile == "arch": installer = ["pacman", "-Syu", "--noconfirm", "--needed"] + elif profile == "freebsd": + installer = ["pkg", "install"] else: print("unable to detect OS profile, use --os= to specify") print(f"\tsupported profiles: {get_possible_profiles()}") @@ -280,6 +284,8 @@ elif command == "install-pip": if args.os == "darwin": install_packages(args.os, args.variant, args.yes, args.debug, ["python"]) + elif args.os == "freebsd": + install_packages(args.os, args.variant, args.yes, args.debug, ["py311-pip"]) else: install_packages( args.os, args.variant, args.yes, args.debug, ["python3-pip"] diff -Nru fwupd-2.0.8/contrib/ci/generate_docker.py fwupd-2.0.20/contrib/ci/generate_docker.py --- fwupd-2.0.8/contrib/ci/generate_docker.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/generate_docker.py 2026-02-26 11:36:18.000000000 +0000 @@ -55,7 +55,7 @@ if OS == "fedora": wfd.write("RUN dnf --enablerepo=updates-testing -y install \\\n") elif OS == "centos": - wfd.write("RUN yum -y install \\\n") + wfd.write("RUN dnf -y install \\\n") elif OS == "debian" or OS == "ubuntu": wfd.write("RUN apt update -qq && \\\n") wfd.write( diff -Nru fwupd-2.0.8/contrib/ci/generate_news.py fwupd-2.0.20/contrib/ci/generate_news.py --- fwupd-2.0.8/contrib/ci/generate_news.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/generate_news.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2020 Dell Inc. -# -# SPDX-License-Identifier: LGPL-2.1-or-later -# -import os -import argparse -import xml.etree.ElementTree as etree - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("version", help="Generate news for release") - args = parser.parse_args() - - tree = etree.parse(os.path.join("data", "org.freedesktop.fwupd.metainfo.xml")) - root = tree.getroot() - for release in root.iter("release"): - if "version" not in release.attrib: - continue - if release.attrib["version"] != args.version: - continue - description = release.find("description") - result = etree.tostring(description, encoding="unicode", method="text") - print(result.strip()) diff -Nru fwupd-2.0.8/contrib/ci/get_test_firmware.sh fwupd-2.0.20/contrib/ci/get_test_firmware.sh --- fwupd-2.0.8/contrib/ci/get_test_firmware.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/get_test_firmware.sh 2026-02-26 11:36:18.000000000 +0000 @@ -1,15 +1,15 @@ #!/bin/sh if [ "$CI_NETWORK" = "true" ]; then - #clone fwupd-test-firmware - rm -rf fwupd-test-firmware - git clone https://github.com/fwupd/fwupd-test-firmware - #If argument is set copy for installed tests - if [ -n "$1" ]; then - cp fwupd-test-firmware/installed-tests/* $1 -LRv - #copy data for self-tests into the source tree - else - cp fwupd-test-firmware/ci-tests/* . -R - fi - rm -rf fwupd-test-firmware + #clone fwupd-test-firmware + rm -rf fwupd-test-firmware + git clone https://github.com/fwupd/fwupd-test-firmware + #If argument is set copy for installed tests + if [ -n "$1" ]; then + cp fwupd-test-firmware/installed-tests/* $1 -LRv + #copy data for self-tests into the source tree + else + cp fwupd-test-firmware/ci-tests/* . -Rv + fi + rm -rf fwupd-test-firmware fi diff -Nru fwupd-2.0.8/contrib/ci/oss-fuzz.py fwupd-2.0.20/contrib/ci/oss-fuzz.py --- fwupd-2.0.8/contrib/ci/oss-fuzz.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/oss-fuzz.py 2026-02-26 11:36:18.000000000 +0000 @@ -150,7 +150,7 @@ out.write(blob) return dst - def compile(self, src: str) -> str: + def compile(self, src: str, argv_extra: Optional[list[str]] = None) -> str: """compile a specific source file""" argv = [self.cc] argv.extend(self.cflags) @@ -159,6 +159,8 @@ fullsrc = os.path.join(self.builddir, src) dst = os.path.basename(src).replace(".c", ".o") argv.extend(["-c", fullsrc, "-o", os.path.join(self.builddir, dst)]) + if argv_extra: + argv.extend(argv_extra) print(f"building {src} into {dst}") try: subprocess.run(argv, cwd=self.srcdir, check=True) @@ -167,19 +169,22 @@ sys.exit(1) return os.path.join(self.builddir, f"{dst}") - def rustgen(self, src: str) -> str: + def rustgen(self, src: str, includes: list[str] = []) -> str: fn_root = os.path.basename(src).replace(".rs", "") fulldst_c = os.path.join(self.builddir, f"{fn_root}-struct.c") fulldst_h = os.path.join(self.builddir, f"{fn_root}-struct.h") try: + argv = [ + "python", + "fwupd/libfwupdplugin/rustgen.py", + src, + fulldst_c, + fulldst_h, + ] + for include in includes: + argv += ["--include", include] subprocess.run( - [ - "python", - "fwupd/libfwupdplugin/rustgen.py", - src, - fulldst_c, - fulldst_h, - ], + argv, cwd=self.srcdir, check=True, ) @@ -283,6 +288,20 @@ self.pattern = pattern or f"{name}-firmware" @property + def name_camel(self) -> str: + acc: str = "" + hot: bool = True + for val in self.name: + if hot: + acc += val.upper() + hot = False + elif val in ["-", "_"]: + hot = True + else: + acc += val + return acc + + @property def new_gtype(self) -> str: return f"g_object_new(FU_TYPE_{self.pattern.replace('-', '_').upper()}, NULL)" @@ -366,6 +385,7 @@ "FWUPD_LIBDIR_PKG": "/tmp", "FWUPD_SYSCONFDIR": "/tmp", "FWUPD_LIBEXECDIR": "/tmp", + "HAVE_FUZZER": None, "HAVE_CBOR": None, "HAVE_CBOR_SET_ALLOCS": None, "HAVE_REALPATH": None, @@ -384,7 +404,7 @@ if src.endswith(".c"): built_objs.append(bld.compile(src)) elif src.endswith(".rs"): - built_objs.append(bld.compile(bld.rustgen(src))) + built_objs.append(bld.compile(bld.rustgen(src, includes=["fwupd.h"]))) # dummy binary entrypoint if "LIB_FUZZING_ENGINE" in os.environ: @@ -394,7 +414,6 @@ # built in formats for fzr in [ - Fuzzer("efi-lz77", pattern="efi-lz77-decompressor"), Fuzzer("csv"), Fuzzer("cab"), Fuzzer("dfuse"), @@ -405,6 +424,7 @@ Fuzzer("fmap"), Fuzzer("hid-descriptor", pattern="hid-descriptor"), Fuzzer("ihex"), + Fuzzer("pefile"), Fuzzer("srec"), Fuzzer("intel-thunderbolt"), Fuzzer("ifwi-cpd"), @@ -476,9 +496,15 @@ fuzz_objs = [] for obj in bld.grep_meson(f"fwupd/plugins/{fzr.srcdir}"): if obj.endswith(".c"): - fuzz_objs.append(bld.compile(obj)) + fuzz_objs.append( + bld.compile( + obj, argv_extra=[f'-DG_LOG_DOMAIN="FuPlugin{fzr.name_camel}"'] + ) + ) elif obj.endswith(".rs"): - fuzz_objs.append(bld.compile(bld.rustgen(obj))) + fuzz_objs.append( + bld.compile(bld.rustgen(obj, includes=["fwupdplugin.h"])) + ) src = bld.substitute( "fwupd/libfwupdplugin/fu-fuzzer-firmware.c.in", { diff -Nru fwupd-2.0.8/contrib/ci/precommit.sh fwupd-2.0.20/contrib/ci/precommit.sh --- fwupd-2.0.8/contrib/ci/precommit.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/precommit.sh 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,4 @@ #!/bin/sh -e # disable the safe directory feature git config --global safe.directory "*" -SKIP=no-commit-to-branch pre-commit run -v --hook-stage commit --all-files +SKIP=no-commit-to-branch pre-commit run -v --show-diff-on-failure --hook-stage commit --all-files diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-blocked-bitset.c fwupd-2.0.20/contrib/ci/tests/fu-blocked-bitset.c --- fwupd-2.0.8/contrib/ci/tests/fu-blocked-bitset.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-blocked-bitset.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: Use FU_BIT_SET() instead + * nocheck:expect: Use FU_BIT_CLEAR() instead + */ + +static void +fu_blocked_bitset_test(void) +{ + guint32 i = 0; + i |= 1u << 6; + i &= ~(1u << 8); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-blocked-funcs.c fwupd-2.0.20/contrib/ci/tests/fu-blocked-funcs.c --- fwupd-2.0.8/contrib/ci/tests/fu-blocked-funcs.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-blocked-funcs.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: contains blocked token cbor_get_uint + */ + +static void +fu_blocked_funcs_test(void) +{ + guint32 i = cbor_get_uint32(n); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-blocked-goto.c fwupd-2.0.20/contrib/ci/tests/fu-blocked-goto.c --- fwupd-2.0.8/contrib/ci/tests/fu-blocked-goto.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-blocked-goto.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: do not use goto + */ + +static void +fu_blocked_goto_test(void) +{ + for (guint i = 0; i < 10; i++) { + if (i == 5) + goto end; + } +end: + return; +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-blocked-rustgen.c fwupd-2.0.20/contrib/ci/tests/fu-blocked-rustgen.c --- fwupd-2.0.8/contrib/ci/tests/fu-blocked-rustgen.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-blocked-rustgen.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,15 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: use rustgen instead + */ + +typedef struct __attribute__((packed)) { + guint8 data; +} Foo; + +typedef struct __attribute__((packed)) { /* nocheck:blocked */ + guint8 e[6]; +} fwupd_guid_native_t; diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-comments.c fwupd-2.0.20/contrib/ci/tests/fu-comments.c --- fwupd-2.0.8/contrib/ci/tests/fu-comments.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-comments.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,15 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: use C style comments + */ + +static void +fu_comments(void) +{ + g_debug("hello"); + // c++ comments + g_debug("world"); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-device-convert-version.c fwupd-2.0.20/contrib/ci/tests/fu-device-convert-version.c --- fwupd-2.0.8/contrib/ci/tests/fu-device-convert-version.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-device-convert-version.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: Use FuDeviceClass->convert_version rather than fu_device_set_version + */ + +static void +fu_device_convert_version_test(void) +{ + fu_device_set_version_raw(device, 0x123); + fu_device_set_version(device, "1.2.3"); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-device-display.c fwupd-2.0.20/contrib/ci/tests/fu-device-display.c --- fwupd-2.0.8/contrib/ci/tests/fu-device-display.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-device-display.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: use fu_device_get_id_display + */ + +static void +fu_device_display_func(void) +{ + g_debug("removing %s (%s)", fu_device_get_name(parent), fu_device_get_id(parent)); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-enum-typedef.c fwupd-2.0.20/contrib/ci/tests/fu-enum-typedef.c --- fwupd-2.0.8/contrib/ci/tests/fu-enum-typedef.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-enum-typedef.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: invalid enum name + */ + +typedef enum { + FOO, +} WrongPrefix; diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-enum.c fwupd-2.0.20/contrib/ci/tests/fu-enum.c --- fwupd-2.0.8/contrib/ci/tests/fu-enum.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-enum.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: invalid enum name + */ + +enum FuBar { + FOO, +} + +enum WrongPrefix { + FOO, +} + +static void +foo_cb(enum flashrom_progress_stage stage, gboolean user_data) +{ +} + +enum { SIGNAL_CHANGED, SIGNAL_ADDED, SIGNAL_LAST }; diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-equals-true.c fwupd-2.0.20/contrib/ci/tests/fu-equals-true.c --- fwupd-2.0.8/contrib/ci/tests/fu-equals-true.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-equals-true.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: do not compare a boolean to TRUE + */ + +static gboolean +fu_equals_true(guint8 foo) +{ + if (foo == TRUE) { + /* this is an inefficient way of saying `return foo;` */ + return TRUE + } + return FALSE; +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-firmware-convert-version.c fwupd-2.0.20/contrib/ci/tests/fu-firmware-convert-version.c --- fwupd-2.0.8/contrib/ci/tests/fu-firmware-convert-version.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-firmware-convert-version.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: Use FuFirmwareClass->convert_version rather than fu_firmware_set_version + */ + +static void +fu_firmware_convert_version_test(void) +{ + fu_firmware_set_version_raw(firmware, 0x123); +} + +static void +fu_firmware_convert_version_test2(void) +{ + fu_firmware_set_version(firmware, "1.2.3"); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-function-length-switch.c fwupd-2.0.20/contrib/ci/tests/fu-function-length-switch.c --- fwupd-2.0.8/contrib/ci/tests/fu-function-length-switch.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-function-length-switch.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,30 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: has too many switches + */ + +static void +fu_function_length_switch_test(void) +{ + switch (foo) { + case 1: + break; + default: + break; + } + switch (bar) { + case 1: + break; + default: + break; + } + switch (baz) { + case 1: + break; + default: + break; + } +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-function-name.c fwupd-2.0.20/contrib/ci/tests/fu-function-name.c --- fwupd-2.0.8/contrib/ci/tests/fu-function-name.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-function-name.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: invalid function name + * nocheck:expect: mixed case function name + * nocheck:expect: function should be called ensure + */ + +#define FU_COMMON_VERSION_DECODE_BCD(val) 43 + +G_DEFINE_TYPE_EXTENDED(FuBackend, + fu_backend, + G_TYPE_OBJECT, + 0, + G_ADD_PRIVATE(FuBackend) + G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, fu_backend_codec_iface_init)); + +typedef guint8 *(*FuConvertFunc)(gpointer user_data); + +static void +wrong_name_test(void) +{ +} + +static gboolean +fu_function_name_set_version(FuExample *self, GError **error) +{ +} + +static gboolean +fu_function_name_Enter_SBL(FuExample *self, GError **error) +{ +} + +const gchar * +fwupd_strerror(gint errnum) /* nocheck:name */ +{ +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gerror-deref.c fwupd-2.0.20/contrib/ci/tests/fu-gerror-deref.c --- fwupd-2.0.8/contrib/ci/tests/fu-gerror-deref.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gerror-deref.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: dereferences GError; use error_local instead + */ + +static gboolean +fu_gerror_deref(void) +{ + g_debug("%s", (*error)->message); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gerror-domain.c fwupd-2.0.20/contrib/ci/tests/fu-gerror-domain.c --- fwupd-2.0.8/contrib/ci/tests/fu-gerror-domain.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gerror-domain.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: uses g_set_error() without using FWUPD_ERROR + */ + +static gboolean +fu_gerror_domain_test(GError **error) +{ + g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED, "foo"); + return FALSE; +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gerror-false-returns.c fwupd-2.0.20/contrib/ci/tests/fu-gerror-false-returns.c --- fwupd-2.0.8/contrib/ci/tests/fu-gerror-false-returns.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gerror-false-returns.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,15 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: uses g_set_error() without returning a value + */ + +static void +fu_gerror_false_returns_test(void) +{ + if (0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "foo"); + } +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gerror-literal.c fwupd-2.0.20/contrib/ci/tests/fu-gerror-literal.c --- fwupd-2.0.8/contrib/ci/tests/fu-gerror-literal.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gerror-literal.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,15 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: missing literal, use g_set_error_literal() instead + */ + +static void +fu_gerror_literal_test(void) +{ + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "test"); + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "valid %m"); + return FALSE; +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gerror-missing-suffix.c fwupd-2.0.20/contrib/ci/tests/fu-gerror-missing-suffix.c --- fwupd-2.0.8/contrib/ci/tests/fu-gerror-missing-suffix.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gerror-missing-suffix.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: missing ': ' suffix + */ + +static void +fu_gerror_missing_suffix_test(void) +{ + if (!foo_cb(self, &error)) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "foo"); + g_prefix_error_literal(error, "foo"); + return FALSE; + } +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gerror-no-return.c fwupd-2.0.20/contrib/ci/tests/fu-gerror-no-return.c --- fwupd-2.0.8/contrib/ci/tests/fu-gerror-no-return.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gerror-no-return.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: uses g_set_error() without returning a value + */ + +static gboolean +fu_gerror_no_return_test(void) +{ + g_autoptr(GError) error = NULL; + if (!foo_cb(self, error)) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "test"); + } +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gerror-not-set.c fwupd-2.0.20/contrib/ci/tests/fu-gerror-not-set.c --- fwupd-2.0.8/contrib/ci/tests/fu-gerror-not-set.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gerror-not-set.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: uses g_prefix_error() without setting GError + */ + +static gboolean +fu_gerror_not_set_test(void) +{ + g_autoptr(GError) error = NULL; + if (0) { + /* should set before prefix */ + g_prefix_error_literal(error, "test: "); + } +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gerror-void-return.c fwupd-2.0.20/contrib/ci/tests/fu-gerror-void-return.c --- fwupd-2.0.8/contrib/ci/tests/fu-gerror-void-return.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gerror-void-return.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: void return type not expected for GError + */ + +static void +fu_gerror_void_return(FuCustomDevice *self, GError **error) +{ + return; +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gobject-derivable.h fwupd-2.0.20/contrib/ci/tests/fu-gobject-derivable.h --- fwupd-2.0.8/contrib/ci/tests/fu-gobject-derivable.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gobject-derivable.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: wrong parent_class + * nocheck:expect: wrong parent GType + */ + +G_DECLARE_DERIVABLE_TYPE(FuDevice, fu_device, FU, DEVICE, FwupdDevice) + +typedef enum { + FU_DEVICE_FLAG_NONE = 0, +} FuDeviceFlags; + +/* usually in the .c file... */ +G_DEFINE_TYPE_WITH_PRIVATE(FuDevice, fu_device, FWUPD_TYPE_FIRMWARE); + +struct _FuDeviceClass { + FwupdFirmwareClass parent_class; + void (*func)(FuDevice *self); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gobject-final.h fwupd-2.0.20/contrib/ci/tests/fu-gobject-final.h --- fwupd-2.0.8/contrib/ci/tests/fu-gobject-final.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gobject-final.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: wrong parent GType + */ + +G_DECLARE_FINAL_TYPE(FuSmbios, fu_smbios, FU, SMBIOS, FuFirmware) + +/* usually in the .c file... */ +G_DEFINE_TYPE(FuSmbios, fu_smbios, FU_TYPE_DEVICE) diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gobject-finalize.c fwupd-2.0.20/contrib/ci/tests/fu-gobject-finalize.c --- fwupd-2.0.8/contrib/ci/tests/fu-gobject-finalize.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gobject-finalize.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: did not have parent ->finalize() + */ + +static void +fu_gobject_finalize(GObject *object); + +static void +fu_gobject_finalize(GObject *object) +{ + g_free(whatever); + G_OBJECT_CLASS(fu_device_parent_class)->destroy(object); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gprint.c fwupd-2.0.20/contrib/ci/tests/fu-gprint.c --- fwupd-2.0.8/contrib/ci/tests/fu-gprint.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gprint.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,24 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: contains blocked token g_print + * nocheck:expect: g_info should not contain newlines + * nocheck:expect: g_debug should not end with a full stop + * nocheck:expect: g_debug should not use sentence case + * nocheck:expect: single line comments should not use sentence case + */ + +static void +fu_gprint(void) +{ + /* + * This is a start of a long story, + * so we're allowing proper prose. + */ + g_printerr("fixme"); + /* But this IS wrong */ + g_debug("This is wrong."); + g_info("fixme\n"); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-gtask.c fwupd-2.0.20/contrib/ci/tests/fu-gtask.c --- fwupd-2.0.8/contrib/ci/tests/fu-gtask.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-gtask.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: missing literal, use g_task_return_new_error_literal + */ + +static void +fu_gtask_test(void) +{ + g_task_return_new_error(task, domain, code, "abc"); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-magic-numbers-defined.c fwupd-2.0.20/contrib/ci/tests/fu-magic-numbers-defined.c --- fwupd-2.0.8/contrib/ci/tests/fu-magic-numbers-defined.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-magic-numbers-defined.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,24 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: file has too many #defined magic values + */ + +#define FOO_001 0x123 +#define FOO_002 0x123 +#define FOO_003 0x123 +#define FOO_004 0x123 +#define FOO_005 0x123 +#define FOO_006 0x123 +#define FOO_007 0x123 +#define FOO_008 0x123 +#define FOO_009 0x123 +#define FOO_010 0x123 +#define FOO_011 0x123 +#define FOO_012 0x123 +#define FOO_013 0x123 +#define FOO_014 0x123 +#define FOO_015 0x123 +#define FOO_016 0x123 diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-magic-numbers-inline.c fwupd-2.0.20/contrib/ci/tests/fu-magic-numbers-inline.c --- fwupd-2.0.8/contrib/ci/tests/fu-magic-numbers-inline.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-magic-numbers-inline.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,96 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: file has too many inline magic values + */ + +static void +fu_magic_numbers_inline(void) +{ + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); + call(self, 0x123); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-memread.c fwupd-2.0.20/contrib/ci/tests/fu-memread.c --- fwupd-2.0.8/contrib/ci/tests/fu-memread.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-memread.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,30 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: too many calls to fu_memread_uintXX + */ + +static void +fu_memread_func(void) +{ + guint16 tmp; + guint64 total; + + tmp = fu_memread_uint16(buf + 0x0, G_LITTLE_ENDIAN); + total += tmp; + tmp = fu_memread_uint16(buf + 0x2, G_LITTLE_ENDIAN); + total += tmp; + tmp = fu_memread_uint16(buf + 0x4, G_LITTLE_ENDIAN); + total += tmp; + tmp = fu_memread_uint16(buf + 0x6, G_LITTLE_ENDIAN); + total += tmp; + tmp = fu_memread_uint16(buf + 0x8, G_LITTLE_ENDIAN); + total += tmp; + tmp = fu_memread_uint16(buf + 0xA, G_LITTLE_ENDIAN); + total += tmp; + tmp = fu_memread_uint16(buf + 0xC, G_LITTLE_ENDIAN); + total += tmp; + tmp = fu_memread_uint16(buf + 0xE, G_LITTLE_ENDIAN); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-nesting-depth.c fwupd-2.0.20/contrib/ci/tests/fu-nesting-depth.c --- fwupd-2.0.8/contrib/ci/tests/fu-nesting-depth.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-nesting-depth.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,24 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: is nested too deep + */ + +static gboolean +fu_nesting_depth_test(void) +{ + if (1) { + if (2) { + if (3) { + if (4) { + if (5) { + /* this is crazy */ + g_debug("foo"); + } + } + } + } + } +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-null-false-returns.c fwupd-2.0.20/contrib/ci/tests/fu-null-false-returns.c --- fwupd-2.0.8/contrib/ci/tests/fu-null-false-returns.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-null-false-returns.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: g_return_val_if_fail() return type invalid + * nocheck:expect: return type invalid for gboolean + */ + +static gboolean +fu_null_false_returns(gpointer foo) +{ + g_return_val_if_fail(foo != NULL, NULL); + if (1) { + /* something here */ + return G_MAXUINT32; + } + return NULL; +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-param-self-device.c fwupd-2.0.20/contrib/ci/tests/fu-param-self-device.c --- fwupd-2.0.8/contrib/ci/tests/fu-param-self-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-param-self-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: invalid parameter name self + */ + +static gboolean +fu_param_self_device(FuDevice *self) +{ +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-param-self-firmware.c fwupd-2.0.20/contrib/ci/tests/fu-param-self-firmware.c --- fwupd-2.0.8/contrib/ci/tests/fu-param-self-firmware.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-param-self-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: invalid parameter name self + */ + +static gboolean +fu_param_self_firmware(FuFirmware *self) +{ +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-param-self-native.c fwupd-2.0.20/contrib/ci/tests/fu-param-self-native.c --- fwupd-2.0.8/contrib/ci/tests/fu-param-self-native.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-param-self-native.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: native device functions should use self as the first parameter not device + */ + +static gboolean +fu_param_self_native_recv(FuHughskiColorhugDevice *self, GError **error) +{ +} + +static gboolean +fu_param_self_native_send(FuDevice *device, GError **error) +{ +} + +/* allowed to be FuDevice... */ +static gboolean +fu_param_self_native_attach(FuDevice *device, GError **error) +{ + FuHughskiColorhugDevice *self = FU_HUGHSKI_COLORHUG_DEVICE(device); + return fu_param_self_native_send(FU_DEVICE(self), error); +} + +static void +fu_param_self_native_class_init(FuHughskiColorhugDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->attach = fu_param_self_native_attach; +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-rustgen-bitshifts.c fwupd-2.0.20/contrib/ci/tests/fu-rustgen-bitshifts.c --- fwupd-2.0.8/contrib/ci/tests/fu-rustgen-bitshifts.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-rustgen-bitshifts.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: endian unsafe construction + */ + +static guint32 +fu_rustgen_bitshifts(void) +{ + return buf[13] << 24 | buf[14] << 16 | buf[15] << 8 | buf[16]; +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-rustgen-vars.c fwupd-2.0.20/contrib/ci/tests/fu-rustgen-vars.c --- fwupd-2.0.8/contrib/ci/tests/fu-rustgen-vars.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-rustgen-vars.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: rustgen structure 'buf' has to have 'st' prefix + */ + +static gboolean +fu_rustgen_vars_test(void) +{ + g_autoptr(FuStructFixme) buf = fu_struct_fixme_new(); +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-small-conditionals-with-braces.c fwupd-2.0.20/contrib/ci/tests/fu-small-conditionals-with-braces.c --- fwupd-2.0.8/contrib/ci/tests/fu-small-conditionals-with-braces.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-small-conditionals-with-braces.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: no {} required for small single-line conditional + */ + +static gboolean +fu_small_conditionals_with_braces(guint8 foo) +{ + if (TRUE) { + g_debug("this"); + g_debug("is fine"); + } + if (TRUE) { + g_debug("here"); + } + if (TRUE) { + g_debug("this is fine too,"); + } else { + g_debug("because of this"); + } + if (fu_this_that_and_something_else(foo) == FU_LONG_DEFINE1 || + fu_this_that_and_something_else(foo) == FU_LONG_DEFINE4) { + g_debug("here"); + } + for (guint i = 0; i < 10; i++) { + if (foo) + g_debug("fine"); + } +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-static-vars.c fwupd-2.0.20/contrib/ci/tests/fu-static-vars.c --- fwupd-2.0.8/contrib/ci/tests/fu-static-vars.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-static-vars.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: static variable FIXME not allowed + */ + +static guint FIXME = TRUE; + +static gboolean +fu_static_vars_test(void) +{ +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-struct-typedef.c fwupd-2.0.20/contrib/ci/tests/fu-struct-typedef.c --- fwupd-2.0.8/contrib/ci/tests/fu-struct-typedef.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-struct-typedef.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: invalid struct name + * nocheck:expect: incorrect struct name + */ + +typedef struct { + guint8 buf; +} FuStructPrefix; + +typedef struct { + guint16 buf; +} WrongPrefix; + +typedef struct { + gchar *id; +} FwupdBiosSettingPrivate; diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-struct.c fwupd-2.0.20/contrib/ci/tests/fu-struct.c --- fwupd-2.0.8/contrib/ci/tests/fu-struct.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-struct.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: invalid struct name + * nocheck:expect: incorrect struct name + */ + +struct FuStructPrefix { + guint8 buf; +} + +struct WrongPrefix { + guint8 buf; +} + +const struct { + guint8 buf; +} map[] = { + {0x0}, +}; diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-variable-lowercase.c fwupd-2.0.20/contrib/ci/tests/fu-variable-lowercase.c --- fwupd-2.0.8/contrib/ci/tests/fu-variable-lowercase.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-variable-lowercase.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: mixed case variable + * nocheck:expect: mixed case struct member + */ + +typedef struct { + guint8 this_is_fine; + guint8 Enter_SBL; +} FuTest; + +static void +fu_variable_lowercase(void) +{ + guint16 Enter_SBL = 0; +} diff -Nru fwupd-2.0.8/contrib/ci/tests/fu-zero-init.c fwupd-2.0.20/contrib/ci/tests/fu-zero-init.c --- fwupd-2.0.8/contrib/ci/tests/fu-zero-init.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/tests/fu-zero-init.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + * + * nocheck:expect: buffer not zero init + */ + +static gboolean +fu_zero_init_test(void) +{ + guint8 buf[10]; +} Binary files /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/contrib/ci/ubuntu-x86_64-test.sh and /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/contrib/ci/ubuntu-x86_64-test.sh differ diff -Nru fwupd-2.0.8/contrib/ci/ubuntu.sh fwupd-2.0.20/contrib/ci/ubuntu.sh --- fwupd-2.0.8/contrib/ci/ubuntu.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/ci/ubuntu.sh 2026-02-26 11:36:18.000000000 +0000 @@ -33,14 +33,23 @@ export BUILD=${root}/build rm -rf ${BUILD} chown -R nobody ${root} -sudo -u nobody meson ${BUILD} \ - -Db_coverage=true \ - -Dman=false \ - -Ddocs=enabled \ - -Dlibxmlb:gtkdoc=false \ - --prefix=${root}/target +sudo -u nobody meson ${BUILD} \ + -Db_coverage=true \ + -Dman=false \ + -Ddocs=enabled \ + -Dlibxmlb:gtkdoc=false \ + --prefix=${root}/target #build with clang -sudo -u nobody ninja -C ${BUILD} test -v +sudo -u nobody ninja -C ${BUILD} -v +sudo -u nobody meson test -C ${BUILD} --print-errorlogs --verbose + +# check meson install tags look fine +./contrib/ci/check-meson-install-tags.py -C "$BUILD" check + +# check we've not become a CPU or memory hog +ninja -C ${BUILD} install -v +./contrib/ci/check-rss.py --limit 3072 ${BUILD}/src/fwupdtool get-devices +./contrib/ci/check-cpu.py --limit 350 ${BUILD}/src/fwupdtool get-devices # check for unused symbols ./contrib/ci/check-unused.py @@ -48,13 +57,12 @@ # check the daemon aborts set +e FWUPD_SYSCALL_FILTER=systemd ${BUILD}/src/fwupd --immediate-exit -if [ $? -ne 1 ] ; then +if [ $? -ne 1 ]; then echo "failed to detect missing syscall filtering" exit 1 fi #make docs available outside of docker -ninja -C ${BUILD} install -v mkdir -p ${root}/dist/share mv ${root}/target/share/doc ${root}/dist/share diff -Nru fwupd-2.0.8/contrib/codespell.cfg fwupd-2.0.20/contrib/codespell.cfg --- fwupd-2.0.8/contrib/codespell.cfg 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/codespell.cfg 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,4 @@ [codespell] -builtin = clear,informal,en-GB_to_en-US -skip = *.po,*.csv,*.svg,*.p7c,subprojects,.git,pcrs,build*,.ossfuzz,*/tests/*,contrib/codespell.cfg -ignore-words-list = conexant,Conexant,gir,GIR,hsi,HSI,cancelled,Cancelled,te,mitre,distroname,wel,FPT,$FPT,inout,som,SoM +builtin = clear,informal,en-GB_to_en-US,usage +skip = *.po,*.csv,*.css,*.svg,*.p7c,*.proto,subprojects,.git,pcrs,build*,.ossfuzz,*/tests/*,contrib/codespell.cfg +ignore-words-list = conexant,Conexant,gir,GIR,hsi,HSI,cancelled,Cancelled,te,mitre,distroname,wel,FPT,$FPT,inout,som,SoM,sme,abl,outin,master,buildd diff -Nru fwupd-2.0.8/contrib/create-plugin.py fwupd-2.0.20/contrib/create-plugin.py --- fwupd-2.0.8/contrib/create-plugin.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/create-plugin.py 2026-02-26 11:36:18.000000000 +0000 @@ -72,6 +72,10 @@ subst[key.lower()] = value.lower() subst[key.upper()] = value.upper() + # overwrite author and email so they appear as entered + subst["Author"] = args.author + subst["Email"] = args.email + except NotImplementedError as e: print(e) sys.exit(1) diff -Nru fwupd-2.0.8/contrib/debian/control.in fwupd-2.0.20/contrib/debian/control.in --- fwupd-2.0.8/contrib/debian/control.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/control.in 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ Maintainer: Debian EFI Uploaders: Steve McIntyre <93sam@debian.org>, Matthias Klumpp , - Mario Limonciello + Mario Limonciello Build-Depends: %%%DYNAMIC%%% Build-Depends-Indep: gi-docgen , Rules-Requires-Root: no @@ -37,7 +37,7 @@ systemd-sysusers Recommends: python3, bolt, - dbus, + default-dbus-system-bus | dbus-system-bus, secureboot-db, udisks2, fwupd-signed, @@ -69,7 +69,7 @@ Depends: ${misc:Depends}, ${shlibs:Depends}, ca-certificates, - dbus-x11, + default-dbus-system-bus | dbus-system-bus, fwupd, gnome-desktop-testing, polkitd | policykit-1, diff -Nru fwupd-2.0.8/contrib/debian/fwupd-qubes-vm-whonix.postinst fwupd-2.0.20/contrib/debian/fwupd-qubes-vm-whonix.postinst --- fwupd-2.0.8/contrib/debian/fwupd-qubes-vm-whonix.postinst 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/fwupd-qubes-vm-whonix.postinst 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,6 @@ #!/bin/bash -HOME_DIR=`getent passwd user | awk '{ split($$0,a,":"); print a[6]}'` +HOME_DIR=$(getent passwd user | awk '{ split($$0,a,":"); print a[6]}') if [ -z "$HOME_DIR" ]; then echo "Default user does not exist!!" >&2 diff -Nru fwupd-2.0.8/contrib/debian/fwupd-qubes-vm-whonix.postrm fwupd-2.0.20/contrib/debian/fwupd-qubes-vm-whonix.postrm --- fwupd-2.0.8/contrib/debian/fwupd-qubes-vm-whonix.postrm 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/fwupd-qubes-vm-whonix.postrm 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,6 @@ #!/bin/bash -HOME_DIR=`getent passwd user | awk '{ split($$0,a,":"); print a[6]}'` +HOME_DIR=$(getent passwd user | awk '{ split($$0,a,":"); print a[6]}') if [ -z "$HOME_DIR" ] && [ "$1" = "purge" ]; then rm -rf $HOME_DIR/.cache/fwupd diff -Nru fwupd-2.0.8/contrib/debian/fwupd-tests.install fwupd-2.0.20/contrib/debian/fwupd-tests.install --- fwupd-2.0.8/contrib/debian/fwupd-tests.install 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/fwupd-tests.install 2026-02-26 11:36:18.000000000 +0000 @@ -4,5 +4,4 @@ #https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=872458 usr/share/installed-tests/* usr/libexec/installed-tests/fwupd/*-self-test -debian/lintian/fwupd-tests usr/share/lintian/overrides usr/share/fwupd/remotes.d/fwupd-tests.conf diff -Nru fwupd-2.0.8/contrib/debian/fwupd-tests.postinst fwupd-2.0.20/contrib/debian/fwupd-tests.postinst --- fwupd-2.0.8/contrib/debian/fwupd-tests.postinst 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/fwupd-tests.postinst 2026-02-26 11:36:18.000000000 +0000 @@ -5,9 +5,9 @@ #only enable on installation not upgrade if [ "$1" = configure ] && [ -z "$2" ]; then - if [ "$CI" = "true" ]; then - fwupdtool enable-test-devices - else - echo "To enable test suite, run `fwupdtool enable-test-devices`" - fi + if [ "$CI" = "true" ]; then + fwupdtool enable-test-devices + else + echo "To enable test suite, run \`fwupdtool enable-test-devices\`" + fi fi diff -Nru fwupd-2.0.8/contrib/debian/fwupd-tests.postrm fwupd-2.0.20/contrib/debian/fwupd-tests.postrm --- fwupd-2.0.8/contrib/debian/fwupd-tests.postrm 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/fwupd-tests.postrm 2026-02-26 11:36:18.000000000 +0000 @@ -5,9 +5,9 @@ # don't run on purge; the commands might be missing if [ "$1" = remove ]; then - if [ "$CI" = "true" ]; then - fwupdtool disable-test-devices - else - echo "To disable test suite, run `fwupdtool disable-test-devices`" - fi + if [ "$CI" = "true" ]; then + fwupdtool disable-test-devices + else + echo "To disable test suite, run \`fwupdtool disable-test-devices\`" + fi fi diff -Nru fwupd-2.0.8/contrib/debian/fwupd.install fwupd-2.0.20/contrib/debian/fwupd.install --- fwupd-2.0.8/contrib/debian/fwupd.install 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/fwupd.install 2026-02-26 11:36:18.000000000 +0000 @@ -16,5 +16,4 @@ data/fwupd.conf etc/fwupd debian/fwupd.pkla /var/lib/polkit-1/localauthority/10-vendor.d usr/lib/*/fwupd-*/*.so -debian/lintian/fwupd usr/share/lintian/overrides obj*/data/motd/85-fwupd /etc/update-motd.d diff -Nru fwupd-2.0.8/contrib/debian/fwupd.maintscript fwupd-2.0.20/contrib/debian/fwupd.maintscript --- fwupd-2.0.8/contrib/debian/fwupd.maintscript 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/fwupd.maintscript 2026-02-26 11:36:18.000000000 +0000 @@ -2,6 +2,7 @@ rm_conffile /etc/fwupd.conf 1.0.0~ rm_conffile /etc/fwupd/remotes.d/fwupd.conf 1.2.7~ rm_conffile /etc/dbus-1/system.d/org.freedesktop.fwupd.conf 1.3.2~ +rm_conffile /etc/modules-load.d/fwupd-i2c.conf 2.0.12~ rm_conffile /etc/modules-load.d/fwupd-msr.conf 1.5.3~ rm_conffile /etc/modules-load.d/fwupd-platform-integrity.conf 1.5.3~ rm_conffile /etc/fwupd/ata.conf 1.5.5~ diff -Nru fwupd-2.0.8/contrib/debian/fwupd.postrm fwupd-2.0.20/contrib/debian/fwupd.postrm --- fwupd-2.0.8/contrib/debian/fwupd.postrm 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/fwupd.postrm 2026-02-26 11:36:18.000000000 +0000 @@ -4,6 +4,6 @@ #DEBHELPER# if [ "$1" = purge ]; then - rm -rf /var/lib/fwupd /var/cache/fwupd /var/cache/fwupdmgr - rm -f /var/cache/app-info/xmls/fwupd.xml + rm -rf /var/lib/fwupd /var/cache/fwupd /var/cache/fwupdmgr + rm -f /var/cache/app-info/xmls/fwupd.xml fi diff -Nru fwupd-2.0.8/contrib/debian/fwupd.preinst fwupd-2.0.20/contrib/debian/fwupd.preinst --- fwupd-2.0.8/contrib/debian/fwupd.preinst 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/fwupd.preinst 2026-02-26 11:36:18.000000000 +0000 @@ -7,5 +7,5 @@ # this directory, but fwupd-refresh.service used DynamicUser directive # meaning no other unit could access it. if [ -L /var/cache/fwupd ]; then - rm -f /var/cache/fwupd + rm -f /var/cache/fwupd fi diff -Nru fwupd-2.0.8/contrib/debian/lintian/fwupd fwupd-2.0.20/contrib/debian/lintian/fwupd --- fwupd-2.0.8/contrib/debian/lintian/fwupd 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/lintian/fwupd 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -#see Debian bug 896012 -fwupd: library-not-linked-against-libc usr/lib/*/fwupd-plugins-*/* diff -Nru fwupd-2.0.8/contrib/debian/lintian/fwupd-tests fwupd-2.0.20/contrib/debian/lintian/fwupd-tests --- fwupd-2.0.8/contrib/debian/lintian/fwupd-tests 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/lintian/fwupd-tests 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -#see Debian bug 896012 -fwupd-tests: library-not-linked-against-libc usr/lib/*/fwupd-plugins-*/* diff -Nru fwupd-2.0.8/contrib/debian/tests/ci fwupd-2.0.20/contrib/debian/tests/ci --- fwupd-2.0.8/contrib/debian/tests/ci 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/tests/ci 2026-02-26 11:36:18.000000000 +0000 @@ -4,9 +4,9 @@ modprobe mtdram || true fwupdtool enable-test-devices fwupdtool modify-config VerboseDomains "*" -sed "s,ConditionVirtualization=.*,," \ - /lib/systemd/system/fwupd.service > \ - /etc/systemd/system/fwupd.service +sed "s,ConditionVirtualization=.*,," \ + /lib/systemd/system/fwupd.service > \ + /etc/systemd/system/fwupd.service systemctl daemon-reload # run the tests -gnome-desktop-testing-runner --timeout=600 fwupd +gnome-desktop-testing-runner --timeout=1200 fwupd diff -Nru fwupd-2.0.8/contrib/debian/tests/libfwupd-dev fwupd-2.0.20/contrib/debian/tests/libfwupd-dev --- fwupd-2.0.8/contrib/debian/tests/libfwupd-dev 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/debian/tests/libfwupd-dev 2026-02-26 11:36:18.000000000 +0000 @@ -19,7 +19,7 @@ cd "$WORKDIR" -cat > trivial.c <<'EOF' +cat >trivial.c <<'EOF' #undef NDEBUG #include diff -Nru fwupd-2.0.8/contrib/firmware_packager/README.md fwupd-2.0.20/contrib/firmware_packager/README.md --- fwupd-2.0.8/contrib/firmware_packager/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/firmware_packager/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -53,7 +53,7 @@ ## Example -Let's say we downloaded `Intel_TBT3_FW_UPDATE_NVM21_318RY_A01_4.21.01.002.exe` (available [here](https://downloads.dell.com/FOLDER04421073M/1/Intel_TBT3_FW_UPDATE_NVM21_318RY_A01_4.21.01.002.exe)) containing updated firmware for Dell laptops thunderbolt controllers. Since Dell hasn't made this available on the LVFS yet, we want to package and install it ourselves. +Let's say we downloaded [`Intel_TBT3_FW_UPDATE_NVM21_318RY_A01_4.21.01.002.exe`](https://downloads.dell.com/FOLDER04421073M/1/Intel_TBT3_FW_UPDATE_NVM21_318RY_A01_4.21.01.002.exe) containing updated firmware for Dell laptops thunderbolt controllers. Since Dell hasn't made this available on the LVFS yet, we want to package and install it ourselves. Opening the .exe with archive manager, we see it has a single folder: `Intel` and inside that, a set of firmware binaries (along with some microsoft junk). We pick the file `0x07BE_secure.bin` since we have a Dell XPS 9560 and that is its device string. diff -Nru fwupd-2.0.8/contrib/firmware_packager/firmware_packager.py fwupd-2.0.20/contrib/firmware_packager/firmware_packager.py --- fwupd-2.0.8/contrib/firmware_packager/firmware_packager.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/firmware_packager/firmware_packager.py 2026-02-26 11:36:18.000000000 +0000 @@ -6,13 +6,21 @@ # import argparse -import subprocess import contextlib import os import shutil +import subprocess import tempfile import time +try: + from jinja2 import Environment, Template +except ImportError: + print( + "Error: jinja2 is required for this script. Install it with: pip install jinja2" + ) + exit(1) + @contextlib.contextmanager def cd(path): @@ -22,43 +30,122 @@ os.chdir(prev_cwd) -firmware_metainfo_template = """ +# Embedded Jinja2 template for firmware.metainfo.xml +FIRMWARE_METAINFO_TEMPLATE = """ - org.{developer_name}.guid{firmware_id} - {firmware_name} - {firmware_summary} + org.{{ developer_name }}.guid{{ firmware_id }} + {{ firmware_name }}{% if name_variant_suffix %} + {{ name_variant_suffix }}{% endif %} + {{ firmware_summary }} - {firmware_description} +

{{ firmware_description }}

- {device_guid} + {{ device_guid }} - {firmware_homepage} + {{ firmware_homepage }} CC0-1.0 proprietary - {contact_info} - {developer_name} + {{ developer_name }} - + + - {release_description} +

{{ release_description }}

+ {% for feature in release_features %} +

{{ feature }}

+ {% endfor %}
- {version_format} - {update_protocol} + {{ version_format }} + {{ update_protocol }} -
-""" + + {% for category in categories %} + {{ category }} + {% endfor %} + +""" + + +def load_template(): + """Load the Jinja2 template from the embedded string with XML autoescape.""" + env = Environment(autoescape=True) + return env.from_string(FIRMWARE_METAINFO_TEMPLATE) def make_firmware_metainfo(firmware_info, dst): local_info = vars(firmware_info) local_info["firmware_id"] = local_info["device_guid"][0:8] - firmware_metainfo = firmware_metainfo_template.format( - **local_info, timestamp=time.time() - ) + + # Convert name-variant-suffix to name_variant_suffix for template + if "name_variant_suffix" not in local_info and hasattr( + firmware_info, "name_variant_suffix" + ): + local_info["name_variant_suffix"] = getattr( + firmware_info, "name_variant_suffix", "" + ) + + # Parse release features into a list for template iteration + if "release_features" in local_info and local_info["release_features"]: + features = local_info["release_features"] + # Handle both string (with | delimiter) and list inputs + if isinstance(features, str): + features = features.split("|") + elif isinstance(features, list): + # If it's already a list, use it directly + pass + else: + features = [] + + # Clean up features and filter out empty ones + cleaned_features = [] + for feature in features: + feature = feature.strip() + if feature: # Only add non-empty features + cleaned_features.append(feature) + + local_info["release_features"] = cleaned_features + else: + local_info["release_features"] = [] + + # Parse firmware categories into a list for template iteration + if "firmware_category" in local_info and local_info["firmware_category"]: + categories = local_info["firmware_category"] + # Handle both string (with | delimiter) and list inputs + if isinstance(categories, str): + categories = categories.split("|") + elif isinstance(categories, list): + # Flatten the list in case we have nested lists from argparse append + pipe-separated values + flattened_categories = [] + for item in categories: + if isinstance(item, str) and "|" in item: + flattened_categories.extend(item.split("|")) + else: + flattened_categories.append(item) + categories = flattened_categories + else: + categories = ["X-System"] # Default fallback + + # Clean up categories and filter out empty ones + cleaned_categories = [] + for category in categories: + category = category.strip() + if category: # Only add non-empty categories + cleaned_categories.append(category) + + local_info["categories"] = ( + cleaned_categories if cleaned_categories else ["X-System"] + ) + else: + # Default category if none provided + local_info["categories"] = ["X-System"] + + # Load and render the Jinja2 template + template = load_template() + firmware_metainfo = template.render(**local_info, date=time.strftime("%Y-%m-%d")) with open(os.path.join(dst, "firmware.metainfo.xml"), "w") as f: f.write(firmware_metainfo) @@ -127,6 +214,11 @@ required=True, ) parser.add_argument( + "--name-variant-suffix", + help="Name variant suffix for the firmware package", + default="", + ) + parser.add_argument( "--firmware-summary", help="One line description of the firmware package" ) parser.add_argument( @@ -139,9 +231,6 @@ ) parser.add_argument("--firmware-homepage", help="Website for the firmware provider") parser.add_argument( - "--contact-info", help="Email address of the firmware developer" - ) - parser.add_argument( "--developer-name", help="Name of the firmware developer", required=True ) parser.add_argument( @@ -160,9 +249,67 @@ required=True, ) parser.add_argument( + "--update-message", + help="Update message for LVFS compliance", + default="This firmware has been tested on target hardware and verified to work correctly", + ) + parser.add_argument( "--release-description", help="Description of the firmware release" ) parser.add_argument( + "--release-features", + help="Features in the release feature list. Accept multiple calls or one call splitting them with |", + action="append", + default=[], + ) + parser.add_argument( + "--release-urgency", + help="Release urgency (e.g. low, medium, high, critical)", + choices=["low", "medium", "high", "critical"], + default="medium", + ) + parser.add_argument( + "--firmware-category", + help="Firmware category (e.g. X-System, X-Device, X-EmbeddedController, ...) Split them with | or use multiple --firmware-category arguments", + action="append", + choices=[ + "X-System", + "X-Device", + "X-EmbeddedController", + "X-ManagementEngine", + "X-Controller", + "X-CorporateManagementEngine", + "X-ConsumerManagementEngine", + "X-ThunderboltController", + "X-PlatformSecurityProcessor", + "X-CpuMicrocode", + "X-Configuration", + "X-Battery", + "X-Camera", + "X-TPM", + "X-Touchpad", + "X-Mouse", + "X-Keyboard", + "X-StorageController", + "X-NetworkInterface", + "X-VideoDisplay", + "X-BaseboardManagementController", + "X-UsbReceiver", + "X-Drive", + "X-FlashDrive", + "X-SolidStateDrive", + "X-Gpu", + "X-Dock", + "X-UsbDock", + "X-FingerprintReader", + "X-GraphicsTablet", + "X-InputController", + "X-Headphones", + "X-Headset", + ], + default=[], + ) + parser.add_argument( "--exe", help="(optional) Executable file to extract firmware from" ) parser.add_argument( diff -Nru fwupd-2.0.8/contrib/firmware_packager/meson.build fwupd-2.0.20/contrib/firmware_packager/meson.build --- fwupd-2.0.8/contrib/firmware_packager/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/firmware_packager/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,10 +1,19 @@ if get_option('firmware-packager') - install_data('firmware_packager.py', - install_dir: 'share/fwupd') - install_data('add_capsule_header.py', - install_dir: 'share/fwupd') - install_data('install_dell_bios_exe.py', - install_dir: 'share/fwupd') + install_data( + 'firmware_packager.py', + install_tag: 'bin-devel', + install_dir: 'share/fwupd', + ) + install_data( + 'add_capsule_header.py', + install_tag: 'bin-devel', + install_dir: 'share/fwupd', + ) + install_data( + 'install_dell_bios_exe.py', + install_tag: 'bin-devel', + install_dir: 'share/fwupd', + ) con2 = configuration_data() con2.set('FWUPD_VERSION', fwupd_version) configure_file( @@ -12,6 +21,7 @@ output: 'simple_client.py', configuration: con2, install: true, + install_tag: 'bin-devel', install_dir: 'share/fwupd', ) endif diff -Nru fwupd-2.0.8/contrib/fwupd.spec.in fwupd-2.0.20/contrib/fwupd.spec.in --- fwupd-2.0.8/contrib/fwupd.spec.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/fwupd.spec.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,4 @@ -%global glib2_version 2.45.8 +%global glib2_version 2.68.0 %global libxmlb_version 0.1.3 %global libusb_version 1.0.9 %global libcurl_version 7.62.0 @@ -6,6 +6,12 @@ %global systemd_version 249 %global json_glib_version 1.1.1 +# to use this feature use `rpmbuild -ba fwupd.spec --with=libfwupdcompat` +%bcond_with libfwupdcompat +%if %{with libfwupdcompat} || 0%{?rhel} +%global libfwupd_19x_version 31 +%endif + # although we ship a few tiny python files these are utilities that 99.99% # of users do not need -- use this to avoid dragging python onto CoreOS %global __requires_exclude ^%{python3}$ @@ -16,6 +22,10 @@ %global enable_tests 1 %global __meson_wrap_mode nodownload +%if 0%{?fedora} >= 30 || 0%{?rhel} >= 10 +%global have_gi_docgen 1 +%endif + # fwupd.efi is only available on these arches %ifarch x86_64 aarch64 riscv64 %global have_uefi 1 @@ -30,11 +40,6 @@ %global have_msr 1 %endif -# Until we actually have seen it outside x86 -%ifarch i686 x86_64 -%global have_thunderbolt 1 -%endif - # only available recently %if 0%{?fedora} >= 30 %global have_modem_manager 1 @@ -51,8 +56,13 @@ License: LGPL-2.1-or-later URL: https://github.com/fwupd/fwupd Source0: http://people.freedesktop.org/~hughsient/releases/%{name}-%{version}.tar.xz +%if 0%{?libfwupd_19x_version} +Source1: https://github.com/fwupd/%{name}-efi/archive/refs/tags/1.7.tar.gz +Source2: https://github.com/fwupd/%{name}/releases/download/1.9.%{libfwupd_19x_version}/%{name}-1.9.%{libfwupd_19x_version}.tar.xz +%endif BuildRequires: gettext +BuildRequires: hwdata BuildRequires: glib2-devel >= %{glib2_version} BuildRequires: libxmlb-devel >= %{libxmlb_version} BuildRequires: libusb1-devel >= %{libusb_version} @@ -68,6 +78,8 @@ BuildRequires: libarchive-devel BuildRequires: libcbor-devel BuildRequires: libblkid-devel +BuildRequires: readline-devel +BuildRequires: libmnl-devel %if 0%{?have_passim} BuildRequires: passim-devel %endif @@ -76,7 +88,9 @@ BuildRequires: valgrind BuildRequires: valgrind-devel %endif +%if 0%{?have_gi_docgen} BuildRequires: gi-docgen +%endif BuildRequires: gnutls-devel BuildRequires: gnutls-utils BuildRequires: meson @@ -88,6 +102,8 @@ BuildRequires: flashrom-devel >= 1.2-2 %endif BuildRequires: libdrm-devel +# For fu-polkit-test +BuildRequires: polkit %if 0%{?have_modem_manager} BuildRequires: ModemManager-glib-devel >= 1.10.0 @@ -111,9 +127,11 @@ Requires: glib2%{?_isa} >= %{glib2_version} Requires: libxmlb%{?_isa} >= %{libxmlb_version} -Requires: libusb1%{?_isa} >= %{libusb_version} Requires: shared-mime-info +# deliberately has no _isa as libusbx in RHEL-9 does not include it +Requires: libusb1 >= %{libusb_version} + %if 0%{?rhel} > 7 || 0%{?fedora} > 28 Recommends: python3 %endif @@ -223,8 +241,37 @@ %prep %autosetup -p1 +# the EFI helper +%if 0%{?libfwupd_19x_version} +mkdir -p subprojects/fwupd-efi +tar xfvs %{SOURCE1} -C subprojects/fwupd-efi --strip-components=1 +cd subprojects/fwupd-efi +cd - +%endif + +# and the old version for libfwupd1 +%if 0%{?libfwupd_19x_version} +tar xfs %{SOURCE2} +%endif + %build +%if 0%{?libfwupd_19x_version} +cd fwupd-1.9.%{libfwupd_19x_version} +%meson \ + -Dbuild=library \ + -Ddocs=disabled \ + -Dcbor=disabled \ + -Dpolkit=disabled \ + -Dtests=false \ + -Dbash_completion=false \ + -Dplugin_msr=disabled \ + -Dintrospection=disabled \ + -Dlaunchd=disabled +%meson_build +cd - +%endif + %meson \ %if 0%{?enable_ci} --werror \ @@ -234,7 +281,11 @@ -Dsupported_build=enabled \ %endif -Dumockdev_tests=disabled \ +%if 0%{?have_gi_docgen} -Ddocs=enabled \ +%else + -Ddocs=disabled \ +%endif %if 0%{?enable_tests} -Dtests=true \ %else @@ -259,7 +310,6 @@ -Dqubes=true \ %endif -Dman=true \ - -Dplugin_qc_firehose=true \ -Dsystemd_unit_user="" \ -Dbluez=enabled @@ -274,6 +324,13 @@ %endif %install + +%if 0%{?libfwupd_19x_version} +cd fwupd-1.9.%{libfwupd_19x_version} +%meson_install +cd - +%endif + %meson_install mkdir -p --mode=0700 $RPM_BUILD_ROOT%{_localstatedir}/lib/fwupd/gnupg @@ -305,7 +362,9 @@ %ifarch x86_64 %{_libexecdir}/fwupd/fwupd-detect-cet %endif +%if 0%{?have_uefi} %{_bindir}/dbxtool +%endif %{_bindir}/fwupdmgr %{_bindir}/fwupdtool %dir %{_sysconfdir}/fwupd @@ -320,6 +379,7 @@ %if 0%{?have_msr} /usr/lib/modules-load.d/fwupd-msr.conf %endif +/usr/lib/modules-load.d/fwupd-i2c.conf %{_datadir}/dbus-1/system.d/org.freedesktop.fwupd.conf %{_datadir}/bash-completion/completions/fwupdmgr %{_datadir}/bash-completion/completions/fwupdtool @@ -336,7 +396,9 @@ %{_datadir}/polkit-1/rules.d/org.freedesktop.fwupd.rules %{_datadir}/dbus-1/system-services/org.freedesktop.fwupd.service %{_mandir}/man1/fwupdtool.1* +%if 0%{?have_uefi} %{_mandir}/man1/dbxtool.* +%endif %{_mandir}/man1/fwupdmgr.1* %{_mandir}/man5/* %{_mandir}/man8/* @@ -353,10 +415,15 @@ %dir %{_localstatedir}/cache/fwupd %dir %{_datadir}/fwupd/quirks.d %{_datadir}/fwupd/quirks.d/builtin.quirk.gz +%if 0%{?have_gi_docgen} %{_datadir}/doc/fwupd/*.html +%endif %if 0%{?have_uefi} %config(noreplace)%{_sysconfdir}/grub.d/35_fwupd %endif +%if 0%{?libfwupd_19x_version} +%{_libdir}/libfwupd.so.2* +%endif %{_libdir}/libfwupd.so.3* %{_libdir}/girepository-1.0/Fwupd-2.0.typelib /usr/lib/systemd/system-shutdown/fwupd.shutdown @@ -379,13 +446,18 @@ %files devel %{_datadir}/gir-1.0/Fwupd-2.0.gir +%if 0%{?have_gi_docgen} %{_datadir}/doc/fwupd/libfwupdplugin %{_datadir}/doc/fwupd/libfwupd %{_datadir}/doc/libfwupdplugin %{_datadir}/doc/libfwupd +%endif %{_datadir}/vala/vapi +%if 0%{?libfwupd_19x_version} +%{_includedir}/fwupd-1 +%endif %{_includedir}/fwupd-3 -%{_libdir}/libfwupd*.so +%{_libdir}/libfwupd.so %{_libdir}/pkgconfig/fwupd.pc %files tests diff -Nru fwupd-2.0.8/contrib/fwupd.wxs.in fwupd-2.0.20/contrib/fwupd.wxs.in --- fwupd-2.0.8/contrib/fwupd.wxs.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/fwupd.wxs.in 2026-02-26 11:36:18.000000000 +0000 @@ -23,7 +23,11 @@ - + + + + + @@ -31,6 +35,7 @@ + diff -Nru fwupd-2.0.8/contrib/generate-ds20.py fwupd-2.0.20/contrib/generate-ds20.py --- fwupd-2.0.8/contrib/generate-ds20.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/generate-ds20.py 2026-02-26 11:36:18.000000000 +0000 @@ -66,6 +66,6 @@ buf = buf.ljust(args.bufsz, b"\0") # success - print("DS20 descriptor control transfer data:") + print(f"DS20 descriptor control transfer data ({len(buf)} Bytes):") print(", ".join([f"0x{val:02x}" for val in list(buf)])) print(base64.b64encode(buf).decode()) diff -Nru fwupd-2.0.8/contrib/launch-venv.sh fwupd-2.0.20/contrib/launch-venv.sh --- fwupd-2.0.8/contrib/launch-venv.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/launch-venv.sh 2026-02-26 11:36:18.000000000 +0000 @@ -10,61 +10,63 @@ export FWUPD_SYSCONFDIR=${DIST}/etc export LD_LIBRARY_PATH=${DIST}/lib/${gcc}:${DIST}/lib64:${DIST}/lib if [ -n "${DEBUG}" ]; then - if ! which gdbserver 1>/dev/null 2>&1; then - echo "install gdbserver to enable debugging" - exit 1 - fi - DEBUG="gdbserver localhost:9091" + if ! which gdbserver 1>/dev/null 2>&1; then + echo "install gdbserver to enable debugging" + exit 1 + fi + DEBUG="gdbserver localhost:9091" fi if [ -f ${DIST}/libexec/fwupd/${BIN} ]; then - EXE=${DIST}/libexec/fwupd/${BIN} + EXE=${DIST}/libexec/fwupd/${BIN} else - EXE=${DIST}/bin/${BIN} + EXE=${DIST}/bin/${BIN} fi if [ ! -f ${EXE} ]; then - echo "Not yet built! Please run:" - echo "" - echo "# build-fwupd" - exit 1 + echo "Not yet built! Please run:" + echo "" + echo "# build-fwupd" + exit 1 fi if [ -z "${G_DEBUG}" ]; then - G_DEBUG="fatal-criticals" + G_DEBUG="fatal-criticals" fi if [ -z "${GLIBC_TUNABLES}" ]; then - GLIBC_TUNABLES=glibc.cpu.hwcaps=SHSTK + GLIBC_TUNABLES=glibc.cpu.hwcaps=SHSTK fi ENV="FWUPD_POLKIT_NOCHECK=1 \ G_DEBUG=${G_DEBUG} \ GLIBC_TUNABLES=${GLIBC_TUNABLES} \ - LD_LIBRARY_PATH=${LD_LIBRARY_PATH}" + LD_LIBRARY_PATH=${LD_LIBRARY_PATH} \ + FWUPD_UEFI_VERBOSE=${FWUPD_UEFI_VERBOSE} \ + TPM2TOOLS_TCTI=${TPM2TOOLS_TCTI}" for var in $(env | grep FWUPD | cut -d= -f1); do - ENV="${ENV} ${var}=${!var}" + ENV="${ENV} ${var}=${!var}" done SUDO=$(which sudo) -if [ "${BIN}" = "fwupd" ] && \ - [ -d "$(dirname ${DBUSPOLICY})" ] && \ - [ ! -f ${DBUSPOLICY} ]; then - echo "Missing D-Bus policy in ${DBUSPOLICY}" - echo "Copy into filesystem? [y/N]" - read -r answer - if [ "${answer}" != "y" ]; then - exit 1 - fi - ${SUDO} cp ${DIST}/share/dbus-1/system.d/org.freedesktop.fwupd.conf ${DBUSPOLICY} +if [ "${BIN}" = "fwupd" ] && + [ -d "$(dirname ${DBUSPOLICY})" ] && + [ ! -f ${DBUSPOLICY} ]; then + echo "Missing D-Bus policy in ${DBUSPOLICY}" + echo "Copy into filesystem? [y/N]" + read -r answer + if [ "${answer}" != "y" ]; then + exit 1 + fi + ${SUDO} cp ${DIST}/share/dbus-1/system.d/org.freedesktop.fwupd.conf ${DBUSPOLICY} fi if [ "${BIN}" = "fwupdmgr" ] && - [ -d "$(dirname ${PKPOLICY})" ] && \ - ! grep "org.freedesktop.fwupd.emulation-save" $PKPOLICY 1>/dev/null 2>&1; then - echo "Missing or outdated PolicyKit policy in ${PKPOLICY}" - echo "Copy into filesystem? [y/N]" - read -r answer - if [ "${answer}" != "y" ]; then - exit 1 - fi - ${SUDO} cp ${DIST}/share/polkit-1/actions/org.freedesktop.fwupd.policy ${PKPOLICY} + [ -d "$(dirname ${PKPOLICY})" ] && + ! grep "org.freedesktop.fwupd.emulation-save" $PKPOLICY 1>/dev/null 2>&1; then + echo "Missing or outdated PolicyKit policy in ${PKPOLICY}" + echo "Copy into filesystem? [y/N]" + read -r answer + if [ "${answer}" != "y" ]; then + exit 1 + fi + ${SUDO} cp ${DIST}/share/polkit-1/actions/org.freedesktop.fwupd.policy ${PKPOLICY} fi ${SUDO} ${ENV} ${DEBUG} ${EXE} "$@" if [ "${BIN}" = "fwupdmgr" ] && [ "${COMMAND}" = "emulation-save" ]; then - ${SUDO} chown "$(id -u)":"$(id -g)" ${ARGUMENT} + ${SUDO} chown "$(id -u)":"$(id -g)" ${ARGUMENT} fi diff -Nru fwupd-2.0.8/contrib/meson.build fwupd-2.0.20/contrib/meson.build --- fwupd-2.0.8/contrib/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -12,16 +12,16 @@ configuration: con2, ) -uswid = find_program('uswid', required: false) +uswid = find_program( + 'uswid', + required: false, +) if uswid.found() - custom_target('gen-sbom', + custom_target( + 'gen-sbom', input: 'sbom.cdx.json', output: 'sbom.cdx.json', - command: [ - uswid, - '--load', '@INPUT@', - '--save', '@OUTPUT@', - ], + command: [uswid, '--load', '@INPUT@', '--save', '@OUTPUT@'], ) endif @@ -29,6 +29,6 @@ configure_file( input: 'fwupd.wxs.in', output: 'fwupd.wxs', - configuration: conf + configuration: conf, ) endif diff -Nru fwupd-2.0.8/contrib/mingw64.cross fwupd-2.0.20/contrib/mingw64.cross --- fwupd-2.0.8/contrib/mingw64.cross 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/mingw64.cross 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,2 @@ [binaries] windmc = '/usr/bin/x86_64-w64-mingw32-windmc' -exe_wrapper = 'wine' diff -Nru fwupd-2.0.8/contrib/pcap2emulation.py fwupd-2.0.20/contrib/pcap2emulation.py --- fwupd-2.0.8/contrib/pcap2emulation.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/pcap2emulation.py 2026-02-26 11:36:18.000000000 +0000 @@ -288,9 +288,9 @@ return {"Id": s, "Data": captured_data} elif layers["usb"]["usb_usb_endpoint_address_direction"] == "1": if "usb_usb_urb_len" in layers["usb"]: - self.bulk_incoming_lens[ - layers["frame"]["frame_frame_number"] - ] = get_int(layers["usb"]["usb_usb_urb_len"]) + self.bulk_incoming_lens[layers["frame"]["frame_frame_number"]] = ( + get_int(layers["usb"]["usb_usb_urb_len"]) + ) return {} def _get_interface_descriptor( diff -Nru fwupd-2.0.8/contrib/qubes/README.md fwupd-2.0.20/contrib/qubes/README.md --- fwupd-2.0.8/contrib/qubes/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/qubes/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -192,7 +192,8 @@ # python3 -m unittest -v test.test_qubes_fwupdmgr ``` -Note: If the whonix tests failed, make sure that you are connected to the Tor +> [!TIP] +> If the whonix tests failed, make sure that you are connected to the Tor ## Whonix support diff -Nru fwupd-2.0.8/contrib/qubes/meson.build fwupd-2.0.20/contrib/qubes/meson.build --- fwupd-2.0.8/contrib/qubes/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/qubes/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,43 +1,43 @@ -install_data([ - 'src/__init__.py', - 'src/fwupd_receive_updates.py', - 'src/qubes_fwupd_common.py', - 'src/qubes_fwupd_heads.py', - 'src/qubes_fwupd_update.py', - 'src/qubes_fwupdmgr.py', +install_data( + [ + 'src/__init__.py', + 'src/fwupd_receive_updates.py', + 'src/qubes_fwupd_common.py', + 'src/qubes_fwupd_heads.py', + 'src/qubes_fwupd_update.py', + 'src/qubes_fwupdmgr.py', ], install_dir: 'share/qubes-fwupd/src', ) -install_data([ - 'test/__init__.py', - 'test/fwupd_logs.py', - 'test/test_qubes_fwupd_heads.py', - 'test/test_qubes_fwupdmgr.py', +install_data( + [ + 'test/__init__.py', + 'test/fwupd_logs.py', + 'test/test_qubes_fwupd_heads.py', + 'test/test_qubes_fwupdmgr.py', ], install_dir: 'share/qubes-fwupd/test', ) -install_data([ - 'test/logs/firmware.metainfo.xml', - 'test/logs/get_devices.log', - 'test/logs/get_updates.log', - 'test/logs/help.log', +install_data( + [ + 'test/logs/firmware.metainfo.xml', + 'test/logs/get_devices.log', + 'test/logs/get_updates.log', + 'test/logs/help.log', ], install_dir: 'share/qubes-fwupd/test/logs', ) -install_data([ - 'src/vms/fwupd_common_vm.py', - 'src/vms/fwupd_download_updates.py', - ], +install_data( + ['src/vms/fwupd_common_vm.py', 'src/vms/fwupd_download_updates.py'], install_dir: 'libexec/qubes-fwupd', install_mode: 'rwxrwxr-x', ) -install_data([ - 'test/logs/metainfo_name/firmware.metainfo.xml', - ], +install_data( + ['test/logs/metainfo_name/firmware.metainfo.xml'], install_dir: 'share/qubes-fwupd/test/logs/metainfo_name', ) diff -Nru fwupd-2.0.8/contrib/qubes/src/fwupd_receive_updates.py fwupd-2.0.20/contrib/qubes/src/fwupd_receive_updates.py --- fwupd-2.0.8/contrib/qubes/src/fwupd_receive_updates.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/qubes/src/fwupd_receive_updates.py 2026-02-26 11:36:18.000000000 +0000 @@ -54,7 +54,7 @@ file_path -- absolute path to jcat file file_directory -- absolute path to the directory to jcat file location """ - assert file_path.startswith("/"), "bad file path {file_path!r}" + assert file_path.startswith("/"), f"bad file path {file_path!r}" cmd_jcat = ["jcat-tool", "verify", file_path, "--public-keys", FWUPD_PKI] p = subprocess.Popen( cmd_jcat, cwd=file_directory, stdout=subprocess.PIPE, stderr=subprocess.PIPE @@ -135,7 +135,7 @@ signature_path -- absolute path to signature file file_path -- absolute path to the signed file location """ - assert file_path.startswith("/"), "bad file path {file_path!r}" + assert file_path.startswith("/"), f"bad file path {file_path!r}" try: self._pgp_parse(signature_path) except Exception: diff -Nru fwupd-2.0.8/contrib/qubes/src/qubes_fwupd_common.py fwupd-2.0.20/contrib/qubes/src/qubes_fwupd_common.py --- fwupd-2.0.8/contrib/qubes/src/qubes_fwupd_common.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/qubes/src/qubes_fwupd_common.py 2026-02-26 11:36:18.000000000 +0000 @@ -37,7 +37,6 @@ class LooseVersion: - """Version numbering for anarchists and software realists. Implements the standard interface for version number classes as described above. A version number consists of a series of numbers, diff -Nru fwupd-2.0.8/contrib/qubes/src/qubes_fwupdmgr.py fwupd-2.0.20/contrib/qubes/src/qubes_fwupdmgr.py --- fwupd-2.0.8/contrib/qubes/src/qubes_fwupdmgr.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/qubes/src/qubes_fwupdmgr.py 2026-02-26 11:36:18.000000000 +0000 @@ -482,9 +482,9 @@ elif isinstance(updev_dict[updev_key][0], str): for i, data in enumerate(updev_dict[updev_key]): if i == 0: - print(output + "\u00B7" + data) + print(output + "\u00b7" + data) continue - print(style + _tabs(" ") + "\u00B7" + data) + print(style + _tabs(" ") + "\u00b7" + data) elif isinstance(updev_dict[updev_key][0], dict): if level == 0 and help_f is True: print(output) diff -Nru fwupd-2.0.8/contrib/setup fwupd-2.0.20/contrib/setup --- fwupd-2.0.8/contrib/setup 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/setup 2026-02-26 11:36:18.000000000 +0000 @@ -6,12 +6,11 @@ HELPER=./contrib/ci/fwupd_setup_helpers.py HELPER_ARGS="-y" -rename_branch() -{ +rename_branch() { OLD=master NEW=main if git log $OLD >/dev/null 2>&1 && - git remote get-url origin 2>&1 | grep fwupd/fwupd.git >/dev/null 2>&1; then + git remote get-url origin 2>&1 | grep fwupd/fwupd.git >/dev/null 2>&1; then echo "" read -p "Rename existing $OLD branch to $NEW? (y/N) " question if [ "$question" = "y" ]; then @@ -23,16 +22,18 @@ fi } -setup_deps() -{ - read -p "Install build dependencies? (y/N) " question +setup_deps() { + if [ -z "$CI" ]; then + read -p "Install build dependencies? (y/N) " question + else + question=y + fi if [ "$question" = "y" ]; then python3 $HELPER install-dependencies $HELPER_ARGS -y fi } -setup_run_wrappers() -{ +setup_run_wrappers() { BASE=../../contrib/ BIN=venv/bin/ TEMPLATE=${BASE}/launch-venv.sh @@ -48,8 +49,7 @@ ln -s ${BASE}/test-venv.sh ${BIN}/test-fwupd } -setup_vscode() -{ +setup_vscode() { # Add default vscode settings and debug launcher SOURCED=./contrib/vscode TARGETD=./.vscode @@ -65,14 +65,14 @@ done } -setup_git() -{ - echo "Configuring git environment" - git config include.path ../.gitconfig +setup_git() { + if [ -z "$CI" ]; then + echo "Configuring git environment" + git config include.path ../.gitconfig + fi } -install_pip() -{ +install_pip() { package=$1 args=$2 if ! python3 -m pip install $package $args; then @@ -82,15 +82,14 @@ python3 -m pip install $package } -setup_virtualenv() -{ +setup_virtualenv() { echo "Setting up virtualenv" if ! which virtualenv >/dev/null 2>&1; then install_pip virtualenv fi virtualenv --system-site-packages venv --prompt fwupd source venv/bin/activate - cat >> venv/bin/activate << EOF + cat >>venv/bin/activate </dev/null + # shellcheck source=/dev/null + . $SNAP_USER_DATA/.last_revision 2>/dev/null fi if [ "$SNAP_DESKTOP_LAST_REVISION" = "$SNAP_REVISION" ]; then - needs_update=false + needs_update=false fi if [ $needs_update = true ]; then - if [ -f $SNAP/usr/lib/$ARCH/glib-2.0/gio-querymodules ]; then - rm -rf $GIO_MODULE_DIR - mkdir -p $GIO_MODULE_DIR - ln -s $SNAP/usr/lib/$ARCH/gio/modules/*.so $GIO_MODULE_DIR - $SNAP/usr/lib/$ARCH/glib-2.0/gio-querymodules $GIO_MODULE_DIR - fi - echo "SNAP_DESKTOP_LAST_REVISION=$SNAP_REVISION" > $SNAP_USER_DATA/.last_revision + if [ -f $SNAP/usr/lib/$ARCH/glib-2.0/gio-querymodules ]; then + rm -rf $GIO_MODULE_DIR + mkdir -p $GIO_MODULE_DIR + ln -s $SNAP/usr/lib/$ARCH/gio/modules/*.so $GIO_MODULE_DIR + $SNAP/usr/lib/$ARCH/glib-2.0/gio-querymodules $GIO_MODULE_DIR + fi + echo "SNAP_DESKTOP_LAST_REVISION=$SNAP_REVISION" >$SNAP_USER_DATA/.last_revision fi # Setup directory structures if command was run as root if [ "$(id -u)" = "0" ]; then - CONF_DIR="${SNAP_COMMON}/var/etc/fwupd" - REMOTE_DIR="${SNAP_COMMON}/var/lib/fwupd/remotes.d" - SHARE_DIR="${SNAP_COMMON}/share/fwupd/remotes.d/vendor/firmware" - mkdir -p ${CONF_DIR} ${REMOTE_DIR} ${SHARE_DIR} - - # copy a writable fwupd.conf for users to use - if [ ! -f "${CONF_DIR}/fwupd.conf" ]; then - cp "$SNAP/etc/fwupd/fwupd.conf" "${CONF_DIR}/fwupd.conf" - fi - - # Migrate remotes from "old" snap guidance and from immutable directory - for BASE in "${SNAP}/etc/fwupd/remotes.d" "${SNAP_USER_DATA}/etc/fwupd/remotes.d/"; do - if [ ! -d "${BASE}" ]; then - continue + CONF_DIR="${SNAP_COMMON}/var/etc/fwupd" + REMOTE_DIR="${SNAP_COMMON}/var/lib/fwupd/remotes.d" + SHARE_DIR="${SNAP_COMMON}/share/fwupd/remotes.d/vendor/firmware" + mkdir -p ${CONF_DIR} ${REMOTE_DIR} ${SHARE_DIR} + + # copy a writable fwupd.conf for users to use + if [ ! -f "${CONF_DIR}/fwupd.conf" ]; then + cp "$SNAP/etc/fwupd/fwupd.conf" "${CONF_DIR}/fwupd.conf" fi - for P in ${BASE}/*.conf; do - REMOTE=$(basename $P) - # vendor.conf doesn't make sense in snap - if [ "${REMOTE}" = "vendor.conf" ]; then - continue - fi - if [ ! -f "${REMOTE_DIR}/${REMOTE}" ]; then - cp ${P} "${REMOTE_DIR}" - fi + + # Migrate remotes from "old" snap guidance and from immutable directory + for BASE in "${SNAP}/etc/fwupd/remotes.d" "${SNAP_USER_DATA}/etc/fwupd/remotes.d/"; do + if [ ! -d "${BASE}" ]; then + continue + fi + for P in ${BASE}/*.conf; do + REMOTE=$(basename $P) + # vendor.conf doesn't make sense in snap + if [ "${REMOTE}" = "vendor.conf" ]; then + continue + fi + if [ ! -f "${REMOTE_DIR}/${REMOTE}" ]; then + cp ${P} "${REMOTE_DIR}" + fi + done done - done fi exec "$@" diff -Nru fwupd-2.0.8/contrib/snap/snapcraft.yaml fwupd-2.0.20/contrib/snap/snapcraft.yaml --- fwupd-2.0.8/contrib/snap/snapcraft.yaml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/snap/snapcraft.yaml 2026-02-26 11:36:18.000000000 +0000 @@ -159,6 +159,7 @@ - libmm-glib-dev - libqmi-glib-dev - libmbim-glib-dev + - libreadline-dev - locales - meson - modemmanager @@ -188,6 +189,7 @@ - libgpgme11 - libprotobuf-c1 - libpolkit-gobject-1-0 + - libreadline8t64 - libtss2-esys-3.0.2-0 - libtss2-fapi1 - libtss2-mu-4.0.1-0t64 diff -Nru fwupd-2.0.8/contrib/tartan.sh fwupd-2.0.20/contrib/tartan.sh --- fwupd-2.0.8/contrib/tartan.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/tartan.sh 2026-02-26 11:36:18.000000000 +0000 @@ -1,9 +1,9 @@ #!/bin/sh -/usr/bin/scan-build-17 \ - -load-plugin /usr/lib64/tartan/17.0/libtartan.so \ - -disable-checker core.CallAndMessage \ - -disable-checker core.NullDereference \ - -disable-checker deadcode.DeadStores \ - -disable-checker unix.Malloc \ - -enable-checker tartan.GErrorChecker \ - --status-bugs -v "$@" +/usr/bin/scan-build-18 \ + -load-plugin /usr/lib64/tartan/18.1/libtartan.so \ + -disable-checker core.CallAndMessage \ + -disable-checker core.NullDereference \ + -disable-checker deadcode.DeadStores \ + -disable-checker unix.Malloc \ + -enable-checker tartan.GErrorChecker \ + --status-bugs -v "$@" diff -Nru fwupd-2.0.8/contrib/test-venv.sh fwupd-2.0.20/contrib/test-venv.sh --- fwupd-2.0.8/contrib/test-venv.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/contrib/test-venv.sh 2026-02-26 11:36:18.000000000 +0000 @@ -3,6 +3,7 @@ VENV=$(dirname $0)/.. BUILD=${VENV}/build INSTALLED_TESTS=${VENV}/dist/share/installed-tests/fwupd +SUDO=$(which sudo) export G_TEST_BUILDDIR=${INSTALLED_TESTS} export G_TEST_SRCDIR=${INSTALLED_TESTS} export GI_TYPELIB_PATH=${BUILD}/libfwupd @@ -13,6 +14,12 @@ echo "Build time test suite" ninja -C ${BUILD} test +echo "Testing mtd-self-test" +${SUDO} modprobe mtdram +ENV="G_TEST_BUILDDIR=${G_TEST_BUILDDIR} \ + G_TEST_SRCDIR=${G_TEST_SRCDIR}" +${SUDO} ${ENV} ${VENV}/dist/libexec/installed-tests/fwupd/mtd-self-test + echo "Testing fwupdtool.sh" ${INSTALLED_TESTS}/fwupdtool.sh diff -Nru fwupd-2.0.8/data/bash-completion/fwupdmgr fwupd-2.0.20/data/bash-completion/fwupdmgr --- fwupd-2.0.8/data/bash-completion/fwupdmgr 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/bash-completion/fwupdmgr 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,8 @@ _fwupdmgr_cmd_list=( 'activate' 'block-firmware' + 'check-reboot-needed' + 'clean-remote' 'clear-results' 'disable-remote' 'device-test' @@ -27,6 +29,7 @@ 'get-updates' 'get-upgrades' 'get-plugins' + 'hwids' 'inhibit' 'uninhibit' 'install' @@ -39,6 +42,7 @@ 'report-devices' 'report-history' 'report-export' + 'search' 'security' 'security-fix' 'security-undo' @@ -69,6 +73,7 @@ '--no-safety-check' '--no-remote-check' '--no-security-fix' + '--only-emulated' '--show-all' '--sign' '--filter' @@ -116,6 +121,7 @@ 'P2pPolicy' 'ReleaseDedupe' 'ReleasePriority' + 'RequireImmutableEnumeration' 'ShowDevicePrivate' 'TestDevices' 'TrustedReports' @@ -311,7 +317,7 @@ esac case $arg in - activate|clear-results|downgrade|get-releases|get-results|unlock|verify|verify-update|get-updates|switch-branch|update|upgrade|report-export) + activate|check-reboot-needed|clear-results|downgrade|get-releases|get-results|unlock|verify|verify-update|get-updates|switch-branch|update|upgrade|report-export) #device ID if [[ "$args" = "2" ]]; then _show_device_ids diff -Nru fwupd-2.0.8/data/bash-completion/fwupdtool fwupd-2.0.20/data/bash-completion/fwupdtool --- fwupd-2.0.8/data/bash-completion/fwupdtool 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/bash-completion/fwupdtool 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,7 @@ _fwupdtool_cmd_list=( 'activate' 'build-cabinet' + 'clean-remote' 'clear-history' 'disable-remote' 'disable-test-devices' @@ -39,6 +40,7 @@ 'get-plugins' 'get-remotes' 'get-report-metadata' + 'get-results' 'get-topology' 'get-version-formats' 'hwids' @@ -50,6 +52,7 @@ 'modify-remote' 'monitor' 'reinstall' + 'search' 'security' 'security-fix' 'security-undo' @@ -120,6 +123,7 @@ 'P2pPolicy' 'ReleaseDedupe' 'ReleasePriority' + 'RequireImmutableEnumeration' 'ShowDevicePrivate' 'TestDevices' 'TrustedReports' @@ -378,7 +382,7 @@ return 0 elif [[ "$args" = "4" ]]; then case $prev in - EnumerateAllDevices|OnlyTrusted|IgnorePower|UpdateMotd|ShowDevicePrivate|ReleaseDedupe|TestDevices) + EnumerateAllDevices|OnlyTrusted|IgnorePower|UpdateMotd|ShowDevicePrivate|ReleaseDedupe|RequireImmutableEnumeration|TestDevices) COMPREPLY=( $(compgen -W "True False" -- "$cur") ) ;; AnotherWriteRequired|NeedsActivation|NeedsReboot|RegistrationSupported|RequestSupported|WriteSupported) diff -Nru fwupd-2.0.8/data/bash-completion/meson.build fwupd-2.0.20/data/bash-completion/meson.build --- fwupd-2.0.8/data/bash-completion/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/bash-completion/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,10 @@ if bashcomp.found() - completions_dir = bashcomp.get_variable(pkgconfig: 'completionsdir', - pkgconfig_define: bashcomp.version().version_compare('>= 2.10') ? ['datadir', datadir] : ['prefix', prefix], + completions_dir = bashcomp.get_variable( + pkgconfig: 'completionsdir', + pkgconfig_define: bashcomp.version().version_compare('>= 2.10') ? ['datadir', datadir] : [ + 'prefix', + prefix, + ], ) con = configuration_data() @@ -11,6 +15,7 @@ output: 'fwupdtool', configuration: con, install: true, + install_tag: 'runtime', install_dir: completions_dir, ) @@ -20,8 +25,9 @@ output: 'fwupdmgr', configuration: con, install: true, + install_tag: 'runtime', install_dir: completions_dir, ) - endif # build_daemon + endif # build_daemon endif # bashcomp.found() diff -Nru fwupd-2.0.8/data/bios-settings.d/README.md fwupd-2.0.20/data/bios-settings.d/README.md --- fwupd-2.0.8/data/bios-settings.d/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/bios-settings.d/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,6 @@ # BIOS Settings -On supported machines fwupd can enforce BIOS settings policy so that a user's desired settings are configured at bootup +On supported machines fwupd can enforce BIOS settings policy so that a user's desired settings are configured at boot-up and prevent fwupd clients from changing them. ## JSON policies diff -Nru fwupd-2.0.8/data/bios-settings.d/meson.build fwupd-2.0.20/data/bios-settings.d/meson.build --- fwupd-2.0.8/data/bios-settings.d/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/bios-settings.d/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,5 +1,7 @@ if build_standalone and host_machine.system() == 'linux' -install_data('README.md', - install_dir: join_paths(sysconfdir, 'fwupd', 'bios-settings.d') -) + install_data( + 'README.md', + install_tag: 'doc', + install_dir: join_paths(sysconfdir, 'fwupd', 'bios-settings.d'), + ) endif diff -Nru fwupd-2.0.8/data/device-tests/caldigit-element.json fwupd-2.0.20/data/device-tests/caldigit-element.json --- fwupd-2.0.8/data/device-tests/caldigit-element.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/device-tests/caldigit-element.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/cc7e51c0f8acb853adf6ca25e99cd6ea41e47f4b2da75a1922bdcc814f05c5a7-CalDigit-Element_DMC15-TBT36.cab", + "url": "cc7e51c0f8acb853adf6ca25e99cd6ea41e47f4b2da75a1922bdcc814f05c5a7-CalDigit-Element_DMC15-TBT36.cab", "components": [ { "version": "36.0", @@ -20,7 +20,7 @@ ] }, { - "url": "https://fwupd.org/downloads/b8fa86d745d9c8f4f207c7782a4a09c7381ad19e3733cc8574acde36d250c88a-CalDigit-Element_DMC16-TBT40.cab", + "url": "b8fa86d745d9c8f4f207c7782a4a09c7381ad19e3733cc8574acde36d250c88a-CalDigit-Element_DMC16-TBT40.cab", "components": [ { "version": "40.84", diff -Nru fwupd-2.0.8/data/device-tests/dell-wd19tb.json fwupd-2.0.20/data/device-tests/dell-wd19tb.json --- fwupd-2.0.8/data/device-tests/dell-wd19tb.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/device-tests/dell-wd19tb.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/c05bacfd8f73f30812559f14245b92a069c680caf300e961c78e00c985efe3e0-WD19FirmwareUpdateLinux_01.00.14.cab", + "url": "c05bacfd8f73f30812559f14245b92a069c680caf300e961c78e00c985efe3e0-WD19FirmwareUpdateLinux_01.00.14.cab", "components": [ { "name": "ec", diff -Nru fwupd-2.0.8/data/device-tests/meson.build fwupd-2.0.20/data/device-tests/meson.build --- fwupd-2.0.8/data/device-tests/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/device-tests/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,7 +1,5 @@ -install_data([ - 'caldigit-element.json', - 'dell-wd19tb.json', - ], +install_data( + ['caldigit-element.json', 'dell-wd19tb.json'], install_dir: join_paths(installed_test_datadir, 'device-tests'), ) @@ -9,7 +7,7 @@ enumeration_datadir = join_paths(installed_test_datadir, 'enumeration-data') con.set('enumeration_datadir', enumeration_datadir) -foreach test: device_tests +foreach test : device_tests configure_file( input: test, output: '@BASENAME@.json', @@ -19,8 +17,9 @@ ) endforeach -foreach data: enumeration_data - install_data(data, - install_dir: enumeration_datadir, - ) +foreach data : enumeration_data + install_data( + data, + install_dir: enumeration_datadir, + ) endforeach diff -Nru fwupd-2.0.8/data/fish-completion/fwupdmgr.fish fwupd-2.0.20/data/fish-completion/fwupdmgr.fish --- fwupd-2.0.8/data/fish-completion/fwupdmgr.fish 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/fish-completion/fwupdmgr.fish 2026-02-26 11:36:18.000000000 +0000 @@ -1,13 +1,75 @@ function __fish_fwupdmgr_devices --description 'Get device IDs used by fwupdmgr' - set -l ids (fwupdmgr get-devices | string replace -f -r '.*Device ID:\s*(.*)' '$1') - set -l names (fwupdmgr get-devices | string replace -f -r '.*─(.*):$' '$1') + set -l ids (fwupdmgr get-devices 2>/dev/null | string replace -f -r '.*Device ID:\s*(.*)' '$1') + set -l names (fwupdmgr get-devices 2>/dev/null | string replace -f -r '.*─(.*):$' '$1') for i in (seq (count $ids)) echo -e "$ids[$i]\t$names[$i]" end end function __fish_fwupdmgr_remotes --description 'Get remote IDs used by fwupdmgr' - fwupdmgr get-remotes | string replace -f -r '.*Remote ID:\s*(.*)' '$1' + fwupdmgr get-remotes 2>/dev/null | string replace -f -r '.*Remote ID:\s*(.*)' '$1' +end + +function __fish_fwupdmgr_bios_settings --description 'Get BIOS setting names via fwupdmgr' + fwupdmgr get-bios-settings --no-authenticate 2>/dev/null | string replace -f -r '^(\S+):$' '$1' +end + +function __fish_fwupdmgr_subcommands --description 'Get fwupdmgr subcommands' + printf '%s\t%s\n' \ + activate 'Activate devices' \ + block-firmware 'Blocks a specific firmware from being installed' \ + check-reboot-needed 'Check if any devices are pending a reboot to complete update' \ + clear-results 'Clears the results from the last update' \ + device-emulate 'Emulate a device using a JSON manifest' \ + device-test 'Test a device using a JSON manifest' \ + device-wait 'Wait for a device to appear' \ + disable-remote 'Disables a given remote' \ + downgrade 'Downgrades the firmware on a device' \ + download 'Download a file' \ + emulation-load 'Load device emulation data' \ + emulation-save 'Save device emulation data' \ + emulation-tag 'Adds devices to watch for future emulation' \ + emulation-untag 'Removes devices to watch for future emulation' \ + enable-remote 'Enables a given remote' \ + get-approved-firmware 'Gets the list of approved firmware' \ + get-bios-settings 'Retrieve BIOS settings' \ + get-blocked-firmware 'Gets the list of blocked firmware' \ + get-details 'Gets details about a firmware file' \ + get-devices 'Get all devices that support firmware updates' \ + get-history 'Show history of firmware updates' \ + get-plugins 'Get all enabled plugins registered with the system' \ + get-releases 'Gets the releases for a device' \ + get-remotes 'Gets the configured remotes' \ + get-results 'Gets the results from the last update' \ + get-updates 'Gets the list of updates for connected hardware' \ + hwids 'Return all the hardware IDs for the machine' \ + inhibit 'Inhibit the system to prevent upgrades' \ + install 'Install a specific firmware file on all devices that match' \ + local-install 'Install a firmware file in cabinet format on this hardware' \ + modify-config 'Modifies a daemon configuration value' \ + modify-remote 'Modifies a given remote' \ + clean-remote 'Clean a given remote' \ + quit 'Asks the daemon to quit' \ + refresh 'Refresh metadata from remote server' \ + reinstall 'Reinstall current firmware on the device' \ + report-devices 'Upload the list of updatable devices to a remote server' \ + report-export 'Export firmware history for manual upload' \ + report-history 'Share firmware history with the developers' \ + reset-config 'Resets a daemon configuration section' \ + search 'Finds firmware releases from the metadata' \ + security 'Gets the host security attributes' \ + security-fix 'Fix a specific host security attribute' \ + security-undo 'Undo the host security attribute fix' \ + set-approved-firmware 'Sets the list of approved firmware' \ + set-bios-setting 'Sets one or more BIOS settings' \ + switch-branch 'Switch the firmware branch on the device' \ + sync 'Sync firmware versions to the chosen configuration' \ + unblock-firmware 'Unblocks a specific firmware from being installed' \ + uninhibit 'Uninhibit the system to allow upgrades' \ + unlock 'Unlocks the device for firmware access' \ + update 'Updates all firmware to latest versions available' \ + verify 'Checks cryptographic hash matches firmware' \ + verify-update 'Update the stored cryptographic hash with current ROM contents' end @@ -15,72 +77,39 @@ complete -c fwupdmgr -s h -l help -d 'Show help options' complete -c fwupdmgr -s v -l verbose -d 'Show extra debugging information' complete -c fwupdmgr -l version -d 'Show client and daemon versions' +complete -c fwupdmgr -l download-retries -x -d 'Set the download retries for transient errors' complete -c fwupdmgr -l allow-reinstall -d 'Allow reinstalling existing firmware versions' complete -c fwupdmgr -l allow-older -d 'Allow downgrading firmware versions' complete -c fwupdmgr -l allow-branch-switch -d 'Allow switching firmware branch' +complete -c fwupdmgr -l only-emulated -d 'Only install onto emulated devices' complete -c fwupdmgr -l force -d 'Force the action by relaxing some runtime checks' complete -c fwupdmgr -s y -l assume-yes -d 'Answer yes to all questions' complete -c fwupdmgr -l sign -d 'Sign the uploaded data with the client certificate' complete -c fwupdmgr -l no-unreported-check -d 'Do not check for unreported history' complete -c fwupdmgr -l no-metadata-check -d 'Do not check for old metadata' +complete -c fwupdmgr -l no-remote-check -d 'Do not check if download remotes should be enabled' complete -c fwupdmgr -l no-reboot-check -d 'Do not check or prompt for reboot after update' complete -c fwupdmgr -l no-safety-check -d 'Do not perform device safety checks' +complete -c fwupdmgr -l no-device-prompt -d 'Do not prompt for devices' complete -c fwupdmgr -l no-history -d 'Do not write to the history database' -complete -c fwupdmgr -l no-security-fix -d 'Do not prompt to fix security issues' complete -c fwupdmgr -l show-all -d 'Show all results' -complete -c fwupdmgr -l disable-ssl-strict -d 'Ignore SSL strict checks when downloading' +complete -c fwupdmgr -l disable-ssl-strict -d 'Ignore SSL strict checks when downloading files' complete -c fwupdmgr -l p2p -d 'Only use peer-to-peer networking when downloading files' complete -c fwupdmgr -l filter -d 'Filter with a set of device flags' +complete -c fwupdmgr -l filter-release -d 'Filter with a set of release flags' +complete -c fwupdmgr -l json -d 'Output in JSON format' +complete -c fwupdmgr -l no-security-fix -d 'Do not prompt to fix security issues' +complete -c fwupdmgr -l no-authenticate -d 'Don\'t prompt for authentication' # complete subcommands -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a activate -d 'Activate devices' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a block-firmware -d 'Blocks a specific firmware from being installed' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a clear-results -d 'Clears the results from the last update' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a disable-remote -d 'Disables a given remote' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a downgrade -d 'Downgrades the firmware on a device' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a device-wait -d 'Wait for a device to appear' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a enable-remote -d 'Enables a given remote' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-approved-firmware -d 'Gets the list of approved firmware' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-blocked-firmware -d 'Gets the list of blocked firmware' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-bios-setting -d 'Retrieve BIOS setting' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-details -d 'Gets details about a firmware file' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-devices -d 'Get all devices that support firmware updates' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-history -d 'Show history of firmware updates' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-plugins -d 'Get all enabled plugins registered with the system' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-releases -d 'Gets the releases for a device' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-remotes -d 'Gets the configured remotes' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-results -d 'Gets the results from the last update' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a get-updates -d 'Gets the list of updates for connected hardware' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a install -d 'Install a firmware file in cabinet format on this hardware' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a modify-config -d 'Modifies a daemon configuration value' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a modify-remote -d 'Modifies a given remote' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a refresh -d 'Refresh metadata from remote server' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a reinstall -d 'Reinstall current firmware on the device' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a report-history -d 'Share firmware history with the developers' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a report-export -d 'Export firmware history for manual upload' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a set-bios-setting -d 'Set a BIOS setting' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a security -d 'Gets the host security attributes' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a set-approved-firmware -d 'Sets the list of approved firmware' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a switch-branch -d 'Switch the firmware branch on the device' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a unblock-firmware -d 'Unblocks a specific firmware from being installed' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a unlock -d 'Unlocks the device for firmware access' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a update -d 'Updates all firmware to latest versions available' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a verify -d 'Checks cryptographic hash matches firmware' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a verify-update -d 'Update the stored cryptographic hash with current ROM contents' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a inhibit -d 'Inhibit the system to prevent upgrades' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a uninhibit -d 'Uninhibit the system to allow upgrades' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a quit -d 'Asks the daemon to quit' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a emulation-load -d 'Load device emulation data' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a emulation-save -d 'Save device emulation data' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a emulation-tag -d 'Adds devices to watch for future emulation' -complete -c fwupdmgr -n '__fish_use_subcommand' -x -a emulation-untag -d 'Removes devices to watch for future emulation' +complete -c fwupdmgr -n '__fish_use_subcommand' -x -a "(__fish_fwupdmgr_subcommands)" # commands exclusively consuming device IDs -set -l deviceid_consumers activate clear-results downgrade get-releases get-results get-updates reinstall switch-branch unlock update verify verify-update +set -l deviceid_consumers activate check-reboot-needed clear-results device-wait downgrade emulation-tag emulation-untag get-releases get-results get-updates install reinstall switch-branch unlock update verify verify-update # complete device IDs complete -c fwupdmgr -n "__fish_seen_subcommand_from $deviceid_consumers" -x -a "(__fish_fwupdmgr_devices)" # complete files and device IDs -complete -c fwupdmgr -n "__fish_seen_subcommand_from install" -r -a "(__fish_fwupdmgr_devices)" +complete -c fwupdmgr -n "__fish_seen_subcommand_from local-install" -r -a "(__fish_fwupdmgr_devices)" # commands exclusively consuming remote IDs set -l remoteid_consumers disable-remote enable-remote modify-remote @@ -88,3 +117,6 @@ complete -c fwupdmgr -n "__fish_seen_subcommand_from $remoteid_consumers" -x -a "(__fish_fwupdmgr_remotes)" # complete files and remote IDs complete -c fwupdmgr -n "__fish_seen_subcommand_from refresh" -r -a "(__fish_fwupdmgr_remotes)" + +# complete BIOS settings +complete -c fwupdmgr -n "__fish_seen_subcommand_from get-bios-settings set-bios-setting" -x -a "(__fish_fwupdmgr_bios_settings)" diff -Nru fwupd-2.0.8/data/fish-completion/meson.build fwupd-2.0.20/data/fish-completion/meson.build --- fwupd-2.0.8/data/fish-completion/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/fish-completion/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,5 @@ -install_data(['fwupdmgr.fish'], +install_data( + ['fwupdmgr.fish'], + install_tag: 'runtime', install_dir: join_paths(datadir, 'fish', 'vendor_completions.d'), ) diff -Nru fwupd-2.0.8/data/fwupd-i2c.conf fwupd-2.0.20/data/fwupd-i2c.conf --- fwupd-2.0.8/data/fwupd-i2c.conf 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/data/fwupd-i2c.conf 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1 @@ +i2c_dev diff -Nru fwupd-2.0.8/data/fwupd.shutdown.in fwupd-2.0.20/data/fwupd.shutdown.in --- fwupd-2.0.8/data/fwupd.shutdown.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/fwupd.shutdown.in 2026-02-26 11:36:18.000000000 +0000 @@ -5,7 +5,7 @@ # activate firmware when we have a read-only filesystem if ! @bindir@/fwupdtool activate; then - ret=$? - [ "$ret" -eq "2" ] && exit 0 - exit $ret + ret=$? + [ "$ret" -eq "2" ] && exit 0 + exit $ret fi diff -Nru fwupd-2.0.8/data/icons/128x128/meson.build fwupd-2.0.20/data/icons/128x128/meson.build --- fwupd-2.0.8/data/icons/128x128/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/icons/128x128/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,5 +1,7 @@ if get_option('metainfo') - install_data(['org.freedesktop.fwupd.png'], - install_dir: join_paths(datadir, 'icons', 'hicolor', '128x128', 'apps') + install_data( + ['org.freedesktop.fwupd.png'], + install_tag: 'doc', + install_dir: join_paths(datadir, 'icons', 'hicolor', '128x128', 'apps'), ) endif diff -Nru fwupd-2.0.8/data/icons/64x64/meson.build fwupd-2.0.20/data/icons/64x64/meson.build --- fwupd-2.0.8/data/icons/64x64/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/icons/64x64/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,5 +1,7 @@ if get_option('metainfo') - install_data(['org.freedesktop.fwupd.png'], - install_dir: join_paths(datadir, 'icons', 'hicolor', '64x64', 'apps') + install_data( + ['org.freedesktop.fwupd.png'], + install_tag: 'doc', + install_dir: join_paths(datadir, 'icons', 'hicolor', '64x64', 'apps'), ) endif diff -Nru fwupd-2.0.8/data/meson.build fwupd-2.0.20/data/meson.build --- fwupd-2.0.8/data/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -12,11 +12,11 @@ endif if get_option('tests') -subdir('device-tests') + subdir('device-tests') endif if build_daemon -subdir('motd') + subdir('motd') endif if get_option('tests') @@ -26,47 +26,49 @@ endif if build_standalone - install_data(['fwupd.conf'], + install_data( + ['fwupd.conf'], + install_tag: 'runtime', install_dir: join_paths(sysconfdir, 'fwupd'), install_mode: 'rw-r-----', ) - plugin_quirks += files([ - 'ds20.quirk', - 'power.quirk', - 'cfi.quirk', - ]) + plugin_quirks += files('cfi.quirk', 'ds20.quirk', 'power.quirk', 'vendors.quirk') endif if get_option('metainfo') - custom_target('metainfo', + custom_target( + 'metainfo', input: 'org.freedesktop.fwupd.metainfo.xml', output: 'org.freedesktop.fwupd.metainfo.xml', - command: [ - generate_metainfo, - '@INPUT@', - '@OUTPUT@', - ], + command: [generate_metainfo, '@INPUT@', '@OUTPUT@'], install: true, + install_tag: 'doc', install_dir: join_paths(datadir, 'metainfo'), ) - install_data(['org.freedesktop.fwupd.svg'], - install_dir: join_paths(datadir, 'icons', 'hicolor', 'scalable', 'apps') + install_data( + ['org.freedesktop.fwupd.svg'], + install_tag: 'doc', + install_dir: join_paths(datadir, 'icons', 'hicolor', 'scalable', 'apps'), ) endif if build_daemon - install_data(['org.freedesktop.fwupd.conf'], - install_dir: join_paths(datadir, 'dbus-1', 'system.d') + install_data( + ['org.freedesktop.fwupd.conf'], + install_tag: 'runtime', + install_dir: join_paths(datadir, 'dbus-1', 'system.d'), ) + build_conf = configuration_data() + build_conf.set('libexecdir', libexecdir) + if libsystemd.found() - con2 = configuration_data() - con2.set('libexecdir', libexecdir) - con2.set('bindir', bindir) - con2.set('datadir', datadir) - con2.set('localstatedir', localstatedir) + build_conf.set('bindir', bindir) + build_conf.set('datadir', datadir) + build_conf.set('localstatedir', localstatedir) + build_conf.set('systemd_service', 'SystemdService=fwupd.service') rw_directories = [] - if allow_uefi_capsule + if allow_uefi rw_directories += [ '-/boot/efi', '-/boot/EFI', @@ -98,6 +100,7 @@ 'char-gpiochip', 'char-hidraw', 'char-mei', + 'char-mtd', 'char-tpm', 'char-usb', 'char-usb_device', @@ -105,14 +108,20 @@ if allow_flashrom device_allows += ['char-mem'] endif - if cc.has_header('linux/nvme_ioctl.h', required: false) + if cc.has_header( + 'linux/nvme_ioctl.h', + required: false, + ) device_allows += ['char-nvme'] endif - if allow_uefi_capsule + if allow_uefi # for BLKSSZGET device_allows += ['block-blkext'] endif - foreach device_allow: device_allows + if get_option('plugin_modem_manager').allowed() + device_allows += ['char-wwan_port'] + endif + foreach device_allow : device_allows dynamic_options += ['DeviceAllow=' + device_allow + ' rw'] endforeach @@ -125,16 +134,18 @@ '@network-io', '@process', '@sync', - '@signal', # for g_local_file_monitor_new - '@timer', # for usbi_create_timer - '@chown', # for sqlite3Step + '@signal', # for g_local_file_monitor_new + '@timer', # for usbi_create_timer + '@chown', # for sqlite3Step 'ioctl', 'uname', 'fadvise64', - 'sysinfo', # for sysconf - 'madvise', # for mtrim - 'mremap', # for g_realloc - 'splice', 'vmsplice', 'copy_file_range', # for g_file_copy + 'sysinfo', # for sysconf + 'madvise', # for mtrim + 'mremap', # for g_realloc + 'splice', + 'vmsplice', + 'copy_file_range', # for g_file_copy ] if allow_flashrom syscall_filter += ['@raw-io'] @@ -151,43 +162,47 @@ if not supported_build dynamic_options += ['Environment="G_DEBUG=fatal-criticals"'] endif - con2.set('dynamic_options', '\n'.join(dynamic_options)) - con2.set('motd_dir', motd_dir) + build_conf.set('dynamic_options', '\n'.join(dynamic_options)) + build_conf.set('motd_dir', motd_dir) # replace @dynamic_options@ configure_file( input: 'fwupd.service.in', output: 'fwupd.service', - configuration: con2, + configuration: build_conf, install: true, + install_tag: 'runtime', install_dir: systemdunitdir, ) + # for various plugins + install_data( + ['fwupd-i2c.conf'], + install_tag: 'runtime', + install_dir: systemd_modules_load_dir, + ) + # for activation configure_file( input: 'fwupd.shutdown.in', output: 'fwupd.shutdown', - configuration: con2, + configuration: build_conf, install: true, + install_tag: 'runtime', install_dir: systemd_shutdown_dir, ) + else + build_conf.set('systemd_service', '') endif - if libsystemd.found() - con2 = configuration_data() - con2.set('libexecdir', libexecdir) - - # replace @libexecdir@ - configure_file( - input: 'org.freedesktop.fwupd.service.in', - output: 'org.freedesktop.fwupd.service', - configuration: con2, - install: true, - install_dir: join_paths(datadir, - 'dbus-1', - 'system-services'), - ) - endif + configure_file( + input: 'org.freedesktop.fwupd.service.in', + output: 'org.freedesktop.fwupd.service', + configuration: build_conf, + install: true, + install_tag: 'runtime', + install_dir: join_paths(datadir, 'dbus-1', 'system-services'), + ) if launchctl.found() con2 = configuration_data() @@ -197,6 +212,7 @@ input: 'org.freedesktop.fwupd.plist', output: 'org.freedesktop.fwupd.plist', configuration: con2, + install_tag: 'runtime', install_dir: get_option('launchd_agent_dir'), ) endif diff -Nru fwupd-2.0.8/data/motd/85-fwupd.motd.in fwupd-2.0.20/data/motd/85-fwupd.motd.in --- fwupd-2.0.8/data/motd/85-fwupd.motd.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/motd/85-fwupd.motd.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,5 +1,5 @@ #!/bin/sh if [ -f @motd_fullpath@ ]; then - cat @motd_fullpath@ + cat @motd_fullpath@ fi diff -Nru fwupd-2.0.8/data/motd/meson.build fwupd-2.0.20/data/motd/meson.build --- fwupd-2.0.8/data/motd/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/motd/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,10 +1,13 @@ if libsystemd.found() - install_data(['fwupd-refresh.timer'], - install_dir: systemdunitdir) - motd_fullpath = join_paths ('/run', motd_dir, motd_file) + install_data( + ['fwupd-refresh.timer'], + install_tag: 'runtime', + install_dir: systemdunitdir, + ) + motd_fullpath = join_paths('/run', motd_dir, motd_file) else - motd_fullpath = join_paths (localstatedir, motd_dir, motd_file) + motd_fullpath = join_paths(localstatedir, motd_dir, motd_file) endif con2 = configuration_data() @@ -15,15 +18,17 @@ if libsystemd.found() if get_option('systemd_unit_user') == '' - con2.set('user', 'DynamicUser=yes') - warning('Using systemd DynamicUser for fwupd-refresh.service. See https://github.com/systemd/systemd/issues/22737 for possible implications') + con2.set('user', 'DynamicUser=yes') + warning( + 'Using systemd DynamicUser for fwupd-refresh.service. See https://github.com/systemd/systemd/issues/22737 for possible implications' + ) else - dynamic_options = [ - 'ProtectSystem=strict', - 'ProtectHome=read-only', - 'User=' + get_option('systemd_unit_user') - ] - con2.set('user','\n'.join(dynamic_options)) + dynamic_options = [ + 'ProtectSystem=strict', + 'ProtectHome=read-only', + 'User=' + get_option('systemd_unit_user'), + ] + con2.set('user', '\n'.join(dynamic_options)) endif configure_file( @@ -31,6 +36,7 @@ output: 'fwupd-refresh.service', configuration: con2, install: true, + install_tag: 'runtime', install_dir: systemdunitdir, ) endif @@ -47,24 +53,37 @@ if libsystemd.found() if get_option('man') - custom_target('fwupd-refresh.service.8', + custom_target( + 'fwupd-refresh.service.8', input: 'fwupd-refresh.service.md', output: 'fwupd-refresh.service.8', command: [ - generate_man, '@INPUT@', '-o', '@OUTPUT@', - '--replace', 'PACKAGE_VERSION', fwupd_version, + generate_man, + '@INPUT@', + '-o', + '@OUTPUT@', + '--replace', + 'PACKAGE_VERSION', + fwupd_version, ], install: true, + install_tag: 'man', install_dir: join_paths(mandir, 'man8'), ) endif if build_docs - md_targets += custom_target('fwupd-refresh.service.md', + md_targets += custom_target( + 'fwupd-refresh.service.md', input: 'fwupd-refresh.service.md', output: 'fwupd-refresh.service.md', command: [ - generate_man, '@INPUT@', '-o', '@OUTPUT@', - '--replace', 'PACKAGE_VERSION', fwupd_version, + generate_man, + '@INPUT@', + '-o', + '@OUTPUT@', + '--replace', + 'PACKAGE_VERSION', + fwupd_version, '--md', ], ) @@ -81,6 +100,7 @@ output: 'fwupd.conf', configuration: con3, install: true, + install_tag: 'runtime', install_dir: systemd_sysusers_dir, ) endif diff -Nru fwupd-2.0.8/data/org.freedesktop.fwupd.metainfo.xml fwupd-2.0.20/data/org.freedesktop.fwupd.metainfo.xml --- fwupd-2.0.8/data/org.freedesktop.fwupd.metainfo.xml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/org.freedesktop.fwupd.metainfo.xml 2026-02-26 11:36:18.000000000 +0000 @@ -22,7 +22,7 @@ https://github.com/fwupd/fwupd/issues https://fwupd.org/ - https://www.transifex.com/freedesktop/fwupd/ + https://hosted.weblate.org/projects/fwupd/fwupd/ https://github.com/fwupd/fwupd richard_at_hughsie.com The fwupd authors @@ -38,6 +38,433 @@ fwupdtool + + +

+ This release adds the following features: +

+
    +
  • Add support for changing AMD UMA carveout size
  • +
  • Warn the user if they are using the blocked-firmware functionality
  • +
+

This release fixes the following bugs:

+
    +
  • Disable the UEFI plugins on 32bit x86
  • +
  • Do not hang when parsing an invalid USB descriptor
  • +
  • Do not return an error if the fastboot property is not provided
  • +
  • Fix a CCGX DMC regression when installing on the HP G5 dock
  • +
  • Fix a harmless heap OOB read in AMD kria SOM EEPROM parser
  • +
  • Fix a potential fastboot string over-read
  • +
  • Fix a regression causing MBIM QDU updates to fail
  • +
  • Honor polkit auth for emulation tag modify device
  • +
  • Speed up calculating the cab checksum by ~21%
  • +
  • Verify the uncompressed size when decompressing CAB files
  • +
+

This release adds support for the following hardware:

+
    +
  • HP Engage One G2 Advanced Hub
  • +
  • PixArt PJP274 (Framework Laptop)
  • +
  • Several new Jabra GNP devices
  • +
+
+
+ + +

+ This release adds the following features: +

+
    +
  • Add two commands to fwupdtool to calculate and find CRCs
  • +
  • Allow systems to use the udev event source without using systemd
  • +
+

This release fixes the following bugs:

+
    +
  • Always show the correct new firmware version in 'fwupdmgr get-history'
  • +
  • Fix an integer underflow when parsing a malicious PE file
  • +
  • Fix a regression when enumerating the dell-dock status component
  • +
  • Fix the fuzzer timeout when parsing a synaptics-rmi SBL container
  • +
  • Fix updating the Intel GPU FWDATA section
  • +
  • Respect 'fwupdmgr --force' when installing firmware
  • +
+

This release adds support for the following hardware:

+
    +
  • Lenovo Sapphire Folio Keyboard
  • +
+
+
+ + +

+ This release adds the following features: +

+
    +
  • Add a MOTD message for devices needing reboot after staged updates
  • +
  • Create the reboot-required file when a firmware update requires reboot
  • +
  • Record the system state for each composite emulation
  • +
  • Update USI docking station firmware without requiring a manual replug
  • +
+

This release fixes the following bugs:

+
    +
  • Add a MTD device problem if the Intel SPI BIOS lock is set
  • +
  • Allow changing the child name when using PARENT_NAME_PREFIX
  • +
  • Allow UpdateCapsule to work on systems that do not support SecureBoot
  • +
  • Correctly parse the EFI_CAPSULE_RESULT_VARIABLE_HEADER
  • +
  • Fall back to the SMBIOS version for BIOS MTD devices
  • +
  • Fix a crash when trying to record an i2c emulation
  • +
  • Fixed Huddly upgrade problems with major version changes
  • +
  • Fix man page compatibility with apropos and whatis
  • +
  • Fix parsing USB BOS descriptors
  • +
  • Fix up the x86_64-specific capsule flags when deploying UEFI firmware
  • +
  • Improve firmware stream searching speed by a huge amount
  • +
  • Only convert the release uint32_t to device version format for UEFI devices
  • +
  • Only handle SIGINT in fwupdtool when required
  • +
  • Refactor the hypervisor and container detection to be usable from plugins
  • +
  • Set PlatformArchitecture as the CPU architecture for RISC-V machines
  • +
  • Use a sensible timeout when doing qc-s5gen2 HID requests
  • +
+

This release adds support for the following hardware:

+
    +
  • HP Portable USB-C 4K HDMI Hub
  • +
  • Lenovo Legion Go 2 (as a HID device)
  • +
  • Synaptics HapticsPad
  • +
+
+
+ + +

+ This release adds the following features: +

+
    +
  • Add support for client-side phased update deployment
  • +
  • Add support for post-quantum signatures
  • +
  • Allow clearing the cache dirirectory
  • +
  • Allow fwupdtpmevlog to dump the raw eventlog data
  • +
  • Build a NVMe GUID derived from the serial number
  • +
  • Make fwupdtool extract work with deeply nested images
  • +
  • Parse VSS and FTW variable stores from EFI volumes
  • +
  • Reintroduce the FreeBSD CI target
  • +
  • Support very old versions of UDisks
  • +
+

This release fixes the following bugs:

+
    +
  • Add 'fwupdmgr hwids' by exposing another daemon property
  • +
  • Add offline hashes for the Microsoft 20250902 dbx
  • +
  • Add the Framework-specific KEK and db hashes
  • +
  • Allow updating IFD BIOS region via parent MTD
  • +
  • Avoid showing reinstall prompts for composite devices
  • +
  • Clean up the fwupdtool lock file in all cases
  • +
  • Correctly match the correct historical composite component
  • +
  • Do not allow PK or KEK updates when system has a test key installed
  • +
  • Do not allow reinstalling when using ONLY_VERSION_UPGRADE
  • +
  • Do not require AC power to run the installed tests
  • +
  • Do not scan EFI volumes when constructing MTD BIOS devices
  • +
  • Ensure REGION is always set for MTD IFD children
  • +
  • Ensure SCSI instance IDs are valid ASCII values
  • +
  • Fix a critical warning when parsing invalid Jabra firmware
  • +
  • Fix an Ilitek parsing crash found when fuzzing
  • +
  • Fix an inotify race when refreshing metadata
  • +
  • Fix a pending-activation problem with Dell docking stations
  • +
  • Fix a potential hang when creating a chunk array with aligned sizes
  • +
  • Fix MTD emulation recording for PCI-backed devices
  • +
  • Fix the device order when the parent specifies install-parent-first
  • +
  • Fix the FLMSTR layout when reading IFD partitions
  • +
  • Fix the thunderbolt controller rushing to finalize before onlining retimers
  • +
  • Fix writing Intel GPU OptionROM data and OptionROM code
  • +
  • Flush stale events to make the Logitech Rallybar more reliable
  • +
  • Ignore all the Intel GPU MTD devices
  • +
  • Ignore errors when writing the last page of Dell dock firmware
  • +
  • Make an error message more specific
  • +
  • Modify the Dell dock needs-activation flag after updates are installed
  • +
  • Only add one devlink device for each PCI card
  • +
  • Parse the FMAP SBOM area as uSWID when required
  • +
  • Relax the USI dock DMC child device checks for new firmware
  • +
  • Revert back to the flashrom deprecated API as the new API is unusable
  • +
  • Rewrite the fwupdmgr manpage to be more useful
  • +
  • Use higher delay when update status for Logitech peripheral devices
  • +
+

This release adds support for the following hardware:

+
    +
  • ASUS CX9406 (touch controller)
  • +
  • Framework Copilot keyboard
  • +
  • Genesys GL352530 and GL352360
  • +
  • Huddly C1
  • +
  • Lexar and Maxio NVMe SSDs
  • +
  • Primax Ryder mouse 2
  • +
+
+
+ + +

+ This release adds the following features: +

+
    +
  • Add a 'search' feature to fwupdtool and fwupdmgr
  • +
+

This release fixes the following bugs:

+
    +
  • Fix missing release locations when loading from artifact
  • +
  • Fix remaining issues to make updates on FreeBSD work
  • +
+
+
+ + +

+ This release adds the following features: +

+
    +
  • Allow child devices to use the parent name as a prefix
  • +
+

This release fixes the following bugs:

+
    +
  • Add newer commands and options for Fish completion
  • +
  • Allow installing archives named as .CAB rather than .cab
  • +
  • Erase Firehose modem devices correctly
  • +
  • Fix Goodix enumeration issues
  • +
  • Fix sending firmware reports without --force
  • +
  • Fix the FreeBSD build
  • +
  • Fix version number of BnR MTD devices
  • +
  • Require additional requirements for the default PS5512 devboard
  • +
  • Require a full system shutdown for all Micron NVMe updates
  • +
  • Use a better name for Elan touchpad and Intel PCH SPI devices
  • +
+

This release adds support for the following hardware:

+
    +
  • Foxconn SDX61 Modem
  • +
  • Jabra Evolve2 child devices
  • +
  • NVIDIA ConnectX-6, ConnectX-7 and ConnectX-8 NICs
  • +
+
+
+ + +

+ This release adds the following features: +

+
    +
  • Add support for ignoring the network connectivity requirement
  • +
  • Allow building on RHEL-9 and RHEL-10
  • +
  • Allow plugins to know the firmware version during update
  • +
  • Allow UEFI capsule devices to opt-out of Capsule-on-Disk
  • +
  • Allow unsetting HwID plugin context flags
  • +
  • Allow upgrading from a zero "empty" UEFI dbx
  • +
+

This release fixes the following bugs:

+
    +
  • Add an automatic firehose counterpart to the QCDM modem device
  • +
  • Disable signature time checks when verifying firmware
  • +
  • Do not add a vendor ID of UNKNOWN when the signature has no vendor
  • +
  • Do not discover ThunderBolt retimer devices when run in single-shot mode
  • +
  • Do not use deprecated libflashrom API
  • +
  • Enhance firmware metadata generation in firmware_packager
  • +
  • Ensure Lexar NVMe drives use a proper version number
  • +
  • Fix parsing and writing UF2 extension sections
  • +
  • Fix Synaptics RMI initialization for new devices
  • +
  • Fix updating DFOTA and MBIM modem devices
  • +
  • Move some vendor name fixups to the quirk file
  • +
  • Remove CapsuleOnDisk HwID match for Dell
  • +
  • Return a sensible error when using build-cabinet wrong
  • +
  • Set the firehose loader filename in a more permissive way
  • +
  • Update the mapping for TPM vendor names
  • +
  • Verify the checksum of the serialized data in tests
  • +
  • Work around a libmbim bug when detaching
  • +
+

This release adds support for the following hardware:

+
    +
  • Egis MoC devices
  • +
  • Framework QMK devices
  • +
  • ILITEK touch controllers
  • +
  • SteelSeries Arctis Nova 3P
  • +
+
+
+ + +

+ This release adds the following features: +

+
    +
  • Add a daemon config option to ignore efivars free space
  • +
  • Add support for glob-aware version comparison requirements
  • +
  • Allow targeting specific regions in FMAP when using flashrom
  • +
  • Detect static variables and magic numbers during code review
  • +
  • Remove the unused hailuck and rts54hid plugins
  • +
+

This release fixes the following bugs:

+
    +
  • Align MTD erase up to the erasesize as necessary
  • +
  • Allow parsing IGSC OptionROM when using fwupdtool
  • +
  • Allow removing private flags from UEFI capsule devices in quirks
  • +
  • Do not copy the vendor for Intel reference ME firmware
  • +
  • Do not use an interactive console if stdout is redirected
  • +
  • Fix the UEFI self-test when the capsule splash is disabled
  • +
  • Get better device information when using PCI-backed MTD devices
  • +
  • Get the Intel GPU SKU and SVN when using BMG hardware
  • +
  • Make MBIM modem devices emulatable
  • +
  • Make sure fwupdtool.exe is available in the Windows PATH
  • +
  • Only show the 'Full Disk Encryption Detected' warning when required
  • +
  • Set all QCDM modem devices to raw mode when updating
  • +
  • Show all devices for fwupdtool get-devices --show-all --force
  • +
  • Show correct dbx version if non-Microsoft entries are present
  • +
  • Show KEK device attributes in fwupdmgr
  • +
  • Use an alternate GUID when the Intel GPU is in recovery mode
  • +
  • Use the kernel netlink hotplug socket when there is no Udev
  • +
  • Various small changes to speed up startup by 60% and lower RSS by 40%
  • +
+

This release adds support for the following hardware:

+
    +
  • HP USB-C 100W G6 Dock
  • +
  • Logitech Bulk Controller pheripherals
  • +
  • More MediaTek scaler devices
  • +
+
+
+ + +

+ This release adds the following features: +

+
    +
  • Add a config option for enforcing immutable device enumeration
  • +
  • Add device emulation support for Thunderbolt host controllers
  • +
  • Do the efivarfs free space checks for dbx, db, KEK and PK devices
  • +
  • Ensure the i2c_dev kernel driver is always loaded if a module
  • +
  • Parse the SBOM data from fwupdx64.efi if provided
  • +
  • Support loading multiple coSWID blobs from PE files
  • +
+

This release fixes the following bugs:

+
    +
  • Added HP Elitedesk G6 mini to not get dbx-updates
  • +
  • Add two more uefi dbx checksum->version entries
  • +
  • Be more useful when building modem device Instance IDs
  • +
  • Convert asus-hid and legion-hid2 to hidraw to avoid possible input blips
  • +
  • Do not create radio for Logitech RDFU-capable devices
  • +
  • Fix a modem-manager regression where a PCI device had no vendor ID
  • +
  • Fix a regression when updating DFOTA modem devices
  • +
  • Fix self tests when building with -Defi_os_dir
  • +
  • Fix self tests when the builder does not support DistroVersion
  • +
  • Fix updating Thunderbolt host controllers with some version formats
  • +
  • Handle HECI unsupported status (0x0b) for Dell hardware
  • +
  • Make tar a dependency of the uefi-capsule tests
  • +
  • Mark the KEK and db updates as affecting FDE like BitLocker
  • +
  • Properly detect the Redfish reboot request for Dell servers
  • +
  • Send the proper artifact firmware filename to the Redfish BMC
  • +
  • Set the correct RMM device version for some Dell dock devices
  • +
  • Use inhibits so that the rts54hub device is marked as non-updatable
  • +
  • Use the virtual size to avoid padding when cutting PE sections
  • +
  • Wait for the Logitech Scribe device to replug after updating
  • +
+

This release adds support for the following hardware:

+
    +
  • HP Portable USB-C Hub
  • +
  • More Foxconn 5G modem products
  • +
  • More Intel Arc Battlemage products
  • +
+
+
+ + +

+ This release adds the following features: +

+
    +
  • Add a new check-reboot-needed command for scripts to use
  • +
  • Read the SELinux state in the report failure metadata
  • +
+

This release fixes the following bugs:

+
    +
  • Add some notes in the README about security-relevant build flags
  • +
  • Add support for the Dell dock ownership command
  • +
  • Add the subsystem VIDPID when provided by ModemManager
  • +
  • Allow changing the rts54hub block size from a quirk entry
  • +
  • Allow Legion HID2 downgrades without --force, and clear config on upgrades
  • +
  • Allow specifying multiple DEVICE-IDs for the get-updates command
  • +
  • Cache the stream when parsing the processed cabinet to fix the report upload
  • +
  • Do not allow DBX updates on the AiStone X5KK4NAG
  • +
  • Do not use translated low-level error messages in the failure report
  • +
  • Fall back to the activation date if the X.509 cert has no suitable subject
  • +
  • Fix newer Synaptics VMM9 devices by adding a delay after disabling RC
  • +
  • Ignore some sanity checks when parsing PK, KEK and db certificates
  • +
  • Increase timeout requested by logitech RDFU devices
  • +
  • Never include systemd.machine_id in the failure report
  • +
  • Parse the correct VendorID from the ModemManager device ID
  • +
  • Process all pending event sources when waiting for replug
  • +
  • Use the UEFI PK report attributes for the other UEFI plugins
  • +
+

This release adds support for the following hardware:

+
    +
  • Lenovo Thunderbolt 5 Smart Dock
  • +
+
+
+ + +

+ This release adds the following features: +

+
    +
  • Include the AGESA version as the summary of the AMD secure processor device
  • +
  • Include the UEFI PK certificate key ID in the uploaded problem report
  • +
  • Provide a way for the client to restrict the GUID list to an emulated device
  • +
+

This release fixes the following bugs:

+
    +
  • Do not allow dbx updates on the HP Elitebook 845 Gen10
  • +
  • Do not warn about BIOS bugs we can easily work around
  • +
  • Fix a regression in fwupdmgr emulation-save when recording some devices
  • +
  • Fix a regression preventing installation of KEKs
  • +
  • Fix a small memory leak when getting security attributes
  • +
  • Never write a UX capsule when using Capsule-On-Disk
  • +
  • Use the 'OnBattery' property from upower to tell if plugged in
  • +
+

This release adds support for the following hardware:

+
    +
  • Lenovo Legion Touchpad
  • +
  • Logitech MX Mechanical
  • +
  • Poly Studio V72 and V12
  • +
+
+
+ + +

+ This release adds the following features: +

+
    +
  • Add some documentation about updating the KEK and db
  • +
  • Allow installing multiple db certificate updates at the same time
  • +
  • Show what certificate signed the EFI authenticated variable
  • +
  • Use readline to look up inputs from user, and make it optional
  • +
+

This release fixes the following bugs:

+
    +
  • Add several devices with broken firmware to the UEFI dbx blocklist
  • +
  • Constructing the authenticated URI properly when using FirmwareBaseURI
  • +
  • Do not enumerate non-updatable OptionROM devices
  • +
  • Do not export Redfish backup partitions as devices
  • +
  • Fix a crash when installing some Wacom firmware types
  • +
  • Fix a crash when parsing uevents that are not KEY=VALUE
  • +
  • Fix parsing the DFU descriptor when not using libusb
  • +
  • Fix PK and KEK enumeration failure on some systems
  • +
  • Fix SMBIOS parsing for ROM size >= 16MiB
  • +
  • Include a resolution for more of the HSI failures
  • +
  • Include more output when using fwupdtool get-devices --json
  • +
  • Never allow updating updatable-hidden devices with fwupdtool
  • +
  • Properly handle redfish location redirect when installing firmware
  • +
  • Recognize a very old dbx hash to allow upgrades
  • +
  • Require a reboot after updating Intel CVS devices
  • +
  • Rework the MEI code so that a device can use more than one interface
  • +
  • Rewrite the ModemManger plugin to be simpler and more supportable
  • +
  • Simplify parsing USB descriptors
  • +
+

This release adds support for the following hardware:

+
    +
  • Intel Arc Battlemage GPUs
  • +
+
+

@@ -437,7 +864,7 @@

This release fixes the following bugs:

  • Add support for capsule on disk for Dell systems
  • -
  • Do not re-use the connection cache to fix Redfish BMC restart
  • +
  • Do not reuse the connection cache to fix Redfish BMC restart
  • Exclude known recovery partitions when choosing an ESP volume
  • Fix the VLI usb3 private flag registration
@@ -2305,7 +2732,7 @@
  • Allow compiling the daemon without polkit support
  • Always look at all TPM eventlog supported algorithms
  • -
  • Change all instances of master/slave to initiator/target
  • +
  • Change all instances of m'ster/sl've to initiator/target
  • Correctly order devices when using logical parents
  • Do not dedupe NVMe or VLI PD devices
  • Do not expose the VLI shared-SPI devices on the USB2 recovery device
  • @@ -2515,7 +2942,7 @@
  • Invert default behavior to be safer for reboot and shutdown prompts
  • Reload the Synaptics prometheus device version after update
  • Use the correct unlocker when using GRWLock
  • -
  • Whitelist VIA USB hub PD and I²C devices
  • +
  • Allowlist VIA USB hub PD and I²C devices
@@ -2834,7 +3261,7 @@
  • Fix a crash if AMT returns an empty response
  • Fix a regression when doing GetReleases on unsupported hardware
  • Fix the 8bitdo version number if the daemon locale is not C.UTF-8
  • -
  • Remove the Wacom DTH generation hardware from the whitelist
  • +
  • Remove the Wacom DTH generation hardware from the allowlist
  • Sanitize the version if the version format has been specified
  • @@ -3207,7 +3634,7 @@
  • Make the error message clearer when sans fonts are missing
  • Support devices with truncated DFU interface data
  • - Use the correct remote-specified username and passord when using + Use the correct remote-specified username and password when using fwupdmgr
  • Use the correct wDetachTimeOut when writing DFU firmware
  • diff -Nru fwupd-2.0.8/data/org.freedesktop.fwupd.service.in fwupd-2.0.20/data/org.freedesktop.fwupd.service.in --- fwupd-2.0.8/data/org.freedesktop.fwupd.service.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/org.freedesktop.fwupd.service.in 2026-02-26 11:36:18.000000000 +0000 @@ -3,5 +3,5 @@ Documentation=https://fwupd.org/ Exec=@libexecdir@/fwupd/fwupd User=root -SystemdService=fwupd.service AssumedAppArmorLabel=unconfined +@systemd_service@ diff -Nru fwupd-2.0.8/data/pki/LVFS-CA-2025PQ.pem fwupd-2.0.20/data/pki/LVFS-CA-2025PQ.pem --- fwupd-2.0.8/data/pki/LVFS-CA-2025PQ.pem 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/data/pki/LVFS-CA-2025PQ.pem 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,162 @@ +-----BEGIN CERTIFICATE----- +MIId2DCCC6+gAwIBAgIBATALBglghkgBZQMEAxMwQTEmMCQGA1UEChMdTGludXgg +VmVuZG9yIEZpcm13YXJlIFByb2plY3QxFzAVBgNVBAMTDkxWRlMgQ0EgMjAyNVBR +MCAXDTI1MDExNjAwMDAwMFoYDzIwNjAwMTE2MDAwMDAwWjBBMSYwJAYDVQQKEx1M +aW51eCBWZW5kb3IgRmlybXdhcmUgUHJvamVjdDEXMBUGA1UEAxMOTFZGUyBDQSAy +MDI1UFEwggoyMAsGCWCGSAFlAwQDEwOCCiEAtcZ8ykbdSjczKjQb3JIfhAiTWlUr +ZQAifIJrPoVUTl0yKTcTdBaNiwrIj1e7BTOUDsaMAw5HzExaNmdIQ0iQSA5JzbIf +yCHt6tSz6gKOPa/AW1WAL8P/lx/BtrNzoVPhFf35eUYMxYAKOCh0cyOoi5JW/7oV +X1886aa86c41bc5z9FvqmvjqLv8h4g9brSlvPEz53ScPrKdZWBA3yXoge5cduzLC +8NgmwvZy4+NnImEBU50UbsL90f91neE/gAi5UOT1MQ3OsAFzy+YtWDtMbr+rUFBX +XvmFNMnLTn4kDSJcjH6c3wo4NNpfhS+TUoShZLE/m7FGDAfCqfea1xRfI9pyb9zO +j291CU/FL5WjKylT9PRgDjMK9jsWX8fa+Kqc8vRUNyDunFQp9fhzrYlmWhPzrtzl +cHuhbuMXHFJbB+lh7K6jwpsg5bMbJ8u3boBse2351PKKtdEMcKVEklHFbMMdvTwH +3FwEtOkm6zdLy35lbGdlelO+EH7+DUAu6usJbaPiHr7dcEClboQtmZe2XkR01faP +IfSy6CPsWgEe6sdyO7vb0/TL7he9tN7U0qNsfvMa4cifvhv8JUJJAMzQ8a26cwUy +SyjJnqXb/wNtv8u9p+SdZ7LXwSZy7bl98SHUF/ltyoYyrI5aDPra9Gt7p+0zPs3q +kzfzgCnTuNUI0OcpBWpNEnsbyFriQqDLQHl9oe/s0fxotril9veipVSeimIekPeH +o4AxVj512Du1CpmEGedRNrXjhNdrW0jcx86/Isc0W+7V9pK5vcv7ZrxZHEwETQUw +lGbDhTZkpRhVh9a80b63PPzKbue51GzhGI4hWN46jt4PkP6GoUxeUdDxNWdwXKss +CroATRe2WAKVh7UWgIBUhN9xS2uwOxos13WcmMCH9VnzyPv4MLwR4P6ICoJ5X1BP +GtY+JSwlqwRf3NlJy4mEXMHhrQ/g2Wrec0a4gruTj2g885Y7z4hJed+SrqMOLwRp +/x1qeCQE+OJARzOVFu/9ATZqYP9JwxWIolpDhzRrgnbl6J6Z9jrbm5C7QK/W/6Ae +ENeuswOqmAHA2mdMUPXefdW1F+EIbMdwud/6jEeNp32OCq+OxPq0Ez2BJCtbRJry +J39zzRDZY+7WUepTOvIctBoUj2yaFFO4/48nJlVcPN22q9GAvMn2RO5lhjLbuF1c +3gzegm9Fqa6xtxSe6696WNiS8rHmgEIHlvhp3cSuPrdRxijZeQbbgHh0SxN5tH8Y +38ktuik5tjWiQqQQGAVZAHQzp8q5l1IJHETzDgzj/5VO2CTNZ6YSEemafrFIHYNo +//oNi934azcDMVewi1LnY+KWG78n76tMgQiJ5Oa8NCVoWdEHlN0zFK+9oBhIHOmX +7abtKuRRmc97cym/EIEyVJeSTlxXRFA2bpo0BT+mzaFniWZPGdP/Yqt8Bsg5ysRq +Ji6RJgbfetWoeRRnPoyZTVV8G/opAPrxCEJHOlPFGAO9W/WRmUbQr+Nji9S3Le3h +bEs2ATy6afHBa4pYwP5dWqVn0g0PHneArv5l/U+pGHNDJMOqT11YK3+0xql9Pts8 +iF0Pnj00O+UJVRCVfUmXt8nKmnfWFGquZEjg0YcnDc6HdUivscxU7e97KrnZTlbp +KGxpwPx5+gnpRgr9rSQosf0yQoIzLBzepEg4TJXluq74JQX1YsktQO1tJEwv5AP9 +6Xb+dIaZ3ZX4oiH2X6brav5tbGfFXjgom5YvLse9LeZprQLVLED2m+m2xjN4woVx +XKOa35xq/4McPWIAo42O1+AR3w/wx281Yn5oiKSQB21uTYar7J3MsYRTEvLiHZl5 +HI6XMmWMQzOS6ts4uguo7bOpdTQ3thN10gNMHUBkozvqwTtAQh/mreRm2YehOziS +RO93199RFjfYCv3Qj1W11bFl6KuU0y1T1PEqNbPph3Mkpawm15uZqnMYGS+BTEAZ +2/UCLDf6mW1KA4IXWM6LzAxUAwI24Nis7myAmSBgJFpdFLosGimwJBrUe8Wcr6QP +vHZlJVCHX9KIrVeE9e7gEa6X+QSQJ1Etck/qdCMZsDbmGVOergWyqc1Df40t20IJ +kAUQJuqTVSdJjiPPbrBa83sdpJZr/t/4uRnUjZ2h7dxQFmdC/0yMTMupF5RLUdl6 +/m6eqWXqxwTAwviqepCWrgjuNrvhJImT4+0V8gLpQxxy9eNxeqHFgzePj/R0N4NN +/MnEF1GlZVm5Zu6kIgS7ekV8lqworiBS3Co0kC8L4/h4EZpYA88x7WyYgn2p9ZdE +x6y5qTro/InhFGTjhxZOTdqu4qrY87XEQztBdDosfSWno+o+sRZrbVw3d5yRkOKy +M+ol1dIhU7DGvwDWAEoKqYhT3glmUiht+/gwaruR/atjKi6VsUSbu2G3RaKpikcz +r9Bf0yvez5H7/vk/8jc20sb0XKB0gFVutB3wgLUKaqD+Kj+e0Tlm8a0lL1X+nwTZ +4Miqy/ZFLIyyNyxY9MtMT5qvYE+gC2ZjWJpgefFG6d3mz2smzjqPmEqdy7hVAeVu +NNk/3uquYmRR0WWBMAxHSQgRrlrftQP6MctmxH468aGeDBy3ckEFARIsEYx0I+tr +pum6w8BXhztBdzfilNqCQQ0oTGWFxtjoegxTTa3u4JGF7BIQn18FcuHbWJdSX1NT +la7UZeGReuVObPGOyu9oGzZoFcHpx4n2fSTKROo9B67DH5iH4tR24UCd1Suoyx+P +mj3CT9pjnGPhFr7deuVgGChzGIHRu1jJLUxk9kq/a/GHo5V1pcry99Zc/22HrZGG +BUSp2IOY/muA8tC2Mh9J2UEmPsDjsqrJb4DfeV3xjiSX+BwoNrfNacfws0yTBgYL +d+wznJI5HYwXe+2Em6q5nDlaUWfLvbnLCeQGHe7nBy1xS9nzXGZhVEgY2p4yDe8D +EwDcqK7b0SNiFOuOSpp/rzMZZSgfghgK0L2Azc+PGFXTa/Lqj2hPMExBGNsWokhE +HNCTtb0qQL1DAJSn9GMCis+wiH4L24sRXrnfjxhphITs79H31xX5CYF9iw7mBWdT +QIKf8dynsobP4hA3XU3SLO53ux0tE4JwqyBzZ6GJtmCkk3FZ7eRzap7zKQraBh7I +XqRGj4CHqLesKdTRIZkdpcqCCo+0F/Zv79dl2dHGbkY+FNqAZQ8XhLMAsvHF2st/ +CtwCKT1VcOzvp+KWvEIEyH9H0zzrFYZtGdbw7iCJ99EJ7AGv4+H5Fd4GykxSlGeq +nC2f0v4Lt4oxWJMkY8HJUKlM10QU7Jy3zUPmU/8sOlL5TI7ZE0r1b8E6ajyeAG6a +W2pgmTlwxJMZMLNvecCgnh2ZpeShFhwk/buqQKLULitQdm1KOqnIppR3YmAdXCHX +iYsqP/8PmeL5nDyX7S/z6aNF8HV8GWiNlSdP3B2gDeGhH0eYbvQsgG/luqZc4At4 +b5krrtZMku4a4bT4Ybc2TVuWgmIzR9cq3Oi1o4G5MIG2MBIGA1UdEwEB/wQIMAYB +Af8CAQEwMAYDVR0RBCkwJ4YVaHR0cDovL3d3dy5md3VwZC5vcmcvgQ5zaWduQGZ3 +dXBkLm9yZzATBgNVHSUEDDAKBggrBgEFBQcDAzAOBgNVHQ8BAf8EBAMCAQYwHQYD +VR0OBBYEFA4xP+DzaY5NPYwKMMZwaZoBupyPMCoGA1UdHwQjMCEwH6AdoBuGGWh0 +dHA6Ly93d3cuZnd1cGQub3JnL3BraS8wCwYJYIZIAWUDBAMTA4ISFACYmig3Ddv0 +U5sliVYkkkyNVGcJ6lxlDQ7NDOrjBkFEOlyyEiV1Q3ISdjDVMc4Wj4wyUjm65vcw +mESMLWkx6FsFMrxdsHDCkx+8ptAlLgih9EEroqI1jRDNOe6hDr9grBQ2ytbbTKil +W4ctj/6/dpL68X2/EHMc0Hy+u238RL7cmYrj/MAL7P99t2U0Dt/U2EutfouUsecc +QnqPphuQCvDrafMZYyo7Pu6Sz8V3q/omuqcxw4f8LRqNokgwEPRxXyQnqIX+qqOt +RbV7HjsJPdpwDegD/HFMwSNh3tP06gaOuCfdgsYKW9gSEz8oJiV9eRS5zUJ5xufK +X4LUfqAaZ71FkegdEFOl+ZofCZh3ryMmQpMjnhZ8UnJ9X28rHStQ3ciRK34j5i+H +3oXLGz63Ik3u8xHrXXe/WG/3whfXsWhtVmO2DuYGIcZq5MNsTGiDDEOgjR/bQd/7 +VvdyLYYo0N2Uk+5nFO9iDc5i1lTq4iIgg2xFGmIgL7++NNQK3wGOqXfv3XQGT9O1 +l+tW/+OrWSQoUbrQJFnRiAR/vlHYlaqhP2IesAAIorsKIa+CQ+EKn9wcEFzZK6hV +Hw8baLhQQ5Gu7sY9YN1fqZbqxcOQoE/oAkfdEtdMX574hzZ1Z5Q0g6F2gvrzgRp3 +4LrgsuEuMUPykfDr+WQVN5SIns0ZJMVmg+8E43s2PSBx3aJkYzDif+6LDHjJKxOf +j9CRwSjPTRHsKXf2sw9iBbCLXdx6USG+75tIMjumCEFPQGoC2yknrx4rDSaAfEkT +XLs9AGRXMgsfHrZWEg+D2MtFfPSViPPUnQuaFPM79VbcS3HU2zIDOA3sbcFKojd5 +TeJ6LbTujsetFtzw3wk9hE+t7qfCGklTQ2CDLCFBPkJOSvzCpxxufMez9AOhOiUj +plY80ZEuWW+lSgw+katwhFtAv9qAC004bTrnMiDq7wIrJzG/L3I2/YthE9+j1feQ +xaFi2ytJ0E53W/8m/1pTQ4zhz/I42pg+JtgtRwTurIgiFBAWUNQA8byeNe849P5K +1cGsbqLYM8nBhG74DPydlDq42sSVyMJDLxNsXU32QDy7+gNtYpALZzvVvJXCEzV2 +LNBFQiebV7Y/9q3EQ0yAqdMqwdUc4D4jBZWhVbNt6Bxuugh+rjtjRb1ba6jYhIvN +A3ubXyW9m95vVuwhA9nZvH1MDfXav7UiVtaHPectdzC0tpYlDvYhHG93E2HhX7RN ++j8fs09RbyRYUCmuUQhfTt1wlc1mECq3kVoP3hs+TatJaKFkGIWyT5BZrh91ELe2 +ccpqJMLuiZOqqxiHtD092GUIGdBpH2UCZBGt7WqcY0JcSdp03qO/PzZv2JuYNsRH +TXLhkX6PNT2QGtBnUZAMt8FZ2NHICyJ1oexT9hlNR8X+jCgt5MBCVjHNrbJO+Ls5 +sOjxc4bslryYxgBB0IG9DrsqWFAuj+fWrYjAlVcaqnuItRDovlCCLGWt9cnvyqtx +Kq2kccuSru2/TU6B2cwINtryemsG1KiiVhxKVKu+dk3XZdmE1WOy4pp0yy4tmTh+ +EqR/ZBQ+mp/MYidUnMvnluCkrnr05eIHwyitEbhuX/vv9KVESvAwN5jM3TWdXkT4 +3M0quX7CViwJQQyR/ZNgtwjqI+zkOE6yH0C+L2jvr5ZhgAxEcbhXU2uB1CokAVB1 +GErWjy17L71/ZxPl70k/RX3aPYqdcklBBpfZ+ZaDThsOVRXkV3hQ86mspLUrYZHI +eWIqHVdPUZds00S5dTKg2rsZ74UVbFvT0X22l4umEZ4tagIWKW4T25AMlbrg69rq +wfCGmOEB4XQXZG7ffEt/ZnXHuvknkUfpXQMZ+6Y91c0kUqHDEF9G1CrskOiZSlMp +uAW7jzOXKG2Zn9Q5qdRlxMGbvzNHk587SrVkl70Ir7mRRuGtcabN9GO8x4RTOfBP +YWYWaMVqjjPTBpC3puH96Wm+ONgpkoM7B+CSpz7LVEcIKqLfp2H2qWh7MSxCps4z +p45Yud1sncZtpS8W8MDdY70o4V+SdPm8+bYkxDez2EwPIw1yDI/mrH/tXxzQlpFn +3Oj7A1n6hKt93zMT/QMgp15CSpbsdGiBMoaxkSSl+gGbe9jm5JcZFHjIhZnkxJel +KjNTlwOlq/N9SSVVcoDI9XjoilwG5/A8xXgUUlyVwc2fT1bSgLgQHoprSxTAW+/l +UZn3DOqTYOkBkPzCCvSHZx+3Nq20BKW7AlQBRWLY6kpPUgAhh1/L/P8G/TJuVq9k +wDwTthXnL1Bdx5Y/R5awXLJLIhKeuVsX4xcooX0HAeTCSn49oiq0jr+2B2+1XCYX +NkJiDuAM54RsI35/+IKiiUVgV1YHpJKMxIpJRhboT8Fo+8Bwov1MwSqUTS0opF2L +O5w/qV0LKXa6Smh6tPhL5sIuieWyqLgqbAOOtW5jm3n/+3sD3lcTFiYc1DcgE/r7 +ZxlqcvPPeM+HetRLoP7eKnv8yjn86eg0ql1NfyMAxnKb6noofOQJw4AiGrAxE0ML +pOWbF8shMGVveO919MbTq/FSVE4CzLEOkNtleVI6R2Sg0uuP8rAdGAYTfuuaVyBd +NyeMXopoveXPEaQdrMTRdYOCYigZMarMTxOohwkY+oTgRbXvFfVtzQz8H9W+Svy+ +WjiGong5SPtgW1hF2J6ZkmN///oVumxAlhbbsNl6Y9SFRSh6vWWu3dfgC34rXULF +emlUE7FvP6kHNnIJLwNNiiYPVSBEgj8XsUb1fitnIVZs1Iaji3639CMfZVzYnBmN +EFZvXD2cp4h9D0eGuIJwqSd6JIJty1T51bwgckfah7wW9y9SxP8mzbyAIHbaMyME +LfOVqP0w4yeqLdwyLYauhqzGg+jEgaCnPfG04Wqw96vLBjoLIz39GDTu5QKf9Bb9 +t6TL1ooAmPqbEzbo8xz/T0w7f/YbbqxAFtFlOQej5xIxm0VbftAg/NgmndMD69ji +zueKwq90T0cZGfNUFoEdkRUGD2d61jb23N6k2whieRpyw6NbtKHoeFDG0EuX192R +GiFlPa6HjorDBeOGCBMXX9Zg3wnp2g1//NdTj6FpAU5nzaRiovYKxc/5u2jcco6m +qOMwrXTBiLeeg61lpQQjjhilTXj4PoAK4J0RISmUrcGKkbYqsBCbRYtR/ilmXStl +ulq8odrEHTYwW3ktS6KF73p/U3EV2DUqeXvA1V7k1RO5Sf/5peLFYhMLIZJaj1Np +Tl3+z2213aYmrDFFmbU9oh1T99fR3oSSlnb2eFOSpIXsnuGpZ2yeLEPvwcLDNMYu +HtVwiI5Kz/0dAM5jvF5NRear1nT5hIFTzLRxhVSuHxrKbVlPdcKNEIY6e/xy1HTe +1DW7yjDzGJwkFi/YHBDVWDM0lDf+Tw7NW/XzWPHpv5ozhRzp1dCrbRm+4bFchv8b +U6ECnMvKppOtilba7F1vgl7Q/Cdq6DqKGhcTobIeqo0XxWXxik7Fq8amkrq9q9ZP +BXDL0t2QkPQlpX3BQyt8ww6Z0SwwSzB/wWl4KejMuuSpBbvOOVorD5QWZTAkGebj +lCtq3t+TgXANbHElfkhUJZynwWO/cC0XtUx7LblBKMhaOG7JqO5VqHzWSpY9cMjf +kyPnt+F0SAS6xENM3i/ScuZXQsgsEW+unmZh3sOAlPIh5pX4vMO9hZF+UksE/oKc +BCWkTea2Y7IXJ0izCesEDS5OJK8bupGYY7oVVm3KUomHg0+acGyhR/fJa1EaZdIL +ZrfBzK0fPDcu5MKpRGatsr7JJ6ib7zLDGBX0BBAOgEtUuIOrRQWQK/nqwJTohOiN +RmB+uNGOfls3kwF7Zy5Gw8ua/LvyBf3+veRXl6ekDet0NeybsOK0eXCKSwgojiXP +H3vOQNfZs069MERCrm9Nh+IIskx8lCDLc/uZe4lJUBhpUz05303rTFXqFWPGcXvX +57MLVgj2tIUSyeZsthx7ffDLABIlGBhY9Yt66k8biafg2gPyl6IrYI/TOznG+r7q +C1OzzlmvQ2LjOWIDk03XKIdcPYpilThla8BLoPFjcpbkyE56HRujGXi46dgipcPE +LTiHD0vsyVnkSJ85dAAQgXhQJoqyf9mwO5lbvUupLIy3/oi/uX2Iw1D+uZk5Ay4Q +TpZO07KZCF/Gdousi/ZBZjBDSmghIsn599tFmlRmI0Hs9751cvixURUl67OrroiK +R99TLSrtR/OgkDPLZltj+kbklj4GBsREtYGeeJ/OXZsaj3Bzd/QENUP9fLhEbtKr +NWbcH5VpAG60YpmzKMYSIi1jQ/W9Gzxbh2HNRZKz82ZPUthMtQNjHHoEDabqv1cs +jPExs/4kRMfQIDPMe+JTFnZVP6ys4H518gWaZgABm1/Cod9Kd6YUEhgx4SabPA5X +eMWO9q5YX20ui9O25zAzvQF+curh8ZSQptndv9hQch2WwAzMX7Sg1ffCUSXd9w0M +pPmhBOi5AH51c0Ax5KPYyQS7K2kN8wjiQhv5pIJa5lP4NVB169DzqQrByBpoypjX +bhboWiaTGkTeTTcZ/4bn5/PPLuaWMNc/i4ON6hANT6/yuptV7oMX1QHwsavuFEW+ +AeyMxFTitXGvowzZx9pTLRqeiMpOa97TT9KQf4wTbSnf/Vir6AQQKtlQZNV09PSR +j13eIJ4n12k3Ci45p+NxPEo/HscT8oTDM2DOv4DEAl5j+M1DT/jcAz87hLM9H/E+ +GpuLmnVrC730Wrk4VmhgkzdTV3ULFLTwwpHoHBiChjzFziHCvZFVZ/oR8cDqSVua +FCi3nm49JbdiC9frqVTD1EX34iHsNME8+b9iCJX8teKXvobtU2R/2TFGPaOfJpPz +6U3ksm2giP0wyEi647uwEoYfe034TuGS4jBByFDzX7mXrYkTy/l5YdjHTfQ7L4Hu +rn3IdC4esPk2iCLD2jHTAfYpnQvvg/CQ5fSfiGTHDIglsyno4f/n7wErZnvVchn2 +7eIanjM/qNT7K9i3Wtzko7V0ILjsb/bk/RkQ3KhVHnmBgS8Qa25hUypv/jZiPn1a +w5cO7GSQugNh9mV+XdK3iQZnH7wNCwixWPZsT0ip6Tjipwnqmmj1cj0bk4irrm1J +FpHJ014g33vW6p3MNNPymaBFAsx+Hz9AgbTDPOBH1EvkZPTNaDbSomPTk8LXwS5w +4sTbMgfeQ3St5bN3LYV6/P3pvZH5AlSDmPBWdOvuhDo1MNS6DeXfTUJgOyMxBY0S +jsyZeY/0bfHdgVKz4jf3MeTlqmrOy7E6iplITh8HlomCfWB5xJSVRrWl3xOsHdmG +aH42ZqhNN2o7JkJwnk7pV+T9oT4C1tuKFYq0GmsDoPn/OVMK4NyVQLse6bdwvBPN +WW3m83UnvPRbo7nTy1uFw3ZyHSdsVJ7am01r59dDVm0AM1MFOV0nrn4YmLIKVjEP +HI+/23/kZX4v1xNnB/z6CX42XL1jkZjL7lcMeZqVCLt6wGAR/1EvKYsxE+3idO2H +HQUn+HMygkN/p669R8+dTHzkl62KPa1vZA8fyhB2RozKnBpetGUVNzvZsDsr1Q16 +L39UqXS+4CwJi1JPAx4hKhuvGMOoYG+/m1Tk0H3LbCgOF/cQMrVQZgeo34vOgkdG +4MJAIr1P9qW7scbOzNGoPeo68qmWE9W/xWX3eVao7yu7SwTjR9npERzG3Q3kRopY +h8ezqTYzXk3EbZLyWHsYxm9qFECu74T/VGeKWweHJHNt93aaQz2ZU8g8sXJcMiFt +4LjV869SvyL6ed25kr2BmioTtrA/cEcSP2Vi3Kbur/pvP9f1ZH2bUZBFaKB2gV/N +LgxKTtMWTq/4NIVkLyE3/mazf2WeYYeRHI2mdB6J9+kWwloFmEeSMNca6GeekApj +ZM2DlndqBAYKq+YAatuVT2SazCYRtiTSlgP1Mb1q/8rqMwz40fk2NRNpvvAI9kjL ++SwYTpy6ueVdtKZUqAA/05w7WYi5G2nmW47ESUdycY3eE/UEe5nCBZFnTSwjCysa +d5vyOqRTsgcZx4xi3X6idFchvdtm8STXLgtkuCtRC+j+hpoSq6o4l1oZREqW+eUn +yZyTFINumb33HJOvxa3bV8BQDFSbmCtu+hknMEm2uufqFjmWxdODvQV4wzU6dX2y +zdX+BCNSVZuwABYYbsErMzp74wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA +AAAAAAgNDxIaICUq +-----END CERTIFICATE----- diff -Nru fwupd-2.0.8/data/pki/meson.build fwupd-2.0.20/data/pki/meson.build --- fwupd-2.0.8/data/pki/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/pki/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,31 +1,35 @@ # only install files that are going to be used -supported_gpg = libjcat.get_variable(pkgconfig: 'supported_gpg', default_value: '1') == '1' -supported_pkcs7 = libjcat.get_variable(pkgconfig: 'supported_pkcs7', default_value: '1') == '1' or host_machine.system() == 'windows' +supported_gpg = libjcat.get_variable( + pkgconfig: 'supported_gpg', + default_value: '1', +) == '1' +supported_pkcs7 = libjcat.get_variable( + pkgconfig: 'supported_pkcs7', + default_value: '1', +) == '1' or host_machine.system() == 'windows' if supported_gpg -install_data([ - 'GPG-KEY-Linux-Foundation-Firmware', - 'GPG-KEY-Linux-Vendor-Firmware-Service', - ], - install_dir: join_paths(sysconfdir, 'pki', 'fwupd') -) -install_data([ - 'GPG-KEY-Linux-Foundation-Metadata', - 'GPG-KEY-Linux-Vendor-Firmware-Service', - ], - install_dir: join_paths(sysconfdir, 'pki', 'fwupd-metadata') -) + install_data( + ['GPG-KEY-Linux-Foundation-Firmware', 'GPG-KEY-Linux-Vendor-Firmware-Service'], + install_tag: 'runtime', + install_dir: join_paths(sysconfdir, 'pki', 'fwupd'), + ) + install_data( + ['GPG-KEY-Linux-Foundation-Metadata', 'GPG-KEY-Linux-Vendor-Firmware-Service'], + install_tag: 'runtime', + install_dir: join_paths(sysconfdir, 'pki', 'fwupd-metadata'), + ) endif if supported_pkcs7 -install_data([ - 'LVFS-CA.pem', - ], - install_dir: join_paths(sysconfdir, 'pki', 'fwupd') -) -install_data([ - 'LVFS-CA.pem', - ], - install_dir: join_paths(sysconfdir, 'pki', 'fwupd-metadata') -) + install_data( + ['LVFS-CA.pem', 'LVFS-CA-2025PQ.pem'], + install_tag: 'runtime', + install_dir: join_paths(sysconfdir, 'pki', 'fwupd'), + ) + install_data( + ['LVFS-CA.pem', 'LVFS-CA-2025PQ.pem'], + install_tag: 'runtime', + install_dir: join_paths(sysconfdir, 'pki', 'fwupd-metadata'), + ) endif diff -Nru fwupd-2.0.8/data/power.quirk fwupd-2.0.20/data/power.quirk --- fwupd-2.0.8/data/power.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/power.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -8,7 +8,3 @@ [HP] BatteryThreshold = 50 - -# Framework -[2185e781-ed4b-5fc2-8839-b5ddb1e7d649] -Flags = discharging-when-fully-changed diff -Nru fwupd-2.0.8/data/remotes.d/lvfs-testing.conf fwupd-2.0.20/data/remotes.d/lvfs-testing.conf --- fwupd-2.0.8/data/remotes.d/lvfs-testing.conf 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/remotes.d/lvfs-testing.conf 2026-02-26 11:36:18.000000000 +0000 @@ -6,6 +6,7 @@ MetadataURI=https://cdn.fwupd.org/downloads/firmware-testing.xml.@compression@ PrivacyURI=https://lvfs.readthedocs.io/en/latest/privacy.html ReportURI=https://fwupd.org/lvfs/firmware/report +FirmwareBaseURI=https://fwupd.org/downloads OrderBefore=lvfs AutomaticReports=false ApprovalRequired=false diff -Nru fwupd-2.0.8/data/remotes.d/lvfs.conf fwupd-2.0.20/data/remotes.d/lvfs.conf --- fwupd-2.0.8/data/remotes.d/lvfs.conf 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/remotes.d/lvfs.conf 2026-02-26 11:36:18.000000000 +0000 @@ -6,6 +6,7 @@ MetadataURI=https://cdn.fwupd.org/downloads/firmware.xml.@compression@ ReportURI=https://fwupd.org/lvfs/firmware/report PrivacyURI=https://lvfs.readthedocs.io/en/latest/privacy.html +FirmwareBaseURI=https://fwupd.org/downloads AutomaticReports=false AutomaticSecurityReports=false ApprovalRequired=false diff -Nru fwupd-2.0.8/data/remotes.d/meson.build fwupd-2.0.20/data/remotes.d/meson.build --- fwupd-2.0.8/data/remotes.d/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/remotes.d/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -11,6 +11,7 @@ output: 'lvfs.conf', configuration: con3, install: true, + install_tag: 'runtime', install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), ) configure_file( @@ -18,6 +19,7 @@ output: 'lvfs-testing.conf', configuration: con3, install: true, + install_tag: 'runtime', install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), ) i18n.merge_file( @@ -27,7 +29,8 @@ po_dir: join_paths(meson.project_source_root(), 'po'), data_dirs: join_paths(meson.project_source_root(), 'po'), install: true, - install_dir: join_paths(get_option('datadir'), 'fwupd', 'metainfo') + install_tag: 'runtime', + install_dir: join_paths(get_option('datadir'), 'fwupd', 'metainfo'), ) i18n.merge_file( input: 'lvfs-testing.metainfo.xml', @@ -36,12 +39,15 @@ po_dir: join_paths(meson.project_source_root(), 'po'), data_dirs: join_paths(meson.project_source_root(), 'po'), install: true, - install_dir: join_paths(get_option('datadir'), 'fwupd', 'metainfo') + install_tag: 'runtime', + install_dir: join_paths(get_option('datadir'), 'fwupd', 'metainfo'), ) endif -install_data('README.md', - install_dir: join_paths(datadir, 'fwupd', 'remotes.d', 'vendor', 'firmware') +install_data( + 'README.md', + install_tag: 'doc', + install_dir: join_paths(datadir, 'fwupd', 'remotes.d', 'vendor', 'firmware'), ) # replace @datadir@ @@ -52,6 +58,7 @@ output: 'vendor.conf', configuration: con2, install: get_option('vendor_metadata'), + install_tag: 'runtime', install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), ) configure_file( @@ -59,5 +66,6 @@ output: 'vendor-directory.conf', configuration: con2, install: true, + install_tag: 'runtime', install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), ) diff -Nru fwupd-2.0.8/data/tests/build-certs.py fwupd-2.0.20/data/tests/build-certs.py --- fwupd-2.0.8/data/tests/build-certs.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/tests/build-certs.py 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,4 @@ -#!/usr/bin/python3 +#!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1+ import os diff -Nru fwupd-2.0.8/data/tests/fwupd.sh fwupd-2.0.20/data/tests/fwupd.sh --- fwupd-2.0.8/data/tests/fwupd.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/tests/fwupd.sh 2026-02-26 11:36:18.000000000 +0000 @@ -3,39 +3,36 @@ exec 0>/dev/null exec 2>&1 -run_test() -{ - if [ -f @installedtestsbindir@/$1 ]; then - @installedtestsbindir@/$1 - fi +run_test() { + if [ -f @installedtestsbindir@/$1 ]; then + @installedtestsbindir@/$1 + fi } -run_device_tests() -{ - if [ -n "$CI_NETWORK" ] && [ -d @devicetestdir@ ]; then - for f in `grep --files-with-matches -r emulation- @devicetestdir@`; do - echo "Emulating for $f" - fwupdmgr device-emulate \ - --download-retries=5 \ - --no-unreported-check \ - --no-remote-check \ - --no-metadata-check \ - --json \ - "$f" - done - fi +run_device_tests() { + if [ -n "$CI_NETWORK" ] && [ -d @devicetestdir@ ]; then + for f in $(grep --files-with-matches -r emulation- @devicetestdir@); do + echo " ● Emulating for $f" + fwupdmgr device-emulate \ + --download-retries=5 \ + --no-unreported-check \ + --no-remote-check \ + --no-metadata-check \ + --json \ + "$f" + done + fi } -run_umockdev_test() -{ - INSPECTOR=@installedtestsdatadir@/unittest_inspector.py - ARG=@installedtestsdatadir@/$1 - if [ -f ${INSPECTOR} ] && [ -f ${ARG} ]; then - TESTS=`${INSPECTOR} ${ARG}` - for test in ${TESTS}; do - ${ARG} ${test} --verbose - done - fi +run_umockdev_test() { + INSPECTOR=@installedtestsdatadir@/unittest_inspector.py + ARG=@installedtestsdatadir@/$1 + if [ -f ${INSPECTOR} ] && [ -f ${ARG} ]; then + TESTS=$(${INSPECTOR} ${ARG}) + for test in ${TESTS}; do + ${ARG} ${test} --verbose + done + fi } export LSAN_OPTIONS="suppressions=@installedtestsdatadir@/lsan-suppressions.txt" @@ -52,8 +49,9 @@ run_test mtd-self-test run_test nitrokey-self-test run_test nvme-self-test -run_test optionrom-self-test +run_test pixart-tp-self-test run_test redfish-self-test +run_test snap-self-test run_test synaptics-prometheus-self-test run_test tpm-self-test run_test uefi-dbx-self-test diff -Nru fwupd-2.0.8/data/tests/fwupd.test.in fwupd-2.0.20/data/tests/fwupd.test.in --- fwupd-2.0.8/data/tests/fwupd.test.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/tests/fwupd.test.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,3 @@ [Test] -Type=session +Type=session-exclusive Exec=sh -c "G_TEST_SRCDIR=@installedtestsdatadir@ G_TEST_BUILDDIR=@installedtestsdatadir@ @installedtestsdir@/fwupd.sh" diff -Nru fwupd-2.0.8/data/tests/fwupdmgr-online.sh fwupd-2.0.20/data/tests/fwupdmgr-online.sh --- fwupd-2.0.8/data/tests/fwupdmgr-online.sh 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/data/tests/fwupdmgr-online.sh 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,240 @@ +#!/bin/sh + +export NO_COLOR=1 + +exec 0>/dev/null +exec 2>&1 + +TMPDIR="$(mktemp -d)" +trap 'rm -rf -- "$TMPDIR"' EXIT + +DEVICE=08d460be0f1f9f128413f816022a6439e0078018 + +error() { + if [ -f "fwupd.txt" ]; then + cat fwupd.txt + else + journalctl -u fwupd -b || true + fi + echo " ● exit code was ${1} and expected ${2}" + exit 1 +} + +expect_rc() { + rc=$? + expected=$1 + + [ "$expected" -eq "$rc" ] || error "$rc" "$expected" +} + +if [ -z "$CI_NETWORK" ]; then + echo " ● Skipping network tests due to CI_NETWORK not being set" + exit 0 +fi + +# --- +echo " ● Verify test device is present" +fwupdmgr get-devices --json | jq -e '.Devices | any(.Plugin == "test")' +if [ $? != 0 ]; then + echo " ● Skipping tests due to no test device enabled" + exit 0 +fi + +# --- +echo " ● Resetting config…" +fwupdmgr reset-config test +expect_rc 0 + +# --- +echo " ● Downloading random file…" +fwupdmgr download https://cdn.fwupd.org/static/img/user-solid.svg --force +expect_rc 0 + +# --- +echo " ● Getting devices (should be one)…" +fwupdmgr get-devices --no-unreported-check +expect_rc 0 + +# --- +echo " ● Clean remote" +fwupdmgr clean-remote lvfs +expect_rc 0 + +# --- +echo " ● Showing remote ages (JSON)…" +fwupdmgr get-remotes lvfs --json +expect_rc 0 + +# --- +echo " ● Refreshing from the LVFS…" +fwupdmgr --download-retries=5 refresh --force +expect_rc 0 +ls -R @localstatedir@/lib/fwupd/metadata/lvfs/firmware.xml.zst* +expect_rc 0 + +# --- +echo " ● Showing remote ages…" +fwupdmgr get-remotes lvfs +expect_rc 0 + +# --- +echo " ● Refreshing (already up to date)…" +cp @localstatedir@/lib/fwupd/metadata/lvfs/firmware.xml.zst* ${TMPDIR} +expect_rc 0 +fwupdmgr refresh ${TMPDIR}/firmware.xml.zst ${TMPDIR}/firmware.xml.zst.jcat lvfs +expect_rc 2 + +# --- +echo " ● Sync BKC…" +fwupdmgr sync +expect_rc 2 + +# --- +echo " ● Check we can search for known tokens…" +fwupdmgr search CVE-2022-21894 +expect_rc 0 + +# --- +echo " ● Check we do not find a random search result…" +fwupdmgr search DOESNOTEXIST +expect_rc 3 + +# --- +echo " ● Install a specific release…" +fwupdmgr --no-unreported-check --no-metadata-check --allow-reinstall --allow-older install ${DEVICE} 1.2.3 +expect_rc 0 + +# --- +echo " ● Getting updates (should be one)…" +fwupdmgr --no-unreported-check --no-metadata-check get-updates +expect_rc 0 + +# --- +echo " ● Getting updates (should still be one)…" +fwupdmgr --no-unreported-check --no-metadata-check get-updates ${DEVICE} +expect_rc 0 + +# --- +echo " ● Installing test firmware…" +fwupdmgr update ${DEVICE} -y +expect_rc 0 + +# --- +echo " ● Update when not needed…" +fwupdmgr update ${DEVICE} -y +expect_rc 0 + +# --- +echo " ● Reinstall current release…" +fwupdmgr reinstall ${DEVICE} -y +expect_rc 0 + +# --- +echo " ● Switch branch of device (impossible)…" +fwupdmgr switch-branch ${DEVICE} impossible +expect_rc 1 + +# --- +echo " ● Activate device (not required)…" +fwupdmgr activate ${DEVICE} +expect_rc 2 + +# --- +echo " ● Getting history (should be one)…" +fwupdmgr get-history +expect_rc 0 + +# --- +echo " ● Getting history (JSON)…" +fwupdmgr get-history --json +expect_rc 0 + +# --- +echo " ● Exporting history…" +fwupdmgr report-export +expect_rc 0 + +# --- +echo " ● Check if anything was tagged for emulation" +fwupdmgr get-devices --json --filter emulation-tag | jq -e '(.Devices | length) > 0' +rc=$? +if [ $rc = 0 ]; then + echo " ● Save device emulation" + fwupdmgr emulation-save /dev/null + expect_rc 0 + echo " ● Save device emulation (bad args)" + fwupdmgr emulation-save + expect_rc 1 +fi + +# --- +echo " ● Verifying results (str)…" +fwupdmgr get-results ${DEVICE} -y +expect_rc 0 + +# --- +echo " ● Verifying results (JSON)…" +fwupdmgr get-results ${DEVICE} -y --json +expect_rc 0 + +# --- +echo " ● Getting updates (should be none)…" +fwupdmgr --no-unreported-check --no-metadata-check get-updates +expect_rc 2 + +# --- +echo " ● Getting updates (JSON) (should be none)…" +fwupdmgr --no-unreported-check --no-metadata-check get-updates --json +expect_rc 0 + +# --- +echo " ● Updating verification…" +fwupdmgr verify-update ${DEVICE} +expect_rc 0 + +# --- +echo " ● Testing verification…" +fwupdmgr verify ${DEVICE} +expect_rc 0 + +# --- +echo " ● Downgrading to older release" +fwupdmgr --download-retries=5 downgrade ${DEVICE} -y +expect_rc 0 + +# --- +echo " ● Downgrading to older release (should be none)" +fwupdmgr downgrade ${DEVICE} +expect_rc 2 + +# --- +echo " ● Updating all devices to latest release" +fwupdmgr --download-retries=5 --no-unreported-check --no-metadata-check --no-reboot-check update -y +expect_rc 0 + +# --- +echo " ● Getting updates (should be none)…" +fwupdmgr --no-unreported-check --no-metadata-check get-updates +expect_rc 2 + +# --- +echo " ● Clearing results…" +fwupdmgr clear-results --json ${DEVICE} +expect_rc 0 + +# --- +echo " ● Check reboot behavior" +fwupdmgr modify-config test NeedsReboot true +expect_rc 0 +fwupdmgr --no-unreported-check --no-metadata-check --allow-reinstall --allow-older install ${DEVICE} 1.2.3 +expect_rc 0 +fwupdmgr check-reboot-needed ${DEVICE} --json +expect_rc 0 + +# --- +echo " ● Resetting config…" +fwupdmgr reset-config test +expect_rc 0 + +# success! +exit 0 diff -Nru fwupd-2.0.8/data/tests/fwupdmgr-online.test.in fwupd-2.0.20/data/tests/fwupdmgr-online.test.in --- fwupd-2.0.8/data/tests/fwupdmgr-online.test.in 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/data/tests/fwupdmgr-online.test.in 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,3 @@ +[Test] +Type=session +Exec=sh -c "@installedtestsdir@/fwupdmgr-online.sh" diff -Nru fwupd-2.0.8/data/tests/fwupdmgr-p2p.sh fwupd-2.0.20/data/tests/fwupdmgr-p2p.sh --- fwupd-2.0.8/data/tests/fwupdmgr-p2p.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/tests/fwupdmgr-p2p.sh 2026-02-26 11:36:18.000000000 +0000 @@ -1,25 +1,44 @@ -#!/bin/sh -e +#!/bin/sh +exec 0>/dev/null exec 2>&1 # only run as root, possibly only in CI if [ "$(id -u)" -ne 0 ]; then exit 0; fi +error() { + if [ -f "fwupd.txt" ]; then + cat fwupd.txt + else + journalctl -u fwupd -b || true + fi + echo " ● Exit code was ${1} and expected ${2}" + exit 1 +} + +expect_rc() { + rc=$? + expected=$1 + + [ "$expected" -eq "$rc" ] || error "$rc" "$expected" +} + # --- -echo "Starting P2P daemon..." +echo " ● Starting P2P daemon…" export FWUPD_DBUS_SOCKET="/run/fwupd.sock" rm -rf ${FWUPD_DBUS_SOCKET} @libexecdir@/fwupd/fwupd -v --timed-exit --no-timestamp & while [ ! -e ${FWUPD_DBUS_SOCKET} ]; do sleep 1; done # --- -echo "Starting P2P client..." +echo " ● Starting P2P client…" fwupdmgr get-devices --json -rc=$?; if [ $rc != 0 ]; then exit $rc; fi +expect_rc 0 # --- -echo "Shutting down P2P daemon..." +echo " ● Shutting down P2P daemon…" fwupdmgr quit +expect_rc 0 # success! exit 0 diff -Nru fwupd-2.0.8/data/tests/fwupdmgr-p2p.test.in fwupd-2.0.20/data/tests/fwupdmgr-p2p.test.in --- fwupd-2.0.8/data/tests/fwupdmgr-p2p.test.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/tests/fwupdmgr-p2p.test.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,3 @@ [Test] -Type=session +Type=session-exclusive Exec=sh -c "@installedtestsdir@/fwupdmgr-p2p.sh" diff -Nru fwupd-2.0.8/data/tests/fwupdmgr.sh fwupd-2.0.20/data/tests/fwupdmgr.sh --- fwupd-2.0.8/data/tests/fwupdmgr.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/tests/fwupdmgr.sh 2026-02-26 11:36:18.000000000 +0000 @@ -4,285 +4,246 @@ exec 0>/dev/null exec 2>&1 -device=08d460be0f1f9f128413f816022a6439e0078018 +DEVICE=08d460be0f1f9f128413f816022a6439e0078018 CAB="@installedtestsdir@/fakedevice123.cab" -error() -{ - rc=$1 +error() { if [ -f "fwupd.txt" ]; then cat fwupd.txt else journalctl -u fwupd -b || true fi - exit $rc + echo " ● Exit code was ${1} and expected ${2}" + exit 1 } -# --- -echo "Verify test device is present" -fwupdtool get-devices --json | jq -e '.Devices | any(.Plugin == "test")' -rc=$?; if [ $rc != 0 ]; then - echo "Enable test device" - fwupdtool enable-test-devices - rc=$?; if [ $rc != 0 ]; then error $rc; fi -fi +expect_rc() { + rc=$? + expected=$1 + + [ "$expected" -eq "$rc" ] || error "$rc" "$expected" +} # --- -echo "Show help output" +echo " ● Show help output" fwupdmgr --help -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Show version output" +echo " ● Show version output" fwupdmgr --version -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Getting the list of plugins..." +echo " ● Getting the list of plugins…" fwupdmgr get-plugins -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 if [ -n "$CI" ]; then # --- - echo "Setting BIOS setting..." + echo " ● Setting BIOS setting…" fwupdmgr set-bios-setting fwupd_self_test value - rc=$?; if [ $rc != 0 ]; then error $rc; fi + expect_rc 0 # --- - echo "Getting BIOS settings..." + echo " ● Getting BIOS settings…" fwupdmgr get-bios-setting - rc=$?; if [ $rc != 0 ]; then error $rc; fi + expect_rc 0 # --- - echo "Getting BIOS settings (json)..." + echo " ● Getting BIOS settings (JSON)…" fwupdmgr get-bios-setting --json - rc=$?; if [ $rc != 0 ]; then error $rc; fi + expect_rc 0 # --- - echo "Getting BIOS settings (unfound)..." + echo " ● Getting BIOS settings (unfound)…" fwupdmgr get-bios-setting foo - rc=$?; if [ $rc != 3 ]; then error $rc; fi + expect_rc 3 # --- - echo "Setting BIOS setting (unfound)..." + echo " ● Setting BIOS setting (unfound)…" fwupdmgr set-bios-setting unfound value - rc=$?; if [ $rc != 3 ]; then error $rc; fi + expect_rc 3 fi # --- -echo "Getting the list of plugins (json)..." +echo " ● Getting the list of plugins (JSON)…" fwupdmgr get-plugins --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 if [ -f ${CAB} ]; then # --- - echo "Examining ${CAB}..." + echo " ● Examining ${CAB}…" fwupdmgr get-details ${CAB} - rc=$?; if [ $rc != 0 ]; then exit $rc; fi + expect_rc 0 # --- - echo "Examining ${CAB} (json)..." + echo " ● Examining ${CAB} (JSON)…" fwupdmgr get-details ${CAB} --json - rc=$?; if [ $rc != 0 ]; then exit $rc; fi + expect_rc 0 # --- - echo "Installing ${CAB} cabinet..." - fwupdmgr install ${CAB} --no-reboot-check - rc=$?; if [ $rc != 0 ]; then exit $rc; fi + echo " ● Installing ${CAB} cabinet…" + fwupdmgr install ${CAB} --no-reboot-check --allow-reinstall --allow-older + expect_rc 0 fi # --- -echo "Update the device hash database..." -fwupdmgr get-releases $device -rc=$?; if [ $rc != 0 ]; then error $rc; fi - -# --- -echo "Getting the list of remotes..." +echo " ● Getting the list of remotes…" fwupdmgr get-remotes -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Disabling vendor-directory remote..." -fwupdmgr disable-remote vendor-directory -rc=$?; if [ $rc != 0 ]; then error $rc; fi - -# --- -echo "Getting the list of remotes (json)..." +echo " ● Getting the list of remotes (JSON)…" fwupdmgr get-remotes --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Enable vendor-directory remote..." -fwupdmgr enable-remote vendor-directory -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Modifying config…" +fwupdmgr modify-config fwupd UpdateMotd false --json +expect_rc 0 # --- -echo "Update the device hash database..." -fwupdmgr verify-update $device -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Inhibiting for 100ms…" +fwupdmgr inhibit test 100 +expect_rc 0 # --- -echo "Getting devices (should be one)..." -fwupdmgr get-devices --no-unreported-check -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Uninhibiting for invalid ID…" +fwupdmgr uninhibit test +expect_rc 3 # --- -echo "Testing the verification of firmware..." -fwupdmgr verify $device -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Add blocked firmware…" +fwupdmgr block-firmware foo +expect_rc 0 # --- -echo "Getting updates (should be one)..." -fwupdmgr --no-unreported-check --no-metadata-check get-updates -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Add blocked firmware (again)…" +fwupdmgr block-firmware foo +expect_rc 2 # --- -echo "Installing test firmware..." -fwupdmgr update $device -y -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Getting blocked firmware…" +fwupdmgr get-blocked-firmware +expect_rc 0 # --- -echo "Check if anything was tagged for emulation" -fwupdmgr get-devices --json --filter emulation-tag | jq -e '(.Devices | length) > 0' -rc=$?; if [ $rc = 0 ]; then - echo "Save device emulation" - fwupdmgr emulation-save /dev/null - rc=$?; if [ $rc != 0 ]; then error $rc; fi - echo "Save device emulation (bad args)" - fwupdmgr emulation-save - rc=$?; if [ $rc != 1 ]; then error $rc; fi -fi +echo " ● Remove blocked firmware…" +fwupdmgr unblock-firmware foo +expect_rc 0 # --- -echo "Verifying results (str)..." -fwupdmgr get-results $device -y -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Remove blocked firmware (again)…" +fwupdmgr unblock-firmware foo +expect_rc 2 # --- -echo "Verifying results (json)..." -fwupdmgr get-results $device -y --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Setting approved firmware…" +fwupdmgr set-approved-firmware foo,bar,baz +expect_rc 0 # --- -echo "Getting updates (should be none)..." -fwupdmgr --no-unreported-check --no-metadata-check get-updates -rc=$?; if [ $rc != 2 ]; then error $rc; fi +echo " ● Getting approved firmware…" +fwupdmgr get-approved-firmware +expect_rc 0 # --- -echo "Getting updates [json] (should be none)..." -fwupdmgr --no-unreported-check --no-metadata-check get-updates --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Getting approved firmware (JSON)…" +fwupdmgr get-approved-firmware --json +expect_rc 0 # --- -echo "Testing the verification of firmware (again)..." -fwupdmgr verify $device -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Run security tests…" +fwupdmgr security # --- -echo "Getting history (should be none)..." -fwupdmgr get-history -rc=$?; if [ $rc != 2 ]; then exit $rc; fi - -if [ -n "$CI_NETWORK" ]; then - # --- - echo "Downgrading to older release (requires network access)" - fwupdmgr --download-retries=5 downgrade $device -y - rc=$?; if [ $rc != 0 ]; then error $rc; fi - - # --- - echo "Downgrading to older release (should be none)" - fwupdmgr downgrade $device - rc=$?; if [ $rc != 2 ]; then error $rc; fi +echo " ● Run security tests (JSON)…" +fwupdmgr security --json - # --- - echo "Updating all devices to latest release (requires network access)" - fwupdmgr --download-retries=5 --no-unreported-check --no-metadata-check --no-reboot-check update -y - rc=$?; if [ $rc != 0 ]; then error $rc; fi +# --- +echo " ● Get HWIDs…" +fwupdmgr hwids +expect_rc 0 - # --- - echo "Getting updates (should be none)..." - fwupdmgr --no-unreported-check --no-metadata-check get-updates - rc=$?; if [ $rc != 2 ]; then error $rc; fi +# --- +echo " ● Get HWIDs (JSON)…" +fwupdmgr hwids --json +expect_rc 0 - # --- - echo "Refreshing from the LVFS (requires network access)..." - fwupdmgr --download-retries=5 refresh - rc=$?; if [ $rc != 0 ]; then error $rc; fi -else - echo "Skipping network tests due to CI_NETWORK not being set" +# --- +echo " ● Verify test device is present" +fwupdmgr get-devices --json | jq -e '.Devices | any(.Plugin == "test")' +if [ $? != 0 ]; then + echo " ● Skipping tests due to no test device enabled" + exit 0 fi # --- -echo "Modifying config..." -fwupdmgr modify-config fwupd UpdateMotd false --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Resetting config…" +fwupdmgr reset-config test +expect_rc 0 # --- -echo "Resetting changed config..." -fwupdmgr reset-config fwupd --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Get releases…" +fwupdmgr get-releases ${DEVICE} +expect_rc 0 # --- -echo "Resetting empty config ..." -fwupdmgr reset-config fwupd --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Get releases (JSON)…" +fwupdmgr get-releases ${DEVICE} --json +expect_rc 0 # --- -echo "Inhibiting for 100ms..." -fwupdmgr inhibit test 100 -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Disabling vendor-directory remote…" +fwupdmgr disable-remote vendor-directory +expect_rc 0 # --- -echo "Add blocked firmware..." -fwupdmgr block-firmware foo -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Enable vendor-directory remote…" +fwupdmgr enable-remote vendor-directory +expect_rc 0 # --- -echo "Add blocked firmware (again)..." -fwupdmgr block-firmware foo -rc=$?; if [ $rc != 2 ]; then error $rc; fi +echo " ● Modify vendor-directory remote…" +fwupdmgr modify-remote vendor-directory Enabled true +expect_rc 0 # --- -echo "Getting blocked firmware..." -fwupdmgr get-blocked-firmware -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Update the device hash database…" +fwupdmgr verify-update ${DEVICE} +expect_rc 0 # --- -echo "Remove blocked firmware..." -fwupdmgr unblock-firmware foo -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Getting devices (should be one)…" +fwupdmgr get-devices --no-unreported-check +expect_rc 0 # --- -echo "Remove blocked firmware (again)..." -fwupdmgr unblock-firmware foo -rc=$?; if [ $rc != 2 ]; then error $rc; fi +echo " ● Getting one device (should be one)…" +fwupdmgr get-devices ${DEVICE} --no-unreported-check +expect_rc 0 # --- -echo "Setting approved firmware..." -fwupdmgr set-approved-firmware foo,bar,baz -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Getting filtered devices (should be none)…" +fwupdmgr get-devices --no-unreported-check --filter wait-for-replug --filter-release trusted-payload -vv +expect_rc 2 # --- -echo "Getting approved firmware..." -fwupdmgr get-approved-firmware -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Testing the verification of firmware…" +fwupdmgr verify ${DEVICE} +expect_rc 0 -UNAME=$(uname -m) -if [ "${UNAME}" = "x86_64" ] || [ "${UNAME}" = "x86" ]; then - EXPECTED=0 -else - EXPECTED=1 -fi # --- -echo "Run security tests..." -fwupdmgr security -rc=$?; if [ $rc != $EXPECTED ]; then error $rc; fi +echo " ● Unlocking device (not needed)…" +fwupdmgr unlock ${DEVICE} +expect_rc 1 # --- -echo "Run security tests (json)..." -fwupdmgr security --json -rc=$?; if [ $rc != $EXPECTED ]; then error $rc; fi +echo " ● Testing waiting for device…" +fwupdmgr device-wait b585990a-003e-5270-89d5-3705a17f9a43 +expect_rc 0 # success! exit 0 diff -Nru fwupd-2.0.8/data/tests/fwupdmgr.test.in fwupd-2.0.20/data/tests/fwupdmgr.test.in --- fwupd-2.0.8/data/tests/fwupdmgr.test.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/tests/fwupdmgr.test.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,3 @@ [Test] -Type=session +Type=session-exclusive Exec=sh -c "@installedtestsdir@/fwupdmgr.sh" diff -Nru fwupd-2.0.8/data/tests/fwupdtool-efiboot.sh fwupd-2.0.20/data/tests/fwupdtool-efiboot.sh --- fwupd-2.0.8/data/tests/fwupdtool-efiboot.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/tests/fwupdtool-efiboot.sh 2026-02-26 11:36:18.000000000 +0000 @@ -15,111 +15,131 @@ mkdir -p ${FWUPD_SYSFSFWDIR}/efi/efivars mkdir -p ${FWUPD_UEFI_ESP_PATH} -error() -{ - rc=$1 - cat fwupdtool.txt - exit $rc +error() { + cat fwupdtool.txt + echo " ● Exit code was ${1} and expected ${2}" + exit 1 } -run() -{ - cmd="fwupdtool -v $*" - echo "cmd: $cmd" >fwupdtool.txt - $cmd 1>>fwupdtool.txt 2>&1 +expect_rc() { + rc=$? + expected=$1 + + [ "$expected" -eq "$rc" ] || error "$rc" "$expected" +} + +run() { + cmd="fwupdtool -v $*" + echo " ● cmd: $cmd" >fwupdtool.txt + $cmd 1>>fwupdtool.txt 2>&1 } # --- -echo "Creating Boot0001" +echo " ● ESP list…" +run esp-list +expect_rc 0 + +# --- +echo " ● ESP list (JSON)…" +run esp-list --json +expect_rc 0 + +# --- +echo " ● Creating Boot0001" run efiboot-create 0001 Fedora shimx64.efi ${FWUPD_UEFI_ESP_PATH} -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Creating Boot0001 again (should fail)..." +echo " ● Creating Boot0001 again (should fail)…" run efiboot-create 0001 Fedora shimx64.efi ${FWUPD_UEFI_ESP_PATH} -rc=$?; if [ $rc != 2 ]; then error $rc; fi +expect_rc 2 # --- -echo "Creating Boot0002 with invalid path (should fail)..." +echo " ● Creating Boot0002 with invalid path (should fail)…" run efiboot-create 0002 Fedora shimx64.efi /mnt/dave -rc=$?; if [ $rc != 3 ]; then error $rc; fi +expect_rc 3 # --- -echo "Getting BootOrder (should fail)" +echo " ● Getting BootOrder (should fail)" run efiboot-order -rc=$?; if [ $rc != 3 ]; then error $rc; fi +expect_rc 3 # --- -echo "Setting BootOrder" +echo " ● Setting BootOrder" run efiboot-order 0001 -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Getting BootOrder" +echo " ● Getting BootOrder" run efiboot-order -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Setting BootNext" +echo " ● Setting BootNext" run efiboot-next 0001 -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Getting BootNext" +echo " ● Getting BootNext" run efiboot-next -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Getting hive cmdline (should fail)" +echo " ● Getting hive cmdline (should fail)" run efiboot-hive 0001 cmdline -rc=$?; if [ $rc != 1 ]; then error $rc; fi +expect_rc 1 # --- -echo "Setting hive cmdline" +echo " ● Setting hive cmdline" run efiboot-hive --force 0001 cmdline acpi=off -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Getting hive cmdline" +echo " ● Getting hive cmdline" run efiboot-hive 0001 cmdline -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Creating Boot0002 as Win10" +echo " ● Creating Boot0002 as Win10" run efiboot-create 0002 Win10 bootmgfw.efi ${FWUPD_UEFI_ESP_PATH} -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Setting hive cmdline for Win10 (should fail)" +echo " ● Setting hive cmdline for Win10 (should fail)" run efiboot-hive --force 0002 cmdline acpi=off -rc=$?; if [ $rc != 1 ]; then error $rc; fi +expect_rc 1 # --- -echo "Showing EFI boot info" +echo " ● Showing EFI boot info" run efiboot-info -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Showing EFI boot info (json)" +echo " ● Showing EFI boot info (JSON)" run efiboot-info --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Showing EFI files" +echo " ● Showing EFI files" run efiboot-files -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 + +# --- +echo " ● Showing EFI files (JSON)…" +run efivar-files --json +expect_rc 0 # --- -echo "Deleting Boot0001" +echo " ● Deleting Boot0001" run efiboot-delete 0001 -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Deleting Boot0001 (should fail)..." +echo " ● Deleting Boot0001 (should fail)…" run efiboot-delete 0001 -rc=$?; if [ $rc != 3 ]; then error $rc; fi +expect_rc 3 # --- -echo "Showing EFI variables" +echo " ● Showing EFI variables" run efivar-list 8be4df61-93ca-11d2-aa0d-00e098032b8c -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 diff -Nru fwupd-2.0.8/data/tests/fwupdtool-efiboot.test.in fwupd-2.0.20/data/tests/fwupdtool-efiboot.test.in --- fwupd-2.0.8/data/tests/fwupdtool-efiboot.test.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/tests/fwupdtool-efiboot.test.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,3 @@ [Test] -Type=session +Type=session-exclusive Exec=sh -c "@installedtestsdir@/fwupdtool-efiboot.sh" diff -Nru fwupd-2.0.8/data/tests/fwupdtool.sh fwupd-2.0.20/data/tests/fwupdtool.sh --- fwupd-2.0.8/data/tests/fwupdtool.sh 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/tests/fwupdtool.sh 2026-02-26 11:36:18.000000000 +0000 @@ -3,6 +3,9 @@ exec 0>/dev/null exec 2>&1 +TMPDIR="$(mktemp -d)" +trap 'rm -rf -- "$TMPDIR"' EXIT + export NO_COLOR=1 export FWUPD_VERBOSE=1 CAB=fakedevice124.cab @@ -11,267 +14,417 @@ @installedtestsdir@/fakedevice124.metainfo.xml" DEVICE=08d460be0f1f9f128413f816022a6439e0078018 -error() -{ - rc=$1 - cat fwupdtool.txt - exit $rc +error() { + cat fwupdtool.txt + echo " ● exit code was ${1} and expected ${2}" + exit 1 +} + +expect_rc() { + rc=$? + expected=$1 + + [ "$expected" -eq "$rc" ] || error "$rc" "$expected" } -run() -{ - cmd="fwupdtool -v $*" - echo "cmd: $cmd" >fwupdtool.txt - $cmd 1>>fwupdtool.txt 2>&1 +run() { + cmd="fwupdtool -v --plugins test $*" + echo " ● cmd: $cmd" >fwupdtool.txt + $cmd 1>>fwupdtool.txt 2>&1 } # --- -echo "Show help output" +echo " ● Show help output" run --help -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Show version output" +echo " ● Show version output" run --version -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Show version output (json)" +echo " ● Show version output (JSON)" run --version --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Showing hwids" +echo " ● Showing hwids" run hwids -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 + +# --- +echo " ● Exporting hwids" +run export-hwids ${TMPDIR}/hwids.ini +expect_rc 0 UNAME=$(uname -m) -if [ "${UNAME}" = "x86_64" ] || [ "${UNAME}" = "x86" ]; then - EXPECTED=0 +if [ "${UNAME}" = "x86_64" ] || [ "${UNAME}" = "i686" ]; then + EXPECTED=0 else - EXPECTED=1 + EXPECTED=1 fi # --- -echo "Showing security" +echo " ● Showing security" run security -rc=$?; if [ $rc != $EXPECTED ]; then error $rc; fi +expect_rc $EXPECTED # --- -echo "Showing plugins" +echo " ● Showing plugins" run get-plugins -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Showing plugins (json)" +echo " ● Showing plugins (JSON)" run get-plugins --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi - -# --- -echo "Enabling test device..." -run enable-test-devices -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Checking device-flags" +echo " ● Checking device-flags" run get-device-flags -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Checking firmware-gtypes" +echo " ● Checking firmware-gtypes" run get-firmware-gtypes -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Checking firmware-types" +echo " ● Checking firmware-types" run get-firmware-types -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Checking for updates" -run get-updates -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Building ${CAB}…" +run build-cabinet ${CAB} ${INPUT} --force +expect_rc 0 # --- -echo "Checking for updates" -run get-updates --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Examining ${CAB}…" +run get-details ${CAB} +expect_rc 0 # --- -echo "Building ${CAB}..." -run build-cabinet ${CAB} ${INPUT} --force -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Testing good version compare" +run vercmp 1.0.0 1.0.0 triplet +expect_rc 0 # --- -echo "Examining ${CAB}..." -run get-details ${CAB} -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Testing bad version compare" +run vercmp 1.0.0 1.0.1 foo +expect_rc 1 # --- -echo "Installing ${CAB} cabinet..." -run install ${CAB} -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Getting supported version formats…" +run get-version-formats +expect_rc 0 # --- -echo "Cleaning ${CAB} generated cabinet ..." -rm -f ${CAB} +echo " ● Getting supported version formats (JSON)…" +run get-version-formats --json +expect_rc 0 # --- -echo "Verifying update..." -run verify-update ${DEVICE} -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Getting report metadata…" +run get-report-metadata +expect_rc 0 # --- -echo "Getting history (should be one)..." -run get-history -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Getting report metadata (JSON)…" +run get-report-metadata --json +expect_rc 0 + +# --- +echo " ● Getting the list of remotes" +run get-remotes --json +expect_rc 0 + +# --- +echo " ● Disabling LVFS remote…" +run modify-remote lvfs Enabled false +expect_rc 0 + +# --- +echo " ● Enabling LVFS remote…" +run modify-remote lvfs Enabled true +expect_rc 0 + +# --- +echo " ● Modify unknown remote (should fail)…" +run modify-remote foo Enabled true +expect_rc 1 + +# --- +echo " ● Modify known remote but unknown key (should fail)…" +run modify-remote lvfs bar true +expect_rc 3 + +BASEDIR=@installedtestsdir@/tests/bios-attrs/dell-xps13-9310/ +if [ -d $BASEDIR ]; then + WORKDIR="$(mktemp -d)" + cp $BASEDIR $WORKDIR -r + export FWUPD_SYSFSFWATTRIBDIR=$WORKDIR/dell-xps13-9310/ + # --- + echo " ● Get BIOS settings…" + run get-bios-settings --json + expect_rc 0 + + # --- + echo " ● Get BIOS setting as json…" + run get-bios-settings WlanAutoSense --json + expect_rc 0 + + # --- + echo " ● Get BIOS setting as a string…" + run get-bios-settings WlanAutoSense + expect_rc 0 + + # --- + echo " ● Modify BIOS setting to different value…" + run set-bios-setting WlanAutoSense Enabled --no-reboot-check + expect_rc 0 + + # --- + echo " ● Modify BIOS setting back to default…" + run set-bios-setting WlanAutoSense Disabled --no-reboot-check + expect_rc 0 + + # --- + echo " ● Modify BIOS setting to bad value (should fail)…" + run set-bios-setting WlanAutoSense foo --no-reboot-check + expect_rc 1 + + # --- + echo " ● Modify Unknown BIOS setting (should fail)…" + run set-bios-setting foo bar --no-reboot-check + expect_rc 3 +fi + +if [ -x /usr/bin/certtool ]; then + # --- + echo " ● Sign ${CAB}" + @installedtestsdir@/build-certs.py ${TMPDIR} + run firmware-sign ${CAB} ${TMPDIR}/testuser.pem ${TMPDIR}/testuser.key --json + expect_rc 0 +fi + +# --- +echo " ● Firmware builder…" +echo "HDRaGVsbG8gd29ybGQ=" >${TMPDIR}/blob.builder.xml +run firmware-build ${TMPDIR}/blob.builder.xml ${TMPDIR}/blob.srec +expect_rc 0 + +# --- +echo " ● Firmware parse blob…" +run firmware-parse ${TMPDIR}/blob.srec srec +expect_rc 0 + +# --- +echo " ● Firmware parse blob (auto)…" +run firmware-parse ${TMPDIR}/blob.srec auto +expect_rc 0 + +# --- +echo " ● Firmware extract…" +run firmware-extract ${TMPDIR}/blob.srec srec +expect_rc 0 + +# --- +echo " ● Firmware export…" +run firmware-export ${TMPDIR}/blob.srec srec +expect_rc 0 + +# --- +echo " ● Firmware convert (not working)…" +run firmware-convert ${TMPDIR}/blob.srec ${TMPDIR}/blob.bin srec srec +expect_rc 3 + +if [ -z "$CI_NETWORK" ]; then + echo " ● Skipping remaining tests due to CI_NETWORK not being set" + exit 0 +fi + +# --- +echo " ● Clean remote" +run clean-remote lvfs +expect_rc 0 + +# --- +echo " ● Refresh remotes (forced)" +run refresh --json --force --verbose +expect_rc 0 + +# --- +echo " ● Showing remote ages…" +run get-remotes --json --verbose +expect_rc 0 # --- -echo "Clearing history..." +echo " ● Refreshing (already up to date)…" +run refresh @localstatedir@/lib/fwupd/metadata/lvfs/firmware.xml.zst @localstatedir@/lib/fwupd/metadata/lvfs/firmware.xml.zst.jcat lvfs +expect_rc 2 + +# --- +echo " ● Search for known tokens…" +run search --json CVE-2022-21894 +expect_rc 0 +run search --json KEK +expect_rc 0 +run search --json org.uefi.dbx +expect_rc 0 +run search --json linux +expect_rc 0 + +# --- +echo " ● Search for random search result…" +run search --json DOESNOTEXIST +expect_rc 3 + +# --- +echo " ● Verify test device is present" +fwupdtool get-devices --json | jq -e '.Devices | any(.Plugin == "test")' +if [ $? != 0 ]; then + echo " ● Skipping tests due to no test device enabled" + exit 0 +fi + +# --- +echo " ● Clearing history…" run clear-history ${DEVICE} -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Getting history (should be none)..." -run get-history -rc=$?; if [ $rc != 2 ]; then error $rc; fi +echo " ● Installing blob…" +echo " ● 0.0.0" >${TMPDIR}/blob.bin +run install-blob ${TMPDIR}/blob.bin ${DEVICE} +expect_rc 0 # --- -echo "Resetting config..." -run reset-config test -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Dumping blob (not possible)…" +run firmware-dump ${TMPDIR}/blob.bin ${DEVICE} --force +expect_rc 1 # --- -echo "Testing good version compare" -run vercmp 1.0.0 1.0.0 triplet -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Reading blob (not possible)…" +run firmware-read ${TMPDIR}/blob.bin ${DEVICE} --force +expect_rc 1 # --- -echo "Testing bad version compare" -run vercmp 1.0.0 1.0.1 foo -rc=$?; if [ $rc != 1 ]; then error $rc; fi +echo " ● Installing ${CAB} cabinet…" +run install ${CAB} +expect_rc 0 # --- -echo "Getting supported version formats..." -run get-version-formats --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Installing same version (not possible)…" +run reinstall ${DEVICE} +expect_rc 1 # --- -echo "Getting report metadata..." -run get-report-metadata --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Cleaning ${CAB} generated cabinet…" +rm -f ${CAB} # --- -echo "Getting the list of remotes" -run get-remotes --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Verifying update…" +run verify-update ${DEVICE} +expect_rc 0 # --- -echo "Disabling LVFS remote..." -run modify-remote lvfs Enabled false -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Getting history (should be one)…" +run get-history +expect_rc 0 # --- -echo "Enabling LVFS remote..." -run modify-remote lvfs Enabled true -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Clearing history…" +run clear-history ${DEVICE} +expect_rc 0 # --- -echo "Modify unknown remote (should fail)..." -run modify-remote foo Enabled true -rc=$?; if [ $rc != 1 ]; then error $rc; fi +echo " ● Getting history (should be none)…" +run get-history +expect_rc 2 # --- -echo "Modify known remote but unknown key (should fail)..." -run modify-remote lvfs bar true -rc=$?; if [ $rc != 3 ]; then error $rc; fi +echo " ● Activating (not required)…" +run activate ${DEVICE} +expect_rc 2 # --- -echo "Getting devices (should be one)..." +echo " ● Activating (not possible)…" +run switch-branch ${DEVICE} impossible +expect_rc 2 + +# --- +echo " ● Resetting config…" +run reset-config test +expect_rc 0 + +# --- +echo " ● Getting devices (should be one)…" run get-devices --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +expect_rc 0 # --- -echo "Changing VALID config on test device..." -run modify-config test AnotherWriteRequired true -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Getting one device (should be one)…" +run get-devices ${DEVICE} +expect_rc 0 # --- -echo "Changing INVALID config on test device...(should fail)" -run modify-config test Foo true -rc=$?; if [ $rc != 1 ]; then error $rc; fi +echo " ● Switching branch…" +run switch-branch ${DEVICE} +expect_rc 1 # --- -echo "Disabling test device..." -run disable-test-devices -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Getting all devices, even unsupported ones…" +run get-devices --show-all --force +expect_rc 0 -BASEDIR=@installedtestsdir@/tests/bios-attrs/dell-xps13-9310/ -if [ -d $BASEDIR ]; then - WORKDIR="$(mktemp -d)" - cp $BASEDIR $WORKDIR -r - export FWUPD_SYSFSFWATTRIBDIR=$WORKDIR/dell-xps13-9310/ - # --- - echo "Get BIOS settings..." - run get-bios-settings --json - rc=$?; if [ $rc != 0 ]; then error $rc; fi - - # --- - echo "Get BIOS setting as json..." - run get-bios-settings WlanAutoSense --json - rc=$?; if [ $rc != 0 ]; then error $rc; fi - - # --- - echo "Get BIOS setting as a string..." - run get-bios-settings WlanAutoSense - rc=$?; if [ $rc != 0 ]; then error $rc; fi - - # --- - echo "Modify BIOS setting to different value..." - run set-bios-setting WlanAutoSense Enabled --no-reboot-check - rc=$?; if [ $rc != 0 ]; then error $rc; fi - - # --- - echo "Modify BIOS setting back to default..." - run set-bios-setting WlanAutoSense Disabled --no-reboot-check - rc=$?; if [ $rc != 0 ]; then error $rc; fi - - # --- - echo "Modify BIOS setting to bad value (should fail)..." - run set-bios-setting WlanAutoSense foo --no-reboot-check - rc=$?; if [ $rc != 1 ]; then error $rc; fi - - # --- - echo "Modify Unknown BIOS setting (should fail)..." - run set-bios-setting foo bar --no-reboot-check - rc=$?; if [ $rc != 3 ]; then error $rc; fi -fi +# --- +echo " ● Changing VALID config on test device…" +run modify-config test AnotherWriteRequired true +expect_rc 0 -if [ -x /usr/bin/certtool ]; then +# --- +echo " ● Changing INVALID config on test device…(should fail)" +run modify-config test Foo true +expect_rc 1 - # --- - echo "Building unsigned ${CAB}..." - INPUT="@installedtestsdir@/fakedevice124.bin \ - @installedtestsdir@/fakedevice124.metainfo.xml" - run build-cabinet ${CAB} ${INPUT} --force - rc=$?; if [ $rc != 0 ]; then error $rc; fi - - # --- - echo "Sign ${CAB}" - @installedtestsdir@/build-certs.py /tmp - run firmware-sign ${CAB} /tmp/testuser.pem /tmp/testuser.key --json - rc=$?; if [ $rc != 0 ]; then error $rc; fi - - # --- - echo "Cleaning self-signed ${CAB}..." - rm -f ${CAB} -fi +# --- +echo " ● Checking for updates" +run get-updates +expect_rc 0 -if [ -z "$CI_NETWORK" ]; then - echo "Skipping remaining tests due to CI_NETWORK not being set" - exit 0 -fi +# --- +echo " ● Checking for updates" +run get-updates --json +expect_rc 0 + +# --- +echo " ● Resetting config…" +run reset-config test +expect_rc 0 # --- -echo "Refresh remotes" -run refresh --json -rc=$?; if [ $rc != 0 ]; then error $rc; fi +echo " ● Calculating CRCs…" +echo "hello world" >${TMPDIR}/crc.txt +run crc b8-autosar ${TMPDIR}/crc.txt +expect_rc 0 +run crc b16-xmodem ${TMPDIR}/crc.txt +expect_rc 0 +run crc b32-standard ${TMPDIR}/crc.txt +expect_rc 0 + +# --- +echo " ● Calculating CRCs (invalid)…" +run crc ${TMPDIR}/crc.txt +expect_rc 1 +run crc invalid ${TMPDIR}/crc.txt +expect_rc 1 + +# --- +echo " ● Finding CRC…" +run crc-find ${TMPDIR}/crc.txt +expect_rc 1 +run crc-find 0xaf083b2d ${TMPDIR}/crc.txt +expect_rc 0 +run crc-find 0x12345678 ${TMPDIR}/crc.txt +expect_rc 1 diff -Nru fwupd-2.0.8/data/tests/fwupdtool.test.in fwupd-2.0.20/data/tests/fwupdtool.test.in --- fwupd-2.0.8/data/tests/fwupdtool.test.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/tests/fwupdtool.test.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,3 @@ [Test] -Type=session +Type=session-exclusive Exec=sh -c "@installedtestsdir@/fwupdtool.sh" diff -Nru fwupd-2.0.8/data/tests/meson.build fwupd-2.0.20/data/tests/meson.build --- fwupd-2.0.8/data/tests/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/data/tests/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -5,6 +5,7 @@ con2.set('devicetestdir', join_paths(installed_test_datadir, 'device-tests')) con2.set('bindir', bindir) con2.set('libexecdir', libexecdir) +con2.set('localstatedir', localstatedir) configure_file( input: 'fwupdmgr.test.in', @@ -15,6 +16,14 @@ ) configure_file( + input: 'fwupdmgr-online.test.in', + output: 'fwupdmgr-online.test', + configuration: con2, + install: true, + install_dir: installed_test_datadir, +) + +configure_file( input: 'fwupdmgr-p2p.test.in', output: 'fwupdmgr-p2p.test', configuration: con2, @@ -54,7 +63,8 @@ install_dir: installed_test_datadir, ) -install_data([ +install_data( + [ 'build-certs.py', 'fakedevice124.bin', 'fakedevice124.jcat', @@ -74,6 +84,14 @@ ) configure_file( + input: 'fwupdmgr-online.sh', + output: 'fwupdmgr-online.sh', + configuration: con2, + install: true, + install_dir: installed_test_datadir, +) + +configure_file( input: 'fwupdtool.sh', output: 'fwupdtool.sh', configuration: con2, @@ -97,29 +115,19 @@ install_dir: installed_test_datadir, ) -custom_target('installed-cab123', - input: [ - 'fakedevice123.bin', - 'fakedevice123.jcat', - 'fakedevice123.metainfo.xml', - ], +custom_target( + 'installed-cab123', + input: ['fakedevice123.bin', 'fakedevice123.jcat', 'fakedevice123.metainfo.xml'], output: 'fakedevice123.cab', - command: [ - gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@', - ], + command: [gcab, '--create', '@OUTPUT@', '@INPUT@'], install: true, install_dir: installed_test_datadir, ) -custom_target('installed-cab124', - input: [ - 'fakedevice124.bin', - 'fakedevice124.jcat', - 'fakedevice124.metainfo.xml', - ], +custom_target( + 'installed-cab124', + input: ['fakedevice124.bin', 'fakedevice124.jcat', 'fakedevice124.metainfo.xml'], output: 'fakedevice124.cab', - command: [ - gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@', - ], + command: [gcab, '--create', '@OUTPUT@', '@INPUT@'], install: true, install_dir: installed_test_datadir, ) @@ -130,23 +138,32 @@ output: 'fwupd-tests.conf', configuration: con2, install: true, + install_tag: 'tests', install_dir: join_paths(datadir, 'fwupd', 'remotes.d'), ) if umockdev_integration_tests.allowed() fwupd_mockdev_tests = files('fwupd_test.py') - r = run_command(unittest_inspector, fwupd_mockdev_tests, check: true) + r = run_command( + unittest_inspector, + fwupd_mockdev_tests, + check: true, + ) unit_tests = r.stdout().strip().split('\n') - foreach ut: unit_tests - test(ut, python3, args: [fwupd_mockdev_tests, ut], is_parallel: false, - env: { - 'CACHE_DIRECTORY': join_paths(meson.project_build_root(), 'cache'), - 'DAEMON_BUILDDIR': join_paths(meson.project_build_root(), 'src'), - 'GI_TYPELIB_PATH': join_paths(meson.project_build_root(), 'libfwupd'), - 'LD_LIBRARY_PATH': join_paths(meson.project_build_root(), 'libfwupd'), - 'STATE_DIRECTORY': join_paths(meson.project_build_root(), 'state') - }, - ) + foreach ut : unit_tests + test( + ut, + python3, + args: [fwupd_mockdev_tests, ut], + is_parallel: false, + env: { + 'CACHE_DIRECTORY': join_paths(meson.project_build_root(), 'cache'), + 'DAEMON_BUILDDIR': join_paths(meson.project_build_root(), 'src'), + 'GI_TYPELIB_PATH': join_paths(meson.project_build_root(), 'libfwupd'), + 'LD_LIBRARY_PATH': join_paths(meson.project_build_root(), 'libfwupd'), + 'STATE_DIRECTORY': join_paths(meson.project_build_root(), 'state'), + }, + ) endforeach configure_file( input: fwupd_mockdev_tests, @@ -154,7 +171,10 @@ configuration: { 'LIBEXECDIR': daemon_dir, }, - install_dir: installed_test_datadir + install_dir: installed_test_datadir, + ) + install_data( + 'fwupd_ioc.py', + install_dir: installed_test_datadir, ) - install_data('fwupd_ioc.py', install_dir: installed_test_datadir) endif diff -Nru fwupd-2.0.8/data/vendors.quirk fwupd-2.0.20/data/vendors.quirk --- fwupd-2.0.8/data/vendors.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/data/vendors.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,69 @@ +# use this file when usb.ids is insufficient to map the ID to a nice name + +[USB\VID_03F0] +Vendor = HP +[USB\VID_045E] +Vendor = Microsoft +[USB\VID_046D] +Vendor = Logitech +[USB\VID_04F2] +Vendor = Chicony +[USB\VID_04F3] +Vendor = Elan +[USB\VID_06CB] +Vendor = Synaptics +[USB\VID_10AB] +Vendor = USI +[USB\VID_1C7A] +Vendor = Egistec +[USB\VID_222A] +Vendor = Ilitek +[USB\VID_273F] +Vendor = Hughski +[USB\VID_27C6] +Vendor = Goodix +[USB\VID_32AC] +Vendor = Framework +[USB\VID_8087] +Vendor = Intel + +[PCI\VEN_1002] +Vendor = AMD +[PCI\VEN_1022] +Vendor = AMD +[PCI\VEN_106B] +Vendor = Apple +[PCI\VEN_10DE] +Vendor = NVIDIA +[PCI\VEN_10EC] +Vendor = Realtek +[PCI\VEN_1179] +Vendor = Toshiba +[PCI\VEN_1344] +Vendor = Micron +[PCI\VEN_15B7] +Vendor = Sandisk +[PCI\VEN_144D] +Vendor = Samsung +[PCI\VEN_15B3] +Vendor = Mellanox +[PCI\VEN_15B7] +Vendor = Sandisk +[PCI\VEN_17CB] +Vendor = Qualcomm +[PCI\VEN_1987] +Vendor = Phison +[PCI\VEN_1CC4] +Vendor = UnionMemory +[PCI\VEN_1D50] +Vendor = OpenMoko +[PCI\VEN_1D97] +Vendor = Lexar +[PCI\VEN_1E0F] +Vendor = KIOXIA +[PCI\VEN_1E4B] +Vendor = MAXIO +[PCI\VEN_2646] +Vendor = Kingston +[PCI\VEN_8086] +Vendor = Intel diff -Nru fwupd-2.0.8/debian/changelog fwupd-2.0.20/debian/changelog --- fwupd-2.0.8/debian/changelog 2025-04-27 19:00:38.000000000 +0000 +++ fwupd-2.0.20/debian/changelog 2026-03-10 21:34:00.000000000 +0000 @@ -1,3 +1,140 @@ +fwupd (2.0.20-1~bpo13+1) trixie-backports; urgency=medium + + * Backport to trixie + * Disable passim support + + -- Mario Limonciello Tue, 10 Mar 2026 16:34:00 -0500 + +fwupd (2.0.20-1) unstable; urgency=medium + + * New upstream version (2.0.20) + + -- Mario Limonciello Thu, 26 Feb 2026 06:49:36 -0600 + +fwupd (2.0.19-1) unstable; urgency=medium + + * New upstream version (2.0.19) + + -- Mario Limonciello Sat, 20 Dec 2025 22:13:58 -0600 + +fwupd (2.0.18-1) unstable; urgency=medium + + * New upstream version (2.0.18) + * Drop upstream patches + + -- Mario Limonciello Sat, 20 Dec 2025 22:06:57 -0600 + +fwupd (2.0.17-6) unstable; urgency=medium + + * Backport a patch to fix x86 32 bit installed tests + + -- Mario Limonciello Mon, 10 Nov 2025 23:02:48 -0600 + +fwupd (2.0.17-5) unstable; urgency=medium + + * d/tests/control: depends on fwupd-tests instead of fwupd + + -- Mario Limonciello Mon, 10 Nov 2025 09:48:40 -0600 + +fwupd (2.0.17-4) unstable; urgency=medium + + * d/t/control: Explicitly add fwupd to depends + + -- Mario Limonciello Sun, 09 Nov 2025 20:22:43 -0600 + +fwupd (2.0.17-3) unstable; urgency=medium + + * d/t/control: Add missing depends for autopkgtest + + -- Mario Limonciello Sat, 08 Nov 2025 08:26:36 -0600 + +fwupd (2.0.17-2) unstable; urgency=medium + + * Backport a fix for installed tests failures with mtdram + + -- Mario Limonciello Thu, 06 Nov 2025 09:54:29 -0600 + +fwupd (2.0.17-1) unstable; urgency=medium + + * New upstream version (2.0.17) + + -- Mario Limonciello Wed, 05 Nov 2025 13:22:11 -0600 + +fwupd (2.0.16-4) unstable; urgency=medium + + * Enable passim support in Debian (disabled in Ubuntu right now). + + -- Mario Limonciello Thu, 30 Oct 2025 10:15:26 -0500 + +fwupd (2.0.16-3) unstable; urgency=medium + + * d/rules: Explicitly set efi_os_dir + (Closes: #1105176) (Closes: #1112464) + * d/control: Drop b-d on python3-typogrify + (Closes: #1105777) + * Changes for build-dependency on libgirepository1.0-dev + (Closes: #1118817) + * Backport a patch to fix a segfault with flashrom (Closes: #1118449) + + -- Mario Limonciello Sat, 25 Oct 2025 14:57:03 -0500 + +fwupd (2.0.16-2) unstable; urgency=medium + + * Backport a patch from upstream to drop python3-toml b-d + * d/copyright, d/control: refresh against dependencies.xml (Closes: #1110022) + + -- Mario Limonciello Mon, 15 Sep 2025 14:24:29 -0500 + +fwupd (2.0.16-1) unstable; urgency=medium + + * New upstream version (2.0.16) + + -- Mario Limonciello Fri, 12 Sep 2025 09:27:26 -0500 + +fwupd (2.0.15-1) unstable; urgency=medium + + * New upstream version (2.0.15) + + -- Mario Limonciello Wed, 10 Sep 2025 12:57:57 -0500 + +fwupd (2.0.14-1) unstable; urgency=medium + + * New upstream version (2.0.14) + + -- Mario Limonciello Fri, 29 Aug 2025 11:28:00 -0500 + +fwupd (2.0.13-2) unstable; urgency=medium + + * Upload to unstable + + -- Mario Limonciello Sun, 10 Aug 2025 12:28:15 -0500 + +fwupd (2.0.13-1) experimental; urgency=medium + + * New upstream version. + + -- Mario Limonciello Tue, 22 Jul 2025 15:58:45 -0500 + +fwupd (2.0.12-1) experimental; urgency=medium + + * New upstream version. + + -- Mario Limonciello Thu, 19 Jun 2025 23:04:37 -0500 + +fwupd (2.0.10-1) experimental; urgency=medium + + * New upstream version. + * d/control: Update my email + + -- Mario Limonciello Fri, 30 May 2025 11:43:44 -0500 + +fwupd (2.0.9-1) experimental; urgency=medium + + * New upstream version (2.0.9) + - Drop all upstream patches. + + -- Mario Limonciello Thu, 08 May 2025 10:51:51 -0500 + fwupd (2.0.8-3) unstable; urgency=medium * Backport a patch fixing crashes from uevents without keys (LP: #2107540) @@ -801,7 +938,7 @@ - Parse the CSR firmware as a DFU file - Prevent dell-dock updates to occur via synaptics-mst plugin - Rather than hardcoding thunderbolt to PCI slot numbers, use domain in GUID - - Remove a dock device from the whitelist that is never going to be updated + - Remove a dock device from the allowlist that is never going to be updated - Validate that gpgme_op_verify_result() returned at least one signature - Wait for the cxaudio device to reboot after writing firmware * Drop following patches, now incorporated upstream: @@ -1345,7 +1482,7 @@ * trivial: Never compare a string against zero to avoid warnings * unifying: Don't log a warning when an unknown report is parsed * trivial: Include all the GTypes in the generated docs - * Check all the device GUIDs against the blacklist when added + * Check all the device GUIDs against the blocklist when added * Fix a hang on 32 bit computers * trivial: Fix a -Wsign-compare warning on 32 bit * trivial: Fix spelling of delimiter @@ -1398,7 +1535,7 @@ * Add capability to enable test suite via /etc/fwupd.conf * rpm: enable test suite via /etc/fwupd.conf * debian: enable test suite via /etc/fwupd.conf - * trivial: clarify delimitter in use for fwupd.conf is a semicolon + * trivial: clarify delimiter in use for fwupd.conf is a semicolon * trivial: adjust get-details and get-devices output Display Name output * trivial: set engine back to idle * Correct a memory leak in Dell plugin (Fixes #158) diff -Nru fwupd-2.0.8/debian/control fwupd-2.0.20/debian/control --- fwupd-2.0.8/debian/control 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/control 2026-03-10 21:24:09.000000000 +0000 @@ -3,7 +3,7 @@ Maintainer: Debian EFI Uploaders: Steve McIntyre <93sam@debian.org>, Matthias Klumpp , - Mario Limonciello + Mario Limonciello Build-Depends: bash-completion, debhelper (>= 12), @@ -19,6 +19,7 @@ gobject-introspection, hwdata, libarchive-dev, + libblkid-dev, libcairo-dev, libcairo-gobject2, libcbor-dev, @@ -27,7 +28,9 @@ libflashrom-dev [!ia64], libfreetype-dev, libftdi1-dev, - libgirepository1.0-dev, + gir1.2-gio-2.0-dev, + gir1.2-gobject-2.0-dev, + gobject-introspection (>= 1.80), libglib2.0-dev (>= 2.45.8), libjaylink-dev [!ia64], libjcat-dev, @@ -35,11 +38,13 @@ liblzma-dev, libmbim-glib-dev, libmm-glib-dev, + libmnl-dev, libpci-dev, libpolkit-gobject-1-dev, libprotobuf-c-dev, libqmi-glib-dev, libqrtr-glib-dev, + libreadline-dev, libsqlite3-dev, libsystemd-dev, libtss2-dev, @@ -57,8 +62,6 @@ python3-packaging, python3-pefile, python3-requests, - python3-toml, - python3-typogrify, shared-mime-info, systemd-dev, udev, @@ -104,12 +107,13 @@ fwupd-signed, jq Suggests: gir1.2-fwupd-2.0 -Provides: fwupdate +Provides: fwupdate, + ${gir:Provides} Conflicts: fwupdate-amd64-signed, fwupdate-i386-signed, fwupdate-arm64-signed, fwupdate-armhf-signed -Breaks: gir1.2-dfu-1.0 (<< 0.9.7-1), +Breaks: ${gir:Depends}, libdfu1 (<< 0.9.7-1), fwupdate (<< 12-7), libdfu-dev (<< 0.9.7-1) @@ -170,12 +174,13 @@ Package: libfwupd-dev Architecture: linux-any Depends: libfwupd3 (= ${binary:Version}), - gir1.2-fwupd-2.0 (= ${binary:Version}), + ${gir:Depends}, libcurl4-gnutls-dev, libglib2.0-dev (>= 2.45.8), libjcat-dev, libjson-glib-dev (>= 1.1.1), ${misc:Depends} +Provides: ${gir:Provides} Breaks: fwupd-dev (<< 0.5.4-2~) Replaces: fwupd-dev (<< 0.5.4-2~) Section: libdevel @@ -193,6 +198,7 @@ Multi-Arch: same Depends: ${misc:Depends}, ${gir:Depends} +Provides: ${gir:Provides} Section: introspection Description: GObject introspection data for libfwupd This package provides the introspection data for libfwupd. diff -Nru fwupd-2.0.8/debian/control.in fwupd-2.0.20/debian/control.in --- fwupd-2.0.8/debian/control.in 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/control.in 2026-03-10 21:22:38.000000000 +0000 @@ -3,7 +3,7 @@ Maintainer: Debian EFI Uploaders: Steve McIntyre <93sam@debian.org>, Matthias Klumpp , - Mario Limonciello + Mario Limonciello Build-Depends: %%%DYNAMIC%%% Build-Depends-Indep: gi-docgen , Rules-Requires-Root: no diff -Nru fwupd-2.0.8/debian/copyright fwupd-2.0.20/debian/copyright --- fwupd-2.0.8/debian/copyright 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/copyright 2026-03-10 21:22:38.000000000 +0000 @@ -3,7 +3,8 @@ Source: https://github.com/fwupd/fwupd Files: * -Copyright: +Copyright: + 1991, 1999 Free Software Foundation, Inc. 2006, 2008 Junio C Hamano 3mdeb Embedded Systems Consulting 9elements Agency GmbH @@ -12,17 +13,20 @@ Advanced Micro Devices Inc. Aleix Pol Aleksander Morgado - Algoltek Algoltek, Inc. Andrew Duggan Andrii Dushko Apollo Ling Apple Inc + Arno Dubois + B&R Industrial Automation GmbH Ben Chuang Benson Leung Canonical Ltd Canonical Ltd. + Chris Hofstaedtler Christian J. Kellner + Chun-wei Fan Collabora Ltd Collabora Ltd. Cypress Semiconductor Corporation. @@ -34,6 +38,7 @@ Denis Pynkin Dylan Van Assche Emmanuel Pacaud + Endless OS Foundation LLC Evan Lojewski Framework Computer Inc Fresco Logic @@ -45,6 +50,7 @@ Google LLC Google, Inc. H.J. Lu + HP Development Company, L.P. Hai Su Hans de Goede Haowei Lo @@ -58,10 +64,12 @@ Jarvis Jiang Jason Gerecke Jason Gerecke + Jason Huang Jeffrey Lin Jeremy Soller Jimmy Yu Jingle Wu + Joe Hong Joshua Dickens Kai Michaelis Kevin Chen @@ -69,6 +77,7 @@ Lennart Poettering Logitech, Inc. LongSoft + Maciej Borzecki Marek Marczykowski-Górecki Mario Limonciello Matthias Klumpp @@ -78,12 +87,12 @@ Michał Kopeć Mike Chang Mike Chang + NVIDIA Corporation & Affiliates Norbert Kaminski Norbert Kamiński Parade Technology, Ltd Peichen Huang Peichen Huang - Pena Christian Peter Jones Peter Marheine Philip Withnall @@ -93,12 +102,14 @@ Rafal Wojtczuk Realtek Corporation Realtek Semiconductor Corporation + Red Hat, Inc. Ricardo Cañuelo - Richard hughes + Richard Hughes Ricky Wu Ryan Chang Sean Rhodes Sergii Dmytruk + Shaun McCance Shihwei Huang Simon McVittie Synaptics @@ -109,6 +120,7 @@ Synaptics Incorporated. TDT AG Texas Instruments Incorporated + Thomas Mühlbacher Twain Byrnes VIA Corporation Victor Cheng diff -Nru fwupd-2.0.8/debian/fwupd-tests.install fwupd-2.0.20/debian/fwupd-tests.install --- fwupd-2.0.8/debian/fwupd-tests.install 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/fwupd-tests.install 2026-03-10 21:22:38.000000000 +0000 @@ -4,5 +4,4 @@ #https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=872458 usr/share/installed-tests/* usr/libexec/installed-tests/fwupd/*-self-test -debian/lintian/fwupd-tests usr/share/lintian/overrides usr/share/fwupd/remotes.d/fwupd-tests.conf diff -Nru fwupd-2.0.8/debian/fwupd-tests.postinst fwupd-2.0.20/debian/fwupd-tests.postinst --- fwupd-2.0.8/debian/fwupd-tests.postinst 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/fwupd-tests.postinst 2026-03-10 21:22:38.000000000 +0000 @@ -5,9 +5,9 @@ #only enable on installation not upgrade if [ "$1" = configure ] && [ -z "$2" ]; then - if [ "$CI" = "true" ]; then - fwupdtool enable-test-devices - else - echo "To enable test suite, run `fwupdtool enable-test-devices`" - fi + if [ "$CI" = "true" ]; then + fwupdtool enable-test-devices + else + echo "To enable test suite, run \`fwupdtool enable-test-devices\`" + fi fi diff -Nru fwupd-2.0.8/debian/fwupd-tests.postrm fwupd-2.0.20/debian/fwupd-tests.postrm --- fwupd-2.0.8/debian/fwupd-tests.postrm 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/fwupd-tests.postrm 2026-03-10 21:22:38.000000000 +0000 @@ -5,9 +5,9 @@ # don't run on purge; the commands might be missing if [ "$1" = remove ]; then - if [ "$CI" = "true" ]; then - fwupdtool disable-test-devices - else - echo "To disable test suite, run `fwupdtool disable-test-devices`" - fi + if [ "$CI" = "true" ]; then + fwupdtool disable-test-devices + else + echo "To disable test suite, run \`fwupdtool disable-test-devices\`" + fi fi diff -Nru fwupd-2.0.8/debian/fwupd.install fwupd-2.0.20/debian/fwupd.install --- fwupd-2.0.8/debian/fwupd.install 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/fwupd.install 2026-03-10 21:22:38.000000000 +0000 @@ -15,5 +15,4 @@ usr/share/man/* data/fwupd.conf etc/fwupd usr/lib/*/fwupd-*/*.so -debian/lintian/fwupd usr/share/lintian/overrides obj*/data/motd/85-fwupd /etc/update-motd.d diff -Nru fwupd-2.0.8/debian/fwupd.postrm fwupd-2.0.20/debian/fwupd.postrm --- fwupd-2.0.8/debian/fwupd.postrm 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/fwupd.postrm 2026-03-10 21:22:38.000000000 +0000 @@ -4,6 +4,6 @@ #DEBHELPER# if [ "$1" = purge ]; then - rm -rf /var/lib/fwupd /var/cache/fwupd /var/cache/fwupdmgr - rm -f /var/cache/app-info/xmls/fwupd.xml + rm -rf /var/lib/fwupd /var/cache/fwupd /var/cache/fwupdmgr + rm -f /var/cache/app-info/xmls/fwupd.xml fi diff -Nru fwupd-2.0.8/debian/fwupd.preinst fwupd-2.0.20/debian/fwupd.preinst --- fwupd-2.0.8/debian/fwupd.preinst 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/fwupd.preinst 2026-03-10 21:22:38.000000000 +0000 @@ -7,5 +7,5 @@ # this directory, but fwupd-refresh.service used DynamicUser directive # meaning no other unit could access it. if [ -L /var/cache/fwupd ]; then - rm -f /var/cache/fwupd + rm -f /var/cache/fwupd fi diff -Nru fwupd-2.0.8/debian/libfwupd3.symbols fwupd-2.0.20/debian/libfwupd3.symbols --- fwupd-2.0.8/debian/libfwupd3.symbols 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/libfwupd3.symbols 2026-03-10 21:22:38.000000000 +0000 @@ -74,13 +74,19 @@ LIBFWUPD_1.9.6@LIBFWUPD_1.9.6 1.9.6 LIBFWUPD_1.9.8@LIBFWUPD_1.9.8 1.9.8 LIBFWUPD_2.0.0@LIBFWUPD_2.0.0 2.0.0 + LIBFWUPD_2.0.10@LIBFWUPD_2.0.10 2.0.10 + LIBFWUPD_2.0.11@LIBFWUPD_2.0.11 2.0.11 + LIBFWUPD_2.0.16@LIBFWUPD_2.0.16 2.0.16 + LIBFWUPD_2.0.17@LIBFWUPD_2.0.17 2.0.17 LIBFWUPD_2.0.1@LIBFWUPD_2.0.1 2.0.1 + LIBFWUPD_2.0.20@LIBFWUPD_2.0.20 2.0.20 LIBFWUPD_2.0.2@LIBFWUPD_2.0.2 2.0.2 LIBFWUPD_2.0.4@LIBFWUPD_2.0.4 2.0.4 LIBFWUPD_2.0.7@LIBFWUPD_2.0.7 2.0.7 fwupd_bios_setting_add_possible_value@LIBFWUPD_1.8.4 1.8.4 fwupd_bios_setting_get_current_value@LIBFWUPD_1.8.4 1.8.4 fwupd_bios_setting_get_description@LIBFWUPD_1.8.4 1.8.4 + fwupd_bios_setting_get_filename@LIBFWUPD_2.0.20 2.0.20 fwupd_bios_setting_get_id@LIBFWUPD_1.8.4 1.8.4 fwupd_bios_setting_get_kind@LIBFWUPD_1.8.4 1.8.4 fwupd_bios_setting_get_lower_bound@LIBFWUPD_1.8.4 1.8.4 @@ -96,6 +102,7 @@ fwupd_bios_setting_new@LIBFWUPD_1.8.4 1.8.4 fwupd_bios_setting_set_current_value@LIBFWUPD_1.8.4 1.8.4 fwupd_bios_setting_set_description@LIBFWUPD_1.8.4 1.8.4 + fwupd_bios_setting_set_filename@LIBFWUPD_2.0.20 2.0.20 fwupd_bios_setting_set_id@LIBFWUPD_1.8.4 1.8.4 fwupd_bios_setting_set_kind@LIBFWUPD_1.8.4 1.8.4 fwupd_bios_setting_set_lower_bound@LIBFWUPD_1.8.4 1.8.4 @@ -117,6 +124,9 @@ fwupd_client_build_report_devices@LIBFWUPD_1.9.20 1.9.20 fwupd_client_build_report_history@LIBFWUPD_2.0.0 2.0.0 fwupd_client_build_report_security@LIBFWUPD_2.0.0 2.0.0 + fwupd_client_clean_remote@LIBFWUPD_2.0.17 2.0.17 + fwupd_client_clean_remote_async@LIBFWUPD_2.0.17 2.0.17 + fwupd_client_clean_remote_finish@LIBFWUPD_2.0.17 2.0.17 fwupd_client_clear_results@LIBFWUPD_0.7.0 1.0.0 fwupd_client_clear_results_async@LIBFWUPD_1.5.0 1.5.0 fwupd_client_clear_results_finish@LIBFWUPD_1.5.0 1.5.0 @@ -184,6 +194,7 @@ fwupd_client_get_host_security_events_finish@LIBFWUPD_1.7.1 1.7.1 fwupd_client_get_host_security_id@LIBFWUPD_1.5.0 1.5.0 fwupd_client_get_host_vendor@LIBFWUPD_1.8.2 1.8.2 + fwupd_client_get_hwids@LIBFWUPD_2.0.17 2.0.17 fwupd_client_get_main_context@LIBFWUPD_1.5.3 1.5.3 fwupd_client_get_only_trusted@LIBFWUPD_1.8.0 1.8.0 fwupd_client_get_percentage@LIBFWUPD_0.7.3 1.0.0 @@ -246,6 +257,9 @@ fwupd_client_reset_config@LIBFWUPD_1.9.15 1.9.15 fwupd_client_reset_config_async@LIBFWUPD_1.9.15 1.9.15 fwupd_client_reset_config_finish@LIBFWUPD_1.9.15 1.9.15 + fwupd_client_search@LIBFWUPD_2.0.16 2.0.16 + fwupd_client_search_async@LIBFWUPD_2.0.16 2.0.16 + fwupd_client_search_finish@LIBFWUPD_2.0.16 2.0.16 fwupd_client_self_sign@LIBFWUPD_1.2.6 1.2.6 fwupd_client_self_sign_async@LIBFWUPD_1.5.0 1.5.0 fwupd_client_self_sign_finish@LIBFWUPD_1.5.0 1.5.0 @@ -298,6 +312,7 @@ fwupd_codec_json_append@LIBFWUPD_2.0.0 2.0.0 fwupd_codec_json_append_bool@LIBFWUPD_2.0.0 2.0.0 fwupd_codec_json_append_int@LIBFWUPD_2.0.0 2.0.0 + fwupd_codec_json_append_map@LIBFWUPD_2.0.10 2.0.10 fwupd_codec_json_append_strv@LIBFWUPD_2.0.0 2.0.0 fwupd_codec_string_append@LIBFWUPD_2.0.0 2.0.0 fwupd_codec_string_append_bool@LIBFWUPD_2.0.0 2.0.0 @@ -532,6 +547,8 @@ fwupd_remote_build_metadata_sig_uri@LIBFWUPD_1.9.8 1.9.8 fwupd_remote_build_metadata_uri@LIBFWUPD_1.9.8 1.9.8 fwupd_remote_build_report_uri@LIBFWUPD_1.9.1 1.9.1 + fwupd_remote_ensure_checksum_sig@LIBFWUPD_2.0.17 2.0.17 + fwupd_remote_ensure_mtime@LIBFWUPD_2.0.17 2.0.17 fwupd_remote_flag_from_string@LIBFWUPD_1.9.4 1.9.4 fwupd_remote_flag_to_string@LIBFWUPD_1.9.4 1.9.4 fwupd_remote_get_age@LIBFWUPD_0.9.5 1.0.0 @@ -547,6 +564,7 @@ fwupd_remote_get_kind@LIBFWUPD_0.9.6 1.0.0 fwupd_remote_get_metadata_uri@LIBFWUPD_0.9.7 1.0.0 fwupd_remote_get_metadata_uri_sig@LIBFWUPD_0.9.7 1.0.0 + fwupd_remote_get_mtime@LIBFWUPD_2.0.17 2.0.17 fwupd_remote_get_order_after@LIBFWUPD_0.9.5 1.0.0 fwupd_remote_get_order_before@LIBFWUPD_0.9.5 1.0.0 fwupd_remote_get_password@LIBFWUPD_0.9.5 1.0.0 @@ -699,6 +717,7 @@ fwupd_security_attr_set_url@LIBFWUPD_1.5.0 1.5.0 fwupd_status_from_string@LIBFWUPD_0.1.1 1.0.0 fwupd_status_to_string@LIBFWUPD_0.1.1 1.0.0 + fwupd_strerror@LIBFWUPD_2.0.11 2.0.11 fwupd_update_state_from_string@LIBFWUPD_0.7.0 1.0.0 fwupd_update_state_to_string@LIBFWUPD_0.7.0 1.0.0 fwupd_version_format_from_string@LIBFWUPD_1.2.9 1.2.9 diff -Nru fwupd-2.0.8/debian/patches/0001-Always-build-the-D-Bus-service.patch fwupd-2.0.20/debian/patches/0001-Always-build-the-D-Bus-service.patch --- fwupd-2.0.8/debian/patches/0001-Always-build-the-D-Bus-service.patch 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/patches/0001-Always-build-the-D-Bus-service.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,109 +0,0 @@ -From 789ff94c60fb76e35eeb3f4a59580dff9e01d91d Mon Sep 17 00:00:00 2001 -From: aartoni -Date: Mon, 14 Apr 2025 00:01:50 +0200 -Subject: [PATCH] Always build the D-Bus service - -This allows the daemon to start automatically even where systemd is -unavailable. For context, without the D-Bus service file, fwupdmgr would -fail with the following error message: - -Failed to connect to daemon: GDBus.Error:org.freedesktop.DBus.Error.ServiceUnknown: The name org.freedesktop.fwupd was not provided by any .service files ---- - data/meson.build | 44 ++++++++++++--------------- - data/org.freedesktop.fwupd.service.in | 2 +- - 2 files changed, 21 insertions(+), 25 deletions(-) - -diff --git a/data/meson.build b/data/meson.build -index f361be2ae..e5af936f1 100644 ---- a/data/meson.build -+++ b/data/meson.build -@@ -59,12 +59,14 @@ if build_daemon - install_dir: join_paths(datadir, 'dbus-1', 'system.d') - ) - -+ build_conf = configuration_data() -+ build_conf.set('libexecdir', libexecdir) -+ - if libsystemd.found() -- con2 = configuration_data() -- con2.set('libexecdir', libexecdir) -- con2.set('bindir', bindir) -- con2.set('datadir', datadir) -- con2.set('localstatedir', localstatedir) -+ build_conf.set('bindir', bindir) -+ build_conf.set('datadir', datadir) -+ build_conf.set('localstatedir', localstatedir) -+ build_conf.set('systemd_service', 'SystemdService=fwupd.service') - rw_directories = [] - if allow_uefi_capsule - rw_directories += [ -@@ -154,14 +156,14 @@ if build_daemon - if not supported_build - dynamic_options += ['Environment="G_DEBUG=fatal-criticals"'] - endif -- con2.set('dynamic_options', '\n'.join(dynamic_options)) -- con2.set('motd_dir', motd_dir) -+ build_conf.set('dynamic_options', '\n'.join(dynamic_options)) -+ build_conf.set('motd_dir', motd_dir) - - # replace @dynamic_options@ - configure_file( - input: 'fwupd.service.in', - output: 'fwupd.service', -- configuration: con2, -+ configuration: build_conf, - install: true, - install_dir: systemdunitdir, - ) -@@ -170,27 +172,21 @@ if build_daemon - configure_file( - input: 'fwupd.shutdown.in', - output: 'fwupd.shutdown', -- configuration: con2, -+ configuration: build_conf, - install: true, - install_dir: systemd_shutdown_dir, - ) -+ else -+ build_conf.set('systemd_service', '') - endif - -- if libsystemd.found() -- con2 = configuration_data() -- con2.set('libexecdir', libexecdir) -- -- # replace @libexecdir@ -- configure_file( -- input: 'org.freedesktop.fwupd.service.in', -- output: 'org.freedesktop.fwupd.service', -- configuration: con2, -- install: true, -- install_dir: join_paths(datadir, -- 'dbus-1', -- 'system-services'), -- ) -- endif -+ configure_file( -+ input: 'org.freedesktop.fwupd.service.in', -+ output: 'org.freedesktop.fwupd.service', -+ configuration: build_conf, -+ install: true, -+ install_dir: join_paths(datadir, 'dbus-1', 'system-services'), -+ ) - - if launchctl.found() - con2 = configuration_data() -diff --git a/data/org.freedesktop.fwupd.service.in b/data/org.freedesktop.fwupd.service.in -index 53f517109..7e00259d0 100644 ---- a/data/org.freedesktop.fwupd.service.in -+++ b/data/org.freedesktop.fwupd.service.in -@@ -3,5 +3,5 @@ Name=org.freedesktop.fwupd - Documentation=https://fwupd.org/ - Exec=@libexecdir@/fwupd/fwupd - User=root --SystemdService=fwupd.service - AssumedAppArmorLabel=unconfined -+@systemd_service@ --- -2.43.0 - diff -Nru fwupd-2.0.8/debian/patches/0001-Fix-PK-and-KEK-enumeration-failure-on-some-systems.patch fwupd-2.0.20/debian/patches/0001-Fix-PK-and-KEK-enumeration-failure-on-some-systems.patch --- fwupd-2.0.8/debian/patches/0001-Fix-PK-and-KEK-enumeration-failure-on-some-systems.patch 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/patches/0001-Fix-PK-and-KEK-enumeration-failure-on-some-systems.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,97 +0,0 @@ -From ef641e4f7e5488238e5098c9955486ad6d55e94e Mon Sep 17 00:00:00 2001 -From: Richard Hughes -Date: Fri, 11 Apr 2025 16:11:48 +0100 -Subject: [PATCH] Fix PK and KEK enumeration failure on some systems - -Fixes https://github.com/fwupd/fwupd/issues/8677 ---- - libfwupdplugin/fu-efi-x509-device.c | 23 +++++++++++------------ - plugins/uefi-pk/fu-uefi-pk-device.c | 20 +++++++++----------- - 2 files changed, 20 insertions(+), 23 deletions(-) - -diff --git a/libfwupdplugin/fu-efi-x509-device.c b/libfwupdplugin/fu-efi-x509-device.c -index bc5fccf8e..83b523254 100644 ---- a/libfwupdplugin/fu-efi-x509-device.c -+++ b/libfwupdplugin/fu-efi-x509-device.c -@@ -30,6 +30,8 @@ fu_efi_x509_device_probe(FuDevice *device, GError **error) - { - FuEfiX509Device *self = FU_EFI_X509_DEVICE(device); - FuEfiX509DevicePrivate *priv = GET_PRIVATE(self); -+ const gchar *subject_name; -+ const gchar *subject_vendor; - - /* sanity check */ - if (priv->sig == NULL) { -@@ -37,22 +39,19 @@ fu_efi_x509_device_probe(FuDevice *device, GError **error) - return FALSE; - } - -- /* these have to exist */ -- fu_device_add_instance_strsafe(device, -- "VENDOR", -- fu_efi_x509_signature_get_subject_vendor(priv->sig)); -- fu_device_add_instance_strsafe(device, -- "NAME", -- fu_efi_x509_signature_get_subject_name(priv->sig)); -- if (!fu_device_build_instance_id(device, error, "UEFI", "VENDOR", "NAME", NULL)) -- return FALSE; -- fu_device_set_name(device, fu_efi_x509_signature_get_subject_name(priv->sig)); -- fu_device_set_vendor(device, fu_efi_x509_signature_get_subject_vendor(priv->sig)); -+ /* the O= key may not exist */ -+ subject_name = fu_efi_x509_signature_get_subject_name(priv->sig); -+ subject_vendor = fu_efi_x509_signature_get_subject_vendor(priv->sig); -+ fu_device_add_instance_strsafe(device, "VENDOR", subject_vendor); -+ fu_device_add_instance_strsafe(device, "NAME", subject_name); -+ fu_device_build_instance_id(device, NULL, "UEFI", "VENDOR", "NAME", NULL); -+ fu_device_set_name(device, subject_name != NULL ? subject_name : "Unknown"); -+ fu_device_set_vendor(device, subject_vendor != NULL ? subject_vendor : "Unknown"); - fu_device_set_version_raw(device, fu_firmware_get_version_raw(FU_FIRMWARE(priv->sig))); - fu_device_set_logical_id(device, fu_firmware_get_id(FU_FIRMWARE(priv->sig))); - fu_device_build_vendor_id(device, - "UEFI", -- fu_efi_x509_signature_get_subject_vendor(priv->sig)); -+ subject_vendor != NULL ? subject_vendor : "UNKNOWN"); - - /* success */ - fu_device_add_instance_strup(device, "CRT", fu_firmware_get_id(FU_FIRMWARE(priv->sig))); -diff --git a/plugins/uefi-pk/fu-uefi-pk-device.c b/plugins/uefi-pk/fu-uefi-pk-device.c -index c6ec116d2..c0b829927 100644 ---- a/plugins/uefi-pk/fu-uefi-pk-device.c -+++ b/plugins/uefi-pk/fu-uefi-pk-device.c -@@ -48,6 +48,9 @@ fu_uefi_pk_device_check(FuUefiPkDevice *self, const gchar *str, GError **error) - static gboolean - fu_uefi_pk_device_parse_certificate(FuUefiPkDevice *self, FuEfiX509Signature *sig, GError **error) - { -+ const gchar *subject_name = fu_efi_x509_signature_get_subject_name(sig); -+ const gchar *subject_vendor = fu_efi_x509_signature_get_subject_vendor(sig); -+ - /* look in issuer and subject */ - if (fu_efi_x509_signature_get_issuer(sig) != NULL) { - if (!fu_uefi_pk_device_check(self, fu_efi_x509_signature_get_issuer(sig), error)) -@@ -58,17 +61,12 @@ fu_uefi_pk_device_parse_certificate(FuUefiPkDevice *self, FuEfiX509Signature *si - return FALSE; - } - -- /* these have to exist */ -- fu_device_add_instance_strsafe(FU_DEVICE(self), -- "VENDOR", -- fu_efi_x509_signature_get_subject_vendor(sig)); -- fu_device_add_instance_strsafe(FU_DEVICE(self), -- "NAME", -- fu_efi_x509_signature_get_subject_name(sig)); -- if (!fu_device_build_instance_id(FU_DEVICE(self), error, "UEFI", "VENDOR", "NAME", NULL)) -- return FALSE; -- fu_device_set_name(FU_DEVICE(self), fu_efi_x509_signature_get_subject_name(sig)); -- fu_device_set_vendor(FU_DEVICE(self), fu_efi_x509_signature_get_subject_vendor(sig)); -+ /* the O= key may not exist */ -+ fu_device_add_instance_strsafe(FU_DEVICE(self), "VENDOR", subject_vendor); -+ fu_device_add_instance_strsafe(FU_DEVICE(self), "NAME", subject_name); -+ fu_device_build_instance_id(FU_DEVICE(self), NULL, "UEFI", "VENDOR", "NAME", NULL); -+ fu_device_set_name(FU_DEVICE(self), subject_name != NULL ? subject_name : "Unknown"); -+ fu_device_set_vendor(FU_DEVICE(self), subject_vendor != NULL ? subject_vendor : "Unknown"); - fu_device_set_version_raw(FU_DEVICE(self), fu_firmware_get_version_raw(FU_FIRMWARE(sig))); - - /* success, certificate was parsed correctly */ --- -2.43.0 - diff -Nru fwupd-2.0.8/debian/patches/0001-Fix-a-crash-when-paring-uevents-that-are-not-KEY-VAL.patch fwupd-2.0.20/debian/patches/0001-Fix-a-crash-when-paring-uevents-that-are-not-KEY-VAL.patch --- fwupd-2.0.8/debian/patches/0001-Fix-a-crash-when-paring-uevents-that-are-not-KEY-VAL.patch 2025-04-18 04:37:13.000000000 +0000 +++ fwupd-2.0.20/debian/patches/0001-Fix-a-crash-when-paring-uevents-that-are-not-KEY-VAL.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,101 +0,0 @@ -From c2a1532e21ad563fa343c4c09d912c4b96e4abc4 Mon Sep 17 00:00:00 2001 -From: Richard Hughes -Date: Thu, 17 Apr 2025 15:57:06 +0100 -Subject: [PATCH] Fix a crash when paring uevents that are not KEY=VALUE - -Fixes https://github.com/fwupd/fwupd/issues/8707 ---- - libfwupdplugin/fu-self-test.c | 16 ++++++++++++++++ - libfwupdplugin/fu-udev-device.c | 11 +++++++---- - libfwupdplugin/tests/meson.build | 1 + - libfwupdplugin/tests/uevent | 3 +++ - 4 files changed, 27 insertions(+), 4 deletions(-) - create mode 100644 libfwupdplugin/tests/uevent - -diff --git a/libfwupdplugin/fu-self-test.c b/libfwupdplugin/fu-self-test.c -index 5fba24d9f..0876a8843 100644 ---- a/libfwupdplugin/fu-self-test.c -+++ b/libfwupdplugin/fu-self-test.c -@@ -38,6 +38,7 @@ - #include "fu-self-test-struct.h" - #include "fu-smbios-private.h" - #include "fu-test-device.h" -+#include "fu-udev-device-private.h" - #include "fu-volume-private.h" - - static GMainLoop *_test_loop = NULL; -@@ -6362,6 +6363,20 @@ fu_plugin_efi_signature_list_func(void) - g_assert_cmpint(fu_firmware_get_version_raw(FU_FIRMWARE(sig)), ==, 2024); - } - -+static void -+fu_device_udev_func(void) -+{ -+ g_autofree gchar *prop = NULL; -+ g_autofree gchar *sysfs_path = g_test_build_filename(G_TEST_DIST, "tests", NULL); -+ g_autoptr(FuContext) ctx = fu_context_new(); -+ g_autoptr(FuUdevDevice) udev_device = fu_udev_device_new(ctx, sysfs_path); -+ g_autoptr(GError) error = NULL; -+ -+ prop = fu_udev_device_read_property(udev_device, "MODALIAS", &error); -+ g_assert_no_error(error); -+ g_assert_cmpstr(prop, ==, "hdaudio:v10EC0298r00100103a01"); -+} -+ - static void - fu_cab_checksum_func(void) - { -@@ -6984,6 +6999,7 @@ main(int argc, char **argv) - 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/device", fu_device_func); -+ g_test_add_func("/fwupd/device{udev}", fu_device_udev_func); - g_test_add_func("/fwupd/device{event}", fu_device_event_func); - g_test_add_func("/fwupd/device{event-uncompressed}", fu_device_event_uncompressed_func); - g_test_add_func("/fwupd/device{event-donor}", fu_device_event_donor_func); -diff --git a/libfwupdplugin/fu-udev-device.c b/libfwupdplugin/fu-udev-device.c -index 67d07a712..96b7d079e 100644 ---- a/libfwupdplugin/fu-udev-device.c -+++ b/libfwupdplugin/fu-udev-device.c -@@ -2131,10 +2131,13 @@ fu_udev_device_read_property(FuUdevDevice *self, const gchar *key, GError **erro - return NULL; - uevent_lines = g_strsplit(str, "\n", -1); - for (guint i = 0; uevent_lines[i] != NULL; i++) { -- g_autofree gchar **kvs = g_strsplit(uevent_lines[i], "=", 2); -- g_hash_table_insert(priv->properties, -- g_steal_pointer(&kvs[0]), -- g_steal_pointer(&kvs[1])); -+ /* only split KEY=VALUE */ -+ if (g_strstr_len(uevent_lines[i], -1, "=") != NULL) { -+ g_autofree gchar **kvs = g_strsplit(uevent_lines[i], "=", 2); -+ g_hash_table_insert(priv->properties, -+ g_steal_pointer(&kvs[0]), -+ g_steal_pointer(&kvs[1])); -+ } - } - priv->properties_valid = TRUE; - } -diff --git a/libfwupdplugin/tests/meson.build b/libfwupdplugin/tests/meson.build -index d8106e891..3c00c12c0 100644 ---- a/libfwupdplugin/tests/meson.build -+++ b/libfwupdplugin/tests/meson.build -@@ -76,6 +76,7 @@ install_data([ - 'pci.ids', - 'pnp.ids', - 'usb.ids', -+ 'uevent', - ], - install_dir: join_paths(installed_test_datadir, 'tests') - ) -diff --git a/libfwupdplugin/tests/uevent b/libfwupdplugin/tests/uevent -new file mode 100644 -index 000000000..6fe095b19 ---- /dev/null -+++ b/libfwupdplugin/tests/uevent -@@ -0,0 +1,3 @@ -+DRIVER=snd_hda_codec_realtek -+MODALIAS=hdaudio:v10EC0298r00100103a01 -+ --- -2.43.0 - diff -Nru fwupd-2.0.8/debian/patches/0001-Fix-the-parents-and-grandparents-for-KEK-and-DBX-dev.patch fwupd-2.0.20/debian/patches/0001-Fix-the-parents-and-grandparents-for-KEK-and-DBX-dev.patch --- fwupd-2.0.8/debian/patches/0001-Fix-the-parents-and-grandparents-for-KEK-and-DBX-dev.patch 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/patches/0001-Fix-the-parents-and-grandparents-for-KEK-and-DBX-dev.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -From d1ca6e46cd94ea9108709386f066475f29b4dfa4 Mon Sep 17 00:00:00 2001 -From: Mario Limonciello -Date: Fri, 11 Apr 2025 12:42:27 -0500 -Subject: [PATCH] Fix the parents and grandparents for KEK and DBX devices - ---- - plugins/uefi-capsule/README.md | 3 +-- - plugins/uefi-db/fu-uefi-db-device.c | 2 +- - plugins/uefi-kek/fu-uefi-kek-device.c | 2 +- - plugins/uefi-pk/fu-uefi-pk-device.c | 2 +- - 4 files changed, 4 insertions(+), 5 deletions(-) - -diff --git a/plugins/uefi-capsule/README.md b/plugins/uefi-capsule/README.md -index a5db53f2b..425f88da2 100644 ---- a/plugins/uefi-capsule/README.md -+++ b/plugins/uefi-capsule/README.md -@@ -109,8 +109,7 @@ required. - - ## GUID Generation - --These devices use the UEFI GUID as provided in the ESRT. Additionally, for the --system device the `main-system-firmware` internal flag is also added. -+These devices use the UEFI GUID as provided in the ESRT. - - For compatibility with Windows 10, the plugin also adds GUIDs of the form - `UEFI\RES_{$(esrt)}`. -diff --git a/plugins/uefi-db/fu-uefi-db-device.c b/plugins/uefi-db/fu-uefi-db-device.c -index e5b009eac..2868e5cd1 100644 ---- a/plugins/uefi-db/fu-uefi-db-device.c -+++ b/plugins/uefi-db/fu-uefi-db-device.c -@@ -146,7 +146,7 @@ fu_uefi_db_device_init(FuUefiDbDevice *self) - { - fu_device_set_physical_id(FU_DEVICE(self), "db"); - fu_device_set_name(FU_DEVICE(self), "UEFI Signature Database"); -- fu_device_add_parent_guid(FU_DEVICE(self), "main-system-firmware"); -+ fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); - fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_EFI_SIGNATURE_LIST); - fu_device_add_icon(FU_DEVICE(self), "application-certificate"); - } -diff --git a/plugins/uefi-kek/fu-uefi-kek-device.c b/plugins/uefi-kek/fu-uefi-kek-device.c -index b94fc7347..c3b892b8d 100644 ---- a/plugins/uefi-kek/fu-uefi-kek-device.c -+++ b/plugins/uefi-kek/fu-uefi-kek-device.c -@@ -103,7 +103,7 @@ fu_uefi_kek_device_init(FuUefiKekDevice *self) - { - fu_device_set_physical_id(FU_DEVICE(self), "KEK"); - fu_device_set_name(FU_DEVICE(self), "UEFI Key Exchange Key"); -- fu_device_add_parent_guid(FU_DEVICE(self), "main-system-firmware"); -+ fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); - fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_EFI_SIGNATURE_LIST); - fu_device_add_icon(FU_DEVICE(self), "application-certificate"); - } -diff --git a/plugins/uefi-pk/fu-uefi-pk-device.c b/plugins/uefi-pk/fu-uefi-pk-device.c -index c0b829927..9a853b58f 100644 ---- a/plugins/uefi-pk/fu-uefi-pk-device.c -+++ b/plugins/uefi-pk/fu-uefi-pk-device.c -@@ -142,7 +142,7 @@ fu_uefi_pk_device_init(FuUefiPkDevice *self) - { - fu_device_set_physical_id(FU_DEVICE(self), "pk"); - fu_device_set_summary(FU_DEVICE(self), "UEFI Platform Key"); -- fu_device_add_parent_guid(FU_DEVICE(self), "main-system-firmware"); -+ fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); - fu_device_add_icon(FU_DEVICE(self), "application-certificate"); - fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_EFI_SIGNATURE_LIST); - fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER); --- -2.43.0 - diff -Nru fwupd-2.0.8/debian/patches/0001-trivial-Fix-the-debian-i686-build.patch fwupd-2.0.20/debian/patches/0001-trivial-Fix-the-debian-i686-build.patch --- fwupd-2.0.8/debian/patches/0001-trivial-Fix-the-debian-i686-build.patch 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/patches/0001-trivial-Fix-the-debian-i686-build.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -From d73f082ba00b671e448114f3a3fea2eb57eaf0f7 Mon Sep 17 00:00:00 2001 -From: Richard Hughes -Date: Wed, 16 Apr 2025 10:18:53 +0100 -Subject: [PATCH] trivial: Fix the debian i686 build - ---- - plugins/modem-manager/fu-dfota-updater.c | 4 +++- - 1 file changed, 3 insertions(+), 1 deletion(-) - -diff --git a/plugins/modem-manager/fu-dfota-updater.c b/plugins/modem-manager/fu-dfota-updater.c -index fba309dd6..b6b8bf4b9 100644 ---- a/plugins/modem-manager/fu-dfota-updater.c -+++ b/plugins/modem-manager/fu-dfota-updater.c -@@ -110,6 +110,7 @@ fu_dfota_updater_parse_upload_result(FuDfotaUpdater *self, - gsize *size, - GError **error) - { -+ guint64 val = 0; - g_autoptr(GBytes) result_bytes = NULL; - g_autoptr(GRegex) result_regex = NULL; - g_autoptr(GMatchInfo) match_info = NULL; -@@ -162,8 +163,9 @@ fu_dfota_updater_parse_upload_result(FuDfotaUpdater *self, - - g_debug("parsed checksum '%s' and size '%s'", checksum_match, size_match); - -- if (!g_ascii_string_to_unsigned(size_match, 10, 0, G_MAXSIZE, size, error)) -+ if (!g_ascii_string_to_unsigned(size_match, 10, 0, G_MAXSIZE, &val, error)) - return FALSE; -+ *size = (gsize)val; - - *checksum = g_steal_pointer(&checksum_match); - --- -2.43.0 - diff -Nru fwupd-2.0.8/debian/patches/0001-trivial-only-use-google-pixel-fastboot-device-on-uns.patch fwupd-2.0.20/debian/patches/0001-trivial-only-use-google-pixel-fastboot-device-on-uns.patch --- fwupd-2.0.8/debian/patches/0001-trivial-only-use-google-pixel-fastboot-device-on-uns.patch 2025-04-27 19:00:38.000000000 +0000 +++ fwupd-2.0.20/debian/patches/0001-trivial-only-use-google-pixel-fastboot-device-on-uns.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,59 +0,0 @@ -From 4650003e3da75c66470eb73c3727e57701682084 Mon Sep 17 00:00:00 2001 -From: Mario Limonciello -Date: Sat, 26 Apr 2025 20:48:17 -0500 -Subject: [PATCH] trivial: only use google pixel fastboot device on unsupported - builds - -This is marked as a pixel3a, but it actually applies to a pixel 9 -as well. We don't plan to be flashing pixel devices with fwupd -and this could cause problems with things like /usr/bin/fastboot -if fwupd takes the device. - -As it's only really useful for test coverage, only install the quirk -on unsupported builds so that distros don't pick it up. ---- - plugins/fastboot/ci.quirk | 3 +++ - plugins/fastboot/fastboot.quirk | 4 ---- - plugins/fastboot/meson.build | 7 +++++-- - 3 files changed, 8 insertions(+), 6 deletions(-) - create mode 100644 plugins/fastboot/ci.quirk - -diff --git a/plugins/fastboot/ci.quirk b/plugins/fastboot/ci.quirk -new file mode 100644 -index 000000000..86a897064 ---- /dev/null -+++ b/plugins/fastboot/ci.quirk -@@ -0,0 +1,3 @@ -+# Google Pixel 3a -+[USB\VID_18D1&PID_4EE0] -+Plugin = fastboot -diff --git a/plugins/fastboot/fastboot.quirk b/plugins/fastboot/fastboot.quirk -index 5170c8de9..1b3abf4a1 100644 ---- a/plugins/fastboot/fastboot.quirk -+++ b/plugins/fastboot/fastboot.quirk -@@ -39,7 +39,3 @@ CounterpartGuid = USB\VID_0489&PID_E0B4 - Plugin = fastboot - Summary = Foxconn T77w968/eSIM LTE modem (fastboot) - CounterpartGuid = USB\VID_0489&PID_E0B5 -- --# Google Pixel 3a --[USB\VID_18D1&PID_4EE0] --Plugin = fastboot -diff --git a/plugins/fastboot/meson.build b/plugins/fastboot/meson.build -index ddb8aa62c..679e661be 100644 ---- a/plugins/fastboot/meson.build -+++ b/plugins/fastboot/meson.build -@@ -13,5 +13,8 @@ plugin_builtins += static_library('fu_plugin_fastboot', - dependencies: plugin_deps, - ) - --enumeration_data += files('tests/fastboot-google-sargo-setup.json') --device_tests += files('tests/fastboot-google-sargo.json') -+if not supported_build -+ plugin_quirks += files('ci.quirk') -+ enumeration_data += files('tests/fastboot-google-sargo-setup.json') -+ device_tests += files('tests/fastboot-google-sargo.json') -+endif --- -2.43.0 - diff -Nru fwupd-2.0.8/debian/patches/0001-trivial-require-libusb-1.0.27.patch fwupd-2.0.20/debian/patches/0001-trivial-require-libusb-1.0.27.patch --- fwupd-2.0.8/debian/patches/0001-trivial-require-libusb-1.0.27.patch 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/patches/0001-trivial-require-libusb-1.0.27.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,28 +0,0 @@ -From 15a1adfeb7c4010f38972fb0eb4ce2d2d040d9c6 Mon Sep 17 00:00:00 2001 -From: Mario Limonciello -Date: Wed, 16 Apr 2025 09:54:37 -0500 -Subject: [PATCH] trivial: require libusb 1.0.27 - -This has libusb_init_context() support needed for binding to udev. - -Closes: https://github.com/fwupd/fwupd/issues/8701 ---- - meson.build | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/meson.build b/meson.build -index a3995479a..250e4307c 100644 ---- a/meson.build -+++ b/meson.build -@@ -242,7 +242,7 @@ endif - conf.set_quoted('FU_LVFS_METADATA_FORMAT', lvfs_metadata_format) - - # FreeBSD is missing some libusb symbols --libusb = dependency('libusb-1.0', version : '>= 0.1.12') -+libusb = dependency('libusb-1.0', version : '>= 0.1.27') - conf.set_quoted('LIBUSB_VERSION', libusb.version()) - if cc.has_header_symbol('libusb.h', 'libusb_set_option', dependencies: libusb) - conf.set('HAVE_LIBUSB_SET_OPTION', '1') --- -2.43.0 - diff -Nru fwupd-2.0.8/debian/patches/flaky-p2p.patch fwupd-2.0.20/debian/patches/flaky-p2p.patch --- fwupd-2.0.8/debian/patches/flaky-p2p.patch 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/patches/flaky-p2p.patch 2026-03-10 21:22:38.000000000 +0000 @@ -1,9 +1,13 @@ +Description: Skip flaky tests +Forwarded: not-needed +Author: Mario Limonciello + Index: fwupd/data/tests/fwupdmgr-p2p.sh =================================================================== --- fwupd.orig/data/tests/fwupdmgr-p2p.sh +++ fwupd/data/tests/fwupdmgr-p2p.sh -@@ -3,6 +3,8 @@ set -e - +@@ -3,6 +3,8 @@ + exec 0>/dev/null exec 2>&1 +if [ -z "${DEB_ALLOW_FLAKY_TESTS}" ]; then exit 0; fi diff -Nru fwupd-2.0.8/debian/patches/series fwupd-2.0.20/debian/patches/series --- fwupd-2.0.8/debian/patches/series 2025-04-27 19:00:38.000000000 +0000 +++ fwupd-2.0.20/debian/patches/series 2026-03-10 21:22:38.000000000 +0000 @@ -1,8 +1 @@ flaky-p2p.patch -0001-trivial-require-libusb-1.0.27.patch -0001-Always-build-the-D-Bus-service.patch -0001-trivial-Fix-the-debian-i686-build.patch -0001-Fix-PK-and-KEK-enumeration-failure-on-some-systems.patch -0001-Fix-the-parents-and-grandparents-for-KEK-and-DBX-dev.patch -0001-Fix-a-crash-when-paring-uevents-that-are-not-KEY-VAL.patch -0001-trivial-only-use-google-pixel-fastboot-device-on-uns.patch diff -Nru fwupd-2.0.8/debian/rules fwupd-2.0.20/debian/rules --- fwupd-2.0.8/debian/rules 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/rules 2026-03-10 21:24:17.000000000 +0000 @@ -34,6 +34,13 @@ CONFARGS += -Dplugin_modem_manager=enabled +ifeq (yes,$(shell dpkg-vendor --derives-from Ubuntu && echo yes)) + CONFARGS += -Defi_os_dir=ubuntu -Dpassim=disabled + +else ifeq (yes,$(shell dpkg-vendor --derives-from Debian && echo yes)) + CONFARGS += -Defi_os_dir=debian +endif + %: dh $@ --with gir diff -Nru fwupd-2.0.8/debian/tests/ci fwupd-2.0.20/debian/tests/ci --- fwupd-2.0.8/debian/tests/ci 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/tests/ci 2026-03-10 21:22:38.000000000 +0000 @@ -4,9 +4,9 @@ modprobe mtdram || true fwupdtool enable-test-devices fwupdtool modify-config VerboseDomains "*" -sed "s,ConditionVirtualization=.*,," \ - /lib/systemd/system/fwupd.service > \ - /etc/systemd/system/fwupd.service +sed "s,ConditionVirtualization=.*,," \ + /lib/systemd/system/fwupd.service > \ + /etc/systemd/system/fwupd.service systemctl daemon-reload # run the tests -gnome-desktop-testing-runner --timeout=600 fwupd +gnome-desktop-testing-runner --timeout=1200 fwupd diff -Nru fwupd-2.0.8/debian/tests/ci-flaky fwupd-2.0.20/debian/tests/ci-flaky --- fwupd-2.0.8/debian/tests/ci-flaky 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/tests/ci-flaky 2026-03-10 21:22:38.000000000 +0000 @@ -4,9 +4,9 @@ modprobe mtdram || true fwupdtool enable-test-devices fwupdtool modify-config VerboseDomains "*" -sed "s,ConditionVirtualization=.*,," \ - /lib/systemd/system/fwupd.service > \ - /etc/systemd/system/fwupd.service +sed "s,ConditionVirtualization=.*,," \ + /lib/systemd/system/fwupd.service > \ + /etc/systemd/system/fwupd.service systemctl daemon-reload # run the tests -gnome-desktop-testing-runner --timeout=600 fwupd +gnome-desktop-testing-runner --timeout=1200 fwupd diff -Nru fwupd-2.0.8/debian/tests/control fwupd-2.0.20/debian/tests/control --- fwupd-2.0.8/debian/tests/control 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/tests/control 2026-03-10 21:22:38.000000000 +0000 @@ -1,9 +1,11 @@ Tests: ci +Depends: kmod, jq, fwupd-tests Restrictions: needs-root Tests: ci-flaky +Depends: kmod, jq, fwupd-tests Restrictions: needs-root, flaky Tests: libfwupd-dev -Depends: build-essential, libfwupd-dev, pkg-config +Depends: build-essential, libfwupd-dev, pkgconf Restrictions: allow-stderr, superficial diff -Nru fwupd-2.0.8/debian/tests/libfwupd-dev fwupd-2.0.20/debian/tests/libfwupd-dev --- fwupd-2.0.8/debian/tests/libfwupd-dev 2025-04-18 04:36:38.000000000 +0000 +++ fwupd-2.0.20/debian/tests/libfwupd-dev 2026-03-10 21:22:38.000000000 +0000 @@ -19,7 +19,7 @@ cd "$WORKDIR" -cat > trivial.c <<'EOF' +cat >trivial.c <<'EOF' #undef NDEBUG #include diff -Nru fwupd-2.0.8/docs/bios-settings.md fwupd-2.0.20/docs/bios-settings.md --- fwupd-2.0.8/docs/bios-settings.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/bios-settings.md 2026-02-26 11:36:18.000000000 +0000 @@ -162,7 +162,7 @@ `fwupd` has the ability to enforce the BIOS settings policy of a system administrator. To use this feature, create a json payload using `fwupdmgr get-bios-setting --json` that reflects the settings you would like to see enforced. -Then copy this payload into `/etc/fwupd/bios-settings.d` with a filename ending in `.json`. The next time that the fwupd daemon is started (such as a system bootup) it will ensure that all BIOS settings are programed to your desired values. It will also mark those settings as read-only so no fwupd clients will be able to modify them. +Then copy this payload into `/etc/fwupd/bios-settings.d` with a filename ending in `.json`. The next time that the fwupd daemon is started (such as a system boot-up) it will ensure that all BIOS settings are programed to your desired values. It will also mark those settings as read-only so no fwupd clients will be able to modify them. This *does not* stop the kernel firmware-attributes API from working. So a determined user with appropriate permissions would be able to modify settings from the kernel API directly, but they would be changed again on fwupd daemon startup. diff -Nru fwupd-2.0.8/docs/building.md fwupd-2.0.20/docs/building.md --- fwupd-2.0.8/docs/building.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/building.md 2026-02-26 11:36:18.000000000 +0000 @@ -2,7 +2,7 @@ title: Building & Debugging fwupd --- -These instructons below can either be used by the silicon vendor, or the consulting company to debug existing and new plugins. Sometimes new hardware is only supported in the development version of fwupd which may not even be available as a Snap or Flatpak yet. +These instructions below can either be used by the silicon vendor, or the consulting company to debug existing and new plugins. Sometimes new hardware is only supported in the development version of fwupd which may not even be available as a Snap or Flatpak yet. ## Prerequisites @@ -174,6 +174,20 @@ This will send the firmware archive from the locally built `fwupdmgr` to the locally built daemon using a file descriptor, which will call the new plugin code with the firmware blob in the archive. The daemon terminal will also show lots of useful debugging during this process. +## Committing Code + +The `./contrib/setup` script automatically sets up [pre-commit](https://pre-commit.com/) to run many different source tools when adding code with `git commit` -- for instance: + +* Reformatting the code to match the project guidelines, using standard language specific linting tools like `black`, `shfmt` and `clang-format` +* Shell scripts are verified using `shellcheck` +* Checking if all the user-visible commands in `fwupdmgr` are documented in `fwupdmgr.md` +* Checking if the correct headers are being used for the plugin +* Checking for common GError and fwupd anti-patterns and other common design problems +* And many more! + +If the `check-source.py` checks fail incorrectly, you can use a `/* nocheck:token */` to ignore the failure if you are 100% sure if the source checker is being overzealous. +In this case you should also file an issue against fwupd with a minimal reproducer so we can fix the tool for future commits. + ## Using Visual Studio code to build and test During build time a set of tasks will have been created for use with Visual Studio Code. diff -Nru fwupd-2.0.8/docs/env.md fwupd-2.0.20/docs/env.md --- fwupd-2.0.8/docs/env.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/env.md 2026-02-26 11:36:18.000000000 +0000 @@ -19,6 +19,7 @@ * `FWUPD_EFIVARS` can be set to `dummy` to emulate an EFI variable store * `FWUPD_FUZZER_RUNNING` if the firmware format is being fuzzed * `FWUPD_POLKIT_NOCHECK` if we should not check for polkit policies to be installed +* `FWUPD_IGNORE_NETWORK_REACHABLE` if we should skip network connectivity tests * standard glibc variables like `LANG` are also honored for CLI tools that are translated * libcurl respects the session proxy, e.g. `http_proxy`, `all_proxy`, `sftp_proxy` and `no_proxy` @@ -31,16 +32,14 @@ ## Self Tests * `CI_NETWORK` if CI is running with network access -* `TPM_SERVER_RUNNING` if an emulated TPM is running +* `TPM2TOOLS_TCTI` if a TPM2.0 TPM is available (even if emulated) * `UMOCKDEV_DIR` if set, running under umockdev Other variables, include: * `FWUPD_DELL_FAKE_SMBIOS` if set, use fake SMBIOS information for tests -* `FWUPD_FORCE_TPM2` ignores a TPM 1.2 device detected in the TPM self tests * `FWUPD_REDFISH_SELF_TEST` if set, do destructive tests on the actual device BMC * `FWUPD_REDFISH_SMBIOS_DATA` use this filename to emulate a specific SMBIOS blob -* `FWUPD_SOLOKEY_EMULATE` emulates a fake device for testing * `FWUPD_UEFI_TEST` used by the UEFI plugins to disable specific sanity checks during self tests * `FWUPD_MACHINE_ID` used by the tests to set a predictable hash normally loaded from `/etc/machine-id` diff -Nru fwupd-2.0.8/docs/fwupd-remotes.d.md fwupd-2.0.20/docs/fwupd-remotes.d.md --- fwupd-2.0.8/docs/fwupd-remotes.d.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/fwupd-remotes.d.md 2026-02-26 11:36:18.000000000 +0000 @@ -60,6 +60,12 @@ legacy metadata and `.xml.xz` for the more modern format. Only prefixes of `http://`, `https://` and `file://` are supported here. +**FirmwareBaseURI=** + + The optional base URL of the cabinet archives to download. + If not specified the `MetadataURI` base URL is used. + Only prefixes of `http://`, `https://` and `file://` are supported here. + **ApprovalRequired=false** If set to `true` then only releases allow-listed with `fwupdmgr set-approved-firmware` will show @@ -79,6 +85,16 @@ If `true`, automatically sent HSI platform security reports when running `fwupdmgr security`. +**NoPhasedUpdates=false** + + If `true`, disregard the requirement check for random client phased deployment. + + A systems eligibility to a phased update is determined by seeding random number generator + with /etc/machine-id, the archive filename, and the remote cache mtime. If the seed divides + by the metadata-provided `phased_update` value with no remainder then the release is considered. + This also implies that the seed will be different on a different machine, for a different update + or if the metadata is refreshed. + **OrderBefore=** This remote will be ordered before any remotes listed here, using commas as the delimiter. diff -Nru fwupd-2.0.8/docs/fwupd.conf.md fwupd-2.0.20/docs/fwupd.conf.md --- fwupd-2.0.8/docs/fwupd.conf.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/fwupd.conf.md 2026-02-26 11:36:18.000000000 +0000 @@ -103,6 +103,16 @@ child, parent or sibling. This is not recommended for production systems, although it may be useful for firmware development. +**IgnoreEfivarsFreeSpace={{IgnoreEfivarsFreeSpace}}** + + Ignore the efivars free space requirement for db, dbx, KEK and PK updates. + This may be required on Linux kernels older than 6.4, or where the hardware does not support UEFI `RT->QueryVariableInfo`. + +**OnlyTrustPostQuantumSignatures={{OnlyTrustPostQuantumSignatures}}** + + Only trust post-quantum cryptographic signatures. + This is only recommended when you are sure the firmware contains a valid PQ signature. + **OnlyTrusted={{OnlyTrusted}}** Only support installing firmware signed with a trusted key. @@ -139,6 +149,12 @@ Set the preferred location used for the EFI system partition (ESP) path. This is typically used if UDisks was not able to automatically identify the location for any reason. +**RequireImmutableEnumeration={{RequireImmutableEnumeration}}** + + Don't allow fwupd plugins to directly interact with devices during probe or setup stages. + The kernel should provide all device information in sysfs files or udev properties. + This will block some plugins from working. + **Manufacturer=** **ProductName=** diff -Nru fwupd-2.0.8/docs/fwupdplugin.toml.in fwupd-2.0.20/docs/fwupdplugin.toml.in --- fwupd-2.0.8/docs/fwupdplugin.toml.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/fwupdplugin.toml.in 2026-02-26 11:36:18.000000000 +0000 @@ -45,6 +45,7 @@ "building.md", "tutorial.md", "hsi.md", + "uefi-db.md", "device-emulation.md", "ds20.md", "hwids.md", diff -Nru fwupd-2.0.8/docs/hsi-tests.d/meson.build fwupd-2.0.20/docs/hsi-tests.d/meson.build --- fwupd-2.0.8/docs/hsi-tests.d/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,7 +1,11 @@ -hsi_test_jsons = files([ +hsi_test_jsons = files( 'org.fwupd.hsi.Amd.RollbackProtection.json', + 'org.fwupd.hsi.Amd.SmmLocked.json', 'org.fwupd.hsi.Amd.SpiReplayProtection.json', 'org.fwupd.hsi.Amd.SpiWriteProtection.json', + 'org.fwupd.hsi.Bios.RollbackProtection.json', + 'org.fwupd.hsi.Cet.Active.json', + 'org.fwupd.hsi.Cet.Enabled.json', 'org.fwupd.hsi.EncryptedRam.json', 'org.fwupd.hsi.IntelBootguard.Acm.json', 'org.fwupd.hsi.IntelBootguard.Enabled.json', @@ -9,9 +13,6 @@ 'org.fwupd.hsi.IntelBootguard.Policy.json', 'org.fwupd.hsi.IntelBootguard.Verified.json', 'org.fwupd.hsi.IntelGds.json', - 'org.fwupd.hsi.Cet.Enabled.json', - 'org.fwupd.hsi.Cet.Active.json', - 'org.fwupd.hsi.Smap.json', 'org.fwupd.hsi.Iommu.json', 'org.fwupd.hsi.Kernel.Lockdown.json', 'org.fwupd.hsi.Kernel.Tainted.json', @@ -23,6 +24,7 @@ 'org.fwupd.hsi.PlatformDebugLocked.json', 'org.fwupd.hsi.PlatformFused.json', 'org.fwupd.hsi.PrebootDma.json', + 'org.fwupd.hsi.Smap.json', 'org.fwupd.hsi.Spi.Bioswe.json', 'org.fwupd.hsi.Spi.Ble.json', 'org.fwupd.hsi.Spi.Descriptor.json', @@ -34,11 +36,9 @@ 'org.fwupd.hsi.Tpm.ReconstructionPcr0.json', 'org.fwupd.hsi.Tpm.Version20.json', 'org.fwupd.hsi.Uefi.BootserviceVars.json', + 'org.fwupd.hsi.Uefi.Db.json', 'org.fwupd.hsi.Uefi.MemoryProtection.json', 'org.fwupd.hsi.Uefi.Pk.json', - 'org.fwupd.hsi.Uefi.Db.json', 'org.fwupd.hsi.Uefi.SecureBoot.json', - 'org.fwupd.hsi.Bios.RollbackProtection.json', - 'org.fwupd.hsi.Amd.SmmLocked.json', -]) +) diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Amd.RollbackProtection.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Amd.RollbackProtection.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Amd.RollbackProtection.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Amd.RollbackProtection.json 2026-02-26 11:36:18.000000000 +0000 @@ -20,6 +20,7 @@ "enabled": "rollback protection enabled" }, "hsi-level": 4, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://www.psacertified.org/blog/anti-rollback-explained/": "Rollback protection", "https://www.amd.com/en/technologies/pro-security": "AMD Secure Processor", diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Amd.SmmLocked.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Amd.SmmLocked.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Amd.SmmLocked.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Amd.SmmLocked.json 2026-02-26 11:36:18.000000000 +0000 @@ -16,6 +16,7 @@ "success-results": { "locked": "SMM is locked down" }, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://www.amd.com/en/resources/product-security/bulletin/amd-sb-7014.html": "AMD Sinkclose" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Amd.SpiReplayProtection.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Amd.SpiReplayProtection.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Amd.SpiReplayProtection.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Amd.SpiReplayProtection.json 2026-02-26 11:36:18.000000000 +0000 @@ -1,8 +1,8 @@ { "id": "org.fwupd.hsi.Amd.SpiReplayProtection", - "name": "AMD SPI Write protections", + "name": "AMD SPI Replay protections", "description": [ - "SOCs may enforce control of the SPI bus to prevent writes other than by verified entities." + "SOCs may include support for replay-protected monotonic counters to prevent replay attacks." ], "failure-impact": [ "SOCs without this feature may be attacked by an attacker modifying the SPI." @@ -13,7 +13,8 @@ "success-results": { "enabled": "SPI protections enabled" }, - "hsi-level": 2, + "hsi-level": 3, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "requires": [ "CPUID\\VID_AuthenticAMD" ], diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Amd.SpiWriteProtection.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Amd.SpiWriteProtection.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Amd.SpiWriteProtection.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Amd.SpiWriteProtection.json 2026-02-26 11:36:18.000000000 +0000 @@ -1,8 +1,8 @@ { "id": "org.fwupd.hsi.Amd.SpiWriteProtection", - "name": "AMD SPI Replay protections", + "name": "AMD SPI Write protections", "description": [ - "SOCs may include support for replay-protected monotonic counters to prevent replay attacks." + "SOCs may enforce control of the SPI bus to prevent writes other than by verified entities." ], "failure-impact": [ "SOCs without this feature may be attacked by an attacker modifying the SPI." @@ -13,7 +13,8 @@ "success-results": { "enabled": "SPI protections enabled" }, - "hsi-level": 3, + "hsi-level": 2, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "requires": [ "CPUID\\VID_AuthenticAMD" ], diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Bios.CapsuleUpdates.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Bios.CapsuleUpdates.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Bios.CapsuleUpdates.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Bios.CapsuleUpdates.json 2026-02-26 11:36:18.000000000 +0000 @@ -13,6 +13,7 @@ "success-results": { "enabled": "BIOS capsule updates enabled" }, + "resolution": "Turn on 'firmware updates' in the UEFI firmware setup.", "hsi-level": 1, "fwupd-version": "1.9.6" } diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Bios.RollbackProtection.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Bios.RollbackProtection.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Bios.RollbackProtection.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Bios.RollbackProtection.json 2026-02-26 11:36:18.000000000 +0000 @@ -14,6 +14,7 @@ "enabled": "rollback protection enabled" }, "hsi-level": 2, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://www.psacertified.org/blog/anti-rollback-explained/": "Rollback protection" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Cet.Active.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Cet.Active.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Cet.Active.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Cet.Active.json 2026-02-26 11:36:18.000000000 +0000 @@ -17,6 +17,7 @@ "supported": "CET being used" }, "hsi-level": 3, + "resolution": "Update your Linux distribution, which may now support CET.", "references": { "https://software.intel.com/sites/default/files/managed/4d/2a/control-flow-enforcement-technology-preview.pdf": "Intel CET Technology Preview" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Cet.Enabled.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Cet.Enabled.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Cet.Enabled.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Cet.Enabled.json 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,7 @@ "supported": "CET feature support by the platform" }, "hsi-level": 3, + "resolution": "Check your CPU supports CET, and ensure the feature is enabled in the UEFI setup screen.", "references": { "https://software.intel.com/sites/default/files/managed/4d/2a/control-flow-enforcement-technology-preview.pdf": "Intel CET Technology Preview" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.EncryptedRam.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.EncryptedRam.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.EncryptedRam.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.EncryptedRam.json 2026-02-26 11:36:18.000000000 +0000 @@ -17,6 +17,7 @@ "encrypted": "detected and enabled" }, "hsi-level": 4, + "resolution": "Ensure that the motherboard and CPU support this feature, and ensure it is enabled in the UEFI setup screen.", "references": { "https://software.intel.com/content/www/us/en/develop/blogs/intel-releases-new-technology-specification-for-memory-encryption.html": "Intel TME Press Release", "https://en.wikichip.org/wiki/x86/sme": "WikiChip SME Overview" diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Acm.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Acm.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Acm.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Acm.json 2026-02-26 11:36:18.000000000 +0000 @@ -15,6 +15,7 @@ "valid": "ACM protected" }, "hsi-level": 2, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "requires": [ "CPUID\\VID_GenuineIntel" ], diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Enabled.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Enabled.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Enabled.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Enabled.json 2026-02-26 11:36:18.000000000 +0000 @@ -19,6 +19,7 @@ "enabled": "detected and enabled" }, "hsi-level": 2, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://github.com/coreboot/coreboot/blob/master/src/soc/intel/jasperlake/include/soc/me.h": "Coreboot documentation" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Otp.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Otp.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Otp.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Otp.json 2026-02-26 11:36:18.000000000 +0000 @@ -16,6 +16,7 @@ "valid": "SOC is locked" }, "hsi-level": 2, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "requires": [ "CPUID\\VID_GenuineIntel" ], diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Policy.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Policy.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Policy.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Policy.json 2026-02-26 11:36:18.000000000 +0000 @@ -15,6 +15,7 @@ "valid": "error enforce policy is set to shutdown" }, "hsi-level": 3, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "requires": [ "CPUID\\VID_GenuineIntel" ], diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Verified.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Verified.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Verified.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelBootguard.Verified.json 2026-02-26 11:36:18.000000000 +0000 @@ -16,6 +16,7 @@ "success": "verified boot chain" }, "hsi-level": 2, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "requires": [ "CPUID\\VID_GenuineIntel" ], diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.IntelGds.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelGds.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.IntelGds.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.IntelGds.json 2026-02-26 11:36:18.000000000 +0000 @@ -16,6 +16,7 @@ "enabled": "microcode mitigation is supported, enabled and locked" }, "hsi-level": 2, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://www.intel.com/content/www/us/en/developer/articles/technical/software-security-guidance/technical-documentation/gather-data-sampling.html": "Gather Data Sampling" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Kernel.Lockdown.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Kernel.Lockdown.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Kernel.Lockdown.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Kernel.Lockdown.json 2026-02-26 11:36:18.000000000 +0000 @@ -15,5 +15,6 @@ "success-results": { "enabled": "lockdown is set to either `integrity` or `confidentiality`." }, + "resolution": "Ensure Secure Boot is enabled in the UEFI firmware setup.", "fwupd-version": "1.5.0" } diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Kernel.Tainted.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Kernel.Tainted.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Kernel.Tainted.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Kernel.Tainted.json 2026-02-26 11:36:18.000000000 +0000 @@ -15,5 +15,6 @@ "success-results": { "not-tainted": "the kernel is trusted" }, + "resolution": "Remove any non-free kernel modules.", "fwupd-version": "1.5.0" } diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Mei.KeyManifest.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Mei.KeyManifest.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Mei.KeyManifest.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Mei.KeyManifest.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "name": "ME BootGuard Platform Key", "description": [ "The BootGuard Platform Key is fused into the CPU PCH during manufacturing by the OEM.", - "At bootup, an authenticated code module computes a hash of the Platform Key and compares it with the one stored in field-programmable fuses.", + "At boot-up, an authenticated code module computes a hash of the Platform Key and compares it with the one stored in field-programmable fuses.", "If the key matches the ACM will pass control to the firmware, otherwise the boot process will stop.", "In 2022 a number of Platform **secret** Keys were leaked by Lenovo and confirmed by Intel." ], @@ -17,6 +17,7 @@ "valid": "device uses a BootGuard Platform Key that is not known to be compromised" }, "hsi-level": 1, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://github.com/phretor/intel-leak-checker/": "Intel leak checker", "https://www.tomshardware.com/news/intel-confirms-6gb-alder-lake-bios-source-code-leak-new-details-emerge": "Tom's Hardware Article" diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Mei.ManufacturingMode.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Mei.ManufacturingMode.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Mei.ManufacturingMode.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Mei.ManufacturingMode.json 2026-02-26 11:36:18.000000000 +0000 @@ -16,6 +16,7 @@ "locked": "device has had manufacturing mode disabled" }, "hsi-level": 1, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://malware.news/t/intel-me-manufacturing-mode-obscured-dangers-and-their-relationship-to-apple-macbook-vulnerability-cve-2018-4251/23214": "ME Manufacturing Mode: obscured dangers", "https://www.intel.com/content/www/us/en/security-center/advisory/intel-sa-00086.html": "Intel security advisory SA-00086" diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Mei.OverrideStrap.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Mei.OverrideStrap.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Mei.OverrideStrap.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Mei.OverrideStrap.json 2026-02-26 11:36:18.000000000 +0000 @@ -15,6 +15,7 @@ "locked": "device in in normal runtime mode" }, "hsi-level": 1, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://chromium.googlesource.com/chromiumos/third_party/flashrom/+/master/Documentation/mysteries_intel.txt": "Chromium documentation for Intel ME" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.PlatformDebugEnabled.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.PlatformDebugEnabled.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.PlatformDebugEnabled.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.PlatformDebugEnabled.json 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,7 @@ "not-enabled": "debugging is not currently enabled" }, "hsi-level": 1, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://www.intel.co.uk/content/www/uk/en/support/articles/000029393/processors.html": "Intel Direct Connect Interface", "https://github.com/chipsec/chipsec/blob/master/chipsec/cfg/8086/pch_4xxlp.xml#L270": "Chipsec 4xxlp register definitions", diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.PlatformDebugLocked.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.PlatformDebugLocked.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.PlatformDebugLocked.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.PlatformDebugLocked.json 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,7 @@ "locked": "device is locked" }, "hsi-level": 2, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://www.intel.co.uk/content/www/uk/en/support/articles/000029393/processors.html": "Intel Direct Connect Interface" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.PlatformFused.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.PlatformFused.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.PlatformFused.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.PlatformFused.json 2026-02-26 11:36:18.000000000 +0000 @@ -13,6 +13,7 @@ "success-results": { "locked": "device is fused" }, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "hsi-level": 1, "fwupd-version": "1.8.0" } diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.PrebootDma.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.PrebootDma.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.PrebootDma.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.PrebootDma.json 2026-02-26 11:36:18.000000000 +0000 @@ -22,6 +22,7 @@ "enabled": "detected correctly" }, "hsi-level": 3, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit": "IOMMU Wikipedia Page", "https://www.amd.com/en/support/tech-docs/amd-io-virtualization-technology-iommu-specification": "AMD I/O Virtualization Technology (IOMMU) Specification" diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Smap.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Smap.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Smap.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Smap.json 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,7 @@ "enabled": "SMAP features are detected and enabled" }, "hsi-level": 4, + "resolution": "Ensure that the CPU supports this feature, and then contact your OEM, who may be able to issue a firmware update.", "references": { "https://en.wikipedia.org/wiki/Supervisor_Mode_Access_Prevention": "SMAP Wikipedia Page" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Spi.Bioswe.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Spi.Bioswe.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Spi.Bioswe.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Spi.Bioswe.json 2026-02-26 11:36:18.000000000 +0000 @@ -17,6 +17,7 @@ "not-enabled": "write enable is disabled" }, "hsi-level": 1, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/6-chipset-c200-chipset-datasheet.pdf": "Intel C200 Datasheet" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Spi.Ble.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Spi.Ble.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Spi.Ble.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Spi.Ble.json 2026-02-26 11:36:18.000000000 +0000 @@ -16,6 +16,7 @@ "enabled": "the register is locked" }, "hsi-level": 1, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/6-chipset-c200-chipset-datasheet.pdf": "Intel C200 Datasheet" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Spi.Descriptor.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Spi.Descriptor.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Spi.Descriptor.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Spi.Descriptor.json 2026-02-26 11:36:18.000000000 +0000 @@ -17,6 +17,7 @@ "locked": "the SPI BAR is locked and read only from all regions" }, "hsi-level": 1, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "requires": [ "CPUID\\VID_GenuineIntel" ], diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Spi.SmmBwp.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Spi.SmmBwp.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Spi.SmmBwp.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Spi.SmmBwp.json 2026-02-26 11:36:18.000000000 +0000 @@ -16,6 +16,7 @@ "locked": "the region is locked" }, "hsi-level": 1, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://www.intel.com/content/dam/www/public/us/en/documents/datasheets/6-chipset-c200-chipset-datasheet.pdf": "Intel C200 Datasheet" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.SupportedCpu.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.SupportedCpu.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.SupportedCpu.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.SupportedCpu.json 2026-02-26 11:36:18.000000000 +0000 @@ -21,6 +21,7 @@ "If the kernel module has loaded but you still don't have data this is NOT a fwupd bug. You will have to contact ", "your motherboard or system manufacturer to enable reporting this information." ], + "resolution": "Contact the silicon vendor and ask them to submit new HSI tests for fwupd.", "hsi-level": 1, "fwupd-version": "1.8.0" } diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.SuspendToIdle.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.SuspendToIdle.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.SuspendToIdle.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.SuspendToIdle.json 2026-02-26 11:36:18.000000000 +0000 @@ -15,5 +15,6 @@ "enabled": "suspend-to-idle being used" }, "hsi-level": 3, + "resolution": "Change this setting in the UEFI setup screen, if supported.", "fwupd-version": "1.5.0" } diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.SuspendToRam.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.SuspendToRam.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.SuspendToRam.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.SuspendToRam.json 2026-02-26 11:36:18.000000000 +0000 @@ -17,6 +17,7 @@ "not-enabled": "suspend-to-ram being used" }, "hsi-level": 3, + "resolution": "Enable this setting in the UEFI setup screen, if supported.", "references": { "https://en.wikipedia.org/wiki/Cold_boot_attack": "Cold Boot Attack Wikipedia Page" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Tpm.EmptyPcr.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Tpm.EmptyPcr.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Tpm.EmptyPcr.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Tpm.EmptyPcr.json 2026-02-26 11:36:18.000000000 +0000 @@ -17,6 +17,7 @@ "valid": "all PCRs from 0 to 7 must have non-empty measurements" }, "hsi-level": 1, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://github.com/google/security-research/blob/master/pocs/bios/tpm-carte-blanche/writeup.md": "TPM Carte Blanche" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Tpm.ReconstructionPcr0.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Tpm.ReconstructionPcr0.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Tpm.ReconstructionPcr0.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Tpm.ReconstructionPcr0.json 2026-02-26 11:36:18.000000000 +0000 @@ -20,6 +20,7 @@ "valid": "all correct" }, "hsi-level": 2, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://www.kernel.org/doc/html/latest/security/tpm/tpm_event_log.html": "Linux Kernel TPM Documentation" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Tpm.Version20.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Tpm.Version20.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Tpm.Version20.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Tpm.Version20.json 2026-02-26 11:36:18.000000000 +0000 @@ -16,6 +16,7 @@ "found": "TPM device found in v2 mode" }, "hsi-level": 1, + "resolution": "Enable this setting in the UEFI setup screen, if supported.", "references": { "https://en.wikipedia.org/wiki/Trusted_Platform_Module": "TPM Wikipedia Page" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Uefi.BootserviceVars.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Uefi.BootserviceVars.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Uefi.BootserviceVars.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Uefi.BootserviceVars.json 2026-02-26 11:36:18.000000000 +0000 @@ -14,6 +14,7 @@ "locked": "bootservice-only data is not visible" }, "hsi-level": 1, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://uefi.org/specs/UEFI/2.10/07_Services_Boot_Services.html": "UEFI Specification" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Uefi.Db.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Uefi.Db.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Uefi.Db.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Uefi.Db.json 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,9 @@ "not-found": "the certificate store does not contain any Microsoft UEFI certificate", "valid": "the certificate store has the Microsoft UEFI 2023 certificate installed" }, + "resolution": "Apply the LVFS KEK update for your hardware, and then apply the LVFS db update.", "references": { + "https://fwupd.github.io/libfwupdplugin/uefi-db.html": "fwupd Documentation for UEFI Secure Boot Certificates", "https://wiki.ubuntu.com/UEFI/SecureBoot/Testing": "Ubuntu SecureBoot Wiki Page" }, "fwupd-version": "2.0.8" diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Uefi.MemoryProtection.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Uefi.MemoryProtection.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Uefi.MemoryProtection.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Uefi.MemoryProtection.json 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,7 @@ "locked": "memory is not executable or writable" }, "hsi-level": 3, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://aka.ms/WHCP": "Microsoft Windows WHCP" }, diff -Nru fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Uefi.Pk.json fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Uefi.Pk.json --- fwupd-2.0.8/docs/hsi-tests.d/org.fwupd.hsi.Uefi.Pk.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hsi-tests.d/org.fwupd.hsi.Uefi.Pk.json 2026-02-26 11:36:18.000000000 +0000 @@ -16,6 +16,7 @@ "valid": "valid key" }, "hsi-level": 1, + "resolution": "Contact your OEM, who may be able to issue a firmware update.", "references": { "https://wiki.ubuntu.com/UEFI/SecureBoot/Testing": "Ubuntu SecureBoot Wiki Page" }, diff -Nru fwupd-2.0.8/docs/hwids.md fwupd-2.0.20/docs/hwids.md --- fwupd-2.0.8/docs/hwids.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/hwids.md 2026-02-26 11:36:18.000000000 +0000 @@ -16,7 +16,7 @@ targets `USB\VID_0BDA&PID_5850`, but *only for Dell* or *only for Lenovo* systems. Microsoft calls these values "CHIDs" and they are generated on Windows from the SBMIOS tables using `ComputerHardwareIds.exe` -binary which can be found [here](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/computerhardwareids). +[binary](https://learn.microsoft.com/en-us/windows-hardware/drivers/devtest/computerhardwareids). The list of CHIDs used in Microsoft Windows is: HardwareID-00 ← Manufacturer + Family + Product Name + SKU Number + BIOS Vendor + BIOS Version + BIOS Major Release + BIOS Minor Release diff -Nru fwupd-2.0.8/docs/meson.build fwupd-2.0.20/docs/meson.build --- fwupd-2.0.8/docs/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,8 +1,9 @@ plugin_readme_targets = [] plugin_readme_outputs = [] -foreach plugin, enabled: plugins +foreach plugin, enabled : plugins plugin_readme_output = '@0@-README.md'.format(plugin) - plugin_readme_targets += custom_target('doc-plugin-@0@'.format(plugin), + plugin_readme_targets += custom_target( + 'doc-plugin-@0@'.format(plugin), input: join_paths(meson.project_source_root(), 'plugins', plugin, 'README.md'), output: plugin_readme_output, command: ['ln', '-sf', '@INPUT0@', '@OUTPUT@'], @@ -14,68 +15,142 @@ if build_standalone if get_option('man') - custom_target('fwupd.conf.5', + custom_target( + 'fwupd.conf.5', input: 'fwupd.conf.md', output: 'fwupd.conf.5', command: [ - generate_man, '@INPUT@', '-o', '@OUTPUT@', - '--replace', 'PACKAGE_VERSION', fwupd_version, - '--replace', 'SYSCONFDIR', sysconfdir, - '--replace', 'plugin_uefi_capsule', '@0@'.format(allow_uefi_capsule), - '--replace', 'plugin_msr', '@0@'.format(has_cpuid), - '--replace', 'plugin_redfish', '@0@'.format(host_machine.system() == 'linux'), - '--replace', 'P2pPolicy', '@0@'.format(get_option('p2p_policy')), - '--defines', join_paths(meson.project_source_root(), 'plugins', 'msr', 'fu-msr-plugin.c'), - '--defines', join_paths(meson.project_source_root(), 'plugins', 'redfish', 'fu-redfish-plugin.c'), - '--defines', join_paths(meson.project_source_root(), 'plugins', 'thunderbolt', 'fu-thunderbolt-plugin.c'), - '--defines', join_paths(meson.project_source_root(), 'plugins', 'uefi-capsule', 'fu-uefi-capsule-plugin.c'), - '--defines', join_paths(meson.project_source_root(), 'src', 'fu-engine-config.c'), + generate_man, + '@INPUT@', + '-o', + '@OUTPUT@', + '--replace', + 'PACKAGE_VERSION', + fwupd_version, + '--replace', + 'SYSCONFDIR', + sysconfdir, + '--replace', + 'plugin_uefi_capsule', + '@0@'.format(allow_uefi), + '--replace', + 'plugin_msr', + '@0@'.format(has_cpuid), + '--replace', + 'plugin_redfish', + '@0@'.format(host_machine.system() == 'linux'), + '--replace', + 'P2pPolicy', + '@0@'.format(get_option('p2p_policy')), + '--defines', + join_paths(meson.project_source_root(), 'plugins', 'msr', 'fu-msr-plugin.c'), + '--defines', + join_paths(meson.project_source_root(), 'plugins', 'redfish', 'fu-redfish-plugin.c'), + '--defines', + join_paths(meson.project_source_root(), 'plugins', 'thunderbolt', 'fu-thunderbolt-plugin.c'), + '--defines', + join_paths( + meson.project_source_root(), + 'plugins', + 'uefi-capsule', + 'fu-uefi-capsule-plugin.c', + ), + '--defines', + join_paths(meson.project_source_root(), 'src', 'fu-engine-config.c'), ], install: true, + install_tag: 'man', install_dir: join_paths(mandir, 'man5'), ) - custom_target('fwupd-remotes.d.5', + custom_target( + 'fwupd-remotes.d.5', input: 'fwupd-remotes.d.md', output: 'fwupd-remotes.d.5', command: [ - generate_man, '@INPUT@', '-o', '@OUTPUT@', - '--replace', 'PACKAGE_VERSION', fwupd_version, - '--replace', 'SYSCONFDIR', sysconfdir, - '--replace', 'LOCALSTATEDIR', localstatedir, - '--defines', join_paths(meson.project_source_root(), 'src', 'fu-remote.c'), + generate_man, + '@INPUT@', + '-o', + '@OUTPUT@', + '--replace', + 'PACKAGE_VERSION', + fwupd_version, + '--replace', + 'SYSCONFDIR', + sysconfdir, + '--replace', + 'LOCALSTATEDIR', + localstatedir, + '--defines', + join_paths(meson.project_source_root(), 'src', 'fu-remote.c'), ], install: true, + install_tag: 'man', install_dir: join_paths(mandir, 'man5'), ) endif if build_docs - md_targets += custom_target('fwupd.conf.md', + md_targets += custom_target( + 'fwupd.conf.md', input: 'fwupd.conf.md', output: 'fwupd.conf.md', command: [ - generate_man, '@INPUT@', '-o', '@OUTPUT@', - '--replace', 'PACKAGE_VERSION', fwupd_version, - '--replace', 'SYSCONFDIR', sysconfdir, - '--replace', 'plugin_uefi_capsule', '@0@'.format(allow_uefi_capsule), - '--replace', 'plugin_msr', '@0@'.format(has_cpuid), - '--replace', 'plugin_redfish', '@0@'.format(host_machine.system() == 'linux'), - '--defines', join_paths(meson.project_source_root(), 'plugins', 'msr', 'fu-msr-plugin.c'), - '--defines', join_paths(meson.project_source_root(), 'plugins', 'redfish', 'fu-redfish-plugin.c'), - '--defines', join_paths(meson.project_source_root(), 'plugins', 'thunderbolt', 'fu-thunderbolt-plugin.c'), - '--defines', join_paths(meson.project_source_root(), 'plugins', 'uefi-capsule', 'fu-uefi-capsule-plugin.c'), - '--defines', join_paths(meson.project_source_root(), 'src', 'fu-engine-config.c'), + generate_man, + '@INPUT@', + '-o', + '@OUTPUT@', + '--replace', + 'PACKAGE_VERSION', + fwupd_version, + '--replace', + 'SYSCONFDIR', + sysconfdir, + '--replace', + 'plugin_uefi_capsule', + '@0@'.format(allow_uefi), + '--replace', + 'plugin_msr', + '@0@'.format(has_cpuid), + '--replace', + 'plugin_redfish', + '@0@'.format(host_machine.system() == 'linux'), + '--defines', + join_paths(meson.project_source_root(), 'plugins', 'msr', 'fu-msr-plugin.c'), + '--defines', + join_paths(meson.project_source_root(), 'plugins', 'redfish', 'fu-redfish-plugin.c'), + '--defines', + join_paths(meson.project_source_root(), 'plugins', 'thunderbolt', 'fu-thunderbolt-plugin.c'), + '--defines', + join_paths( + meson.project_source_root(), + 'plugins', + 'uefi-capsule', + 'fu-uefi-capsule-plugin.c', + ), + '--defines', + join_paths(meson.project_source_root(), 'src', 'fu-engine-config.c'), '--md', ], ) - md_targets += custom_target('fwupd-remotes.d.md', + md_targets += custom_target( + 'fwupd-remotes.d.md', input: 'fwupd-remotes.d.md', output: 'fwupd-remotes.d.md', command: [ - generate_man, '@INPUT@', '-o', '@OUTPUT@', - '--replace', 'PACKAGE_VERSION', fwupd_version, - '--replace', 'SYSCONFDIR', sysconfdir, - '--replace', 'LOCALSTATEDIR', localstatedir, - '--defines', join_paths(meson.project_source_root(), 'libfwupd', 'fwupd-remote.c'), + generate_man, + '@INPUT@', + '-o', + '@OUTPUT@', + '--replace', + 'PACKAGE_VERSION', + fwupd_version, + '--replace', + 'SYSCONFDIR', + sysconfdir, + '--replace', + 'LOCALSTATEDIR', + localstatedir, + '--defines', + join_paths(meson.project_source_root(), 'libfwupd', 'fwupd-remote.c'), '--md', ], ) @@ -93,20 +168,18 @@ fwupd_toml = configure_file( input: 'fwupd.toml.in', output: 'fwupd.toml', - configuration: toml_conf + configuration: toml_conf, ) fwupdplugin_toml = configure_file( input: 'fwupdplugin.toml.in', output: 'fwupdplugin.toml', - configuration: toml_conf + configuration: toml_conf, ) - custom_target('doc-fwupd', - input: [ - fwupd_toml, - fwupd_gir[0], - ], + custom_target( + 'doc-fwupd', + input: [fwupd_toml, fwupd_gir[0]], output: 'libfwupd', command: [ gidocgen_app, @@ -119,43 +192,39 @@ '--content-dir=@0@'.format(meson.current_source_dir()), '@INPUT1@', ], - depends: [ - fwupd_gir[0], - ], + depends: [fwupd_gir[0]], build_by_default: true, install: true, + install_tag: 'doc', install_dir: join_paths(datadir, 'doc'), ) subdir('hsi-tests.d') - hsi_md = custom_target('generate-hsi-spec', + hsi_md = custom_target( + 'generate-hsi-spec', input: hsi_test_jsons, - output : 'hsi.md', - command : [ - python3, - files(['generate-hsi-spec.py', 'hsi.md.in']), - '@OUTPUT@', - '@INPUT@', - ], + output: 'hsi.md', + command: [python3, files('generate-hsi-spec.py', 'hsi.md.in'), '@OUTPUT@', '@INPUT@'], ) - custom_target('generate-oval', + custom_target( + 'generate-oval', input: hsi_test_jsons, - output : 'oval.xml', - command : [ + output: 'oval.xml', + command: [ python3, - files(['generate-oval.py']), - '--version', fwupd_version, - '--schema-version', '5.11.3', + files('generate-oval.py'), + '--version', + fwupd_version, + '--schema-version', + '5.11.3', '@OUTPUT@', '@INPUT@', ], ) - custom_target('doc-fwupdplugin', - input: [ - fwupdplugin_toml, - fwupdplugin_gir[0], - ], + custom_target( + 'doc-fwupdplugin', + input: [fwupdplugin_toml, fwupdplugin_gir[0]], output: 'libfwupdplugin', command: [ gidocgen_app, @@ -174,47 +243,52 @@ '--content-dir=@0@'.format(meson.current_build_dir() / '../plugins/uefi-capsule'), '@INPUT1@', ], - depends: [ - fwupdplugin_gir[0], - hsi_md, - plugin_readme_targets, - md_targets, - ], + depends: [fwupdplugin_gir[0], hsi_md, plugin_readme_targets, md_targets], build_by_default: true, install: true, + install_tag: 'doc', install_dir: join_paths(datadir, 'doc'), ) man_cmd = [] - foreach man: man_md + foreach man : man_md man_cmd += '-m @0@'.format(man) endforeach - custom_target('index.html', + custom_target( + 'index.html', input: 'index.html', output: 'index.html', - command: [ - generate_index, '@INPUT@', '-o', '@OUTPUT@', - man_cmd, - ], + command: [generate_index, '@INPUT@', '-o', '@OUTPUT@', man_cmd], install: true, - install_dir: join_paths(datadir, 'doc', 'fwupd') + install_tag: 'doc', + install_dir: join_paths(datadir, 'doc', 'fwupd'), ) if hsi - install_data(['hsi.html'], - install_dir : join_paths(datadir, 'doc', 'fwupd') + install_data( + ['hsi.html'], + install_tag: 'doc', + install_dir: join_paths(datadir, 'doc', 'fwupd'), ) endif - install_data(['urlmap_fwupd.js'], - install_dir: join_paths(datadir, 'doc', 'libfwupd') - ) - install_data(['urlmap_fwupdplugin.js'], - install_dir: join_paths(datadir, 'doc', 'libfwupdplugin') + install_data( + ['urlmap_fwupd.js'], + install_tag: 'doc', + install_dir: join_paths(datadir, 'doc', 'libfwupd'), + ) + install_data( + ['urlmap_fwupdplugin.js'], + install_tag: 'doc', + install_dir: join_paths(datadir, 'doc', 'libfwupdplugin'), ) #make devhelp work - install_symlink('libfwupd', + install_symlink( + 'libfwupd', + install_tag: 'doc', install_dir: join_paths(datadir, 'doc', 'fwupd'), pointing_to: join_paths('..', 'libfwupd'), ) - install_symlink('libfwupdplugin', + install_symlink( + 'libfwupdplugin', + install_tag: 'doc', install_dir: join_paths(datadir, 'doc', 'fwupd'), pointing_to: join_paths('..', 'libfwupdplugin'), ) diff -Nru fwupd-2.0.8/docs/nda.md fwupd-2.0.20/docs/nda.md --- fwupd-2.0.8/docs/nda.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/nda.md 2026-02-26 11:36:18.000000000 +0000 @@ -48,7 +48,7 @@ -------------- Companies typically propose that NDAs are subject to governing laws and jurisdictions which are most -favourable to them, or relate to where in the world they have a presence. +favorable to them, or relate to where in the world they have a presence. The complexity and cost of reviewing an NDA under a governing law unfamiliar to us is significant. diff -Nru fwupd-2.0.8/docs/tutorial.md fwupd-2.0.20/docs/tutorial.md --- fwupd-2.0.8/docs/tutorial.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/docs/tutorial.md 2026-02-26 11:36:18.000000000 +0000 @@ -1007,8 +1007,8 @@ There are traits that control the generation of enum code. These include: - `ToString`: for `fu_example_family_to_string()`, needed to create output -- `ToBitString`: for `fu_example_family_to_string()`, needed to create output for bitfields - `FromString`: for `fu_example_family_from_string()`, needed to parse input +- `Bitfield`: for `ToString` and `FromString`, to hint these are actually bitfields **NOTE:** Enums are defined as a native unsigned type, and should not be copied by reference without first casting to an integer of known width. diff -Nru fwupd-2.0.8/docs/uefi-db.md fwupd-2.0.20/docs/uefi-db.md --- fwupd-2.0.8/docs/uefi-db.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/docs/uefi-db.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,191 @@ +--- +title: UEFI Secure Boot Certificates +--- + +## Executive Summary + +On the 26th June 2026 a root certificate used for signing boot media will expire. Microsoft will not sign updated boot media with the old key, and that **at least one major OEM** is not going to be shipping the expired key on new hardware. This means that existing install media may not boot on some new laptop, desktop and server devices, and that future updates to boot packages may not boot on old hardware. It is important to note that machines running Linux **will not stop booting** when the certificate expires. + +Microsoft is shipping fixes for select OEMs using Windows Updates automatically. The workaround for Linux is to manually disable secure boot which would be unpopular with anyone that cares about security. Using fwupd is a way that can distribute the updated certificates in Linux. + +## Important Terms + +* `PK`: “*Platform Key*” – one X509 certificate created by the OEM, e.g. Lenovo +* `KEK`: “*Key Exchange Key*” – multiple certificates (created by Microsoft and the OEM), used to update db, signed by the vendor PK +* `db`: “*Signature Database*” – multiple certificates (created by Microsoft and the OEM), hashes, and signatures, used to allow binaries, signed by KEK +* `dbx`: “*Forbidden Signatures Database*” – multiple certificates, hashes, and signatures, used to block binaries, signed by KEK +* 3rd party certificate – the certificate Microsoft uses to sign non-MS bootloaders that Linux uses, e.g. Shim + * Windows Production CA 2011 – the old “PCA” certificate + * Microsoft UEFI CA 2023 (third-party) – the new certificate + +## Introduction + +The Microsoft certificate which is used for signing “3rd party” boot media (e.g. `shimx64.efi`) will expire on the 26th June 2026. Microsoft has created a new certificate which can be used for signing now. Whilst we can “dual sign” the shim binary with the old and new certificate until the cut-over date, when the certificate has expired we can only sign shim with the new 3rd party certificate. New laptop, desktop and server products from some OEMs may ship from the factory with **only** the new Microsoft Windows UEFI CA 2023 certificate. This means any shim binaries signed with the old PCA 2011 certificate will not be allowed to run. + +This means **it may be impossible to install existing Linux releases on newer machines**. Once the certificate has expired, newly signed `shimx64.efi` binaries will only be signed with the new certificate and thus **will not boot on any existing system that does not have the new certificate installed in the db**. This would potentially mean Linux distributons couldn’t deliver security updates to shim once the old certificate has expired. + +Some OEMs have issued BIOS updates to update the KEK and db, but some have instead opted for Microsoft to update the various dbx, db, and KEKs – using new functionality [built into Windows 11](https://techcommunity.microsoft.com/blog/windows-itpro-blog/updating-microsoft-secure-boot-keys/4055324). This means something had to be done for Linux too. + +## Solution + +At the moment the LVFS distributes `dbx` updates (signed by the Microsoft `KEK`) and users deploy them using fwupd – this has been done over 10M times and with a \>99% success rate. The fwupd project now needs to distribute two additional artifacts, the **vendor-specific** `KEK` and the **generic** Microsoft `db`. This will likely need backporting into any distribution release that needs to run with Secure Boot turned on when installing onto new hardware. Should backporting the fwupd package be impossible, it is also be possible to build the changes into just Fedora, and then use a `Fedora.iso` LiveUSB to preload the new certificates into the non-volatile machine storage, and *then* install any distribution release that includes the shim signed with the new key. It is not possible to load the expired `db` certificate into a machine with only the new `KEK`. + +Updating the KEK and db is a generally safe procedure, with the only limitations being: + +* The amount of NVRAM space may be insufficient (or fragmented) – although real-world testing suggests this is a “*failure to install the update*” rather than a “*failure to boot*” scenario. This may be worked around by doing a “factory reset” of the secure boot keys in the BIOS setup. +* Some firmware has a toggle to “turn off” the MS 3rd party certificate and the *new* 3rd party cert won’t be matched. Some OEMs are planning firmware updates to add the hash for the new 3rd party certificate. Given that the 3rd party certificate has to be enabled if secure boot is turned on *just to boot Linux* I’m not really worried about accidentally enabling the new certificate as the old one can’t have been turned off. +* Updating the `db` means that the Microsoft Windows BitLocker recovery code may be needed, if the device is dual-booted. It may also cause full disk encryption to not work in the same way on Linux. + +## Example of Affected System (Lenovo P50) + + fwupdmgr security + Host Security ID: HSI:1! (v2.0.9) + ... + ✘ UEFI db: Invalid + +### Affected fwupdmgr security --json + + ... + { + "AppstreamId" : "org.fwupd.hsi.Uefi.Db", + "HsiResult" : "not-valid", + "Name" : "UEFI db", + "Description" : "The UEFI db contains the list of valid certificates that can be used to authorize what EFI binaries are allowed to run.", + "Uri" : "https://fwupd.github.io/libfwupdplugin/hsi.html#org.fwupd.hsi.Uefi.Db", + "Flags" : [ + "runtime-issue", + "action-config-fw" + ] + }, + ... + +### Affected KEK + + + + d5cea02c70cff53bb24bd8cce5035897e565463b + C=JP,ST=Kanagawa,L=Yokohama,O=Lenovo Ltd.,CN=Lenovo Ltd. KEK CA 2012 + C=JP,ST=Kanagawa,L=Yokohama,O=Lenovo Ltd.,CN=Lenovo Ltd. KEK CA 2012 + + + b1d0e26aac012618513d33bdb176bbf53962350e + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Corporation Third Party Marketplace Root + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Corporation KEK CA 2011 + + + +### Affected db + + + + 7bef7077a4d5017e88764fdbf8d274e74a4411af + C=JP,ST=Kanagawa,L=Yokohama,O=Lenovo Ltd.,CN=Lenovo Ltd. Root CA 2012 + C=JP,ST=Kanagawa,L=Yokohama,O=Lenovo Ltd.,CN=ThinkPad Product CA 2012 + + + 5e53870688239a03e705b05e4f57c33746db42f9 + C=US,ST=North Carolina,O=Lenovo,CN=Lenovo UEFI CA 2014 + C=US,ST=North Carolina,O=Lenovo,CN=Lenovo UEFI CA 2014 + + + 03de12be14ca397df20cee646c7d9b727fcce5f8 + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Corporation Third Party Marketplace Root + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Corporation UEFI CA 2011 + + + cbbbf4b136db90d11fd37a4a9b2106973aecc095 + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Root Certificate Authority 2010 + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Windows Production PCA 2011 + + + +## Example of Unaffected Machine (Lenovo ThinkPad P16v) + +### Unaffected fwupdmgr security + + Host Security ID: HSI:4 (v2.0.9) + ... + ✔ UEFI db: Valid + +### Unaffected fwupdmgr security --json + + ... + { + "AppstreamId" : "org.fwupd.hsi.Uefi.Db", + "HsiResult" : "valid", + "Name" : "UEFI db", + "Description" : "The UEFI db contains the list of valid certificates that can be used to authorize what EFI binaries are allowed to run.", + "Uri" : "https://fwupd.github.io/libfwupdplugin/hsi.html#org.fwupd.hsi.Uefi.Db", + "Flags" : [ + "success", + "runtime-issue", + "action-config-fw" + ] + }, + ... + +### Unaffected PK + + + + 9aef2123f4de7c19afabd909bb2c8cac4411e07e + C=JP,ST=Kanagawa,L=Yokohama,O=Lenovo Ltd.,CN=Lenovo Ltd. PK CA 2012 + C=JP,ST=Kanagawa,L=Yokohama,O=Lenovo Ltd.,CN=Lenovo Ltd. PK CA 2012 + + + +### Unaffected KEK + + + always-search + + d5cea02c70cff53bb24bd8cce5035897e565463b + C=JP,ST=Kanagawa,L=Yokohama,O=Lenovo Ltd.,CN=Lenovo Ltd. KEK CA 2012 + C=JP,ST=Kanagawa,L=Yokohama,O=Lenovo Ltd.,CN=Lenovo Ltd. KEK CA 2012 + + + b1d0e26aac012618513d33bdb176bbf53962350e + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Corporation Third Party Marketplace Root + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Corporation KEK CA 2011 + + + 4e17021ce9f830eaed13d2817db58a7d9f995838 + C=US,O=Microsoft Corporation,CN=Microsoft RSA Devices Root CA 2021 + C=US,O=Microsoft Corporation,CN=Microsoft Corporation KEK 2K CA 2023 + + + +### Unaffected db + + + + 7bef7077a4d5017e88764fdbf8d274e74a4411af + C=JP,ST=Kanagawa,L=Yokohama,O=Lenovo Ltd.,CN=Lenovo Ltd. Root CA 2012 + C=JP,ST=Kanagawa,L=Yokohama,O=Lenovo Ltd.,CN=ThinkPad Product CA 2012 + + + 5e53870688239a03e705b05e4f57c33746db42f9 + C=US,ST=North Carolina,O=Lenovo,CN=Lenovo UEFI CA 2014 + C=US,ST=North Carolina,O=Lenovo,CN=Lenovo UEFI CA 2014 + + + cbbbf4b136db90d11fd37a4a9b2106973aecc095 + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Root Certificate Authority 2010 + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Windows Production PCA 2011 + + + db926014f95ac9ec837442d5d96178538c62434f + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Root Certificate Authority 2010 + C=US,O=Microsoft Corporation,CN=Windows UEFI CA 2023 + + + 03de12be14ca397df20cee646c7d9b727fcce5f8 + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Corporation Third Party Marketplace Root + C=US,ST=Washington,L=Redmond,O=Microsoft Corporation,CN=Microsoft Corporation UEFI CA 2011 + + + a5b7c551cedc06b94d0c5b920f473e03c2f142f2 + C=US,O=Microsoft Corporation,CN=Microsoft RSA Devices Root CA 2021 + C=US,O=Microsoft Corporation,CN=Microsoft UEFI CA 2023 + + diff -Nru fwupd-2.0.8/generate-build/generate-man.py fwupd-2.0.20/generate-build/generate-man.py --- fwupd-2.0.8/generate-build/generate-man.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/generate-build/generate-man.py 2026-02-26 11:36:18.000000000 +0000 @@ -70,6 +70,7 @@ troff_lines.append(".hy") # hyphenate # content + in_name_section = False for section in sections[1:]: lines = section.split("\n") sectkind: str = ".PP" # begin a new paragraph @@ -79,6 +80,8 @@ if lines[-1].startswith("##"): lines = [lines[-1].strip("#").strip()] sectkind = ".SH" + # track if we're entering the NAME section + in_name_section = lines[0].upper() == "NAME" # join long lines line = "" @@ -111,11 +114,19 @@ line = _replace_bookend(line, "`", "\\f[B]", "\\f[R]") line = _replace_bookend(line, '"', "“", "”") + # in the NAME section, replace em dash with troff hyphen for whatis compatibility + if in_name_section and sectkind != ".SH": + line = line.replace("—", "\\-") + # add troff if sectalign != 4: troff_lines.append(f".RS {sectalign}") - troff_lines.append(sectkind) - troff_lines.append(line) + if sectkind == ".SH": + # section headers must be on same line for apropos/whatis compatibility + troff_lines.append(f"{sectkind} {line.strip()}") + else: + troff_lines.append(sectkind) + troff_lines.append(line) if sectalign != 4: troff_lines.append(".RE") diff -Nru fwupd-2.0.8/generate-build/meson.build fwupd-2.0.20/generate-build/meson.build --- fwupd-2.0.8/generate-build/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/generate-build/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -7,6 +7,9 @@ generate_index = [python3, files('generate-index.py')] fix_translations = [python3, files('fix_translations.py')] if umockdev_integration_tests.allowed() - unittest_inspector = [python3, files('unittest_inspector.py')] - install_data(files('unittest_inspector.py'), install_dir: installed_test_datadir) + unittest_inspector = [python3, files('unittest_inspector.py')] + install_data( + files('unittest_inspector.py'), + install_dir: installed_test_datadir, + ) endif diff -Nru fwupd-2.0.8/libfwupd/README.md fwupd-2.0.20/libfwupd/README.md --- fwupd-2.0.8/libfwupd/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -3,11 +3,8 @@ ## Planned API/ABI changes for next release * Typedef `FwupdFeatureFlags` to `guint64` so it's the same size on all platforms - -## Migration from Version 2.0.0 - -* Migrate from `fu_firmware_parse_full()` to `fu_firmware_parse_bytes()` -* Migrate from `fu_firmware_parse()` to `fu_firmware_parse_bytes()` by adding an offset of 0x0 +* Remove `FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM` +* Remove `FWUPD_INSTALL_FLAG_NO_SEARCH` ## Migration from Version 1.9.x diff -Nru fwupd-2.0.8/libfwupd/fwupd-bios-setting.c fwupd-2.0.20/libfwupd/fwupd-bios-setting.c --- fwupd-2.0.8/libfwupd/fwupd-bios-setting.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-bios-setting.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,7 +8,6 @@ #include "fwupd-bios-setting.h" #include "fwupd-codec.h" -#include "fwupd-common-private.h" #include "fwupd-enums-private.h" #include "fwupd-error.h" @@ -27,6 +26,7 @@ gchar *name; gchar *description; gchar *path; + gchar *value_filename; gchar *current_value; guint64 lower_bound; guint64 upper_bound; @@ -125,6 +125,48 @@ } /** + * fwupd_bios_setting_get_filename: + * @self: a #FwupdBiosSetting + * + * Gets the filename within @path where values are read/written. + * + * Returns: the value filename or NULL if not set + * + * Since: 2.0.20 + **/ +const gchar * +fwupd_bios_setting_get_filename(FwupdBiosSetting *self) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(self), NULL); + return priv->value_filename; +} + +/** + * fwupd_bios_setting_set_filename: + * @self: a #FwupdBiosSetting + * @filename: the filename within @path for value operations + * + * Sets the filename within @path where values are read/written. + * If not set, defaults to "current_value". + * + * Since: 2.0.20 + **/ +void +fwupd_bios_setting_set_filename(FwupdBiosSetting *self, const gchar *filename) +{ + FwupdBiosSettingPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_BIOS_SETTING(self)); + + /* not changed */ + if (g_strcmp0(priv->value_filename, filename) == 0) + return; + + g_free(priv->value_filename); + priv->value_filename = g_strdup(filename); +} + +/** * fwupd_bios_setting_get_lower_bound: * @self: a #FwupdBiosSetting * @@ -626,22 +668,22 @@ if (!_fu_strtoull_simple(value, &tmp, error)) return FALSE; if (tmp < priv->lower_bound) { - g_set_error(error, + g_set_error(error, /* nocheck:error */ FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "%s is too small (%" G_GUINT64_FORMAT - "); expected at least %" G_GUINT64_FORMAT, + ") expected at least %" G_GUINT64_FORMAT, value, tmp, priv->lower_bound); return FALSE; } if (tmp > priv->upper_bound) { - g_set_error(error, + g_set_error(error, /* nocheck:error */ FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "%s is too big (%" G_GUINT64_FORMAT - "); expected no more than %" G_GUINT64_FORMAT, + ") expected no more than %" G_GUINT64_FORMAT, value, tmp, priv->upper_bound); @@ -652,22 +694,22 @@ if (priv->kind == FWUPD_BIOS_SETTING_KIND_STRING) { gsize tmp = strlen(value); if (tmp < priv->lower_bound) { - g_set_error(error, + g_set_error(error, /* nocheck:error */ FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "%s is too short (%" G_GSIZE_FORMAT - "); expected at least %" G_GUINT64_FORMAT, + ") expected at least %" G_GUINT64_FORMAT, value, tmp, priv->lower_bound); return FALSE; } if (tmp > priv->upper_bound) { - g_set_error(error, + g_set_error(error, /* nocheck:error */ FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "%s is too long (%" G_GSIZE_FORMAT - "); expected no more than %" G_GUINT64_FORMAT, + ") expected no more than %" G_GUINT64_FORMAT, value, tmp, priv->upper_bound); @@ -799,6 +841,12 @@ FWUPD_RESULT_KEY_DESCRIPTION, g_variant_new_string(priv->description)); } + if (priv->value_filename != NULL) { + g_variant_builder_add(builder, + "{sv}", + FWUPD_RESULT_KEY_BIOS_SETTING_FILENAME, + g_variant_new_string(priv->value_filename)); + } g_variant_builder_add(builder, "{sv}", FWUPD_RESULT_KEY_BIOS_SETTING_READ_ONLY, @@ -864,6 +912,10 @@ fwupd_bios_setting_set_current_value(self, g_variant_get_string(value, NULL)); return; } + if (g_strcmp0(key, FWUPD_RESULT_KEY_BIOS_SETTING_FILENAME) == 0) { + fwupd_bios_setting_set_filename(self, g_variant_get_string(value, NULL)); + return; + } if (g_strcmp0(key, FWUPD_RESULT_KEY_DESCRIPTION) == 0) { fwupd_bios_setting_set_description(self, g_variant_get_string(value, NULL)); return; @@ -940,6 +992,11 @@ fwupd_bios_setting_add_possible_value(self, tmp); } } + fwupd_bios_setting_set_filename( + self, + json_object_get_string_member_with_default(obj, + FWUPD_RESULT_KEY_BIOS_SETTING_FILENAME, + NULL)); fwupd_bios_setting_set_lower_bound( self, json_object_get_int_member_with_default(obj, @@ -981,6 +1038,10 @@ FWUPD_RESULT_KEY_BIOS_SETTING_READ_ONLY, priv->read_only); fwupd_codec_json_append_int(builder, FWUPD_RESULT_KEY_BIOS_SETTING_TYPE, priv->kind); + fwupd_codec_json_append(builder, + FWUPD_RESULT_KEY_BIOS_SETTING_FILENAME, + priv->value_filename); + if (priv->kind == FWUPD_BIOS_SETTING_KIND_ENUMERATION) { if (priv->possible_values->len > 0) { json_builder_set_member_name(builder, @@ -1026,6 +1087,10 @@ priv->current_value); fwupd_codec_string_append(str, idt, + FWUPD_RESULT_KEY_BIOS_SETTING_FILENAME, + priv->value_filename); + fwupd_codec_string_append(str, + idt, FWUPD_RESULT_KEY_BIOS_SETTING_READ_ONLY, priv->read_only ? "True" : "False"); if (priv->kind == FWUPD_BIOS_SETTING_KIND_ENUMERATION) { @@ -1082,6 +1147,7 @@ g_free(priv->name); g_free(priv->description); g_free(priv->path); + g_free(priv->value_filename); g_ptr_array_unref(priv->possible_values); G_OBJECT_CLASS(fwupd_bios_setting_parent_class)->finalize(object); diff -Nru fwupd-2.0.8/libfwupd/fwupd-bios-setting.h fwupd-2.0.20/libfwupd/fwupd-bios-setting.h --- fwupd-2.0.8/libfwupd/fwupd-bios-setting.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-bios-setting.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,6 @@ #include #include "fwupd-build.h" -#include "fwupd-enums.h" G_BEGIN_DECLS @@ -143,4 +142,9 @@ void fwupd_bios_setting_set_id(FwupdBiosSetting *self, const gchar *id) G_GNUC_NON_NULL(1); +const gchar * +fwupd_bios_setting_get_filename(FwupdBiosSetting *self) G_GNUC_NON_NULL(1); +void +fwupd_bios_setting_set_filename(FwupdBiosSetting *self, const gchar *filename) G_GNUC_NON_NULL(1); + G_END_DECLS diff -Nru fwupd-2.0.8/libfwupd/fwupd-build.h fwupd-2.0.20/libfwupd/fwupd-build.h --- fwupd-2.0.8/libfwupd/fwupd-build.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-build.h 2026-02-26 11:36:18.000000000 +0000 @@ -17,3 +17,21 @@ #define G_GNUC_NON_NULL(params...) #endif #endif + +#if !GLIB_CHECK_VERSION(2, 70, 0) +#define g_prefix_error_literal g_prefix_error +#define g_spawn_check_wait_status g_spawn_check_exit_status +#define g_source_set_static_name(n1, n2) +#endif + +#if !GLIB_CHECK_VERSION(2, 80, 0) +#define g_task_return_new_error_literal g_task_return_new_error +#endif + +#ifndef G_GNUC_FLAG_ENUM +#if g_macro__has_attribute(flag_enum) +#define G_GNUC_FLAG_ENUM __attribute__((flag_enum)) +#else +#define G_GNUC_FLAG_ENUM +#endif +#endif diff -Nru fwupd-2.0.8/libfwupd/fwupd-client-sync.c fwupd-2.0.20/libfwupd/fwupd-client-sync.c --- fwupd-2.0.8/libfwupd/fwupd-client-sync.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-client-sync.c 2026-02-26 11:36:18.000000000 +0000 @@ -337,6 +337,55 @@ } static void +fwupd_client_search_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->array = fwupd_client_search_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_search: + * @self: a #FwupdClient + * @token: (not nullable): a search term + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Gets all the releases that match a specific token. + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 2.0.16 + **/ +GPtrArray * +fwupd_client_search(FwupdClient *self, + const gchar *token, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(token != NULL, NULL); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return NULL; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_search_async(self, token, cancellable, fwupd_client_search_cb, helper); + g_main_loop_run(helper->loop); + if (helper->array == NULL) { + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + return g_steal_pointer(&helper->array); +} + +static void fwupd_client_get_downgrades_cb(GObject *source, GAsyncResult *res, gpointer user_data) { FwupdClientHelper *helper = (FwupdClientHelper *)user_data; @@ -1870,6 +1919,61 @@ g_main_loop_run(helper->loop); if (!helper->ret) { g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + return TRUE; +} + +static void +fwupd_client_clean_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FwupdClientHelper *helper = (FwupdClientHelper *)user_data; + helper->ret = fwupd_client_clean_remote_finish(FWUPD_CLIENT(source), res, &helper->error); + g_main_loop_quit(helper->loop); +} + +/** + * fwupd_client_clean_remote: + * @self: a #FwupdClient + * @remote_id: the remote ID, e.g. `lvfs-testing` + * @cancellable: (nullable): optional #GCancellable + * @error: (nullable): optional return location for an error + * + * Cleans a system remote, deleting metadata as required. + * + * NOTE: User authentication may be required to complete this action. + * + * Returns: %TRUE for success + * + * Since: 2.0.17 + **/ +gboolean +fwupd_client_clean_remote(FwupdClient *self, + const gchar *remote_id, + GCancellable *cancellable, + GError **error) +{ + g_autoptr(FwupdClientHelper) helper = NULL; + + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(remote_id != NULL, FALSE); + g_return_val_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* connect */ + if (!fwupd_client_connect(self, cancellable, error)) + return FALSE; + + /* call async version and run loop until complete */ + helper = fwupd_client_helper_new(self); + fwupd_client_clean_remote_async(self, + remote_id, + cancellable, + fwupd_client_clean_remote_cb, + helper); + g_main_loop_run(helper->loop); + if (!helper->ret) { + g_propagate_error(error, g_steal_pointer(&helper->error)); return FALSE; } return TRUE; diff -Nru fwupd-2.0.8/libfwupd/fwupd-client-sync.h fwupd-2.0.20/libfwupd/fwupd-client-sync.h --- fwupd-2.0.8/libfwupd/fwupd-client-sync.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-client-sync.h 2026-02-26 11:36:18.000000000 +0000 @@ -36,6 +36,11 @@ GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); GPtrArray * +fwupd_client_search(FwupdClient *self, + const gchar *token, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); +GPtrArray * fwupd_client_get_downgrades(FwupdClient *self, const gchar *device_id, GCancellable *cancellable, @@ -186,6 +191,11 @@ GCancellable *cancellable, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2, 3); gboolean +fwupd_client_clean_remote(FwupdClient *self, + const gchar *remote_id, + GCancellable *cancellable, + GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2, 3); +gboolean fwupd_client_modify_device(FwupdClient *self, const gchar *device_id, const gchar *key, diff -Nru fwupd-2.0.8/libfwupd/fwupd-client.c fwupd-2.0.20/libfwupd/fwupd-client.c --- fwupd-2.0.8/libfwupd/fwupd-client.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-client.c 2026-02-26 11:36:18.000000000 +0000 @@ -23,7 +23,6 @@ #include "fwupd-bios-setting.h" #include "fwupd-client-private.h" -#include "fwupd-client-sync.h" #include "fwupd-codec.h" #include "fwupd-common-private.h" #include "fwupd-device-private.h" @@ -81,6 +80,8 @@ gchar *user_agent; GHashTable *hints; /* str:str */ GHashTable *immediate_requests; /* str:FwupdRequest */ + GStrv hwid_keys; + GStrv hwid_values; } FwupdClientPrivate; typedef struct { @@ -332,7 +333,7 @@ fwupd_client_build_user_agent_system(void) { #ifdef HAVE_UTSNAME_H - struct utsname name_tmp; + struct utsname name_tmp = {0}; #endif g_autofree gchar *locale = NULL; g_autofree gchar *os_release = NULL; @@ -340,7 +341,6 @@ /* system, architecture and kernel, e.g. "Linux i686 4.14.5" */ #ifdef HAVE_UTSNAME_H - memset(&name_tmp, 0, sizeof(struct utsname)); if (uname(&name_tmp) >= 0) { g_ptr_array_add(ids, g_strdup_printf("%s %s %s", @@ -523,7 +523,7 @@ if (priv->status == status) return; priv->status = status; - g_debug("Emitting ::status-changed() [%s]", fwupd_status_to_string(priv->status)); + g_debug("emitting ::status-changed() [%s]", fwupd_status_to_string(priv->status)); fwupd_client_object_notify(self, "status"); } @@ -695,7 +695,7 @@ g_autoptr(FwupdDevice) dev = NULL; g_autoptr(GError) error = NULL; if (g_strcmp0(signal_name, "Changed") == 0) { - g_debug("Emitting ::changed()"); + g_debug("emitting ::changed()"); fwupd_client_signal_emit_changed(self); return; } @@ -705,7 +705,7 @@ g_warning("failed to build FwupdDevice[DeviceAdded]: %s", error->message); return; } - g_debug("Emitting ::device-added(%s)", fwupd_device_get_id(dev)); + g_debug("emitting ::device-added(%s)", fwupd_device_get_id(dev)); fwupd_client_signal_emit_object(self, SIGNAL_DEVICE_ADDED, G_OBJECT(dev)); return; } @@ -715,7 +715,7 @@ g_warning("failed to build FwupdDevice[DeviceRemoved]: %s", error->message); return; } - g_debug("Emitting ::device-removed(%s)", fwupd_device_get_id(dev)); + g_debug("emitting ::device-removed(%s)", fwupd_device_get_id(dev)); fwupd_client_signal_emit_object(self, SIGNAL_DEVICE_REMOVED, G_OBJECT(dev)); return; } @@ -725,7 +725,7 @@ g_warning("failed to build FwupdDevice[DeviceChanged]: %s", error->message); return; } - g_debug("Emitting ::device-changed(%s)", fwupd_device_get_id(dev)); + g_debug("emitting ::device-changed(%s)", fwupd_device_get_id(dev)); fwupd_client_signal_emit_object(self, SIGNAL_DEVICE_CHANGED, G_OBJECT(dev)); /* invalidate request */ @@ -746,7 +746,7 @@ g_warning("failed to convert DeviceRequest: %s", error->message); return; } - g_debug("Emitting ::device-request(%s)", fwupd_request_get_id(req)); + g_debug("emitting ::device-request(%s)", fwupd_request_get_id(req)); fwupd_client_signal_emit_object(self, SIGNAL_DEVICE_REQUEST, G_OBJECT(req)); /* we may need to invalidate this later */ @@ -758,7 +758,7 @@ } return; } - g_debug("Unknown signal name '%s' from %s", signal_name, sender_name); + g_debug("unknown signal name '%s' from %s", signal_name, sender_name); } /** @@ -920,7 +920,7 @@ (void)curl_easy_setopt(helper->curl, CURLOPT_CAINFO, "ca-bundle.crt"); #endif #if CURL_AT_LEAST_VERSION(7, 71, 0) - (void)curl_easy_setopt(helper->curl, CURLOPT_SSL_OPTIONS, CURLSSLOPT_NATIVE_CA); + (void)curl_easy_setopt(helper->curl, CURLOPT_SSL_OPTIONS, (glong)CURLSSLOPT_NATIVE_CA); #endif /* this disables the double-compression of the firmware.xml.gz file */ @@ -973,6 +973,7 @@ g_autoptr(GVariant) val8 = NULL; g_autoptr(GVariant) val9 = NULL; g_autoptr(GVariant) val10 = NULL; + g_autoptr(GVariant) val_hwids = NULL; g_autoptr(GMutexLocker) locker = NULL; proxy = g_dbus_proxy_new_finish(res, &error); @@ -1034,6 +1035,20 @@ if (val9 != NULL) priv->only_trusted = g_variant_get_boolean(val9); + val_hwids = g_dbus_proxy_get_cached_property(priv->proxy, "Hwids"); + if (val_hwids != NULL) { + guint size = g_variant_n_children(val_hwids); + priv->hwid_keys = g_new0(gchar *, size + 1); + priv->hwid_values = g_new0(gchar *, size + 1); + for (guint i = 0; i < size; i++) { + const gchar *hwid_value; + const gchar *hwid_key; + g_variant_get_child(val_hwids, i, "(&s&s)", &hwid_key, &hwid_value); + priv->hwid_keys[i] = g_strdup(hwid_key); + priv->hwid_values[i] = g_strdup(hwid_value); + } + } + /* build client hints */ g_variant_builder_init(&builder, G_VARIANT_TYPE("a{ss}")); g_hash_table_iter_init(&iter, priv->hints); @@ -1224,9 +1239,13 @@ val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); if (val == NULL) { - fwupd_client_fixup_dbus_error(error); - g_task_return_error(task, g_steal_pointer(&error)); - return; + if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CLOSED)) { + g_debug("ignoring: %s", error->message); + } else { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } } /* success */ @@ -2317,6 +2336,93 @@ } static void +fwupd_client_search_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) array = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + array = fwupd_codec_array_from_variant(val, FWUPD_TYPE_RELEASE, &error); + if (array == NULL) { + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_pointer(task, g_steal_pointer(&array), (GDestroyNotify)g_ptr_array_unref); +} + +/** + * fwupd_client_search_async: + * @self: a #FwupdClient + * @token: (not nullable): a search term + * @cancellable: (nullable): optional #GCancellable + * @callback: (scope async) (closure callback_data): the function to run on completion + * @callback_data: the data to pass to @callback + * + * Gets all the releases that match a specific token. + * + * You must have called [method@Client.connect_async] on @self before using + * this method. + * + * Since: 2.0.16 + **/ +void +fwupd_client_search_async(FwupdClient *self, + const gchar *token, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(token != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "Search", + g_variant_new("(s)", token), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_search_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_search_finish: + * @self: a #FwupdClient + * @res: (not nullable): the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.search_async]. + * + * Returns: (element-type FwupdRelease) (transfer container): results + * + * Since: 2.0.16 + **/ +GPtrArray * +fwupd_client_search_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), NULL); + g_return_val_if_fail(g_task_is_valid(res, self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + return g_task_propagate_pointer(G_TASK(res), error); +} + +static void fwupd_client_get_downgrades_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(GTask) task = G_TASK(user_data); @@ -3267,10 +3373,10 @@ callback_data); #else g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); - g_task_return_new_error(task, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Install CAB only supported on Linux"); + g_task_return_new_error_literal(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Install CAB only supported on Linux"); #endif } @@ -3352,10 +3458,10 @@ callback_data); #else g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); - g_task_return_new_error(task, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Install CAB async only supported on Linux"); + g_task_return_new_error_literal(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Install CAB async only supported on Linux"); #endif } @@ -3527,10 +3633,10 @@ /* get the default release only until other parts of fwupd can cope */ locations = fwupd_release_get_locations(data->release); if (locations->len == 0) { - g_task_return_new_error(task, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "release missing URI"); + g_task_return_new_error_literal(task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "release missing URI"); return; } uri_tmp = g_ptr_array_index(locations, 0); @@ -3590,10 +3696,10 @@ } } if (uris_built->len == 0) { - g_task_return_new_error(task, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "No URIs to download"); + g_task_return_new_error_literal(task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "No URIs to download"); return; } @@ -3832,10 +3938,10 @@ fwupd_client_get_details_stream_async(self, istr, cancellable, callback, callback_data); #else g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); - g_task_return_new_error(task, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Get Details only supported on Linux"); + g_task_return_new_error_literal(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Get Details only supported on Linux"); #endif } @@ -3901,10 +4007,10 @@ fwupd_client_get_details_stream_async(self, istr, cancellable, callback, callback_data); #else g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); - g_task_return_new_error(task, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Get Details only supported on Linux"); + g_task_return_new_error_literal(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Get Details only supported on Linux"); #endif } @@ -4056,6 +4162,27 @@ } /** + * fwupd_client_get_hwids: + * @self: a #FwupdClient + * @keys: (out) (optional): CHID keys + * @values: (out) (optional): CHID values + * + * Gets the daemon hardware IDs, sometimes called CHIDs. + * + * Since: 2.0.17 + **/ +void +fwupd_client_get_hwids(FwupdClient *self, GStrv *keys, GStrv *values) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FWUPD_IS_CLIENT(self)); + if (keys != NULL) + *keys = g_strdupv(priv->hwid_keys); + if (values != NULL) + *values = g_strdupv(priv->hwid_values); +} + +/** * fwupd_client_get_battery_level: * @self: a #FwupdClient * @@ -4069,7 +4196,7 @@ fwupd_client_get_battery_level(FwupdClient *self) { FwupdClientPrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FWUPD_IS_CLIENT(self), FWUPD_BATTERY_LEVEL_INVALID); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), G_MAXUINT32); return priv->battery_level; } @@ -4088,7 +4215,7 @@ fwupd_client_get_battery_threshold(FwupdClient *self) { FwupdClientPrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FWUPD_IS_CLIENT(self), FWUPD_BATTERY_LEVEL_INVALID); + g_return_val_if_fail(FWUPD_IS_CLIENT(self), G_MAXUINT32); return priv->battery_threshold; } @@ -4301,10 +4428,10 @@ callback_data); #else g_autoptr(GTask) task = g_task_new(self, cancellable, callback, callback_data); - g_task_return_new_error(task, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Update metadata only supported on Linux"); + g_task_return_new_error_literal(task, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Update metadata only supported on Linux"); #endif } @@ -4433,7 +4560,7 @@ } data->signature = g_steal_pointer(&bytes); if (!fwupd_remote_load_signature_bytes(data->remote, data->signature, &error)) { - g_prefix_error(&error, "Failed to load signature: "); + g_prefix_error_literal(&error, "Failed to load signature: "); g_task_return_error(task, g_steal_pointer(&error)); return; } @@ -5259,6 +5386,84 @@ } static void +fwupd_client_clean_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(GTask) task = G_TASK(user_data); + g_autoptr(GError) error = NULL; + g_autoptr(GVariant) val = NULL; + + val = g_dbus_proxy_call_finish(G_DBUS_PROXY(source), res, &error); + if (val == NULL) { + fwupd_client_fixup_dbus_error(error); + g_task_return_error(task, g_steal_pointer(&error)); + return; + } + + /* success */ + g_task_return_boolean(task, TRUE); +} + +/** + * fwupd_client_clean_remote_async: + * @self: a #FwupdClient + * @remote_id: the remote ID, e.g. `lvfs-testing` + * @cancellable: (nullable): optional #GCancellable + * @callback: (scope async) (closure callback_data): the function to run on completion + * @callback_data: the data to pass to @callback + * + * Cleans a system remote, deleting metadata as required. + * + * Since: 2.0.17 + **/ +void +fwupd_client_clean_remote_async(FwupdClient *self, + const gchar *remote_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + g_autoptr(GTask) task = NULL; + + g_return_if_fail(FWUPD_IS_CLIENT(self)); + g_return_if_fail(remote_id != NULL); + g_return_if_fail(cancellable == NULL || G_IS_CANCELLABLE(cancellable)); + g_return_if_fail(priv->proxy != NULL); + + /* call into daemon */ + task = g_task_new(self, cancellable, callback, callback_data); + g_dbus_proxy_call(priv->proxy, + "CleanRemote", + g_variant_new("(s)", remote_id), + G_DBUS_CALL_FLAGS_NONE, + FWUPD_CLIENT_DBUS_PROXY_TIMEOUT, + cancellable, + fwupd_client_clean_remote_cb, + g_steal_pointer(&task)); +} + +/** + * fwupd_client_clean_remote_finish: + * @self: a #FwupdClient + * @res: (not nullable): the asynchronous result + * @error: (nullable): optional return location for an error + * + * Gets the result of [method@FwupdClient.clean_remote_async]. + * + * Returns: %TRUE for success + * + * Since: 2.0.17 + **/ +gboolean +fwupd_client_clean_remote_finish(FwupdClient *self, GAsyncResult *res, GError **error) +{ + g_return_val_if_fail(FWUPD_IS_CLIENT(self), FALSE); + g_return_val_if_fail(g_task_is_valid(res, self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return g_task_propagate_boolean(G_TASK(res), error); +} + +static void fwupd_client_modify_device_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(GTask) task = G_TASK(user_data); @@ -5602,6 +5807,14 @@ res = curl_easy_perform(curl); fwupd_client_set_status(self, FWUPD_STATUS_IDLE); fwupd_client_set_percentage(self, 100); + if (res == CURLE_SEND_ERROR || res == CURLE_RECV_ERROR) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_TIMED_OUT, + "transient failure: %s", + errbuf); + return NULL; + } if (res != CURLE_OK) { if (errbuf[0] != '\0') { g_set_error(error, @@ -5623,6 +5836,15 @@ curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code); g_info("status-code was %ld", status_code); if (status_code == 429) { + g_autofree gchar *str = g_strndup((const gchar *)buf->data, MIN(buf->len, 4000)); + if (g_str_is_ascii(str)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_TIMED_OUT, + "Failed to download due to server limit: %s", + str); + return NULL; + } g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, @@ -5677,32 +5899,48 @@ return TRUE; } -static GBytes * -fwupd_client_download_http_retry(FwupdClient *self, CURL *curl, const gchar *url, GError **error) +static gboolean +fwupd_client_test_network(const gchar *url, GError **error) { - FwupdClientPrivate *priv = GET_PRIVATE(self); - GNetworkMonitor *monitor = g_network_monitor_get_default(); - gulong delay_ms = 2500; + GNetworkMonitor *monitor; + g_autoptr(GUri) uri = NULL; g_autoptr(GError) error_monitor = NULL; g_autoptr(GSocketConnectable) address = NULL; - g_autoptr(GUri) uri = NULL; - /* test if we can reach this network */ + if (g_getenv("FWUPD_IGNORE_NETWORK_REACHABLE") != NULL) + return TRUE; + uri = g_uri_parse(url, G_URI_FLAGS_NONE, error); if (uri == NULL) - return NULL; + return FALSE; + address = g_network_address_parse(g_uri_get_host(uri), g_uri_get_port(uri), error); if (address == NULL) - return NULL; + return FALSE; + + monitor = g_network_monitor_get_default(); if (!g_network_monitor_can_reach(monitor, address, NULL, &error_monitor)) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_REACHABLE, "network is unreachable: %s", error_monitor->message); - return NULL; + return FALSE; } + return TRUE; +} + +static GBytes * +fwupd_client_download_http_retry(FwupdClient *self, CURL *curl, const gchar *url, GError **error) +{ + FwupdClientPrivate *priv = GET_PRIVATE(self); + gulong delay_ms = 2500; + + /* test if we can reach this network */ + if (!fwupd_client_test_network(url, error)) + return NULL; + for (guint i = 0;; i++, delay_ms *= 2) { g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error_local = NULL; @@ -5752,6 +5990,7 @@ FWUPD_ERROR_INVALID_FILE, "not sure how to handle: %s", url); + /* nocheck:error-false-return */ } if (i == helper->urls->len - 1) { g_task_return_error(task, g_steal_pointer(&error)); @@ -6034,17 +6273,17 @@ /* parse */ bytes = fwupd_client_upload_bytes_finish(FWUPD_CLIENT(source), res, &error); if (bytes == NULL) { - g_prefix_error(&error, "failed to upload report: "); + g_prefix_error_literal(&error, "failed to upload report: "); g_task_return_error(task, g_steal_pointer(&error)); return; } /* server returned nothing, and probably exploded in a ball of flames */ if (g_bytes_get_size(bytes) == 0) { - g_task_return_new_error(task, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "failed to upload, zero length data"); + g_task_return_new_error_literal(task, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to upload, zero length data"); return; } @@ -7649,6 +7888,8 @@ FwupdClient *self = FWUPD_CLIENT(object); FwupdClientPrivate *priv = GET_PRIVATE(self); + g_strfreev(priv->hwid_keys); + g_strfreev(priv->hwid_values); g_clear_pointer(&priv->main_ctx, g_main_context_unref); g_free(priv->user_agent); g_free(priv->package_name); diff -Nru fwupd-2.0.8/libfwupd/fwupd-client.h fwupd-2.0.20/libfwupd/fwupd-client.h --- fwupd-2.0.8/libfwupd/fwupd-client.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-client.h 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,6 @@ #include "fwupd-build.h" #include "fwupd-device.h" #include "fwupd-enums.h" -#include "fwupd-plugin.h" #include "fwupd-remote.h" #include "fwupd-request.h" @@ -61,7 +60,7 @@ FWUPD_CLIENT_DOWNLOAD_FLAG_ONLY_P2P = 1 << 0, /*< private >*/ FWUPD_CLIENT_DOWNLOAD_FLAG_LAST -} FwupdClientDownloadFlags; +} G_GNUC_FLAG_ENUM FwupdClientDownloadFlags; /** * FwupdClientUploadFlags: @@ -87,7 +86,7 @@ FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART = 1 << 0, /*< private >*/ FWUPD_CLIENT_UPLOAD_FLAG_LAST -} FwupdClientUploadFlags; +} G_GNUC_FLAG_ENUM FwupdClientUploadFlags; FwupdClient * fwupd_client_new(void); @@ -153,6 +152,16 @@ GAsyncResult *res, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); void +fwupd_client_search_async(FwupdClient *self, + const gchar *token, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) G_GNUC_NON_NULL(1, 2); +GPtrArray * +fwupd_client_search_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); +void fwupd_client_get_downgrades_async(FwupdClient *self, const gchar *device_id, GCancellable *cancellable, @@ -413,6 +422,16 @@ GAsyncResult *res, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); void +fwupd_client_clean_remote_async(FwupdClient *self, + const gchar *remote_id, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer callback_data) G_GNUC_NON_NULL(1, 2, 3); +gboolean +fwupd_client_clean_remote_finish(FwupdClient *self, + GAsyncResult *res, + GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); +void fwupd_client_modify_device_async(FwupdClient *self, const gchar *device_id, const gchar *key, @@ -516,6 +535,8 @@ fwupd_client_get_host_machine_id(FwupdClient *self) G_GNUC_NON_NULL(1); const gchar * fwupd_client_get_host_security_id(FwupdClient *self) G_GNUC_NON_NULL(1); +void +fwupd_client_get_hwids(FwupdClient *self, GStrv *keys, GStrv *values) G_GNUC_NON_NULL(1); guint32 fwupd_client_get_battery_level(FwupdClient *self) G_GNUC_NON_NULL(1); guint32 diff -Nru fwupd-2.0.8/libfwupd/fwupd-codec.c fwupd-2.0.20/libfwupd/fwupd-codec.c --- fwupd-2.0.8/libfwupd/fwupd-codec.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-codec.c 2026-02-26 11:36:18.000000000 +0000 @@ -256,7 +256,10 @@ json_generator_set_root(json_generator, json_root); data = json_generator_to_data(json_generator, NULL); if (data == NULL) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed to convert to json"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to convert to json"); return NULL; } return g_steal_pointer(&data); @@ -605,7 +608,7 @@ return; date = g_date_time_new_from_unix_utc((gint64)value); - tmp = g_date_time_format(date, "%F"); + tmp = g_date_time_format(date, "%F %T"); fwupd_codec_string_append(str, idt, key, tmp); } @@ -717,3 +720,35 @@ json_builder_add_string_value(builder, value[i]); json_builder_end_array(builder); } + +/** + * fwupd_codec_json_append_map: + * @builder: (not nullable): a #JsonBuilder + * @key: (not nullable): a string + * @value: (element-type utf8 utf8): a hash table + * + * Appends a key and string hash map to a JSON builder. + * + * Since: 2.0.10 + */ +void +fwupd_codec_json_append_map(JsonBuilder *builder, const gchar *key, GHashTable *value) +{ + GHashTableIter iter; + gpointer hash_key, hash_value; + + g_return_if_fail(JSON_IS_BUILDER(builder)); + g_return_if_fail(key != NULL); + + if (value == NULL) + return; + json_builder_set_member_name(builder, key); + json_builder_begin_object(builder); + g_hash_table_iter_init(&iter, value); + while (g_hash_table_iter_next(&iter, &hash_key, &hash_value)) { + fwupd_codec_json_append(builder, + (const gchar *)hash_key, + (const gchar *)hash_value); + } + json_builder_end_object(builder); +} diff -Nru fwupd-2.0.8/libfwupd/fwupd-codec.h fwupd-2.0.20/libfwupd/fwupd-codec.h --- fwupd-2.0.8/libfwupd/fwupd-codec.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-codec.h 2026-02-26 11:36:18.000000000 +0000 @@ -44,7 +44,7 @@ * Since: 2.0.8 */ FWUPD_CODEC_FLAG_COMPRESSED = 1 << 1, -} FwupdCodecFlags; +} G_GNUC_FLAG_ENUM FwupdCodecFlags; struct _FwupdCodecInterface { GTypeInterface g_iface; @@ -123,6 +123,9 @@ fwupd_codec_json_append_strv(JsonBuilder *builder, const gchar *key, gchar **value) G_GNUC_NON_NULL(1, 2); void +fwupd_codec_json_append_map(JsonBuilder *builder, const gchar *key, GHashTable *value) + G_GNUC_NON_NULL(1, 2); +void fwupd_codec_json_append_int(JsonBuilder *builder, const gchar *key, guint64 value) G_GNUC_NON_NULL(1, 2); void diff -Nru fwupd-2.0.8/libfwupd/fwupd-common.c fwupd-2.0.20/libfwupd/fwupd-common.c --- fwupd-2.0.8/libfwupd/fwupd-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -7,9 +7,7 @@ #include "config.h" #include "fwupd-common-private.h" -#include "fwupd-device.h" #include "fwupd-error.h" -#include "fwupd-release.h" #ifdef HAVE_GIO_UNIX #include @@ -308,7 +306,7 @@ fwupd_guid_hash_data(const guint8 *data, gsize datasz, FwupdGuidFlags flags) { gsize digestlen = 20; - guint8 hash[20]; + guint8 hash[20] = {0}; fwupd_guid_t uu_new; g_autoptr(GChecksum) csum = NULL; const fwupd_guid_t uu_default = {0x6b, @@ -502,7 +500,7 @@ rc = g_unlink(tmp_file); if (rc != 0) { if (!g_close(fd, error)) { - g_prefix_error(error, "failed to close temporary file: "); + g_prefix_error_literal(error, "failed to close temporary file: "); return NULL; } g_set_error_literal(error, @@ -535,7 +533,7 @@ FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "failed to seek: %s", - g_strerror(errno)); + fwupd_strerror(errno)); return NULL; } return G_UNIX_INPUT_STREAM(g_unix_input_stream_new(fd, TRUE)); @@ -549,7 +547,12 @@ { gint fd = open(fn, O_RDONLY); if (fd < 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "failed to open %s", fn); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to open %s: %s", + fn, + fwupd_strerror(errno)); return NULL; } return G_UNIX_INPUT_STREAM(g_unix_input_stream_new(fd, TRUE)); @@ -563,7 +566,12 @@ { gint fd = g_open(fn, O_RDWR | O_CREAT, S_IRWXU); if (fd < 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "failed to open %s", fn); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to open %s: %s", + fn, + fwupd_strerror(errno)); return NULL; } return G_UNIX_OUTPUT_STREAM(g_unix_output_stream_new(fd, TRUE)); diff -Nru fwupd-2.0.8/libfwupd/fwupd-common.h fwupd-2.0.20/libfwupd/fwupd-common.h --- fwupd-2.0.8/libfwupd/fwupd-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -68,7 +68,7 @@ FWUPD_GUID_FLAG_MIXED_ENDIAN = 1 << 1, /*< private >*/ FWUPD_GUID_FLAG_LAST -} FwupdGuidFlags; +} G_GNUC_FLAG_ENUM FwupdGuidFlags; /* GObject Introspection does not understand typedefs with sizes */ #ifdef __GI_SCANNER__ diff -Nru fwupd-2.0.8/libfwupd/fwupd-context-test.c fwupd-2.0.20/libfwupd/fwupd-context-test.c --- fwupd-2.0.8/libfwupd/fwupd-context-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-context-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -31,7 +31,7 @@ g_autoptr(GMainContextPusher) pusher = g_main_context_pusher_new(context); g_assert_nonnull(pusher); - g_message("Calling fwupd_client_get_devices() in thread %p with main context %p", + g_message("calling fwupd_client_get_devices() in thread %p with main context %p", g_thread_self(), g_main_context_get_thread_default()); if (!fwupd_client_connect(self->client, NULL, &error_local)) @@ -95,10 +95,10 @@ /* only some of the CI targets have a DBus daemon */ if (!fwupd_thread_test_has_system_bus()) { - g_message("D-Bus system bus unavailable, skipping tests."); + g_message("D-Bus system bus unavailable, skipping tests"); return 0; } - g_message("Created FwupdClient in thread %p with main context %p", + g_message("created FwupdClient in thread %p with main context %p", g_thread_self(), g_main_context_get_thread_default()); g_signal_connect(FWUPD_CLIENT(client), diff -Nru fwupd-2.0.8/libfwupd/fwupd-device.c fwupd-2.0.20/libfwupd/fwupd-device.c --- fwupd-2.0.8/libfwupd/fwupd-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -85,6 +85,7 @@ PROP_BATTERY_LEVEL, PROP_BATTERY_THRESHOLD, PROP_PROBLEMS, + PROP_VENDOR, PROP_LAST }; @@ -1026,6 +1027,7 @@ g_free(priv->vendor); priv->vendor = g_strdup(vendor); + g_object_notify(G_OBJECT(self), "vendor"); } static void @@ -1350,7 +1352,7 @@ fwupd_device_get_battery_level(FwupdDevice *self) { FwupdDevicePrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FWUPD_IS_DEVICE(self), G_MAXUINT); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), G_MAXUINT32); return priv->battery_level; } @@ -1399,7 +1401,7 @@ { FwupdDevicePrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FWUPD_IS_DEVICE(self), FWUPD_BATTERY_LEVEL_INVALID); + g_return_val_if_fail(FWUPD_IS_DEVICE(self), G_MAXUINT32); /* default value */ if (priv->battery_threshold == FWUPD_BATTERY_LEVEL_INVALID) @@ -3044,9 +3046,11 @@ fwupd_codec_json_append(builder, FWUPD_RESULT_KEY_VERSION_BOOTLOADER, priv->version_bootloader); - fwupd_codec_json_append(builder, - FWUPD_RESULT_KEY_VERSION_FORMAT, - fwupd_version_format_to_string(priv->version_format)); + if (priv->version_format != FWUPD_VERSION_FORMAT_UNKNOWN) { + fwupd_codec_json_append(builder, + FWUPD_RESULT_KEY_VERSION_FORMAT, + fwupd_version_format_to_string(priv->version_format)); + } if (priv->flashes_left > 0) { fwupd_codec_json_append_int(builder, FWUPD_RESULT_KEY_FLASHES_LEFT, @@ -3521,10 +3525,12 @@ idt, FWUPD_RESULT_KEY_VERSION_BOOTLOADER, priv->version_bootloader); - fwupd_codec_string_append(str, - idt, - FWUPD_RESULT_KEY_VERSION_FORMAT, - fwupd_version_format_to_string(priv->version_format)); + if (priv->version_format != FWUPD_VERSION_FORMAT_UNKNOWN) { + fwupd_codec_string_append(str, + idt, + FWUPD_RESULT_KEY_VERSION_FORMAT, + fwupd_version_format_to_string(priv->version_format)); + } if (priv->flashes_left < 2) { fwupd_codec_string_append_int(str, idt, @@ -3600,6 +3606,9 @@ case PROP_VERSION: g_value_set_string(value, priv->version); break; + case PROP_VENDOR: + g_value_set_string(value, priv->vendor); + break; case PROP_VERSION_FORMAT: g_value_set_uint(value, priv->version_format); break; @@ -3647,6 +3656,9 @@ case PROP_VERSION: fwupd_device_set_version(self, g_value_get_string(value)); break; + case PROP_VENDOR: + fwupd_device_set_vendor(self, g_value_get_string(value)); + break; case PROP_ID: fwupd_device_set_id(self, g_value_get_string(value)); break; @@ -3714,6 +3726,20 @@ g_object_class_install_property(object_class, PROP_VERSION, pspec); /** + * FwupdDevice:vendor: + * + * The device vendor. + * + * Since: 2.0.17 + */ + pspec = g_param_spec_string("vendor", + NULL, + NULL, + NULL, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_VENDOR, pspec); + + /** * FwupdDevice:id: * * The device ID. diff -Nru fwupd-2.0.8/libfwupd/fwupd-device.h fwupd-2.0.20/libfwupd/fwupd-device.h --- fwupd-2.0.8/libfwupd/fwupd-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,7 +8,6 @@ #include -#include "fwupd-enums.h" #include "fwupd-release.h" #include "fwupd-request.h" diff -Nru fwupd-2.0.8/libfwupd/fwupd-enums-private.h fwupd-2.0.20/libfwupd/fwupd-enums-private.h --- fwupd-2.0.8/libfwupd/fwupd-enums-private.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-enums-private.h 2026-02-26 11:36:18.000000000 +0000 @@ -631,6 +631,14 @@ **/ #define FWUPD_RESULT_KEY_KERNEL_CURRENT_VALUE "KernelCurrentValue" /** + * FWUPD_RESULT_KEY_BIOS_SETTING_FILENAME: + * + * Result key to represent the filename within @path for BIOS setting value operations. + * + * The D-Bus type signature string is 's' i.e. a string. + **/ +#define FWUPD_RESULT_KEY_BIOS_SETTING_FILENAME "BiosSettingFilename" +/** * FWUPD_RESULT_KEY_KERNEL_TARGET_VALUE: * * Result key to represent the target kernel setting. diff -Nru fwupd-2.0.8/libfwupd/fwupd-enums.c fwupd-2.0.20/libfwupd/fwupd-enums.c --- fwupd-2.0.8/libfwupd/fwupd-enums.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-enums.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,8 +21,6 @@ const gchar * fwupd_status_to_string(FwupdStatus status) { - if (status == FWUPD_STATUS_UNKNOWN) - return "unknown"; if (status == FWUPD_STATUS_IDLE) return "idle"; if (status == FWUPD_STATUS_DECOMPRESSING) @@ -67,8 +65,6 @@ FwupdStatus fwupd_status_from_string(const gchar *status) { - if (g_strcmp0(status, "unknown") == 0) - return FWUPD_STATUS_UNKNOWN; if (g_strcmp0(status, "idle") == 0) return FWUPD_STATUS_IDLE; if (g_strcmp0(status, "decompressing") == 0) @@ -97,7 +93,7 @@ return FWUPD_STATUS_SHUTDOWN; if (g_strcmp0(status, "waiting-for-user") == 0) return FWUPD_STATUS_WAITING_FOR_USER; - return FWUPD_STATUS_LAST; + return FWUPD_STATUS_UNKNOWN; } /** @@ -329,6 +325,10 @@ return "display-required"; if (device_problem == FWUPD_DEVICE_PROBLEM_LOWER_PRIORITY) return "lower-priority"; + if (device_problem == FWUPD_DEVICE_PROBLEM_INSECURE_PLATFORM) + return "insecure-platform"; + if (device_problem == FWUPD_DEVICE_PROBLEM_FIRMWARE_LOCKED) + return "firmware-locked"; if (device_problem == FWUPD_DEVICE_PROBLEM_UNKNOWN) return "unknown"; return NULL; @@ -375,6 +375,10 @@ return FWUPD_DEVICE_PROBLEM_DISPLAY_REQUIRED; if (g_strcmp0(device_problem, "lower-priority") == 0) return FWUPD_DEVICE_PROBLEM_LOWER_PRIORITY; + if (g_strcmp0(device_problem, "insecure-platform") == 0) + return FWUPD_DEVICE_PROBLEM_INSECURE_PLATFORM; + if (g_strcmp0(device_problem, "firmware-locked") == 0) + return FWUPD_DEVICE_PROBLEM_FIRMWARE_LOCKED; return FWUPD_DEVICE_PROBLEM_UNKNOWN; } @@ -433,6 +437,8 @@ return "ready"; if (plugin_flag == FWUPD_PLUGIN_FLAG_TEST_ONLY) return "test-only"; + if (plugin_flag == FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION) + return "mutable-enumeration"; return NULL; } @@ -489,6 +495,8 @@ return FWUPD_PLUGIN_FLAG_READY; if (g_strcmp0(plugin_flag, "test-only") == 0) return FWUPD_PLUGIN_FLAG_TEST_ONLY; + if (g_strcmp0(plugin_flag, "mutable-enumeration") == 0) + return FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION; return FWUPD_PLUGIN_FLAG_UNKNOWN; } @@ -505,8 +513,6 @@ const gchar * fwupd_update_state_to_string(FwupdUpdateState update_state) { - if (update_state == FWUPD_UPDATE_STATE_UNKNOWN) - return "unknown"; if (update_state == FWUPD_UPDATE_STATE_PENDING) return "pending"; if (update_state == FWUPD_UPDATE_STATE_SUCCESS) @@ -533,8 +539,6 @@ FwupdUpdateState fwupd_update_state_from_string(const gchar *update_state) { - if (g_strcmp0(update_state, "unknown") == 0) - return FWUPD_UPDATE_STATE_UNKNOWN; if (g_strcmp0(update_state, "pending") == 0) return FWUPD_UPDATE_STATE_PENDING; if (g_strcmp0(update_state, "success") == 0) @@ -855,14 +859,10 @@ return FWUPD_INSTALL_FLAG_NO_HISTORY; if (g_strcmp0(str, "allow-branch-switch") == 0) return FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; - if (g_strcmp0(str, "ignore-checksum") == 0) - return FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM; - if (g_strcmp0(str, "ignore-vid-pid") == 0) - return FWUPD_INSTALL_FLAG_IGNORE_VID_PID; - if (g_strcmp0(str, "no-search") == 0) - return FWUPD_INSTALL_FLAG_NO_SEARCH; if (g_strcmp0(str, "ignore-requirements") == 0) return FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS; + if (g_strcmp0(str, "only-emulated") == 0) + return FWUPD_INSTALL_FLAG_ONLY_EMULATED; return FWUPD_INSTALL_FLAG_UNKNOWN; } @@ -892,13 +892,9 @@ return "no-history"; if (install_flags == FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) return "allow-branch-switch"; - if (install_flags == FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) - return "ignore-checksum"; - if (install_flags == FWUPD_INSTALL_FLAG_IGNORE_VID_PID) - return "ignore-vid-pid"; - if (install_flags == FWUPD_INSTALL_FLAG_NO_SEARCH) - return "no-search"; if (install_flags == FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS) return "ignore-requirements"; + if (install_flags == FWUPD_INSTALL_FLAG_ONLY_EMULATED) + return "only-emulated"; return NULL; } diff -Nru fwupd-2.0.8/libfwupd/fwupd-enums.h fwupd-2.0.20/libfwupd/fwupd-enums.h --- fwupd-2.0.8/libfwupd/fwupd-enums.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-enums.h 2026-02-26 11:36:18.000000000 +0000 @@ -6,7 +6,7 @@ #pragma once -#include +#include "fwupd-build.h" G_BEGIN_DECLS @@ -236,7 +236,7 @@ FWUPD_FEATURE_FLAG_REQUESTS_NON_GENERIC = 1 << 9, /* Since: 1.9.8 */ /*< private >*/ FWUPD_FEATURE_FLAG_UNKNOWN = G_MAXUINT64, -} FwupdFeatureFlags; +} G_GNUC_FLAG_ENUM FwupdFeatureFlags; /** * FwupdDeviceFlags: @@ -597,7 +597,7 @@ */ /*< private >*/ FWUPD_DEVICE_FLAG_UNKNOWN = G_MAXUINT64, -} FwupdDeviceFlags; +} G_GNUC_FLAG_ENUM FwupdDeviceFlags; /** * FwupdDeviceProblem: @@ -720,6 +720,23 @@ */ FWUPD_DEVICE_PROBLEM_LOWER_PRIORITY = 1ull << 12, /** + * FWUPD_DEVICE_PROBLEM_INSECURE_PLATFORM: + * + * The device is signed with an insecure key + * + * Since: 2.0.17 + */ + FWUPD_DEVICE_PROBLEM_INSECURE_PLATFORM = 1ull << 13, + /** + * FWUPD_DEVICE_PROBLEM_FIRMWARE_LOCKED: + * + * The firmware is locked in the system setup. + * + * Since: 2.0.18 + */ + FWUPD_DEVICE_PROBLEM_FIRMWARE_LOCKED = 1ull << 14, + + /** * FWUPD_DEVICE_PROBLEM_UNKNOWN: * * This problem is not defined, this typically will happen from mismatched @@ -728,7 +745,7 @@ * Since: 1.8.1 */ FWUPD_DEVICE_PROBLEM_UNKNOWN = G_MAXUINT64, -} FwupdDeviceProblem; +} G_GNUC_FLAG_ENUM FwupdDeviceProblem; /** * FwupdReleaseFlags: @@ -824,24 +841,54 @@ * Since: 1.2.6 */ FWUPD_RELEASE_FLAG_UNKNOWN = G_MAXUINT64, -} FwupdReleaseFlags; +} G_GNUC_FLAG_ENUM FwupdReleaseFlags; /** * FwupdReleaseUrgency: - * @FWUPD_RELEASE_URGENCY_UNKNOWN: Unknown - * @FWUPD_RELEASE_URGENCY_LOW: Low - * @FWUPD_RELEASE_URGENCY_MEDIUM: Medium - * @FWUPD_RELEASE_URGENCY_HIGH: High - * @FWUPD_RELEASE_URGENCY_CRITICAL: Critical, e.g. a security fix * * The release urgency. **/ typedef enum { - FWUPD_RELEASE_URGENCY_UNKNOWN, /* Since: 1.4.0 */ - FWUPD_RELEASE_URGENCY_LOW, /* Since: 1.4.0 */ - FWUPD_RELEASE_URGENCY_MEDIUM, /* Since: 1.4.0 */ - FWUPD_RELEASE_URGENCY_HIGH, /* Since: 1.4.0 */ - FWUPD_RELEASE_URGENCY_CRITICAL, /* Since: 1.4.0 */ + /** + * FWUPD_RELEASE_URGENCY_UNKNOWN: + * + * Unknown. + * + * Since: 1.4.0 + */ + FWUPD_RELEASE_URGENCY_UNKNOWN, + /** + * FWUPD_RELEASE_URGENCY_LOW: + * + * Low. + * + * Since: 1.4.0 + */ + FWUPD_RELEASE_URGENCY_LOW, + /** + * FWUPD_RELEASE_URGENCY_MEDIUM: + * + * Medium. + * + * Since: 1.4.0 + */ + FWUPD_RELEASE_URGENCY_MEDIUM, + /** + * FWUPD_RELEASE_URGENCY_HIGH: + * + * High. + * + * Since: 1.4.0 + */ + FWUPD_RELEASE_URGENCY_HIGH, + /** + * FWUPD_RELEASE_URGENCY_CRITICAL: + * + * Critical, e.g. a security fix. + * + * Since: 1.4.0 + */ + FWUPD_RELEASE_URGENCY_CRITICAL, /*< private >*/ FWUPD_RELEASE_URGENCY_LAST } FwupdReleaseUrgency; @@ -1026,6 +1073,15 @@ */ FWUPD_PLUGIN_FLAG_TEST_ONLY = 1ull << 18, /** + * FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION: + * + * Some devices supported by the plugin may cause a device to momentarily + * stop working while probing. + * + * Since: 2.0.12 + */ + FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION = 1ull << 19, + /** * FWUPD_PLUGIN_FLAG_UNKNOWN: * * The plugin flag is unknown. @@ -1034,7 +1090,7 @@ * Since: 1.5.0 */ FWUPD_PLUGIN_FLAG_UNKNOWN = G_MAXUINT64 -} FwupdPluginFlags; +} G_GNUC_FLAG_ENUM FwupdPluginFlags; /** * FwupdInstallFlags: @@ -1093,7 +1149,7 @@ /** * FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM: * - * Ignore firmware CRCs and checksums. + * This is now unused; see #FuFirmwareParseFlags. * * Since: 1.5.0 */ @@ -1101,7 +1157,7 @@ /** * FWUPD_INSTALL_FLAG_IGNORE_VID_PID: * - * Ignore firmware vendor and project checks. + * This is now unused; see #FuFirmwareParseFlags. * * Since: 1.5.0 */ @@ -1109,7 +1165,7 @@ /** * FWUPD_INSTALL_FLAG_NO_SEARCH: * - * Do not use heuristics when parsing the image. + * This is now only for internal use. * * Since: 1.5.0 */ @@ -1122,9 +1178,17 @@ * Since: 1.9.21 */ FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS = 1 << 9, + /** + * FWUPD_INSTALL_FLAG_ONLY_EMULATED: + * + * Only install to emulated devices. + * + * Since: 2.0.10 + */ + FWUPD_INSTALL_FLAG_ONLY_EMULATED = 1 << 10, /*< private >*/ FWUPD_INSTALL_FLAG_UNKNOWN = G_MAXUINT64, -} FwupdInstallFlags; +} G_GNUC_FLAG_ENUM FwupdInstallFlags; /** * FwupdSelfSignFlags: @@ -1158,7 +1222,7 @@ FWUPD_SELF_SIGN_FLAG_ADD_CERT = 1 << 1, /*< private >*/ FWUPD_SELF_SIGN_FLAG_UNKNOWN = G_MAXUINT64, -} FwupdSelfSignFlags; +} G_GNUC_FLAG_ENUM FwupdSelfSignFlags; /** * FwupdUpdateState: diff -Nru fwupd-2.0.8/libfwupd/fwupd-error.c fwupd-2.0.20/libfwupd/fwupd-error.c --- fwupd-2.0.8/libfwupd/fwupd-error.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-error.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,6 @@ #include #include "fwupd-common.h" -#include "fwupd-enums.h" #include "fwupd-error.h" /** @@ -200,6 +199,9 @@ {G_IO_ERROR, G_IO_ERROR_FAILED, FWUPD_ERROR_INTERNAL}, {G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, FWUPD_ERROR_INVALID_DATA}, {G_IO_ERROR, G_IO_ERROR_INVALID_DATA, FWUPD_ERROR_INVALID_DATA}, +#if GLIB_CHECK_VERSION(2, 74, 0) + {G_IO_ERROR, G_IO_ERROR_NO_SUCH_DEVICE, FWUPD_ERROR_NOT_FOUND}, +#endif {G_IO_ERROR, G_IO_ERROR_NOT_CONNECTED, FWUPD_ERROR_NOT_FOUND}, {G_IO_ERROR, G_IO_ERROR_NOT_DIRECTORY, FWUPD_ERROR_NOT_SUPPORTED}, {G_IO_ERROR, G_IO_ERROR_NOT_FOUND, FWUPD_ERROR_NOT_FOUND}, @@ -250,3 +252,22 @@ error->domain = FWUPD_ERROR; error->code = FWUPD_ERROR_INTERNAL; } + +/** + * fwupd_strerror: + * + * Returns an untranslated string corresponding to the given error code, e.g. “no such process”. + * + * Returns: string describing the error code + * + * Since: 2.0.11 + **/ +const gchar * +fwupd_strerror(gint errnum) /* nocheck:name */ +{ +#ifdef HAVE_STRERRORDESC_NP + return strerrordesc_np(errnum); +#else + return g_strerror(errnum); /* nocheck:blocked */ +#endif +} diff -Nru fwupd-2.0.8/libfwupd/fwupd-error.h fwupd-2.0.20/libfwupd/fwupd-error.h --- fwupd-2.0.8/libfwupd/fwupd-error.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-error.h 2026-02-26 11:36:18.000000000 +0000 @@ -206,5 +206,7 @@ fwupd_error_from_string(const gchar *error); void fwupd_error_convert(GError **perror); +const gchar * +fwupd_strerror(gint errnum); G_END_DECLS diff -Nru fwupd-2.0.8/libfwupd/fwupd-plugin.c fwupd-2.0.20/libfwupd/fwupd-plugin.c --- fwupd-2.0.8/libfwupd/fwupd-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,6 @@ #include #include "fwupd-codec.h" -#include "fwupd-common-private.h" #include "fwupd-enums-private.h" #include "fwupd-plugin.h" diff -Nru fwupd-2.0.8/libfwupd/fwupd-remote-private.h fwupd-2.0.20/libfwupd/fwupd-remote-private.h --- fwupd-2.0.8/libfwupd/fwupd-remote-private.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-remote-private.h 2026-02-26 11:36:18.000000000 +0000 @@ -31,8 +31,14 @@ fwupd_remote_set_filename_cache(FwupdRemote *self, const gchar *filename) G_GNUC_NON_NULL(1); void fwupd_remote_set_metadata_uri(FwupdRemote *self, const gchar *metadata_uri) G_GNUC_NON_NULL(1); +guint64 +fwupd_remote_get_mtime(FwupdRemote *self) G_GNUC_NON_NULL(1); void fwupd_remote_set_mtime(FwupdRemote *self, guint64 mtime) G_GNUC_NON_NULL(1); +gboolean +fwupd_remote_ensure_mtime(FwupdRemote *self, GError **error) G_GNUC_NON_NULL(1); +gboolean +fwupd_remote_ensure_checksum_sig(FwupdRemote *self, GError **error) G_GNUC_NON_NULL(1); gchar ** fwupd_remote_get_order_after(FwupdRemote *self) G_GNUC_NON_NULL(1); gchar ** diff -Nru fwupd-2.0.8/libfwupd/fwupd-remote.c fwupd-2.0.20/libfwupd/fwupd-remote.c --- fwupd-2.0.8/libfwupd/fwupd-remote.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-remote.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,6 @@ #include #include "fwupd-codec.h" -#include "fwupd-common-private.h" #include "fwupd-enums-private.h" #include "fwupd-error.h" #include "fwupd-remote-private.h" @@ -109,6 +108,8 @@ return "allow-p2p-metadata"; if (flag == FWUPD_REMOTE_FLAG_ALLOW_P2P_FIRMWARE) return "allow-p2p-firmware"; + if (flag == FWUPD_REMOTE_FLAG_NO_PHASED_UPDATES) + return "no-phased-updates"; return NULL; } @@ -137,6 +138,8 @@ return FWUPD_REMOTE_FLAG_ALLOW_P2P_METADATA; if (g_strcmp0(flag, "allow-p2p-firmware") == 0) return FWUPD_REMOTE_FLAG_ALLOW_P2P_FIRMWARE; + if (g_strcmp0(flag, "no-phased-updates") == 0) + return FWUPD_REMOTE_FLAG_NO_PHASED_UPDATES; return FWUPD_REMOTE_FLAG_NONE; } @@ -513,22 +516,20 @@ GError **error) { FwupdRemotePrivate *priv = GET_PRIVATE(self); - g_autofree gchar *url = NULL; + const gchar *path_suffix = NULL; g_autoptr(curlptr) tmp_uri = NULL; g_autoptr(CURLU) uri = curl_url(); /* sanity check */ if (url_noauth == NULL) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, "no URI set"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, "no URI set"); return NULL; } /* the LVFS can't accept basic auth on an endpoint not expecting authentication */ if (!g_str_has_suffix(url_noauth, "/auth") && (priv->username != NULL || priv->password != NULL)) { - url = g_strdup_printf("%s/auth", url_noauth); - } else { - url = g_strdup(url_noauth); + path_suffix = "auth"; } /* create URI, substituting if required */ @@ -537,44 +538,48 @@ g_autofree gchar *path_new = NULL; g_autoptr(curlptr) path = NULL; g_autoptr(CURLU) uri_tmp = curl_url(); - if (curl_url_set(uri_tmp, CURLUPART_URL, url, 0) != CURLUE_OK) { + + if (curl_url_set(uri_tmp, CURLUPART_URL, url_noauth, 0) != CURLUE_OK) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "Failed to parse url '%s'", - url); + "failed to parse url '%s'", + url_noauth); return NULL; } (void)curl_url_get(uri_tmp, CURLUPART_PATH, &path, 0); basename = g_path_get_basename(path); - path_new = g_build_filename(priv->firmware_base_uri, basename, NULL); + path_new = g_build_path("/", priv->firmware_base_uri, basename, path_suffix, NULL); (void)curl_url_set(uri, CURLUPART_URL, path_new, 0); - /* use the base URI of the metadata to build the full path */ - } else if (g_strstr_len(url, -1, "/") == NULL) { + } else if (g_strstr_len(url_noauth, -1, "/") == NULL) { g_autofree gchar *basename = NULL; g_autofree gchar *path_new = NULL; g_autoptr(curlptr) path = NULL; + + /* use the base URI of the metadata to build the full path */ if (curl_url_set(uri, CURLUPART_URL, priv->metadata_uri, 0) != CURLUE_OK) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "Failed to parse url '%s'", + "failed to parse url '%s'", priv->metadata_uri); return NULL; } (void)curl_url_get(uri, CURLUPART_PATH, &path, 0); basename = g_path_get_dirname(path); - path_new = g_build_filename(basename, url, NULL); + path_new = g_build_path("/", basename, url_noauth, NULL); (void)curl_url_set(uri, CURLUPART_URL, path_new, 0); - /* a normal URI */ } else { + g_autofree gchar *url = g_build_path("/", url_noauth, path_suffix, NULL); + + /* a normal URI */ if (curl_url_set(uri, CURLUPART_URL, url, 0) != CURLUE_OK) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "Failed to parse URI '%s'", + "failed to parse URI '%s'", url); return NULL; } @@ -854,20 +859,8 @@ } /* load the signature checksum */ - if (priv->filename_cache_sig != NULL && - g_file_test(priv->filename_cache_sig, G_FILE_TEST_EXISTS)) { - gsize sz = 0; - g_autofree gchar *buf = NULL; - g_autoptr(GChecksum) checksum_sig = g_checksum_new(G_CHECKSUM_SHA256); - if (!g_file_get_contents(priv->filename_cache_sig, &buf, &sz, error)) { - g_prefix_error(error, "failed to get signature checksum: "); - return FALSE; - } - g_checksum_update(checksum_sig, (guchar *)buf, (gssize)sz); - fwupd_remote_set_checksum_sig(self, g_checksum_get_string(checksum_sig)); - } else { - fwupd_remote_set_checksum_sig(self, NULL); - } + if (!fwupd_remote_ensure_checksum_sig(self, error)) + return FALSE; /* success */ return TRUE; @@ -877,7 +870,7 @@ * fwupd_remote_get_order_after: * @self: a #FwupdRemote * - * Gets the list of remotes this plugin should be ordered after. + * Gets the list of remotes this remote should be ordered after. * * Returns: (transfer none): an array * @@ -895,7 +888,7 @@ * fwupd_remote_get_order_before: * @self: a #FwupdRemote * - * Gets the list of remotes this plugin should be ordered before. + * Gets the list of remotes this remote should be ordered before. * * Returns: (transfer none): an array * @@ -1049,7 +1042,7 @@ * @self: a #FwupdRemote * @priority: an integer, where 1 is better * - * Sets the plugin priority. + * Sets the remote priority. * * Since: 0.9.5 **/ @@ -1062,11 +1055,29 @@ } /** + * fwupd_remote_get_mtime: + * @self: a #FwupdRemote + * + * Gets the remote mtime in seconds. + * + * Returns: value in seconds + * + * Since: 2.0.17 + **/ +guint64 +fwupd_remote_get_mtime(FwupdRemote *self) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FWUPD_IS_REMOTE(self), G_MAXUINT64); + return priv->mtime; +} + +/** * fwupd_remote_set_mtime: * @self: a #FwupdRemote * @mtime: a UNIX timestamp * - * Sets the plugin modification time. + * Sets the remote modification time. * * Since: 0.9.5 **/ @@ -1079,10 +1090,94 @@ } /** + * fwupd_remote_ensure_checksum_sig: + * @self: a #FwupdRemote + * @error: (nullable): optional return location for an error + * + * Calculates the signature checksum of the remote using the filename cache. + * + * Returns: %TRUE for success + * + * Since: 2.0.17 + **/ +gboolean +fwupd_remote_ensure_checksum_sig(FwupdRemote *self, GError **error) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + + g_return_val_if_fail(FWUPD_IS_REMOTE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (priv->filename_cache_sig != NULL && + g_file_test(priv->filename_cache_sig, G_FILE_TEST_EXISTS)) { + gsize sz = 0; + g_autofree gchar *buf = NULL; + g_autoptr(GChecksum) checksum_sig = g_checksum_new(G_CHECKSUM_SHA256); + if (!g_file_get_contents(priv->filename_cache_sig, &buf, &sz, error)) { + g_prefix_error_literal(error, "failed to get signature checksum: "); + return FALSE; + } + g_checksum_update(checksum_sig, (guchar *)buf, (gssize)sz); + fwupd_remote_set_checksum_sig(self, g_checksum_get_string(checksum_sig)); + } else { + fwupd_remote_set_checksum_sig(self, NULL); + } + + /* success */ + return TRUE; +} + +/** + * fwupd_remote_ensure_mtime: + * @self: a #FwupdRemote + * @error: (nullable): optional return location for an error + * + * Calculates the mtime of the remote using the filename cache. + * + * Returns: %TRUE for success + * + * Since: 2.0.17 + **/ +gboolean +fwupd_remote_ensure_mtime(FwupdRemote *self, GError **error) +{ + FwupdRemotePrivate *priv = GET_PRIVATE(self); + g_autoptr(GFile) file = NULL; + g_autoptr(GFileInfo) info = NULL; + + g_return_val_if_fail(FWUPD_IS_REMOTE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (priv->filename_cache == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no filename cache set"); + return FALSE; + } + file = g_file_new_for_path(priv->filename_cache); + if (!g_file_query_exists(file, NULL)) { + priv->mtime = G_MAXUINT64; + return TRUE; + } + info = g_file_query_info(file, + G_FILE_ATTRIBUTE_TIME_MODIFIED, + G_FILE_QUERY_INFO_NONE, + NULL, + error); + if (info == NULL) { + fwupd_error_convert(error); + return FALSE; + } + priv->mtime = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED); + return TRUE; +} + +/** * fwupd_remote_get_refresh_interval: * @self: a #FwupdRemote * - * Gets the plugin refresh interval in seconds. + * Gets the remote refresh interval in seconds. * * Returns: value in seconds * @@ -1101,7 +1196,7 @@ * @self: a #FwupdRemote * @refresh_interval: value in seconds * - * Sets the plugin refresh interval in seconds. + * Sets the remote refresh interval in seconds. * * Since: 2.0.0 **/ diff -Nru fwupd-2.0.8/libfwupd/fwupd-remote.h fwupd-2.0.20/libfwupd/fwupd-remote.h --- fwupd-2.0.8/libfwupd/fwupd-remote.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-remote.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,8 +8,6 @@ #include -#include "fwupd-enums.h" - G_BEGIN_DECLS #define FWUPD_TYPE_REMOTE (fwupd_remote_get_type()) @@ -125,7 +123,15 @@ * Since: 1.9.5 */ FWUPD_REMOTE_FLAG_ALLOW_P2P_FIRMWARE = 1 << 5, -} FwupdRemoteFlags; + /** + * FWUPD_REMOTE_FLAG_NO_PHASED_UPDATES: + * + * Do not slow deployment using phased updates. + * + * Since: 2.0.17 + */ + FWUPD_REMOTE_FLAG_NO_PHASED_UPDATES = 1 << 6, +} G_GNUC_FLAG_ENUM FwupdRemoteFlags; FwupdRemoteKind fwupd_remote_kind_from_string(const gchar *kind); diff -Nru fwupd-2.0.8/libfwupd/fwupd-report.c fwupd-2.0.20/libfwupd/fwupd-report.c --- fwupd-2.0.8/libfwupd/fwupd-report.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-report.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,7 +12,6 @@ #include "fwupd-codec.h" #include "fwupd-common-private.h" #include "fwupd-enums-private.h" -#include "fwupd-error.h" #include "fwupd-report.h" /** diff -Nru fwupd-2.0.8/libfwupd/fwupd-request.c fwupd-2.0.20/libfwupd/fwupd-request.c --- fwupd-2.0.8/libfwupd/fwupd-request.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-request.c 2026-02-26 11:36:18.000000000 +0000 @@ -7,7 +7,6 @@ #include "config.h" #include "fwupd-codec.h" -#include "fwupd-common-private.h" #include "fwupd-enums-private.h" #include "fwupd-request-private.h" @@ -69,8 +68,6 @@ const gchar * fwupd_request_kind_to_string(FwupdRequestKind kind) { - if (kind == FWUPD_REQUEST_KIND_UNKNOWN) - return "unknown"; if (kind == FWUPD_REQUEST_KIND_POST) return "post"; if (kind == FWUPD_REQUEST_KIND_IMMEDIATE) @@ -91,13 +88,11 @@ FwupdRequestKind fwupd_request_kind_from_string(const gchar *kind) { - if (g_strcmp0(kind, "unknown") == 0) - return FWUPD_REQUEST_KIND_UNKNOWN; if (g_strcmp0(kind, "post") == 0) return FWUPD_REQUEST_KIND_POST; if (g_strcmp0(kind, "immediate") == 0) return FWUPD_REQUEST_KIND_IMMEDIATE; - return FWUPD_REQUEST_KIND_LAST; + return FWUPD_REQUEST_KIND_UNKNOWN; } /** diff -Nru fwupd-2.0.8/libfwupd/fwupd-request.h fwupd-2.0.20/libfwupd/fwupd-request.h --- fwupd-2.0.8/libfwupd/fwupd-request.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-request.h 2026-02-26 11:36:18.000000000 +0000 @@ -194,7 +194,7 @@ * Since: 1.8.6 */ FWUPD_REQUEST_FLAG_UNKNOWN = G_MAXUINT64, -} FwupdRequestFlags; +} G_GNUC_FLAG_ENUM FwupdRequestFlags; const gchar * fwupd_request_kind_to_string(FwupdRequestKind kind); diff -Nru fwupd-2.0.8/libfwupd/fwupd-security-attr.c fwupd-2.0.20/libfwupd/fwupd-security-attr.c --- fwupd-2.0.8/libfwupd/fwupd-security-attr.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-security-attr.c 2026-02-26 11:36:18.000000000 +0000 @@ -7,7 +7,6 @@ #include "config.h" #include -#include #include "fwupd-codec.h" #include "fwupd-common-private.h" @@ -1635,7 +1634,8 @@ fwupd_codec_json_append(builder, FWUPD_RESULT_KEY_APPSTREAM_ID, priv->appstream_id); if (priv->created > 0) fwupd_codec_json_append_int(builder, FWUPD_RESULT_KEY_CREATED, priv->created); - fwupd_codec_json_append_int(builder, FWUPD_RESULT_KEY_HSI_LEVEL, priv->level); + if (priv->level > 0) + fwupd_codec_json_append_int(builder, FWUPD_RESULT_KEY_HSI_LEVEL, priv->level); fwupd_codec_json_append(builder, FWUPD_RESULT_KEY_HSI_RESULT, fwupd_security_attr_result_to_string(priv->result)); diff -Nru fwupd-2.0.8/libfwupd/fwupd-security-attr.h fwupd-2.0.20/libfwupd/fwupd-security-attr.h --- fwupd-2.0.8/libfwupd/fwupd-security-attr.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-security-attr.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,6 @@ #include #include "fwupd-build.h" -#include "fwupd-enums.h" G_BEGIN_DECLS @@ -106,7 +105,7 @@ * The fix can be automatically reverted. */ FWUPD_SECURITY_ATTR_FLAG_CAN_UNDO = 1 << 15, -} FwupdSecurityAttrFlags; +} G_GNUC_FLAG_ENUM FwupdSecurityAttrFlags; /** * FwupdSecurityAttrLevel: diff -Nru fwupd-2.0.8/libfwupd/fwupd-self-test.c fwupd-2.0.20/libfwupd/fwupd-self-test.c --- fwupd-2.0.8/libfwupd/fwupd-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,6 @@ #include #include "fwupd-bios-setting.h" -#include "fwupd-client-private.h" #include "fwupd-client-sync.h" #include "fwupd-codec.h" #include "fwupd-common.h" @@ -59,17 +58,17 @@ g_assert_cmpstr(tmp, !=, NULL); g_assert_cmpint(fwupd_error_from_string(tmp), ==, i); } - for (guint i = 0; i < FWUPD_STATUS_LAST; i++) { + for (guint i = FWUPD_STATUS_UNKNOWN + 1; i < FWUPD_STATUS_LAST; i++) { const gchar *tmp = fwupd_status_to_string(i); g_assert_cmpstr(tmp, !=, NULL); g_assert_cmpint(fwupd_status_from_string(tmp), ==, i); } - for (guint i = 0; i < FWUPD_UPDATE_STATE_LAST; i++) { + for (guint i = FWUPD_UPDATE_STATE_UNKNOWN + 1; i < FWUPD_UPDATE_STATE_LAST; i++) { const gchar *tmp = fwupd_update_state_to_string(i); g_assert_cmpstr(tmp, !=, NULL); g_assert_cmpint(fwupd_update_state_from_string(tmp), ==, i); } - for (guint i = 0; i < FWUPD_REQUEST_KIND_LAST; i++) { + for (guint i = FWUPD_REQUEST_KIND_UNKNOWN + 1; i < FWUPD_REQUEST_KIND_LAST; i++) { const gchar *tmp = fwupd_request_kind_to_string(i); g_assert_cmpstr(tmp, !=, NULL); g_assert_cmpint(fwupd_request_kind_from_string(tmp), ==, i); @@ -128,7 +127,7 @@ break; g_assert_cmpint(fwupd_remote_flag_from_string(tmp), ==, i); } - for (guint64 i = 1; i <= FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS; i *= 2) { + for (guint64 i = 1; i <= FWUPD_INSTALL_FLAG_ONLY_EMULATED; i *= 2) { const gchar *tmp = fwupd_install_flags_to_string(i); if (tmp == NULL) continue; @@ -264,7 +263,7 @@ " Tags: tag\n" " License: license\n" " Size: 1.2 kB\n" - " Created: 1970-01-01\n" + " Created: 1970-01-01 01:34:38\n" " Uri: location\n" " Homepage: homepage\n" " DetailsUrl: details_url\n" @@ -392,6 +391,36 @@ } static void +fwupd_remote_func(void) +{ + g_autofree gchar *uri1 = NULL; + g_autofree gchar *uri2 = NULL; + g_autofree gchar *uri3 = NULL; + g_autoptr(FwupdRemote) remote = fwupd_remote_new(); + g_autoptr(GError) error = NULL; + + uri1 = fwupd_remote_build_firmware_uri(remote, + "https://example.org/downloads/foo.cab", + &error); + g_assert_no_error(error); + g_assert_cmpstr(uri1, ==, "https://example.org/downloads/foo.cab"); + + fwupd_remote_set_firmware_base_uri(remote, "https://example.org/mirror"); + uri2 = fwupd_remote_build_firmware_uri(remote, + "https://example.org/downloads/foo.cab", + &error); + g_assert_no_error(error); + g_assert_cmpstr(uri2, ==, "https://example.org/mirror/foo.cab"); + + fwupd_remote_set_username(remote, "admin"); + uri3 = fwupd_remote_build_firmware_uri(remote, + "https://example.org/downloads/foo.cab", + &error); + g_assert_no_error(error); + g_assert_cmpstr(uri3, ==, "https://admin@example.org/mirror/foo.cab/auth"); +} + +static void fwupd_request_func(void) { gboolean ret; @@ -821,8 +850,8 @@ " VendorId: USB:0x1234\n" " VendorId: PCI:0x5678\n" " Icon: input-gaming,input-mouse\n" - " Created: 1970-01-01\n" - " Modified: 1970-01-02\n" + " Created: 1970-01-01 00:00:01\n" + " Modified: 1970-01-02 00:00:00\n" " FwupdRelease:\n" " AppstreamId: org.dave.ColorHug.firmware\n" " Description:

    Hi there!

    \n" @@ -1041,7 +1070,7 @@ conn = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, NULL); if (conn != NULL) return TRUE; - g_debug("D-Bus system bus unavailable, skipping tests."); + g_debug("D-Bus system bus unavailable, skipping tests"); return FALSE; } @@ -1449,6 +1478,7 @@ g_test_add_func("/fwupd/release", fwupd_release_func); g_test_add_func("/fwupd/report", fwupd_report_func); g_test_add_func("/fwupd/plugin", fwupd_plugin_func); + g_test_add_func("/fwupd/remote", fwupd_remote_func); g_test_add_func("/fwupd/request", fwupd_request_func); g_test_add_func("/fwupd/device", fwupd_device_func); g_test_add_func("/fwupd/device{filter}", fwupd_device_filter_func); diff -Nru fwupd-2.0.8/libfwupd/fwupd-thread-test.c fwupd-2.0.20/libfwupd/fwupd-thread-test.c --- fwupd-2.0.8/libfwupd/fwupd-thread-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd-thread-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -31,7 +31,7 @@ g_autoptr(GMainContextPusher) pusher = g_main_context_pusher_new(context); g_assert_nonnull(pusher); - g_message("Calling fwupd_client_get_devices() in thread %p with main context %p", + g_message("calling fwupd_client_get_devices() in thread %p with main context %p", g_thread_self(), g_main_context_get_thread_default()); devices = fwupd_client_get_devices(self->client, NULL, &error_local); @@ -90,11 +90,11 @@ /* only some of the CI targets have a DBus daemon */ if (!fwupd_thread_test_has_system_bus()) { - g_message("D-Bus system bus unavailable, skipping tests."); + g_message("D-Bus system bus unavailable, skipping tests"); return 0; } - g_message("Created FwupdClient in thread %p with main context %p", + g_message("created FwupdClient in thread %p with main context %p", g_thread_self(), g_main_context_get_thread_default()); g_signal_connect(G_APPLICATION(app), diff -Nru fwupd-2.0.8/libfwupd/fwupd.map fwupd-2.0.20/libfwupd/fwupd.map --- fwupd-2.0.8/libfwupd/fwupd.map 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/fwupd.map 2026-02-26 11:36:18.000000000 +0000 @@ -1015,3 +1015,42 @@ fwupd_security_attr_set_fwupd_version; local: *; } LIBFWUPD_2.0.4; + +LIBFWUPD_2.0.10 { + global: + fwupd_codec_json_append_map; + local: *; +} LIBFWUPD_2.0.7; + +LIBFWUPD_2.0.11 { + global: + fwupd_strerror; + local: *; +} LIBFWUPD_2.0.10; + +LIBFWUPD_2.0.16 { + global: + fwupd_client_search; + fwupd_client_search_async; + fwupd_client_search_finish; + local: *; +} LIBFWUPD_2.0.11; + +LIBFWUPD_2.0.17 { + global: + fwupd_client_clean_remote; + fwupd_client_clean_remote_async; + fwupd_client_clean_remote_finish; + fwupd_client_get_hwids; + fwupd_remote_ensure_checksum_sig; + fwupd_remote_ensure_mtime; + fwupd_remote_get_mtime; + local: *; +} LIBFWUPD_2.0.16; + +LIBFWUPD_2.0.20 { + global: + fwupd_bios_setting_get_filename; + fwupd_bios_setting_set_filename; + local: *; +} LIBFWUPD_2.0.17; diff -Nru fwupd-2.0.8/libfwupd/meson.build fwupd-2.0.20/libfwupd/meson.build --- fwupd-2.0.8/libfwupd/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupd/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,11 +1,11 @@ if get_option('tests') -subdir('tests') + subdir('tests') endif fwupd_version_h = configure_file( input: 'fwupd-version.h.in', output: 'fwupd-version.h', - configuration: conf + configuration: conf, ) base_dir = 'fwupd-' + libfwupd_lt_current @@ -15,7 +15,8 @@ subdir: base_dir, ) -install_headers([ +install_headers( + [ 'fwupd-build.h', 'fwupd-client.h', 'fwupd-client-sync.h', @@ -36,28 +37,23 @@ subdir: join_paths(base_dir, 'libfwupd'), ) -libfwupd_deps = [ - giounix, - libjcat, - libjsonglib, - libcurl, -] +libfwupd_deps = [giounix, libjcat, libjsonglib, libcurl] libfwupd_src = [ 'fwupd-client.c', 'fwupd-client-sync.c', - 'fwupd-common.c', # fuzzing - 'fwupd-codec.c', # fuzzing - 'fwupd-device.c', # fuzzing - 'fwupd-enums.c', # fuzzing - 'fwupd-error.c', # fuzzing - 'fwupd-bios-setting.c', # fuzzing - 'fwupd-security-attr.c', # fuzzing - 'fwupd-release.c', # fuzzing + 'fwupd-common.c', # fuzzing + 'fwupd-codec.c', # fuzzing + 'fwupd-device.c', # fuzzing + 'fwupd-enums.c', # fuzzing + 'fwupd-error.c', # fuzzing + 'fwupd-bios-setting.c', # fuzzing + 'fwupd-security-attr.c', # fuzzing + 'fwupd-release.c', # fuzzing 'fwupd-plugin.c', 'fwupd-remote.c', - 'fwupd-report.c', # fuzzing - 'fwupd-request.c', # fuzzing + 'fwupd-report.c', # fuzzing + 'fwupd-request.c', # fuzzing 'fwupd-version.c', ] @@ -69,26 +65,24 @@ soversion: libfwupd_lt_current, version: libfwupd_lt_version, dependencies: libfwupd_deps, - c_args: [ - '-DG_LOG_DOMAIN="Fwupd"', - '-DLOCALSTATEDIR="' + localstatedir + '"', - ], + c_args: ['-DG_LOG_DOMAIN="Fwupd"', '-DLOCALSTATEDIR="' + localstatedir + '"'], include_directories: root_incdir, link_args: cc.get_supported_link_arguments([vflag]), link_depends: fwupd_mapfile, - install: true + install: true, + install_tag: 'lib', ) libfwupd_dep = declare_dependency( link_with: fwupd, include_directories: [root_incdir, include_directories('.')], - dependencies: libfwupd_deps + dependencies: libfwupd_deps, ) pkgg = import('pkgconfig') pkgg.generate( fwupd, - requires: [ 'gio-2.0', 'json-glib-1.0' ], + requires: ['gio-2.0', 'json-glib-1.0'], subdirs: base_dir, version: meson.project_version(), name: 'fwupd', @@ -97,11 +91,9 @@ ) if introspection.allowed() - fwupd_gir_deps = [ - giounix, - libcurl, - ] - fwupd_gir = gnome.generate_gir(fwupd, + fwupd_gir_deps = [giounix, libcurl] + fwupd_gir = gnome.generate_gir( + fwupd, sources: [ 'fwupd-client.c', 'fwupd-client.h', @@ -147,15 +139,12 @@ export_packages: 'fwupd', header: 'fwupd.h', dependencies: fwupd_gir_deps, - includes: [ - 'Gio-2.0', - 'GObject-2.0', - 'Json-1.0', - ], - install: true + includes: ['Gio-2.0', 'GObject-2.0', 'Json-1.0'], + install: true, ) - gnome.generate_vapi('fwupd', + gnome.generate_vapi( + 'fwupd', sources: fwupd_gir[0], packages: ['gio-2.0', 'json-glib-1.0'], install: true, @@ -169,23 +158,17 @@ # # To avoid the circular dep, and to ensure we don't change exported API # accidentally actually check in a version of the version script to git. - mapfile_target = custom_target('fwupd_mapfile', + mapfile_target = custom_target( + 'fwupd_mapfile', input: fwupd_gir[0], output: 'fwupd.map', - command: [ - generate_version_script, - 'LIBFWUPD', - '@INPUT@', - '@OUTPUT@', - ], + command: [generate_version_script, 'LIBFWUPD', '@INPUT@', '@OUTPUT@'], + ) + test( + 'fwupd-exported-api', + diffcmd, + args: ['-urNp', files('fwupd.map'), mapfile_target], ) - test('fwupd-exported-api', diffcmd, - args: [ - '-urNp', - files('fwupd.map'), - mapfile_target, - ], - ) endif if get_option('tests') @@ -194,15 +177,9 @@ env.set('G_TEST_BUILDDIR', meson.current_build_dir()) e = executable( 'fwupd-self-test', - sources: [ - 'fwupd-self-test.c' - ], - include_directories: [ - root_incdir, - ], - dependencies: [ - libfwupd_deps, - ], + sources: ['fwupd-self-test.c'], + include_directories: [root_incdir], + dependencies: [libfwupd_deps], link_with: fwupd, c_args: [ '-DG_LOG_DOMAIN="Fwupd"', @@ -210,42 +187,39 @@ '-DLOCALSTATEDIR="' + localstatedir + '"', ], ) - test('fwupd-self-test', e, timeout: 60, env: env) - if run_sanitize_unsafe_tests and gio.version().version_compare ('>= 2.64.0') + test( + 'fwupd-self-test', + e, + timeout: 60, + env: env, + ) + if run_sanitize_unsafe_tests and gio.version().version_compare('>= 2.64.0') e = executable( 'fwupd-thread-test', - sources: [ - 'fwupd-thread-test.c' - ], - include_directories: [ - root_incdir, - ], - dependencies: [ - libfwupd_deps, - ], + sources: ['fwupd-thread-test.c'], + include_directories: [root_incdir], + dependencies: [libfwupd_deps], link_with: fwupd, - c_args: [ - '-DG_LOG_DOMAIN="Fwupd"', - ], + c_args: ['-DG_LOG_DOMAIN="Fwupd"'], + ) + test( + 'fwupd-thread-test', + e, + timeout: 60, ) - test('fwupd-thread-test', e, timeout: 60) e = executable( 'fwupd-context-test', - sources: [ - 'fwupd-context-test.c' - ], - include_directories: [ - root_incdir, - ], - dependencies: [ - libfwupd_deps, - ], + sources: ['fwupd-context-test.c'], + include_directories: [root_incdir], + dependencies: [libfwupd_deps], link_with: fwupd, - c_args: [ - '-DG_LOG_DOMAIN="Fwupd"', - ], + c_args: ['-DG_LOG_DOMAIN="Fwupd"'], + ) + test( + 'fwupd-context-test', + e, + timeout: 60, ) - test('fwupd-context-test', e, timeout: 60) endif endif diff -Nru fwupd-2.0.8/libfwupdplugin/README.md fwupd-2.0.20/libfwupdplugin/README.md --- fwupd-2.0.8/libfwupdplugin/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -6,7 +6,8 @@ Use `./contrib/migrate.py` to migrate up out-of-tree plugins to the new API. -Remember: Plugins should be upstream! +> [!IMPORTANT] +> Plugins should be upstream! ## 1.5.5 @@ -128,7 +129,7 @@ ## 2.0.0 * `fu_hid_device_parse_descriptor()`: Use `fu_hid_device_parse_descriptors()` instead -* `fu_io_channel_new_file()`: Add some `FuIoChannelOpenFlag`, e.g. `FU_IO_CHANNEL_OPEN_FLAG_READ|FU_IO_CHANNEL_OPEN_FLAG_WRITE` +* `fu_io_channel_new_file()`: Add some `FuIoChannelOpenFlags`, e.g. `FU_IO_CHANNEL_OPEN_FLAG_READ|FU_IO_CHANNEL_OPEN_FLAG_WRITE` * `fu_udev_device_set_flags()`: Use `fu_udev_device_add_flag()` instead * `fu_udev_device_get_slot_depth()`: Use `fu_udev_device_get_subsystem_depth()` instead * `fu_usb_device_is_open()`: Use `fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_IS_OPEN)` instead @@ -154,6 +155,8 @@ * `fu_udev_device_get_subsystem_model`: Use `fu_pci_device_get_subsystem_pid()` instead * `fu_udev_device_get_revision`: Use `fu_pci_device_get_revision()` instead * `fu_udev_device_set_revision`: Use `fu_pci_device_set_revision()` instead +* `fu_firmware_parse_full()`: Use `fu_firmware_parse_bytes()` instead +* `fu_firmware_parse()`: Use `fu_firmware_parse_bytes()` by adding an offset of 0x0 ## 2.0.2 @@ -178,3 +181,27 @@ * `fu_context_get_smbios_string()`: Add an expected structure length, typically `FU_SMBIOS_STRUCTURE_LENGTH_ANY`. * `fu_context_get_smbios_integer()`: Add an expected structure length, typically `FU_SMBIOS_STRUCTURE_LENGTH_ANY`. * `fu_context_get_smbios_data()`: Add an expected structure length, typically `FU_SMBIOS_STRUCTURE_LENGTH_ANY`. + +## 2.0.11 + +* `fu_device_read_firmware()`: Add `FuFirmwareParseFlags` to `fu_device_read_firmware`. + +## 2.0.12 + +* Plugins that don't allow devices to function 100% through probe should use `FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION` +* `fu_device_get_contents_bytes()`: Add a maximum read size, typically `G_MAXSIZE` + +## 2.0.14 + +* `fu_error_convert()`: Use `fwupd_error_convert()` instead + +## 2.0.17 + +* `fu_firmware_add_image_full()`: Use `fu_firmware_add_image()` instead. +* `fu_firmware_add_image()`: Add a `GError` and check the return value. + +## 2.0.18 + +* `fu_input_stream_find()`: Add a start offset, typically `0x0`. +* `fu_device_get_parent()`: Add a `GError` +* `fu_device_get_proxy()`: Add a `GError` diff -Nru fwupd-2.0.8/libfwupdplugin/fu-acpi-table.c fwupd-2.0.20/libfwupdplugin/fu-acpi-table.c --- fwupd-2.0.8/libfwupdplugin/fu-acpi-table.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-acpi-table.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,6 @@ #include "fu-acpi-table.h" #include "fu-common.h" #include "fu-input-stream.h" -#include "fu-sum.h" /** * FuAcpiTable: @@ -117,7 +116,7 @@ static gboolean fu_acpi_table_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAcpiTable *self = FU_ACPI_TABLE(firmware); @@ -142,7 +141,7 @@ length = fu_struct_acpi_table_get_length(st); if (!fu_input_stream_size(stream, &streamsz, error)) return FALSE; - if (length > streamsz || length < st->len) { + if (length > streamsz || length < st->buf->len) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, @@ -154,7 +153,7 @@ fu_firmware_set_size(firmware, length); /* checksum */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint8 checksum_actual = 0; if (!fu_input_stream_compute_sum8(stream, &checksum_actual, error)) return FALSE; @@ -163,7 +162,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "CRC failed, expected %02x, got %02x", + "CRC failed, expected 0x%02x, got 0x%02x", (guint)checksum - checksum_actual, checksum); return FALSE; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-archive-firmware.c fwupd-2.0.20/libfwupdplugin/fu-archive-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-archive-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-archive-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -50,13 +50,13 @@ FuFirmware *firmware = FU_FIRMWARE(user_data); g_autoptr(FuFirmware) img = fu_firmware_new_from_bytes(bytes); fu_firmware_set_id(img, filename); - return fu_firmware_add_image_full(firmware, img, error); + return fu_firmware_add_image(firmware, img, error); } static gboolean fu_archive_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuArchive) archive = NULL; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-archive.c fwupd-2.0.20/libfwupdplugin/fu-archive.c --- fwupd-2.0.8/libfwupdplugin/fu-archive.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-archive.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,8 +19,6 @@ #include "fwupd-error.h" #include "fu-archive.h" -#include "fu-bytes.h" -#include "fu-input-stream.h" /** * FuArchive: diff -Nru fwupd-2.0.8/libfwupdplugin/fu-archive.h fwupd-2.0.20/libfwupdplugin/fu-archive.h --- fwupd-2.0.8/libfwupdplugin/fu-archive.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-archive.h 2026-02-26 11:36:18.000000000 +0000 @@ -27,7 +27,7 @@ FU_ARCHIVE_FLAG_IGNORE_PATH = 1 << 0, /*< private >*/ FU_ARCHIVE_FLAG_LAST -} FuArchiveFlags; +} G_GNUC_FLAG_ENUM FuArchiveFlags; /** * FuArchiveIterateFunc: diff -Nru fwupd-2.0.8/libfwupdplugin/fu-backend.c fwupd-2.0.20/libfwupdplugin/fu-backend.c --- fwupd-2.0.8/libfwupdplugin/fu-backend.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-backend.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,9 +8,8 @@ #include "config.h" -#include "fu-backend-private.h" +#include "fu-device-locker.h" #include "fu-device-private.h" -#include "fu-string.h" /** * FuBackend: @@ -82,10 +81,9 @@ fu_device_set_created_usec(device, g_get_real_time()); /* sanity check */ - if ((g_getenv("FWUPD_UEFI_TEST") == NULL) && - g_hash_table_contains(priv->devices, fu_device_get_backend_id(device))) { - g_warning("replacing existing device with backend_id %s", - fu_device_get_backend_id(device)); + if (g_hash_table_contains(priv->devices, fu_device_get_backend_id(device))) { + g_debug("replacing existing device with backend_id %s", + fu_device_get_backend_id(device)); } /* add */ @@ -407,11 +405,22 @@ json_array = json_object_get_array_member(json_object, "UsbDevices"); for (guint i = 0; i < json_array_get_length(json_array); i++) { JsonNode *node_tmp = json_array_get_element(json_array, i); - JsonObject *object_tmp = json_node_get_object(node_tmp); + JsonObject *object_tmp; FuDevice *device_old; g_autoptr(FuDevice) device_tmp = NULL; const gchar *device_gtypestr; GType device_gtype; + g_autofree gchar *id_display = NULL; + + /* sanity check */ + if (!JSON_NODE_HOLDS_OBJECT(node_tmp)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "not JSON object"); + return FALSE; + } + object_tmp = json_node_get_object(node_tmp); /* get the GType */ device_gtypestr = @@ -433,7 +442,7 @@ fu_device_add_flag(device_tmp, FWUPD_DEVICE_FLAG_EMULATED); if (fwupd_version != NULL) fu_device_set_fwupd_version(device_tmp, fwupd_version); - if (!fwupd_codec_from_json(FWUPD_CODEC(device_tmp), node_tmp, error)) + if (!fu_device_from_json(device_tmp, object_tmp, error)) return FALSE; if (fu_device_get_backend_id(device_tmp) == NULL) { g_set_error(error, @@ -443,6 +452,7 @@ device_gtypestr); return FALSE; } + id_display = fu_device_get_id_display(device_tmp); /* does a device with this platform ID [and the same created date] already exist */ device_old = fu_backend_lookup_by_id(self, fu_device_get_backend_id(device_tmp)); @@ -456,23 +466,20 @@ if (device_old != NULL && fu_device_get_created_usec(device_old) == fu_device_get_created_usec(device_tmp)) { GPtrArray *events = fu_device_get_events(device_tmp); + fu_device_clear_events(device_old); for (guint j = 0; j < events->len; j++) { FuDeviceEvent *event = g_ptr_array_index(events, j); fu_device_add_event(device_old, event); } - g_debug("changed %s [%s]", - fu_device_get_name(device_tmp), - fu_device_get_backend_id(device_tmp)); + g_debug("changed %s", id_display); fu_backend_device_changed(self, device_old); g_ptr_array_remove(devices_remove, device_old); continue; } /* new to us! */ - g_debug("not found %s [%s], adding", - fu_device_get_name(device_tmp), - fu_device_get_backend_id(device_tmp)); + g_debug("not found %s, adding", id_display); g_ptr_array_add(devices_added, g_object_ref(device_tmp)); } @@ -532,7 +539,7 @@ if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATION_TAG)) continue; json_builder_begin_object(builder); - fwupd_codec_to_json(FWUPD_CODEC(device), builder, FWUPD_CODEC_FLAG_NONE); + fu_device_add_json(device, builder, FWUPD_CODEC_FLAG_NONE); json_builder_end_object(builder); } json_builder_end_array(builder); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-backend.h fwupd-2.0.20/libfwupdplugin/fu-backend.h --- fwupd-2.0.8/libfwupdplugin/fu-backend.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-backend.h 2026-02-26 11:36:18.000000000 +0000 @@ -31,7 +31,7 @@ * Since: 2.0.0 **/ FU_BACKEND_SETUP_FLAG_USE_HOTPLUG = 1 << 0, -} FuBackendSetupFlags; +} G_GNUC_FLAG_ENUM FuBackendSetupFlags; struct _FuBackendClass { GObjectClass parent_class; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-bios-setting.c fwupd-2.0.20/libfwupdplugin/fu-bios-setting.c --- fwupd-2.0.8/libfwupdplugin/fu-bios-setting.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-bios-setting.c 2026-02-26 11:36:18.000000000 +0000 @@ -27,7 +27,10 @@ if (fwupd_bios_setting_get_path(self) == NULL) return TRUE; - fn = g_build_filename(fwupd_bios_setting_get_path(self), "current_value", NULL); + + fn = g_build_filename(fwupd_bios_setting_get_path(self), + fwupd_bios_setting_get_filename(self), + NULL); io = fu_io_channel_new_file(fn, FU_IO_CHANNEL_OPEN_FLAG_WRITE, error); if (io == NULL) return FALSE; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-bios-settings.c fwupd-2.0.20/libfwupdplugin/fu-bios-settings.c --- fwupd-2.0.8/libfwupdplugin/fu-bios-settings.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-bios-settings.c 2026-02-26 11:36:18.000000000 +0000 @@ -66,7 +66,7 @@ tmp = g_build_filename(fwupd_bios_setting_get_path(attr), key, NULL); if (!g_file_get_contents(tmp, value_out, NULL, error)) { g_prefix_error(error, "failed to load %s: ", key); - fu_error_convert(error); + fwupd_error_convert(error); return FALSE; } g_strchomp(*value_out); @@ -81,7 +81,7 @@ g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(attr), FALSE); - /* Try ID, then name, and then key */ + /* try ID, then name, and then key */ value = g_hash_table_lookup(self->descriptions, fwupd_bios_setting_get_id(attr)); if (value != NULL) { fwupd_bios_setting_set_description(attr, value); @@ -276,7 +276,7 @@ fu_bios_settings_add_attribute(FuBiosSettings *self, FwupdBiosSetting *attr) { g_return_if_fail(FU_IS_BIOS_SETTINGS(self)); - g_return_if_fail(FU_IS_BIOS_SETTING(attr)); + g_return_if_fail(FWUPD_IS_BIOS_SETTING(attr)); g_ptr_array_add(self->attrs, g_object_ref(attr)); } @@ -303,6 +303,7 @@ fwupd_bios_setting_set_id(attr, id); if (g_file_test(path, G_FILE_TEST_IS_DIR)) { + fwupd_bios_setting_set_filename(attr, "current_value"); if (!fu_bios_settings_set_folder_attributes(self, attr, error)) return FALSE; } else { @@ -393,7 +394,7 @@ sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW_ATTRIB); class_dir = g_dir_open(sysfsfwdir, 0, error); if (class_dir == NULL) { - fu_error_convert(error); + fwupd_error_convert(error); return FALSE; } do { @@ -409,7 +410,7 @@ } driver_dir = g_dir_open(path, 0, error); if (driver_dir == NULL) { - fu_error_convert(error); + fwupd_error_convert(error); return FALSE; } do { @@ -633,3 +634,44 @@ { return g_object_new(FU_TYPE_FIRMWARE_ATTRS, NULL); } + +/** + * fu_bios_settings_register_attr: + * @self: a #FuBiosSettings + * @attr: a #FwupdBiosSetting + * @error: (nullable): optional return location for an error + * + * Registers a BIOS setting that was created by a plugin. + * This is a public API for plugins to register custom BIOS settings. + * + * Returns: TRUE if the setting was registered successfully + * + * Since: 2.0.20 + **/ +gboolean +fu_bios_settings_register_attr(FuBiosSettings *self, FwupdBiosSetting *attr, GError **error) +{ + g_return_val_if_fail(FU_IS_BIOS_SETTINGS(self), FALSE); + g_return_val_if_fail(FWUPD_IS_BIOS_SETTING(attr), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (fwupd_bios_setting_get_id(attr) == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "BIOS setting missing required id property"); + return FALSE; + } + + if (fu_bios_settings_get_attr(self, fwupd_bios_setting_get_id(attr)) != NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "BIOS setting with id %s already exists", + fwupd_bios_setting_get_id(attr)); + return FALSE; + } + + fu_bios_settings_add_attribute(self, attr); + return TRUE; +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-bios-settings.h fwupd-2.0.20/libfwupdplugin/fu-bios-settings.h --- fwupd-2.0.8/libfwupdplugin/fu-bios-settings.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-bios-settings.h 2026-02-26 11:36:18.000000000 +0000 @@ -19,3 +19,6 @@ G_GNUC_NON_NULL(1); FwupdBiosSetting * fu_bios_settings_get_attr(FuBiosSettings *self, const gchar *val) G_GNUC_NON_NULL(1, 2); +gboolean +fu_bios_settings_register_attr(FuBiosSettings *self, FwupdBiosSetting *attr, GError **error) + G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-block-device.c fwupd-2.0.20/libfwupdplugin/fu-block-device.c --- fwupd-2.0.8/libfwupdplugin/fu-block-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-block-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,6 @@ #include "fu-block-device.h" #include "fu-dump.h" -#include "fu-usb-device.h" /** * FuBlockDevice diff -Nru fwupd-2.0.8/libfwupdplugin/fu-block-partition.c fwupd-2.0.20/libfwupdplugin/fu-block-partition.c --- fwupd-2.0.8/libfwupdplugin/fu-block-partition.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-block-partition.c 2026-02-26 11:36:18.000000000 +0000 @@ -60,6 +60,7 @@ g_free(priv->fs_type); priv->fs_type = fu_strsafe(fs_type, fs_typelen); } +#endif static void fu_block_partition_set_fs_uuid(FuBlockPartition *self, const gchar *fs_uuid, gsize fs_uuidlen) @@ -86,16 +87,15 @@ g_free(priv->fs_label); priv->fs_label = fu_strsafe(fs_label, fs_labellen); } -#endif static void -fu_block_partition_incorporate(FuDevice *self, FuDevice *donor) +fu_block_partition_incorporate(FuDevice *device, FuDevice *donor) { - FuBlockPartition *uself = FU_BLOCK_PARTITION(self); + FuBlockPartition *uself = FU_BLOCK_PARTITION(device); FuBlockPartition *udonor = FU_BLOCK_PARTITION(donor); FuBlockPartitionPrivate *priv = GET_PRIVATE(uself); - g_return_if_fail(FU_IS_BLOCK_PARTITION(self)); + g_return_if_fail(FU_IS_BLOCK_PARTITION(device)); g_return_if_fail(FU_IS_BLOCK_PARTITION(donor)); if (priv->fs_type == NULL) @@ -240,10 +240,13 @@ FuBlockPartitionPrivate *priv = GET_PRIVATE(self); FuDeviceEvent *event = NULL; FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(device)); + g_autofree gchar *attr_partname = NULL; + g_autofree gchar *attr_partuuid = NULL; g_autofree gchar *event_id = NULL; #ifdef HAVE_BLKID gint rc; const gchar *data; + gint superblocks_flags = BLKID_SUBLKS_TYPE; gsize datalen = 0; g_auto(blkid_probe) pr = NULL; #endif @@ -277,6 +280,12 @@ return FALSE; } + /* use uevent as a free data source */ + attr_partname = fu_udev_device_read_property(FU_UDEV_DEVICE(self), "PARTNAME", NULL); + fu_block_partition_set_fs_label(self, attr_partname, G_MAXSIZE); + attr_partuuid = fu_udev_device_read_property(FU_UDEV_DEVICE(self), "PARTUUID", NULL); + fu_block_partition_set_fs_uuid(self, attr_partuuid, G_MAXSIZE); + #ifdef HAVE_BLKID /* probe */ pr = blkid_new_probe(); @@ -287,9 +296,11 @@ "failed to create blkid prober"); return FALSE; } - blkid_probe_set_superblocks_flags(pr, - BLKID_SUBLKS_UUID | BLKID_SUBLKS_TYPE | - BLKID_SUBLKS_LABEL); + if (priv->fs_uuid == NULL) + superblocks_flags |= BLKID_SUBLKS_UUID; + if (priv->fs_label == NULL) + superblocks_flags |= BLKID_SUBLKS_LABEL; + blkid_probe_set_superblocks_flags(pr, superblocks_flags); rc = blkid_probe_set_device(pr, fu_io_channel_unix_get_fd(io_channel), 0x0, 0x0); if (rc < 0) { g_set_error(error, @@ -325,10 +336,10 @@ if (blkid_probe_lookup_value(pr, "LABEL", &data, &datalen) == 0) fu_block_partition_set_fs_label(self, data, datalen); #else - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "not supported as not found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported as not found"); return FALSE; #endif diff -Nru fwupd-2.0.8/libfwupdplugin/fu-bluez-device.c fwupd-2.0.20/libfwupdplugin/fu-bluez-device.c --- fwupd-2.0.8/libfwupdplugin/fu-bluez-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-bluez-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,6 @@ #include "fu-bluez-device.h" #include "fu-dump.h" #include "fu-firmware-common.h" -#include "fu-string.h" #define DEFAULT_PROXY_TIMEOUT 5000 @@ -115,7 +114,7 @@ NULL, error); if (uuid_helper->proxy == NULL) { - g_prefix_error(error, "Failed to create GDBusProxy for uuid_helper: "); + g_prefix_error_literal(error, "Failed to create GDBusProxy for uuid_helper: "); return FALSE; } g_dbus_proxy_set_default_timeout(uuid_helper->proxy, DEFAULT_PROXY_TIMEOUT); @@ -280,7 +279,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "property %s not found in %s: ", + "property %s not found in %s", prop_name, obj_path); return NULL; @@ -514,7 +513,8 @@ obj_path, "org.bluez.GattCharacteristic1", error)) { - g_prefix_error(error, "failed to add characteristic uuid: "); + g_prefix_error_literal(error, + "failed to add characteristic UUID: "); return FALSE; } valid += 1; @@ -525,7 +525,7 @@ obj_path, "org.bluez.GattService1", error)) { - g_prefix_error(error, "failed to add service uuid: "); + g_prefix_error_literal(error, "failed to add service UUID: "); return FALSE; } valid += 1; @@ -538,7 +538,7 @@ obj_path, "org.bluez.Battery1", error)) { - g_prefix_error(error, "failed to add battery: "); + g_prefix_error_literal(error, "failed to add battery: "); return FALSE; } } @@ -690,7 +690,7 @@ NULL, error); if (val == NULL) { - g_prefix_error(error, "Failed to read GattCharacteristic1: "); + g_prefix_error_literal(error, "Failed to read GattCharacteristic1: "); return NULL; } g_variant_get(val, "(ay)", &iter); @@ -792,7 +792,7 @@ NULL, error); if (ret == NULL) { - g_prefix_error(error, "Failed to write GattCharacteristic1: "); + g_prefix_error_literal(error, "Failed to write GattCharacteristic1: "); return FALSE; } @@ -836,7 +836,7 @@ NULL, error); if (retval == NULL) { - g_prefix_error(error, "Failed to enable notifications: "); + g_prefix_error_literal(error, "Failed to enable notifications: "); return FALSE; } @@ -879,7 +879,7 @@ NULL, error); if (retval == NULL) { - g_prefix_error(error, "Failed to enable notifications: "); + g_prefix_error_literal(error, "Failed to enable notifications: "); return FALSE; } @@ -915,7 +915,7 @@ g_variant_new("(@a{sv})", opt_variant), G_DBUS_CALL_FLAGS_NONE, -1, - NULL, // fd list + NULL, /* fd list */ &out_fd_list, NULL, error); @@ -985,14 +985,14 @@ } static void -fu_bluez_device_incorporate(FuDevice *self, FuDevice *donor) +fu_bluez_device_incorporate(FuDevice *device, FuDevice *donor) { - FuBluezDevice *uself = FU_BLUEZ_DEVICE(self); + FuBluezDevice *uself = FU_BLUEZ_DEVICE(device); FuBluezDevice *udonor = FU_BLUEZ_DEVICE(donor); FuBluezDevicePrivate *priv = GET_PRIVATE(uself); FuBluezDevicePrivate *privdonor = GET_PRIVATE(udonor); - g_return_if_fail(FU_IS_BLUEZ_DEVICE(self)); + g_return_if_fail(FU_IS_BLUEZ_DEVICE(device)); g_return_if_fail(FU_IS_BLUEZ_DEVICE(donor)); if (g_hash_table_size(priv->uuids) == 0) { diff -Nru fwupd-2.0.8/libfwupdplugin/fu-byte-array.c fwupd-2.0.20/libfwupdplugin/fu-byte-array.c --- fwupd-2.0.8/libfwupdplugin/fu-byte-array.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-byte-array.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,7 @@ #include "fu-byte-array.h" #include "fu-common.h" #include "fu-firmware-common.h" -#include "fu-mem.h" +#include "fu-mem-private.h" /** * fu_byte_array_to_string: @@ -91,7 +91,7 @@ void fu_byte_array_append_uint16(GByteArray *array, guint16 data, FuEndianType endian) { - guint8 buf[2]; + guint8 buf[2]; /* nocheck:zero-init */ fu_memwrite_uint16(buf, data, endian); g_byte_array_append(array, buf, sizeof(buf)); } @@ -109,7 +109,7 @@ void fu_byte_array_append_uint24(GByteArray *array, guint32 data, FuEndianType endian) { - guint8 buf[3]; + guint8 buf[3]; /* nocheck:zero-init */ fu_memwrite_uint24(buf, data, endian); g_byte_array_append(array, buf, sizeof(buf)); } @@ -127,7 +127,7 @@ void fu_byte_array_append_uint32(GByteArray *array, guint32 data, FuEndianType endian) { - guint8 buf[4]; + guint8 buf[4]; /* nocheck:zero-init */ fu_memwrite_uint32(buf, data, endian); g_byte_array_append(array, buf, sizeof(buf)); } @@ -145,7 +145,7 @@ void fu_byte_array_append_uint64(GByteArray *array, guint64 data, FuEndianType endian) { - guint8 buf[8]; + guint8 buf[8]; /* nocheck:zero-init */ fu_memwrite_uint64(buf, data, endian); g_byte_array_append(array, buf, sizeof(buf)); } @@ -166,6 +166,56 @@ } /** + * fu_byte_array_append_array: + * @array: a #GByteArray + * @array2: another #GByteArray + * + * Adds the content of @array2 to @array. + * + * Since: 2.0.17 + **/ +void +fu_byte_array_append_array(GByteArray *array, GByteArray *array2) +{ + g_return_if_fail(array != NULL); + g_return_if_fail(array2 != NULL); + g_byte_array_append(array, array2->data, array2->len); +} + +/** + * fu_byte_array_append_safe: + * @array: a #GByteArray + * @buf: a raw byte buffer + * @bufsz: size of @buf + * @offset: offset in bytes + * @n: number of bytes + * @error: (nullable): optional return location for an error + * + * Adds the content of @buf at @offset to @array. + * + * Returns: %TRUE if the access is safe, %FALSE otherwise + * + * Since: 2.1.1 + **/ +gboolean +fu_byte_array_append_safe(GByteArray *array, + const guint8 *buf, + gsize bufsz, + gsize offset, + gsize n, + GError **error) +{ + g_return_val_if_fail(array != NULL, FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!fu_memchk_read(bufsz, offset, n, error)) + return FALSE; + g_byte_array_append(array, buf + offset, n); + return TRUE; +} + +/** * fu_byte_array_set_size: * @array: a #GByteArray * @length: the new size of the GByteArray diff -Nru fwupd-2.0.8/libfwupdplugin/fu-byte-array.h fwupd-2.0.20/libfwupdplugin/fu-byte-array.h --- fwupd-2.0.8/libfwupdplugin/fu-byte-array.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-byte-array.h 2026-02-26 11:36:18.000000000 +0000 @@ -34,5 +34,14 @@ G_GNUC_NON_NULL(1); void fu_byte_array_append_bytes(GByteArray *array, GBytes *bytes) G_GNUC_NON_NULL(1, 2); +void +fu_byte_array_append_array(GByteArray *array, GByteArray *array2) G_GNUC_NON_NULL(1, 2); +gboolean +fu_byte_array_append_safe(GByteArray *array, + const guint8 *buf, + gsize bufsz, + gsize offset, + gsize n, + GError **error) G_GNUC_NON_NULL(1, 2); gboolean fu_byte_array_compare(GByteArray *buf1, GByteArray *buf2, GError **error) G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-bytes.c fwupd-2.0.20/libfwupdplugin/fu-bytes.c --- fwupd-2.0.8/libfwupdplugin/fu-bytes.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-bytes.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,8 +12,6 @@ #include "fu-byte-array.h" #include "fu-bytes.h" -#include "fu-common.h" -#include "fu-input-stream.h" #include "fu-mem.h" /** @@ -278,7 +276,7 @@ { const guint8 *buf = g_bytes_get_data(bytes, bufsz); if (buf == NULL) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid data"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid data"); return NULL; } return buf; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-cab-firmware.c fwupd-2.0.20/libfwupdplugin/fu-cab-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-cab-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-cab-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,6 @@ #include #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-cab-firmware-private.h" #include "fu-cab-image.h" #include "fu-cab-struct.h" @@ -108,7 +107,7 @@ typedef struct { GInputStream *stream; - FwupdInstallFlags install_flags; + FuFirmwareParseFlags parse_flags; gsize rsvd_folder; gsize rsvd_block; gsize size_total; @@ -139,23 +138,27 @@ fu_cab_firmware_compute_checksum(const guint8 *buf, gsize bufsz, guint32 *checksum, GError **error) { guint32 tmp = *checksum; - for (gsize i = 0; i < bufsz; i += 4) { - gsize chunksz = bufsz - i; - if (G_LIKELY(chunksz >= 4)) { - /* 3,2,1,0 */ - tmp ^= ((guint32)buf[i + 3] << 24) | ((guint32)buf[i + 2] << 16) | - ((guint32)buf[i + 1] << 8) | (guint32)buf[i + 0]; - } else if (chunksz == 3) { - /* 0,1,2 -- yes, weird */ - tmp ^= ((guint32)buf[i + 0] << 16) | ((guint32)buf[i + 1] << 8) | - (guint32)buf[i + 2]; - } else if (chunksz == 2) { - /* 0,1 -- yes, weird */ - tmp ^= ((guint32)buf[i + 0] << 8) | (guint32)buf[i + 1]; - } else { + while (bufsz != 0) { + if (G_UNLIKELY(bufsz == 1)) { /* 0 */ - tmp ^= (guint32)buf[i + 0]; + tmp ^= (guint32)buf[0]; + break; + } + if (G_UNLIKELY(bufsz == 2)) { + /* 0,1 -- yes, weird */ + tmp ^= ((guint32)buf[0] << 8) | (guint32)buf[1]; + break; + } + if (G_UNLIKELY(bufsz == 3)) { + /* 0,1,2 -- yes, weird, nocheck:endian */ + tmp ^= ((guint32)buf[0] << 16) | ((guint32)buf[1] << 8) | (guint32)buf[2]; + break; } + /* 3,2,1,0 nocheck:endian */ + tmp ^= ((guint32)buf[3] << 24) | ((guint32)buf[2] << 16) | ((guint32)buf[1] << 8) | + (guint32)buf[0]; + buf += 4; + bufsz -= 4; } *checksum = tmp; return TRUE; @@ -235,16 +238,16 @@ return FALSE; } - hdr_sz = st->len + helper->rsvd_block; + hdr_sz = st->buf->len + helper->rsvd_block; /* verify checksum */ partial_stream = fu_partial_input_stream_new(helper->stream, *offset + hdr_sz, blob_comp, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut cabinet checksum: "); + g_prefix_error_literal(error, "failed to cut cabinet checksum: "); return FALSE; } - if ((helper->install_flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((helper->parse_flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint32 checksum = fu_struct_cab_data_get_checksum(st); if (checksum != 0) { guint32 checksum_actual = 0; @@ -328,7 +331,30 @@ zError(zret)); return FALSE; } + + /* prevent decompression bombs */ + if (size_max > 0 && buf->len > size_max) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "decompressed data too large (0x%x, limit 0x%x)", + (guint)buf->len, + (guint)size_max); + return FALSE; + } } + + /* verify actual decompressed size matches declared size */ + if (buf->len != blob_uncomp) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "decompressed size mismatch (0x%x, specified 0x%x)", + (guint)buf->len, + (guint)blob_uncomp); + return FALSE; + } + zret = inflateReset(&helper->zstrm); if (zret != Z_OK) { g_set_error(error, @@ -371,7 +397,7 @@ GError **error) { FuCabFirmwarePrivate *priv = GET_PRIVATE(self); - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructCabFolder) st = NULL; /* parse header */ st = fu_struct_cab_folder_parse_stream(helper->stream, offset, error); @@ -429,7 +455,7 @@ guint16 index; guint16 time; g_autoptr(FuCabImage) img = fu_cab_image_new(); - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructCabFile) st = NULL; g_autoptr(GDateTime) created = NULL; g_autoptr(GInputStream) stream = NULL; g_autoptr(GString) filename = g_string_new(NULL); @@ -488,12 +514,12 @@ fu_struct_cab_file_get_usize(st), error); if (stream == NULL) { - g_prefix_error(error, "failed to cut cabinet image: "); + g_prefix_error_literal(error, "failed to cut cabinet image: "); return FALSE; } - if (!fu_firmware_parse_stream(FU_FIRMWARE(img), stream, 0x0, helper->install_flags, error)) + if (!fu_firmware_parse_stream(FU_FIRMWARE(img), stream, 0x0, helper->parse_flags, error)) return FALSE; - if (!fu_firmware_add_image_full(FU_FIRMWARE(self), FU_FIRMWARE(img), error)) + if (!fu_firmware_add_image(FU_FIRMWARE(self), FU_FIRMWARE(img), error)) return FALSE; /* set created date time */ @@ -520,7 +546,7 @@ } static FuCabFirmwareParseHelper * -fu_cab_firmware_parse_helper_new(GInputStream *stream, FwupdInstallFlags flags, GError **error) +fu_cab_firmware_parse_helper_new(GInputStream *stream, FuFirmwareParseFlags flags, GError **error) { int zret; g_autoptr(FuCabFirmwareParseHelper) helper = g_new0(FuCabFirmwareParseHelper, 1); @@ -539,7 +565,7 @@ } helper->stream = g_object_ref(stream); - helper->install_flags = flags; + helper->parse_flags = flags; helper->folder_data = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); helper->decompress_bufsz = FU_CAB_FIRMWARE_DECOMPRESS_BUFSZ; return g_steal_pointer(&helper); @@ -548,14 +574,14 @@ static gboolean fu_cab_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuCabFirmware *self = FU_CAB_FIRMWARE(firmware); gsize off_cffile = 0; gsize offset = 0; gsize streamsz = 0; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructCabHeader) st = NULL; g_autoptr(FuCabFirmwareParseHelper) helper = NULL; /* get size */ @@ -629,13 +655,13 @@ helper->ndatabsz = streamsz; /* reserved sizes */ - offset += st->len; + offset += st->buf->len; if (fu_struct_cab_header_get_flags(st) & 0x0004) { - g_autoptr(GByteArray) st2 = NULL; + g_autoptr(FuStructCabHeaderReserve) st2 = NULL; st2 = fu_struct_cab_header_reserve_parse_stream(stream, offset, error); if (st2 == NULL) return FALSE; - offset += st2->len; + offset += st2->buf->len; offset += fu_struct_cab_header_reserve_get_rsvd_hdr(st2); helper->rsvd_block = fu_struct_cab_header_reserve_get_rsvd_block(st2); helper->rsvd_folder = fu_struct_cab_header_reserve_get_rsvd_folder(st2); @@ -677,8 +703,8 @@ gsize archive_size; gsize offset; guint32 index_into = 0; - g_autoptr(GByteArray) st_hdr = fu_struct_cab_header_new(); - g_autoptr(GByteArray) st_folder = fu_struct_cab_folder_new(); + g_autoptr(FuStructCabHeader) st_hdr = fu_struct_cab_header_new(); + g_autoptr(FuStructCabFolder) st_folder = fu_struct_cab_folder_new(); g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); g_autoptr(GByteArray) cfdata_linear = g_byte_array_new(); g_autoptr(GBytes) cfdata_linear_blob = NULL; @@ -802,7 +828,7 @@ fu_struct_cab_folder_set_compression(st_folder, priv->compressed ? FU_CAB_COMPRESSION_MSZIP : FU_CAB_COMPRESSION_NONE); - g_byte_array_append(st_hdr, st_folder->data, st_folder->len); + fu_byte_array_append_array(st_hdr->buf, st_folder->buf); /* create each CFFILE */ for (guint i = 0; i < imgs->len; i++) { @@ -810,7 +836,7 @@ FuCabFileAttribute fattr = FU_CAB_FILE_ATTRIBUTE_NONE; GDateTime *created = fu_cab_image_get_created(FU_CAB_IMAGE(img)); const gchar *filename_win32 = fu_cab_image_get_win32_filename(FU_CAB_IMAGE(img)); - g_autoptr(GByteArray) st_file = fu_struct_cab_file_new(); + g_autoptr(FuStructCabFile) st_file = fu_struct_cab_file_new(); g_autoptr(GBytes) img_blob = fu_firmware_get_bytes(img, NULL); if (!g_str_is_ascii(filename_win32)) @@ -828,10 +854,12 @@ (g_date_time_get_minute(created) << 5) + (g_date_time_get_second(created) / 2)); } - g_byte_array_append(st_hdr, st_file->data, st_file->len); + fu_byte_array_append_array(st_hdr->buf, st_file->buf); - g_byte_array_append(st_hdr, (const guint8 *)filename_win32, strlen(filename_win32)); - fu_byte_array_append_uint8(st_hdr, 0x0); + g_byte_array_append(st_hdr->buf, + (const guint8 *)filename_win32, + strlen(filename_win32)); + fu_byte_array_append_uint8(st_hdr->buf, 0x0); index_into += g_bytes_get_size(img_blob); } @@ -841,7 +869,7 @@ GByteArray *chunk_zlib = g_ptr_array_index(chunks_zlib, i); g_autoptr(FuChunk) chk = NULL; g_autoptr(GByteArray) hdr = g_byte_array_new(); - g_autoptr(GByteArray) st_data = fu_struct_cab_data_new(); + g_autoptr(FuStructCabData) st_data = fu_struct_cab_data_new(); /* prepare chunk */ chk = fu_chunk_array_index(chunks, i, error); @@ -862,12 +890,12 @@ fu_struct_cab_data_set_checksum(st_data, checksum); fu_struct_cab_data_set_comp(st_data, chunk_zlib->len); fu_struct_cab_data_set_uncomp(st_data, fu_chunk_get_data_sz(chk)); - g_byte_array_append(st_hdr, st_data->data, st_data->len); - g_byte_array_append(st_hdr, chunk_zlib->data, chunk_zlib->len); + fu_byte_array_append_array(st_hdr->buf, st_data->buf); + g_byte_array_append(st_hdr->buf, chunk_zlib->data, chunk_zlib->len); } /* success */ - return g_steal_pointer(&st_hdr); + return g_steal_pointer(&st_hdr->buf); } static gboolean diff -Nru fwupd-2.0.8/libfwupdplugin/fu-cab-image.c fwupd-2.0.20/libfwupdplugin/fu-cab-image.c --- fwupd-2.0.8/libfwupdplugin/fu-cab-image.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-cab-image.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,6 @@ #include "fu-cab-image.h" #include "fu-common.h" -#include "fu-string.h" struct _FuCabImage { FuFirmware parent_instance; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-cfi-device.c fwupd-2.0.20/libfwupdplugin/fu-cfi-device.c --- fwupd-2.0.8/libfwupdplugin/fu-cfi-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-cfi-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -189,7 +189,7 @@ sizeof(buf), progress, error)) { - g_prefix_error(error, "failed to want to status: "); + g_prefix_error_literal(error, "failed to want to status: "); return FALSE; } if ((buf[0x1] & helper->mask) != helper->value) { @@ -250,7 +250,7 @@ sizeof(buf_req), progress, error)) { - g_prefix_error(error, "failed to request JEDEC ID: "); + g_prefix_error_literal(error, "failed to request JEDEC ID: "); return FALSE; } if ((buf_req[0] == 0x0 && buf_req[1] == 0x0 && buf_req[2] == 0x0) || @@ -647,13 +647,13 @@ } static gboolean -fu_cfi_device_chip_select_assert(GObject *device, GError **error) +fu_cfi_device_chip_select_assert_cb(FuDevice *device, GError **error) { return fu_cfi_device_chip_select(FU_CFI_DEVICE(device), TRUE, error); } static gboolean -fu_cfi_device_chip_select_deassert(GObject *device, GError **error) +fu_cfi_device_chip_select_deassert_cb(FuDevice *device, GError **error) { return fu_cfi_device_chip_select(FU_CFI_DEVICE(device), FALSE, error); } @@ -673,9 +673,9 @@ { g_return_val_if_fail(FU_IS_CFI_DEVICE(self), NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); - return fu_device_locker_new_full(self, - fu_cfi_device_chip_select_assert, - fu_cfi_device_chip_select_deassert, + return fu_device_locker_new_full(FU_DEVICE(self), + fu_cfi_device_chip_select_assert_cb, + fu_cfi_device_chip_select_deassert_cb, error); } @@ -882,11 +882,11 @@ /* erase */ if (!fu_cfi_device_write_enable(self, error)) { - g_prefix_error(error, "failed to enable writes: "); + g_prefix_error_literal(error, "failed to enable writes: "); return FALSE; } if (!fu_cfi_device_chip_erase(self, error)) { - g_prefix_error(error, "failed to erase: "); + g_prefix_error_literal(error, "failed to erase: "); return FALSE; } fu_progress_step_done(progress); @@ -897,7 +897,7 @@ FU_CHUNK_PAGESZ_NONE, fu_cfi_device_get_page_size(self)); if (!fu_cfi_device_write_pages(self, pages, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write pages: "); + g_prefix_error_literal(error, "failed to write pages: "); return FALSE; } fu_progress_step_done(progress); @@ -908,11 +908,11 @@ fu_progress_get_child(progress), error); if (fw_verify == NULL) { - g_prefix_error(error, "failed to verify blocks: "); + g_prefix_error_literal(error, "failed to verify blocks: "); return FALSE; } if (!fu_bytes_compare(fw_verify, fw, error)) { - g_prefix_error(error, "verify failed: "); + g_prefix_error_literal(error, "verify failed: "); return FALSE; } fu_progress_step_done(progress); @@ -922,7 +922,7 @@ } static void -fu_cfi_device_set_progress(FuDevice *self, FuProgress *progress) +fu_cfi_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -950,7 +950,9 @@ fu_device_add_protocol(FU_DEVICE(self), "org.jedec.cfi"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_ENFORCE_REQUIRES); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); fu_device_build_vendor_id(FU_DEVICE(self), "SPI", "*"); fu_device_set_summary(FU_DEVICE(self), "CFI flash chip"); } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-cfi-device.h fwupd-2.0.20/libfwupdplugin/fu-cfi-device.h --- fwupd-2.0.8/libfwupdplugin/fu-cfi-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-cfi-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -7,7 +7,7 @@ #pragma once #include "fu-cfi-struct.h" -#include "fu-device.h" +#include "fu-device-locker.h" #define FU_TYPE_CFI_DEVICE (fu_cfi_device_get_type()) G_DECLARE_DERIVABLE_TYPE(FuCfiDevice, fu_cfi_device, FU, CFI_DEVICE, FuDevice) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-cfu-offer.c fwupd-2.0.20/libfwupdplugin/fu-cfu-offer.c --- fwupd-2.0.8/libfwupdplugin/fu-cfu-offer.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-cfu-offer.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,7 +8,6 @@ #include "config.h" -#include "fu-byte-array.h" #include "fu-cfu-firmware-struct.h" #include "fu-cfu-offer.h" #include "fu-common.h" @@ -419,7 +418,7 @@ static gboolean fu_cfu_offer_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuCfuOffer *self = FU_CFU_OFFER(firmware); @@ -427,7 +426,7 @@ guint8 flags1; guint8 flags2; guint8 flags3; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructCfuOffer) st = NULL; /* parse */ st = fu_struct_cfu_offer_parse_stream(stream, 0x0, error); @@ -463,7 +462,7 @@ { FuCfuOffer *self = FU_CFU_OFFER(firmware); FuCfuOfferPrivate *priv = GET_PRIVATE(self); - g_autoptr(GByteArray) st = fu_struct_cfu_offer_new(); + g_autoptr(FuStructCfuOffer) st = fu_struct_cfu_offer_new(); /* component info */ fu_struct_cfu_offer_set_segment_number(st, priv->segment_number); @@ -483,7 +482,7 @@ fu_struct_cfu_offer_set_product_id(st, priv->product_id); /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static gboolean diff -Nru fwupd-2.0.8/libfwupdplugin/fu-cfu-payload.c fwupd-2.0.20/libfwupdplugin/fu-cfu-payload.c --- fwupd-2.0.8/libfwupdplugin/fu-cfu-payload.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-cfu-payload.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,10 +9,8 @@ #include "config.h" #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-cfu-firmware-struct.h" #include "fu-cfu-payload.h" -#include "fu-common.h" #include "fu-input-stream.h" /** @@ -32,7 +30,7 @@ static gboolean fu_cfu_payload_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize offset = 0; @@ -45,12 +43,12 @@ guint8 chunk_size = 0; g_autoptr(FuChunk) chk = NULL; g_autoptr(GBytes) blob = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructCfuPayload) st = NULL; st = fu_struct_cfu_payload_parse_stream(stream, offset, error); if (st == NULL) return FALSE; - offset += st->len; + offset += st->buf->len; chunk_size = fu_struct_cfu_payload_get_size(st); if (chunk_size == 0) { g_set_error_literal(error, @@ -85,10 +83,10 @@ return NULL; for (guint i = 0; i < chunks->len; i++) { FuChunk *chk = g_ptr_array_index(chunks, i); - g_autoptr(GByteArray) st = fu_struct_cfu_payload_new(); + g_autoptr(FuStructCfuPayload) st = fu_struct_cfu_payload_new(); fu_struct_cfu_payload_set_addr(st, fu_chunk_get_address(chk)); fu_struct_cfu_payload_set_size(st, fu_chunk_get_data_sz(chk)); - g_byte_array_append(buf, st->data, st->len); + fu_byte_array_append_array(buf, st->buf); g_byte_array_append(buf, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); } return g_steal_pointer(&buf); @@ -97,6 +95,7 @@ static void fu_cfu_payload_init(FuCfuPayload *self) { + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION); } static void diff -Nru fwupd-2.0.8/libfwupdplugin/fu-chunk-array.c fwupd-2.0.20/libfwupdplugin/fu-chunk-array.c --- fwupd-2.0.8/libfwupdplugin/fu-chunk-array.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-chunk-array.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,7 +8,6 @@ #include "config.h" -#include "fu-bytes.h" #include "fu-chunk-array.h" #include "fu-chunk-private.h" #include "fu-input-stream.h" diff -Nru fwupd-2.0.8/libfwupdplugin/fu-chunk.c fwupd-2.0.20/libfwupdplugin/fu-chunk.c --- fwupd-2.0.8/libfwupdplugin/fu-chunk.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-chunk.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,9 +8,6 @@ #include "config.h" -#include - -#include "fu-bytes.h" #include "fu-chunk-private.h" #include "fu-common.h" #include "fu-mem.h" @@ -63,7 +60,7 @@ guint fu_chunk_get_idx(FuChunk *self) { - g_return_val_if_fail(FU_IS_CHUNK(self), G_MAXUINT32); + g_return_val_if_fail(FU_IS_CHUNK(self), G_MAXUINT); return self->idx; } @@ -96,7 +93,7 @@ guint fu_chunk_get_page(FuChunk *self) { - g_return_val_if_fail(FU_IS_CHUNK(self), G_MAXUINT32); + g_return_val_if_fail(FU_IS_CHUNK(self), G_MAXUINT); return self->page; } @@ -129,7 +126,7 @@ gsize fu_chunk_get_address(FuChunk *self) { - g_return_val_if_fail(FU_IS_CHUNK(self), G_MAXUINT32); + g_return_val_if_fail(FU_IS_CHUNK(self), G_MAXSIZE); return self->address; } @@ -191,7 +188,7 @@ gsize fu_chunk_get_data_sz(FuChunk *self) { - g_return_val_if_fail(FU_IS_CHUNK(self), G_MAXUINT32); + g_return_val_if_fail(FU_IS_CHUNK(self), G_MAXSIZE); return self->data_sz; } @@ -314,6 +311,8 @@ datastr = g_base64_encode(self->data, self->data_sz); } xb_builder_node_insert_text(bn, "data", datastr, "size", dataszstr, NULL); + } else { + fu_xmlb_builder_insert_kx(bn, "size", self->data_sz); } } @@ -441,11 +440,17 @@ } /* cut the packet so it does not straddle multiple blocks */ - if (page_sz != packet_sz && page_sz > 0) - chunksz = MIN(chunksz, (offset + packet_sz) % page_sz); - g_ptr_array_add( - chunks, - fu_chunk_new(chunks->len, page, address_offset, data + offset, chunksz)); + if (page_sz != packet_sz && page_sz > 0) { + gsize chunksz_tmp = MIN(chunksz, (offset + packet_sz) % page_sz); + if (chunksz_tmp != 0) + chunksz = chunksz_tmp; + } + g_ptr_array_add(chunks, + fu_chunk_new(chunks->len, + page, + address_offset, + data != NULL ? data + offset : NULL, + chunksz)); offset += chunksz; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-common-freebsd.c fwupd-2.0.20/libfwupdplugin/fu-common-freebsd.c --- fwupd-2.0.8/libfwupdplugin/fu-common-freebsd.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-common-freebsd.c 2026-02-26 11:36:18.000000000 +0000 @@ -34,7 +34,7 @@ connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); if (connection == NULL) { - g_prefix_error(error, "failed to get system bus: "); + g_prefix_error_literal(error, "failed to get system bus: "); return NULL; } proxy = g_dbus_proxy_new_sync(connection, @@ -60,7 +60,7 @@ error); if (output == NULL) { if (error != NULL) - g_dbus_error_strip_remote_error(*error); + g_dbus_error_strip_remote_error(*error); /* nocheck:error */ g_prefix_error(error, "failed to call %s.%s(): ", UDISKS_DBUS_MANAGER_INTERFACE, @@ -118,3 +118,13 @@ { return fu_kenv_get_string("kernel_options", error); } + +gchar * +fu_common_get_olson_timezone_id_impl(GError **error) +{ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "getting the Olson timezone ID is not supported on FreeBSD"); + return NULL; +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-common-guid.c fwupd-2.0.20/libfwupdplugin/fu-common-guid.c --- fwupd-2.0.8/libfwupdplugin/fu-common-guid.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-common-guid.c 2026-02-26 11:36:18.000000000 +0000 @@ -23,13 +23,13 @@ gboolean fu_common_guid_is_plausible(const guint8 *buf) { - guint guint_sum = 0; + guint sum = 0; for (guint i = 0; i < 16; i++) - guint_sum += buf[i]; - if (guint_sum == 0x00) + sum += buf[i]; + if (sum == 0x00) return FALSE; - if (guint_sum < 0xff) + if (sum < 0xff) return FALSE; return TRUE; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-common-linux.c fwupd-2.0.20/libfwupdplugin/fu-common-linux.c --- fwupd-2.0.8/libfwupdplugin/fu-common-linux.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-common-linux.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,14 +15,77 @@ #include "fu-kernel.h" #include "fu-path.h" -#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2/Manager" +#define UDISKS_DBUS_PATH "/org/freedesktop/UDisks2" +#define UDISKS_DBUS_MANAGER_PATH "/org/freedesktop/UDisks2/Manager" #define UDISKS_DBUS_MANAGER_INTERFACE "org.freedesktop.UDisks2.Manager" +#define UDISKS_METHOD_TIMEOUT 10000 /* ms */ + +/* required for udisks <= 2.1.7 */ +static GPtrArray * +fu_common_get_block_devices_legacy(GError **error) +{ + g_autolist(GDBusObject) dbus_objects = NULL; + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GDBusObjectManager) dbus_object_manager = NULL; + g_autoptr(GPtrArray) devices = + g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); + if (connection == NULL) { + g_prefix_error_literal(error, "failed to get system bus: "); + return NULL; + } + dbus_object_manager = + g_dbus_object_manager_client_new_sync(connection, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_NONE, + UDISKS_DBUS_SERVICE, + UDISKS_DBUS_PATH, + NULL, + NULL, + NULL, + NULL, + error); + if (dbus_object_manager == NULL) + return NULL; + dbus_objects = g_dbus_object_manager_get_objects(dbus_object_manager); + for (GList *l = dbus_objects; l != NULL; l = l->next) { + GDBusObject *dbus_object = G_DBUS_OBJECT(l->data); + const gchar *obj = g_dbus_object_get_object_path(dbus_object); + g_autoptr(GDBusInterface) dbus_iface_blk = NULL; + g_autoptr(GDBusProxy) proxy_blk = NULL; + + dbus_iface_blk = + g_dbus_object_get_interface(dbus_object, UDISKS_DBUS_INTERFACE_BLOCK); + if (dbus_iface_blk == NULL) { + g_debug("skipping %s as has no block interface", obj); + continue; + } + proxy_blk = g_dbus_proxy_new_sync(connection, + G_DBUS_PROXY_FLAGS_NONE, + NULL, + UDISKS_DBUS_SERVICE, + obj, + UDISKS_DBUS_INTERFACE_BLOCK, + NULL, + error); + if (proxy_blk == NULL) { + g_prefix_error(error, "failed to initialize d-bus proxy for %s: ", obj); + return NULL; + } + g_ptr_array_add(devices, g_steal_pointer(&proxy_blk)); + } + + /* success */ + return g_steal_pointer(&devices); +} + GPtrArray * fu_common_get_block_devices(GError **error) { GVariantBuilder builder; const gchar *obj; + g_autoptr(GError) error_local = NULL; g_autoptr(GVariant) output = NULL; g_autoptr(GDBusProxy) proxy = NULL; g_autoptr(GPtrArray) devices = NULL; @@ -31,14 +94,14 @@ connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); if (connection == NULL) { - g_prefix_error(error, "failed to get system bus: "); + g_prefix_error_literal(error, "failed to get system bus: "); return NULL; } proxy = g_dbus_proxy_new_sync(connection, G_DBUS_PROXY_FLAGS_NONE, NULL, UDISKS_DBUS_SERVICE, - UDISKS_DBUS_PATH, + UDISKS_DBUS_MANAGER_PATH, UDISKS_DBUS_MANAGER_INTERFACE, NULL, error); @@ -53,16 +116,20 @@ "GetBlockDevices", g_variant_new("(a{sv})", &builder), G_DBUS_CALL_FLAGS_NONE, - -1, + UDISKS_METHOD_TIMEOUT, NULL, - error); + &error_local); if (output == NULL) { - if (error != NULL) - g_dbus_error_strip_remote_error(*error); - g_prefix_error(error, - "failed to call %s.%s(): ", - UDISKS_DBUS_MANAGER_INTERFACE, - "GetBlockDevices"); + if (g_error_matches(error_local, G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_METHOD)) { + g_debug("ignoring %s, trying fallback", error_local->message); + return fu_common_get_block_devices_legacy(error); + } + g_dbus_error_strip_remote_error(error_local); + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to call %s.%s(): ", + UDISKS_DBUS_MANAGER_INTERFACE, + "GetBlockDevices"); return NULL; } @@ -109,6 +176,7 @@ "apparmor", "audit", "auto", + "bluetooth.disable_ertm", "boot", "BOOT_IMAGE", "console", @@ -119,6 +187,7 @@ "earlycon", "earlyprintk", "ether", + "init", "initrd", "ip", "LANG", @@ -137,7 +206,9 @@ "nfs.nfs4_unique_id", "nfsroot", "noplymouth", + "nowatchdog", "ostree", + "preempt", "quiet", "rd.dm.uuid", "rd.luks.allow-discards", @@ -156,12 +227,15 @@ "ro", "root", "rootflags", + "rootfstype", "roothash", "rw", "security", + "selinux", "showopts", "splash", "swap", + "systemd.machine_id", "systemd.mask", "systemd.show_status", "systemd.unit", @@ -172,6 +246,7 @@ "verbose", "vt.handoff", "zfs", + "zswap.enabled", NULL, /* last entry */ }; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-common-windows.c fwupd-2.0.20/libfwupdplugin/fu-common-windows.c --- fwupd-2.0.8/libfwupdplugin/fu-common-windows.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-common-windows.c 2026-02-26 11:36:18.000000000 +0000 @@ -217,7 +217,7 @@ } name = g_utf16_to_utf8(tzinfo.StandardName, -1, NULL, NULL, error); if (name == NULL) { - g_prefix_error(error, "cannot convert timezone name to UTF-8: "); + g_prefix_error_literal(error, "cannot convert timezone name to UTF-8: "); return NULL; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-common.c fwupd-2.0.20/libfwupdplugin/fu-common.c --- fwupd-2.0.8/libfwupdplugin/fu-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,68 @@ #include "fu-string.h" /** + * fu_size_checked_add: + * @a: The #gsize left operand + * @b: The #gsize right operand + * + * Performs a checked addition of a and b, ensuring the result does not overflow. + * + * Returns: @a+@b, or %G_MAXSIZE on overflow + * + * Since: 2.0.19 + **/ +gsize +fu_size_checked_add(gsize a, gsize b) +{ + gsize tmp = 0; + if (!g_size_checked_add(&tmp, a, b)) + return G_MAXSIZE; + return tmp; +} + +/** + * fu_error_map_entry_to_gerror: + * @value: the value to look up + * @entries: the #FuErrorMapEntry map + * @n_entries: number of @entries + * @error: (nullable): optional return location for an error + * + * Sets the #GError from the integer value and the error map. + * + * Any entries with a error_code of `FWUPD_ERROR_LAST` will return success. + * + * Returns: boolean success + * + * Since: 2.0.13 + **/ +gboolean +fu_error_map_entry_to_gerror(guint value, + const FuErrorMapEntry entries[], + guint n_entries, + GError **error) +{ + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + for (guint i = 0; i < n_entries; i++) { + const FuErrorMapEntry entry = entries[i]; + if (entry.value != value) + continue; + if (entry.code == FWUPD_ERROR_LAST) + return TRUE; + g_set_error(error, + FWUPD_ERROR, + entry.code, + "%s [0x%x]", + entry.message != NULL ? entry.message + : fwupd_error_to_string(entry.value), + entry.value); + return FALSE; + } + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "generic failure [0x%x]", value); + return FALSE; +} + +/** * fu_cpuid: * @leaf: the CPUID level, now called the 'leaf' by Intel * @eax: (out) (nullable): EAX register @@ -75,8 +137,7 @@ { gsize bufsz = 0; g_autofree gchar *buf = NULL; - g_autofree gchar *procpath = fu_path_from_kind(FU_PATH_KIND_PROCFS); - g_autofree gchar *fn = g_build_filename(procpath, "cpuinfo", NULL); + g_autofree gchar *fn = fu_path_build(FU_PATH_KIND_PROCFS, "cpuinfo", NULL); g_autoptr(GHashTable) hash = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); g_return_val_if_fail(error == NULL || *error == NULL, NULL); @@ -232,43 +293,49 @@ gboolean fu_power_state_is_ac(FuPowerState power_state) { - if (power_state == FU_POWER_STATE_UNKNOWN || power_state == FU_POWER_STATE_AC || - power_state == FU_POWER_STATE_AC_CHARGING || - power_state == FU_POWER_STATE_AC_FULLY_CHARGED) - return TRUE; - return FALSE; + return power_state != FU_POWER_STATE_BATTERY; } /** * fu_error_convert: + * @entries: the #FuErrorConvertEntry map + * @n_entries: number of @entries * @perror: (nullable): A #GError, perhaps with domain #GIOError * * Convert the error to a #FwupdError, if required. * - * Since: 2.0.0 + * Since: 2.0.14 **/ -void -fu_error_convert(GError **perror) +gboolean +fu_error_convert(const FuErrorConvertEntry entries[], guint n_entries, GError **perror) { GError *error = (perror != NULL) ? *perror : NULL; /* sanity check */ if (error == NULL) - return; + return TRUE; /* convert GIOError and GFileError */ fwupd_error_convert(perror); if (error->domain == FWUPD_ERROR) - return; + return FALSE; + for (guint i = 0; i < n_entries; i++) { + if (g_error_matches(error, entries[i].domain, entries[i].code)) { + error->domain = FWUPD_ERROR; + error->code = entries[i].error; + return FALSE; + } + } #ifndef SUPPORTED_BUILD /* fallback */ - g_critical("GError %s:%i sending over D-Bus was not converted to FwupdError", + g_critical("GError %s:%i was not converted to FwupdError", g_quark_to_string(error->domain), error->code); #endif error->domain = FWUPD_ERROR; error->code = FWUPD_ERROR_INTERNAL; + return FALSE; } /** @@ -307,7 +374,7 @@ g_autofree gchar *value_hex = NULL; if (value == 0) return; - value_hex = g_strdup_printf("0x%x", (guint)value); + value_hex = g_strdup_printf("0x%lx", (gulong)value); xb_builder_node_insert_text(bn, key, value_hex, NULL); } @@ -326,18 +393,3 @@ { xb_builder_node_insert_text(bn, key, value ? "true" : "false", NULL); } - -/** - * fu_snap_is_in_snap: - * - * Check whether the current process is running inside a snap. - * - * Returns: TRUE if current process is running inside a snap. - * - * Since: 2.0.4 - **/ -gboolean -fu_snap_is_in_snap(void) -{ - return getenv("SNAP") != NULL; -} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-common.h fwupd-2.0.20/libfwupdplugin/fu-common.h --- fwupd-2.0.8/libfwupdplugin/fu-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -12,36 +12,6 @@ #include "fu-common-struct.h" /** - * FuCpuVendor: - * - * The CPU vendor. - **/ - -/** - * FuPowerState: - * - * The system power state. - * - * This does not have to be exactly what the battery is doing, but is supposed to represent the - * 40,000ft view of the system power state. - * - * For example, it is perfectly correct to set %FU_POWER_STATE_AC if the system is connected to - * AC power, but the battery cells are discharging for health or for other performance reasons. - **/ - -/** - * FuLidState: - * - * The device lid state. - **/ - -/** - * FuDisplayState: - * - * The device lid state. - **/ - -/** * FU_BIT_SET: * @val: integer value * @pos: bit position, where 0 is the least significant byte @@ -108,8 +78,30 @@ fu_common_align_up(gsize value, guint8 alignment); gboolean fu_power_state_is_ac(FuPowerState power_state); -void -fu_error_convert(GError **perror); +gsize +fu_size_checked_add(gsize a, gsize b); + +typedef struct { + guint value; + FwupdError code; + const gchar *message; +} FuErrorMapEntry; + +gboolean +fu_error_map_entry_to_gerror(guint value, + const FuErrorMapEntry entries[], + guint n_entries, + GError **error) G_GNUC_NON_NULL(2); + +typedef struct { + GQuark domain; + gint code; + FwupdError error; +} FuErrorConvertEntry; + +gboolean +fu_error_convert(const FuErrorConvertEntry entries[], guint n_entries, GError **perror) + G_GNUC_NON_NULL(1); void fu_xmlb_builder_insert_kv(XbBuilderNode *bn, const gchar *key, const gchar *value) @@ -118,6 +110,3 @@ fu_xmlb_builder_insert_kx(XbBuilderNode *bn, const gchar *key, guint64 value) G_GNUC_NON_NULL(1); void fu_xmlb_builder_insert_kb(XbBuilderNode *bn, const gchar *key, gboolean value) G_GNUC_NON_NULL(1); - -gboolean -fu_snap_is_in_snap(void); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-common.rs fwupd-2.0.20/libfwupdplugin/fu-common.rs --- fwupd-2.0.8/libfwupdplugin/fu-common.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-common.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,23 +1,31 @@ // Copyright 2024 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later +// The CPU vendor. +// Since: 1.5.5 enum FuCpuVendor { Unknown, Intel, Amd, } +// The system power state. +// +// This does not have to be exactly what the battery is doing, but is supposed to represent the +// 40,000ft view of the system power state. +// +// For example, it is perfectly correct to set %FU_POWER_STATE_AC if the system is connected to +// AC power, but the battery cells are discharging for health or for other performance reasons. +// Since: 1.8.11 #[derive(ToString)] enum FuPowerState { Unknown, Ac, // On AC power - AcCharging, // Charging on AC - AcFullyCharged, // Fully charged on AC Battery, // On system battery - BatteryDischarging, // System battery discharging - BatteryEmpty, // System battery empty } +// The device lid state. +// Since: 1.7.4 #[derive(ToString)] enum FuLidState { Unknown, @@ -25,6 +33,8 @@ Closed, } +// The device lid state. +// Since: 1.9.6 #[derive(ToString, FromString)] enum FuDisplayState { Unknown, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-composite-input-stream.c fwupd-2.0.20/libfwupdplugin/fu-composite-input-stream.c --- fwupd-2.0.8/libfwupdplugin/fu-composite-input-stream.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-composite-input-stream.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,6 @@ #include "fwupd-codec.h" #include "fu-composite-input-stream.h" -#include "fu-input-stream.h" #include "fu-partial-input-stream-private.h" /** @@ -175,7 +174,7 @@ /* create a partial stream that is actually the size of the entire GInputStream */ partial_stream = fu_partial_input_stream_new(stream, 0x0, G_MAXSIZE, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to add input stream: "); + g_prefix_error_literal(error, "failed to add input stream: "); return FALSE; } fu_composite_input_stream_add_partial_stream(self, FU_PARTIAL_INPUT_STREAM(partial_stream)); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-config-private.h fwupd-2.0.20/libfwupdplugin/fu-config-private.h --- fwupd-2.0.8/libfwupdplugin/fu-config-private.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-config-private.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,10 +8,19 @@ #include "fu-config.h" +typedef enum { + FU_CONFIG_LOAD_FLAG_NONE = 0, + FU_CONFIG_LOAD_FLAG_WATCH_FILES = 1 << 0, + FU_CONFIG_LOAD_FLAG_FIX_PERMISSIONS = 1 << 1, + FU_CONFIG_LOAD_FLAG_MIGRATE_FILES = 1 << 2, +} G_GNUC_FLAG_ENUM FuConfigLoadFlags; + FuConfig * fu_config_new(void); gboolean -fu_config_load(FuConfig *self, GError **error) G_GNUC_NON_NULL(1); +fu_config_load(FuConfig *self, FuConfigLoadFlags flags, GError **error) G_GNUC_NON_NULL(1); +void +fu_config_set_basename(FuConfig *self, const gchar *basename) G_GNUC_NON_NULL(1); gboolean fu_config_reset_defaults(FuConfig *self, const gchar *section, GError **error) G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-config.c fwupd-2.0.20/libfwupdplugin/fu-config.c --- fwupd-2.0.8/libfwupdplugin/fu-config.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-config.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,7 +13,6 @@ #include #include -#include "fu-byte-array.h" #include "fu-bytes.h" #include "fu-config-private.h" #include "fu-path.h" @@ -37,6 +36,7 @@ GKeyFile *keyfile; GHashTable *default_values; GPtrArray *items; /* (element-type FuConfigItem) */ + gchar *basename; } FuConfigPrivate; G_DEFINE_TYPE_WITH_PRIVATE(FuConfig, fu_config, G_TYPE_OBJECT) @@ -187,6 +187,7 @@ {"fwupd", "VerboseDomains", NULL}, {"fwupd", "OnlyTrusted", NULL}, {"fwupd", "IgnorePower", NULL}, + {"fwupd", "RequireImmutableEnumeration", NULL}, {"fwupd", "DisabledPlugins", "test;test_ble;invalid"}, {"fwupd", "DisabledPlugins", "test;test_ble"}, {"redfish", "IpmiDisableCreateUser", NULL}, @@ -239,19 +240,9 @@ } static gboolean -fu_config_reload(FuConfig *self, GError **error) +fu_config_ensure_permissions(FuConfig *self, GError **error) { FuConfigPrivate *priv = GET_PRIVATE(self); - g_autoptr(GPtrArray) legacy_cfg_files = g_ptr_array_new_with_free_func(g_free); - const gchar *fn_merge[] = {"daemon.conf", - "msr.conf", - "redfish.conf", - "thunderbolt.conf", - "uefi_capsule.conf", - NULL}; - -#ifndef _WIN32 - /* ensure mutable config files are set to the correct permissions */ for (guint i = 0; i < priv->items->len; i++) { FuConfigItem *item = g_ptr_array_index(priv->items, i); guint32 st_mode; @@ -268,7 +259,7 @@ NULL, error); if (info == NULL) { - g_prefix_error(error, "failed to query info about %s", item->filename); + g_prefix_error(error, "failed to query info about %s: ", item->filename); return FALSE; } st_mode = g_file_info_get_attribute_uint32(info, G_FILE_ATTRIBUTE_UNIX_MODE) & 0777; @@ -292,43 +283,23 @@ } } } -#endif - /* we have to copy each group/key from a temporary GKeyFile as g_key_file_load_from_file() - * clears all keys before loading each file, and we want to allow the mutable version to be - * incomplete and just *override* a specific option */ - if (!g_key_file_load_from_data(priv->keyfile, "", -1, G_KEY_FILE_NONE, error)) - return FALSE; - for (guint i = 0; i < priv->items->len; i++) { - FuConfigItem *item = g_ptr_array_index(priv->items, i); - g_autoptr(GError) error_load = NULL; - g_autoptr(GBytes) blob_item = NULL; + /* success */ + return TRUE; +} - g_debug("trying to load config values from %s", item->filename); - blob_item = fu_bytes_get_contents(item->filename, &error_load); - if (blob_item == NULL) { - if (g_error_matches(error_load, - FWUPD_ERROR, - FWUPD_ERROR_PERMISSION_DENIED)) { - g_debug("ignoring config file %s: ", error_load->message); - continue; - } else if (g_error_matches(error_load, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE)) { - g_debug("%s", error_load->message); - continue; - } - g_propagate_error(error, g_steal_pointer(&error_load)); - g_prefix_error(error, "failed to read %s: ", item->filename); - return FALSE; - } - if (!fu_config_load_bytes_replace(self, blob_item, error)) { - g_prefix_error(error, "failed to load %s: ", item->filename); - return FALSE; - } - } +static gboolean +fu_config_migrate_keyfiles(FuConfig *self, GError **error) +{ + FuConfigPrivate *priv = GET_PRIVATE(self); + g_autoptr(GPtrArray) legacy_cfg_files = g_ptr_array_new_with_free_func(g_free); + const gchar *fn_merge[] = {"daemon.conf", + "msr.conf", + "redfish.conf", + "thunderbolt.conf", + "uefi_capsule.conf", + NULL}; - /* are any of the legacy files found in this location? */ for (guint i = 0; i < priv->items->len; i++) { FuConfigItem *item = g_ptr_array_index(priv->items, i); g_autofree gchar *dirname = g_path_get_dirname(item->filename); @@ -396,6 +367,66 @@ return TRUE; } +static gboolean +fu_config_reload(FuConfig *self, FuConfigLoadFlags flags, GError **error) +{ + FuConfigPrivate *priv = GET_PRIVATE(self); + +#ifdef _WIN32 + /* not relevant */ + flags &= ~FU_CONFIG_LOAD_FLAG_FIX_PERMISSIONS; +#endif + + /* ensure mutable config files are set to the correct permissions */ + if (flags & FU_CONFIG_LOAD_FLAG_FIX_PERMISSIONS) { + if (!fu_config_ensure_permissions(self, error)) + return FALSE; + } + + /* we have to copy each group/key from a temporary GKeyFile as g_key_file_load_from_file() + * clears all keys before loading each file, and we want to allow the mutable version to be + * incomplete and just *override* a specific option */ + if (!g_key_file_load_from_data(priv->keyfile, "", -1, G_KEY_FILE_NONE, error)) + return FALSE; + for (guint i = 0; i < priv->items->len; i++) { + FuConfigItem *item = g_ptr_array_index(priv->items, i); + g_autoptr(GError) error_load = NULL; + g_autoptr(GBytes) blob_item = NULL; + + g_debug("trying to load config values from %s", item->filename); + blob_item = fu_bytes_get_contents(item->filename, &error_load); + if (blob_item == NULL) { + if (g_error_matches(error_load, + FWUPD_ERROR, + FWUPD_ERROR_PERMISSION_DENIED)) { + g_debug("ignoring config file %s: ", error_load->message); + continue; + } else if (g_error_matches(error_load, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE)) { + g_debug("%s", error_load->message); + continue; + } + g_propagate_error(error, g_steal_pointer(&error_load)); + g_prefix_error(error, "failed to read %s: ", item->filename); + return FALSE; + } + if (!fu_config_load_bytes_replace(self, blob_item, error)) { + g_prefix_error(error, "failed to load %s: ", item->filename); + return FALSE; + } + } + + /* are any of the legacy files found in this location? */ + if (flags & FU_CONFIG_LOAD_FLAG_MIGRATE_FILES) { + if (!fu_config_migrate_keyfiles(self, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + static void fu_config_monitor_changed_cb(GFileMonitor *monitor, GFile *file, @@ -415,7 +446,7 @@ /* reload everything */ g_info("%s changed, reloading all configs", fn); - if (!fu_config_reload(self, &error)) + if (!fu_config_reload(self, FU_CONFIG_LOAD_FLAG_NONE, &error)) g_warning("failed to rescan daemon config: %s", error->message); fu_config_emit_changed(self); } @@ -465,7 +496,7 @@ FU_CONFIG_FILE_MODE_SECURE, /* only for root */ error)) return FALSE; - return fu_config_reload(self, error); + return fu_config_reload(self, FU_CONFIG_LOAD_FLAG_NONE, error); } /* failed */ @@ -503,7 +534,7 @@ /* sanity check */ if (priv->items->len == 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no config to load"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no config to load"); return FALSE; } @@ -676,6 +707,26 @@ return value; } +/** + * fu_config_set_basename: + * @self: a #FuConfig + * @basename: (nullable): a file basename, typically, `fwupd.conf` + * + * Sets the name of the filename to load. + * + * Since: 2.0.18 + **/ +void +fu_config_set_basename(FuConfig *self, const gchar *basename) +{ + FuConfigPrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_CONFIG(self)); + if (g_strcmp0(priv->basename, basename) == 0) + return; + g_free(priv->basename); + priv->basename = g_strdup(basename); +} + static gboolean fu_config_add_location(FuConfig *self, const gchar *dirname, gboolean is_mutable, GError **error) { @@ -683,7 +734,7 @@ g_autoptr(FuConfigItem) item = g_new0(FuConfigItem, 1); item->is_mutable = is_mutable; - item->filename = g_build_filename(dirname, "fwupd.conf", NULL); + item->filename = g_build_filename(dirname, priv->basename, NULL); item->file = g_file_new_for_path(item->filename); /* is writable */ @@ -714,6 +765,7 @@ /** * fu_config_load: * @self: a #FuConfig + * @flags: some #FuConfigLoadFlags, typically %FU_CONFIG_LOAD_FLAG_NONE * @error: (nullable): optional return location for an error * * Loads the configuration files from all possible locations. @@ -723,7 +775,7 @@ * Since: 1.9.1 **/ gboolean -fu_config_load(FuConfig *self, GError **error) +fu_config_load(FuConfig *self, FuConfigLoadFlags flags, GError **error) { FuConfigPrivate *priv = GET_PRIVATE(self); g_autofree gchar *configdir_mut = fu_path_from_kind(FU_PATH_KIND_LOCALCONFDIR_PKG); @@ -737,20 +789,22 @@ return FALSE; if (!fu_config_add_location(self, configdir_mut, TRUE, error)) return FALSE; - if (!fu_config_reload(self, error)) + if (!fu_config_reload(self, flags, error)) return FALSE; /* set up a notify watches */ - for (guint i = 0; i < priv->items->len; i++) { - FuConfigItem *item = g_ptr_array_index(priv->items, i); - g_autoptr(GFile) file = g_file_new_for_path(item->filename); - item->monitor = g_file_monitor(file, G_FILE_MONITOR_NONE, NULL, error); - if (item->monitor == NULL) - return FALSE; - g_signal_connect(G_FILE_MONITOR(item->monitor), - "changed", - G_CALLBACK(fu_config_monitor_changed_cb), - self); + if (flags & FU_CONFIG_LOAD_FLAG_WATCH_FILES) { + for (guint i = 0; i < priv->items->len; i++) { + FuConfigItem *item = g_ptr_array_index(priv->items, i); + g_autoptr(GFile) file = g_file_new_for_path(item->filename); + item->monitor = g_file_monitor(file, G_FILE_MONITOR_NONE, NULL, error); + if (item->monitor == NULL) + return FALSE; + g_signal_connect(G_FILE_MONITOR(item->monitor), + "changed", + G_CALLBACK(fu_config_monitor_changed_cb), + self); + } } /* success */ @@ -762,6 +816,7 @@ fu_config_init(FuConfig *self) { FuConfigPrivate *priv = GET_PRIVATE(self); + priv->basename = g_strdup("fwupd.conf"); priv->keyfile = g_key_file_new(); priv->items = g_ptr_array_new_with_free_func((GDestroyNotify)fu_config_item_free); priv->default_values = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); @@ -772,6 +827,7 @@ { FuConfig *self = FU_CONFIG(obj); FuConfigPrivate *priv = GET_PRIVATE(self); + g_free(priv->basename); g_key_file_unref(priv->keyfile); g_ptr_array_unref(priv->items); g_hash_table_unref(priv->default_values); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-context-private.h fwupd-2.0.20/libfwupdplugin/fu-context-private.h --- fwupd-2.0.8/libfwupdplugin/fu-context-private.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-context-private.h 2026-02-26 11:36:18.000000000 +0000 @@ -13,16 +13,10 @@ #include "fu-quirks.h" #include "fu-volume.h" -typedef enum { - FU_CONTEXT_HWID_FLAG_NONE = 0, - FU_CONTEXT_HWID_FLAG_LOAD_CONFIG = 1 << 0, - FU_CONTEXT_HWID_FLAG_LOAD_SMBIOS = 1 << 1, - FU_CONTEXT_HWID_FLAG_LOAD_FDT = 1 << 2, - FU_CONTEXT_HWID_FLAG_LOAD_DMI = 1 << 3, - FU_CONTEXT_HWID_FLAG_LOAD_KENV = 1 << 4, - FU_CONTEXT_HWID_FLAG_LOAD_DARWIN = 1 << 5, - FU_CONTEXT_HWID_FLAG_LOAD_ALL = G_MAXUINT, -} FuContextHwidFlags; +#define FU_CONTEXT_HWID_FLAG_LOAD_ALL \ + (FU_CONTEXT_HWID_FLAG_LOAD_CONFIG | FU_CONTEXT_HWID_FLAG_LOAD_SMBIOS | \ + FU_CONTEXT_HWID_FLAG_LOAD_FDT | FU_CONTEXT_HWID_FLAG_LOAD_DMI | \ + FU_CONTEXT_HWID_FLAG_LOAD_KENV | FU_CONTEXT_HWID_FLAG_LOAD_DARWIN) FuContext * fu_context_new(void); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-context.c fwupd-2.0.20/libfwupdplugin/fu-context.c --- fwupd-2.0.8/libfwupdplugin/fu-context.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-context.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,7 +21,7 @@ #include "fu-hwids-private.h" #include "fu-path.h" #include "fu-pefile-firmware.h" -#include "fu-smbios-private.h" +#include "fu-volume-locker.h" #include "fu-volume-private.h" /** @@ -75,22 +75,63 @@ #define GET_PRIVATE(o) (fu_context_get_instance_private(o)) +static gboolean +fu_context_ensure_smbios_uefi_enabled(FuContext *self, GError **error) +{ + GBytes *bios_blob; + g_autoptr(FuStructSmbiosBiosInformation) st = NULL; + g_autoptr(GPtrArray) bios_tables = NULL; + + bios_tables = fu_context_get_smbios_data(self, 0, FU_SMBIOS_STRUCTURE_LENGTH_ANY, NULL); + if (bios_tables == NULL) { + const gchar *tmp = g_getenv("FWUPD_DELL_FAKE_SMBIOS"); + if (tmp != NULL) + return TRUE; + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "SMBIOS not supported"); + return FALSE; + } + bios_blob = g_ptr_array_index(bios_tables, 0); + + st = fu_struct_smbios_bios_information_parse_bytes(bios_blob, 0x0, error); + if (st == NULL) + return FALSE; + if (fu_struct_smbios_bios_information_get_length(st) < 0x14) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "SMBIOS 2.3 not supported"); + return FALSE; + } + if ((fu_struct_smbios_bios_information_get_characteristics_ext(st) & + FU_SMBIOS_BIOS_CHARACTERISTICS_EXT_UEFI_SPECIFICATION_SUPPORTED) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "System does not support UEFI mode"); + return FALSE; + } + + /* success */ + fu_context_add_flag(self, FU_CONTEXT_FLAG_SMBIOS_UEFI_ENABLED); + return TRUE; +} + static GFile * fu_context_get_fdt_file(GError **error) { g_autofree gchar *fdtfn_local = NULL; g_autofree gchar *fdtfn_sys = NULL; - g_autofree gchar *localstatedir_pkg = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); - g_autofree gchar *sysfsdir = NULL; /* look for override first, fall back to system value */ - fdtfn_local = g_build_filename(localstatedir_pkg, "system.dtb", NULL); + fdtfn_local = fu_path_build(FU_PATH_KIND_LOCALSTATEDIR_PKG, "system.dtb", NULL); if (g_file_test(fdtfn_local, G_FILE_TEST_EXISTS)) return g_file_new_for_path(fdtfn_local); /* actual hardware value */ - sysfsdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - fdtfn_sys = g_build_filename(sysfsdir, "fdt", NULL); + fdtfn_sys = fu_path_build(FU_PATH_KIND_SYSFSDIR_FW, "fdt", NULL); if (g_file_test(fdtfn_sys, G_FILE_TEST_EXISTS)) return g_file_new_for_path(fdtfn_sys); @@ -132,8 +173,11 @@ g_autoptr(GFile) file = fu_context_get_fdt_file(error); if (file == NULL) return NULL; - if (!fu_firmware_parse_file(fdt_tmp, file, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) { - g_prefix_error(error, "failed to parse FDT: "); + if (!fu_firmware_parse_file(fdt_tmp, + file, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, + error)) { + g_prefix_error_literal(error, "failed to parse FDT: "); return NULL; } priv->fdt = g_steal_pointer(&fdt_tmp); @@ -162,6 +206,48 @@ } /** + * fu_context_efivars_check_free_space: + * @self: a #FuContext + * @count: size in bytes + * @error: (nullable): optional return location for an error + * + * Checks for a given amount of free space in the EFI NVRAM variable store. + * + * Returns: %TRUE for success + * + * Since: 2.0.12 + **/ +gboolean +fu_context_efivars_check_free_space(FuContext *self, gsize count, GError **error) +{ + FuContextPrivate *priv = GET_PRIVATE(self); + guint64 total; + + g_return_val_if_fail(FU_IS_CONTEXT(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* escape hatch */ + if (fu_context_has_flag(self, FU_CONTEXT_FLAG_IGNORE_EFIVARS_FREE_SPACE)) + return TRUE; + + total = fu_efivars_space_free(priv->efivars, error); + if (total == G_MAXUINT64) + return FALSE; + if (total < count) { + g_autofree gchar *countstr = g_format_size(count); + g_autofree gchar *totalstr = g_format_size(total); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BROKEN_SYSTEM, + "Not enough efivarfs space, requested %s and got %s", + countstr, + totalstr); + return FALSE; + } + return TRUE; +} + +/** * fu_context_get_smbios: * @self: a #FuContext * @@ -1054,8 +1140,73 @@ FuContextPrivate *priv = GET_PRIVATE(self); if (value != NULL) { g_auto(GStrv) values = g_strsplit(value, ",", -1); - for (guint j = 0; values[j] != NULL; j++) - g_hash_table_add(priv->hwid_flags, g_strdup(values[j])); + for (guint i = 0; values[i] != NULL; i++) { + const gchar *value_tmp = values[i]; + if (g_str_has_prefix(value, "~")) { + g_hash_table_remove(priv->hwid_flags, value_tmp + 1); + continue; + } + g_hash_table_add(priv->hwid_flags, g_strdup(value_tmp)); + } + } +} + +static void +fu_context_detect_container(FuContext *self) +{ + gsize bufsz = 0; + g_autofree gchar *buf = NULL; + + if (!g_file_get_contents("/proc/1/cgroup", &buf, &bufsz, NULL)) + return; + if (g_strstr_len(buf, (gssize)bufsz, "docker") != NULL) { + fu_context_add_flag(self, FU_CONTEXT_FLAG_IS_CONTAINER); + return; + } + if (g_strstr_len(buf, (gssize)bufsz, "lxc") != NULL) { + fu_context_add_flag(self, FU_CONTEXT_FLAG_IS_CONTAINER); + return; + } +} + +static void +fu_context_detect_hypervisor_privileged(FuContext *self) +{ + g_autofree gchar *xen_privileged_fn = NULL; + + /* privileged xen can access most hardware */ + xen_privileged_fn = fu_path_build(FU_PATH_KIND_SYSFSDIR_FW_ATTRIB, + "hypervisor", + "start_flags", + "privileged", + NULL); + if (!g_file_test(xen_privileged_fn, G_FILE_TEST_EXISTS)) { + g_autofree gchar *contents = NULL; + if (g_file_get_contents(xen_privileged_fn, &contents, NULL, NULL)) { + if (g_strcmp0(contents, "1") == 0) { + fu_context_add_flag(self, FU_CONTEXT_FLAG_IS_HYPERVISOR_PRIVILEGED); + return; + } + } + } +} + +static void +fu_context_detect_hypervisor(FuContext *self) +{ + const gchar *flags; + g_autoptr(GHashTable) cpu_attrs = NULL; + + cpu_attrs = fu_cpu_get_attrs(NULL); + if (cpu_attrs == NULL) + return; + flags = g_hash_table_lookup(cpu_attrs, "flags"); + if (flags == NULL) + return; + if (g_strstr_len(flags, -1, "hypervisor") != NULL) { + fu_context_add_flag(self, FU_CONTEXT_FLAG_IS_HYPERVISOR); + fu_context_detect_hypervisor_privileged(self); + return; } } @@ -1079,8 +1230,11 @@ GError **error) { FuContextPrivate *priv = GET_PRIVATE(self); + FuConfigLoadFlags config_load_flags = FU_CONFIG_LOAD_FLAG_NONE; GPtrArray *guids; + const gchar *machine_kind = g_getenv("FWUPD_MACHINE_KIND"); g_autoptr(GError) error_hwids = NULL; + g_autoptr(GError) error_smbios = NULL; g_autoptr(GError) error_bios_settings = NULL; struct { const gchar *name; @@ -1103,10 +1257,15 @@ fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "hwids-setup"); fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 3, "set-flags"); fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "detect-fde"); - fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 94, "reload-bios-settings"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "detect-hypervisor-container"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 93, "reload-bios-settings"); /* required always */ - if (!fu_config_load(priv->config, error)) + if (flags & FU_CONTEXT_HWID_FLAG_WATCH_FILES) + config_load_flags |= FU_CONFIG_LOAD_FLAG_WATCH_FILES; + if (flags & FU_CONTEXT_HWID_FLAG_FIX_PERMISSIONS) + config_load_flags |= FU_CONFIG_LOAD_FLAG_FIX_PERMISSIONS; + if (!fu_config_load(priv->config, config_load_flags, error)) return FALSE; /* run all the HWID setup funcs */ @@ -1128,6 +1287,10 @@ g_warning("Failed to load HWIDs: %s", error_hwids->message); fu_progress_step_done(progress); + /* does the system support UEFI mode? */ + if (!fu_context_ensure_smbios_uefi_enabled(self, &error_smbios)) + g_debug("%s", error_smbios->message); + /* set the hwid flags */ guids = fu_context_get_hwid_guids(self); for (guint i = 0; i < guids->len; i++) { @@ -1143,6 +1306,28 @@ fu_context_detect_full_disk_encryption(self); fu_progress_step_done(progress); + /* allow overriding for development */ + if (machine_kind != NULL) { + if (g_strcmp0(machine_kind, "physical") == 0) { + /* nothing to do */ + } else if (g_strcmp0(machine_kind, "container") == 0) { + fu_context_add_flag(self, FU_CONTEXT_FLAG_IS_CONTAINER); + } else if (g_strcmp0(machine_kind, "virtual") == 0) { + fu_context_add_flag(self, FU_CONTEXT_FLAG_IS_HYPERVISOR); + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "invalid machine kind specified: %s", + machine_kind); + return FALSE; + } + } else { + fu_context_detect_hypervisor(self); + fu_context_detect_container(self); + } + fu_progress_step_done(progress); + fu_context_add_udev_subsystem(self, "firmware-attributes", NULL); if (!fu_context_reload_bios_settings(self, &error_bios_settings)) g_debug("%s", error_bios_settings->message); @@ -1233,16 +1418,6 @@ { FuContextPrivate *priv = GET_PRIVATE(self); g_return_if_fail(FU_IS_CONTEXT(self)); - - /* quirk for behavior on Framework systems where the EC reports as discharging - * while on AC but at 100% */ - if (power_state == FU_POWER_STATE_BATTERY_DISCHARGING && priv->battery_level == 100 && - fu_context_has_hwid_flag(self, "discharging-when-fully-changed")) { - power_state = FU_POWER_STATE_AC_FULLY_CHARGED; - g_debug("quirking power state to %s", fu_power_state_to_string(power_state)); - } - - /* is the same */ if (priv->power_state == power_state) return; priv->power_state = power_state; @@ -1734,11 +1909,11 @@ FuVolume *esp = g_ptr_array_index(esp_volumes, i); guint score = 0; g_autofree gchar *kind = NULL; - g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuVolumeLocker) locker = NULL; g_autoptr(GError) error_local = NULL; /* ignore the volume completely if we cannot mount it */ - locker = fu_volume_locker(esp, &error_local); + locker = fu_volume_locker_new(esp, &error_local); if (locker == NULL) { g_warning("failed to mount ESP: %s", error_local->message); continue; @@ -1778,10 +1953,10 @@ } if (g_hash_table_size(esp_scores) == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no EFI system partition found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no EFI system partition found"); return NULL; } @@ -1796,10 +1971,10 @@ if (esp_volumes->len == 1) { FuVolume *esp = g_ptr_array_index(esp_volumes, 0); - g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuVolumeLocker) locker = NULL; /* ensure it can be mounted */ - locker = fu_volume_locker(esp, error); + locker = fu_volume_locker_new(esp, error); if (locker == NULL) return NULL; @@ -1864,7 +2039,7 @@ } /* failed */ - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "could not find EFI DP"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "could not find EFI DP"); return NULL; } @@ -1874,7 +2049,7 @@ g_autoptr(FuFirmware) firmware = fu_pefile_firmware_new(); g_autoptr(GFile) file = g_file_new_for_path(filename); fu_firmware_set_filename(firmware, filename); - if (!fu_firmware_parse_file(firmware, file, FWUPD_INSTALL_FLAG_NONE, error)) { + if (!fu_firmware_parse_file(firmware, file, FU_FIRMWARE_PARSE_FLAG_NONE, error)) { g_prefix_error(error, "failed to load %s: ", filename); return NULL; } @@ -1917,7 +2092,7 @@ g_autofree gchar *filename = NULL; g_autofree gchar *mount_point = NULL; g_autofree gchar *shim_name = fu_context_build_uefi_basename_for_arch("shim"); - g_autoptr(FuDeviceLocker) volume_locker = NULL; + g_autoptr(FuVolumeLocker) volume_locker = NULL; g_autoptr(FuEfiFilePathDevicePath) dp_path = NULL; g_autoptr(FuEfiHardDriveDevicePath) dp_hdd = NULL; g_autoptr(FuFirmware) dp_list = NULL; @@ -1956,7 +2131,7 @@ "cannot mount volume by policy"); return FALSE; } - volume_locker = fu_volume_locker(volume, error); + volume_locker = fu_volume_locker_new(volume, error); if (volume_locker == NULL) return FALSE; dp_filename = fu_efi_file_path_device_path_get_name(dp_path, error); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-context.h fwupd-2.0.20/libfwupdplugin/fu-context.h --- fwupd-2.0.8/libfwupdplugin/fu-context.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-context.h 2026-02-26 11:36:18.000000000 +0000 @@ -11,6 +11,7 @@ #include "fu-bios-settings.h" #include "fu-common-struct.h" #include "fu-common.h" +#include "fu-context-struct.h" #include "fu-efi-hard-drive-device-path.h" #include "fu-efivars.h" #include "fu-firmware.h" @@ -27,46 +28,6 @@ }; /** - * FuContextQuirkSource: - * - * The source of the quirk data, ordered by how good the data is. - **/ -typedef enum { - /** - * FU_CONTEXT_QUIRK_SOURCE_DEVICE: - * - * From the device itself, perhaps a USB descriptor. - * - * Since: 2.0.2 - **/ - FU_CONTEXT_QUIRK_SOURCE_DEVICE, - /** - * FU_CONTEXT_QUIRK_SOURCE_FILE: - * - * From an internal `.quirk` file. - * - * Since: 2.0.2 - **/ - FU_CONTEXT_QUIRK_SOURCE_FILE, - /** - * FU_CONTEXT_QUIRK_SOURCE_DB: - * - * From the database, populated from `usb.ids` and `pci.ids`. - * - * Since: 2.0.2 - **/ - FU_CONTEXT_QUIRK_SOURCE_DB, - /** - * FU_CONTEXT_QUIRK_SOURCE_FALLBACK: - * - * A good fallback, perhaps from the PCI class information. - * - * Since: 2.0.2 - **/ - FU_CONTEXT_QUIRK_SOURCE_FALLBACK, -} FuContextQuirkSource; - -/** * FuContextLookupIter: * @self: a #FuContext * @key: a key @@ -82,85 +43,13 @@ FuContextQuirkSource source, gpointer user_data); -/** - * FuContextFlags: - * - * The context flags. - **/ -typedef enum { - /** - * FU_CONTEXT_FLAG_NONE: - * - * No flags set. - * - * Since: 1.8.5 - **/ - FU_CONTEXT_FLAG_NONE = 0, - /** - * FU_CONTEXT_FLAG_SAVE_EVENTS: - * - * Save events so that they can be replayed to emulate devices. - * - * Since: 1.8.5 - **/ - FU_CONTEXT_FLAG_SAVE_EVENTS = 1u << 0, - /** - * FU_CONTEXT_FLAG_SYSTEM_INHIBIT: - * - * All devices are not updatable due to a system-wide inhibit. - * - * Since: 1.8.10 - **/ - FU_CONTEXT_FLAG_SYSTEM_INHIBIT = 1u << 1, - /** - * FU_CONTEXT_FLAG_LOADED_HWINFO: - * - * Hardware information has been loaded with a call to fu_context_load_hwinfo(). - * - * Since: 1.9.10 - **/ - FU_CONTEXT_FLAG_LOADED_HWINFO = 1u << 2, - /** - * FU_CONTEXT_FLAG_INHIBIT_VOLUME_MOUNT: - * - * Do not allow mounting volumes, usually set in self tests. - * - * Since: 2.0.2 - **/ - FU_CONTEXT_FLAG_INHIBIT_VOLUME_MOUNT = 1u << 3, - /** - * FU_CONTEXT_FLAG_FDE_BITLOCKER: - * - * Bitlocker style full disk encryption is in use - * - * Since: 2.0.5 - **/ - FU_CONTEXT_FLAG_FDE_BITLOCKER = 1u << 4, - /** - * FU_CONTEXT_FLAG_FDE_SNAPD: - * - * Snapd style full disk encryption is in use - * - * Since: 2.0.5 - **/ - FU_CONTEXT_FLAG_FDE_SNAPD = 1u << 5, - - /** - * FU_CONTEXT_FLAG_LOADED_UNKNOWN: - * - * Unknown flag value. - * - * Since: 2.0.0 - **/ - FU_CONTEXT_FLAG_LOADED_UNKNOWN = G_MAXUINT64, -} FuContextFlags; - void fu_context_add_flag(FuContext *context, FuContextFlags flag) G_GNUC_NON_NULL(1); void fu_context_remove_flag(FuContext *context, FuContextFlags flag) G_GNUC_NON_NULL(1); gboolean -fu_context_has_flag(FuContext *context, FuContextFlags flag) G_GNUC_NON_NULL(1); +fu_context_has_flag(FuContext *context, FuContextFlags flag) G_GNUC_WARN_UNUSED_RESULT + G_GNUC_NON_NULL(1); const gchar * fu_context_get_smbios_string(FuContext *self, @@ -261,54 +150,9 @@ fu_context_get_esp_location(FuContext *self); FuEfivars * fu_context_get_efivars(FuContext *self) G_GNUC_NON_NULL(1); - -/** - * FuContextEspFileFlags: - * - * The flags to use when loading files in the ESP. - **/ -typedef enum { - /** - * FU_CONTEXT_ESP_FILE_FLAG_NONE: - * - * No flags set. - * - * Since: 2.0.0 - **/ - FU_CONTEXT_ESP_FILE_FLAG_NONE = 0, - /** - * FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_FIRST_STAGE: - * - * Include 1st stage bootloaders like shim. - * - * Since: 2.0.0 - **/ - FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_FIRST_STAGE = 1 << 0, - /** - * FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_SECOND_STAGE: - * - * Include 2nd stage bootloaders like shim. - * - * Since: 2.0.0 - **/ - FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_SECOND_STAGE = 1 << 1, - /** - * FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_REVOCATIONS: - * - * Include revokcations, for example the `revocations.efi` file used by shim. - * - * Since: 2.0.0 - **/ - FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_REVOCATIONS = 1 << 2, - /** - * FU_CONTEXT_ESP_FILE_FLAG_UNKNOWN: - * - * Unknown flag value. - * - * Since: 2.0.0 - **/ - FU_CONTEXT_ESP_FILE_FLAG_UNKNOWN = G_MAXUINT64, -} FuContextEspFileFlags; +gboolean +fu_context_efivars_check_free_space(FuContext *self, gsize count, GError **error) + G_GNUC_NON_NULL(1); GPtrArray * fu_context_get_esp_files(FuContext *self, FuContextEspFileFlags flags, GError **error) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-context.rs fwupd-2.0.20/libfwupdplugin/fu-context.rs --- fwupd-2.0.8/libfwupdplugin/fu-context.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-context.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,46 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[derive(Bitfield)] +enum FuContextFlags { + None = 0, + SaveEvents = 1 << 0, // so that they can be replayed to emulate devices + SystemInhibit = 1 << 1, // all devices are not updatable + LoadedHwinfo = 1 << 2, // has called fu_context_load_hwinfo() + InhibitVolumeMount = 1 << 3, // usually for self tests + FdeBitlocker = 1 << 4, // full disk encryption + FdeSnapd = 1 << 5, // full disk encryption + IgnoreEfivarsFreeSpace = 1 << 6, // ignore the free space requirement for db, dbx, etc + NoIdleSources = 1 << 7, + InsecureUefi = 1 << 8, + IsHypervisor = 1 << 9, + IsHypervisorPrivileged = 1 << 10, // privileged xen can access most hardware + IsContainer = 1 << 11, + SmbiosUefiEnabled = 1 << 12, +} + +enum FuContextHwidFlags { + None = 0, + LoadConfig = 1 << 0, + LoadSmbios = 1 << 1, + LoadFdt = 1 << 2, + LoadDmi = 1 << 3, + LoadKenv = 1 << 4, + LoadDarwin = 1 << 5, + WatchFiles = 1 << 6, + FixPermissions = 1 << 7, +} + +enum FuContextEspFileFlags { + None = 0, + IncludeFirstStage = 1 << 0, // e.g. shim + IncludeSecondStage = 1 << 1, // e.g. grub + IncludeRevocations = 1 << 2, // e.g. the `revocations.efi` file used by shim +} + +enum FuContextQuirkSource { + Device, // perhaps a USB descriptor + File, // an internal .quirk file. + Db, // populated from usb.ids and pci.ids + Fallback, // perhaps from the PCI class information +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-coswid-firmware.c fwupd-2.0.20/libfwupdplugin/fu-coswid-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-coswid-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-coswid-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,7 +13,6 @@ #endif #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-coswid-common.h" #include "fu-coswid-firmware.h" @@ -32,6 +31,8 @@ gchar *product; gchar *summary; gchar *colloquial_version; + gchar *persistent_id; + gchar *device_id; FuCoswidVersionScheme version_scheme; GPtrArray *links; /* of FuCoswidFirmwareLink */ GPtrArray *entities; /* of FuCoswidFirmwareEntity */ @@ -125,20 +126,62 @@ for (gsize i = 0; i < cbor_map_size(item); i++) { FuCoswidTag tag_id = 0; - if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) + if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) { + g_prefix_error(error, "failed to parse meta tag %u: ", (guint)i); return FALSE; + } if (tag_id == FU_COSWID_TAG_SUMMARY) { g_free(priv->summary); priv->summary = fu_coswid_read_string(pairs[i].value, error); if (priv->summary == NULL) { - g_prefix_error(error, "failed to parse summary: "); + g_prefix_error_literal(error, "failed to parse summary: "); return FALSE; } } else if (tag_id == FU_COSWID_TAG_COLLOQUIAL_VERSION) { g_free(priv->colloquial_version); priv->colloquial_version = fu_coswid_read_string(pairs[i].value, error); if (priv->colloquial_version == NULL) { - g_prefix_error(error, "failed to parse colloquial-version: "); + g_prefix_error_literal(error, + "failed to parse colloquial-version: "); + return FALSE; + } + } else if (tag_id == FU_COSWID_TAG_PERSISTENT_ID) { + g_free(priv->persistent_id); + priv->persistent_id = fu_coswid_read_string(pairs[i].value, error); + if (priv->persistent_id == NULL) { + g_prefix_error_literal(error, "failed to parse persistent-id: "); + return FALSE; + } + } else { + g_debug("unhandled tag %s from %s", + fu_coswid_tag_to_string(tag_id), + fu_coswid_tag_to_string(FU_COSWID_TAG_SOFTWARE_META)); + } + } + + /* success */ + return TRUE; +} + +/* @userdata: a #FuCoswidFirmware */ +static gboolean +fu_coswid_firmware_parse_evidence(cbor_item_t *item, gpointer user_data, GError **error) +{ + FuCoswidFirmware *self = FU_COSWID_FIRMWARE(user_data); + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + struct cbor_pair *pairs = cbor_map_handle(item); + + for (gsize i = 0; i < cbor_map_size(item); i++) { + FuCoswidTag tag_id = 0; + if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) { + g_prefix_error(error, "failed to parse evidence tag %u: ", (guint)i); + return FALSE; + } + if (tag_id == FU_COSWID_TAG_DEVICE_ID) { + g_free(priv->device_id); + priv->device_id = fu_coswid_read_string(pairs[i].value, error); + if (priv->device_id == NULL) { + g_prefix_error_literal(error, "failed to parse device-id: "); return FALSE; } } else { @@ -163,19 +206,21 @@ for (gsize i = 0; i < cbor_map_size(item); i++) { FuCoswidTag tag_id = 0; - if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) + if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) { + g_prefix_error(error, "failed to parse link tag %u: ", (guint)i); return FALSE; + } if (tag_id == FU_COSWID_TAG_HREF) { g_free(link->href); link->href = fu_coswid_read_string(pairs[i].value, error); if (link->href == NULL) { - g_prefix_error(error, "failed to parse link href: "); + g_prefix_error_literal(error, "failed to parse link href: "); return FALSE; } } else if (tag_id == FU_COSWID_TAG_REL) { gint8 tmp = 0; if (!fu_coswid_read_s8(pairs[i].value, &tmp, error)) { - g_prefix_error(error, "failed to parse link rel: "); + g_prefix_error_literal(error, "failed to parse link rel: "); return FALSE; } link->rel = tmp; @@ -226,7 +271,7 @@ return FALSE; } if (!fu_coswid_read_u8(hash_item_alg_id, &alg_id8, error)) { - g_prefix_error(error, "failed to parse hash alg-id: "); + g_prefix_error_literal(error, "failed to parse hash alg-id: "); return FALSE; } @@ -234,7 +279,7 @@ hash->alg_id = alg_id8; hash->value = fu_coswid_read_byte_array(hash_item_value, error); if (hash->value == NULL) { - g_prefix_error(error, "failed to parse hash value: "); + g_prefix_error_literal(error, "failed to parse hash value: "); return FALSE; } g_ptr_array_add(payload->hashes, g_steal_pointer(&hash)); @@ -264,13 +309,15 @@ for (gsize i = 0; i < cbor_map_size(item); i++) { FuCoswidTag tag_id = 0; - if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) + if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) { + g_prefix_error(error, "failed to parse file tag %u: ", (guint)i); return FALSE; + } if (tag_id == FU_COSWID_TAG_FS_NAME) { g_free(payload->name); payload->name = fu_coswid_read_string(pairs[i].value, error); if (payload->name == NULL) { - g_prefix_error(error, "failed to parse payload name: "); + g_prefix_error_literal(error, "failed to parse payload name: "); return FALSE; } } else if (tag_id == FU_COSWID_TAG_SIZE) { @@ -294,10 +341,10 @@ return FALSE; } } else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "hashes neither an array or array of array"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "hashes neither an array or array of array"); return FALSE; } } else { @@ -320,8 +367,10 @@ struct cbor_pair *pairs = cbor_map_handle(item); for (gsize i = 0; i < cbor_map_size(item); i++) { FuCoswidTag tag_id = 0; - if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) + if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) { + g_prefix_error(error, "failed to parse elements tag %u: ", (guint)i); return FALSE; + } if (tag_id == FU_COSWID_TAG_FILE) { if (!fu_coswid_parse_one_or_many(pairs[i].value, fu_coswid_firmware_parse_file, @@ -347,8 +396,10 @@ struct cbor_pair *pairs = cbor_map_handle(item); for (gsize i = 0; i < cbor_map_size(item); i++) { FuCoswidTag tag_id = 0; - if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) + if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) { + g_prefix_error(error, "failed to parse directory tag %u: ", (guint)i); return FALSE; + } if (tag_id == FU_COSWID_TAG_PATH_ELEMENTS) { if (!fu_coswid_parse_one_or_many(pairs[i].value, fu_coswid_firmware_parse_path_elements, @@ -374,8 +425,10 @@ struct cbor_pair *pairs = cbor_map_handle(item); for (gsize i = 0; i < cbor_map_size(item); i++) { FuCoswidTag tag_id = 0; - if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) + if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) { + g_prefix_error(error, "failed to parse payload tag %u: ", (guint)i); return FALSE; + } if (tag_id == FU_COSWID_TAG_FILE) { if (!fu_coswid_parse_one_or_many(pairs[i].value, fu_coswid_firmware_parse_file, @@ -409,7 +462,7 @@ entity->name = fu_coswid_read_string(item, error); if (entity->name == NULL) { - g_prefix_error(error, "failed to parse entity name: "); + g_prefix_error_literal(error, "failed to parse entity name: "); return FALSE; } @@ -427,7 +480,7 @@ entity->regid = fu_coswid_read_string(item, error); if (entity->regid == NULL) { - g_prefix_error(error, "failed to parse entity regid: "); + g_prefix_error_literal(error, "failed to parse entity regid: "); return FALSE; } @@ -443,7 +496,7 @@ if (cbor_isa_uint(item)) { guint8 role8 = 0; if (!fu_coswid_read_u8(item, &role8, error)) { - g_prefix_error(error, "failed to parse entity role: "); + g_prefix_error_literal(error, "failed to parse entity role: "); return FALSE; } if (role8 >= FU_COSWID_ENTITY_ROLE_LAST) { @@ -461,7 +514,7 @@ guint8 role8 = 0; g_autoptr(cbor_item_t) value = cbor_array_get(item, j); if (!fu_coswid_read_u8(value, &role8, error)) { - g_prefix_error(error, "failed to parse entity role: "); + g_prefix_error_literal(error, "failed to parse entity role: "); return FALSE; } if (role8 >= FU_COSWID_ENTITY_ROLE_LAST) { @@ -497,8 +550,10 @@ for (gsize i = 0; i < cbor_map_size(item); i++) { FuCoswidTag tag_id = 0; - if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) + if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) { + g_prefix_error(error, "failed to parse entity tag %u: ", (guint)i); return FALSE; + } if (tag_id == FU_COSWID_TAG_ENTITY_NAME) { if (!fu_coswid_firmware_parse_entity_name(entity, pairs[i].value, error)) return FALSE; @@ -570,7 +625,7 @@ static gboolean fu_coswid_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { #ifdef HAVE_CBOR @@ -615,14 +670,16 @@ pairs = cbor_map_handle(item); for (gsize i = 0; i < cbor_map_size(item); i++) { FuCoswidTag tag_id = 0; - if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) + if (!fu_coswid_read_tag(pairs[i].key, &tag_id, error)) { + g_prefix_error(error, "failed to parse root tag %u: ", (guint)i); return FALSE; + } /* identity can be specified as a string or in binary */ if (tag_id == FU_COSWID_TAG_TAG_ID) { g_autofree gchar *str = fu_coswid_read_string(pairs[i].value, error); if (str == NULL) { - g_prefix_error(error, "failed to parse tag-id: "); + g_prefix_error_literal(error, "failed to parse tag-id: "); return FALSE; } fu_firmware_set_id(firmware, str); @@ -630,13 +687,13 @@ g_free(priv->product); priv->product = fu_coswid_read_string(pairs[i].value, error); if (priv->product == NULL) { - g_prefix_error(error, "failed to parse product: "); + g_prefix_error_literal(error, "failed to parse product: "); return FALSE; } } else if (tag_id == FU_COSWID_TAG_SOFTWARE_VERSION) { g_autofree gchar *str = fu_coswid_read_string(pairs[i].value, error); if (str == NULL) { - g_prefix_error(error, "failed to parse software-version: "); + g_prefix_error_literal(error, "failed to parse software-version: "); return FALSE; } fu_firmware_set_version(firmware, str); @@ -651,6 +708,12 @@ self, /* user_data */ error)) return FALSE; + } else if (tag_id == FU_COSWID_TAG_EVIDENCE) { + if (!fu_coswid_parse_one_or_many(pairs[i].value, + fu_coswid_firmware_parse_evidence, + self, /* user_data */ + error)) + return FALSE; } else if (tag_id == FU_COSWID_TAG_LINK) { if (!fu_coswid_parse_one_or_many(pairs[i].value, fu_coswid_firmware_parse_link, @@ -742,46 +805,184 @@ return NULL; } +/** + * fu_coswid_firmware_get_product: + * @self: a #FuCoswidFirmware + * + * Gets the product name. + * + * Returns: string, or %NULL for unset + * + * Since: 2.0.12 + **/ +const gchar * +fu_coswid_firmware_get_product(FuCoswidFirmware *self) +{ + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_COSWID_FIRMWARE(self), NULL); + return priv->product; +} + +/** + * fu_coswid_firmware_get_persistent_id: + * @self: a #FuCoswidFirmware + * + * Gets the persistent ID. + * + * Returns: string, or %NULL for unset + * + * Since: 2.0.17 + **/ +const gchar * +fu_coswid_firmware_get_persistent_id(FuCoswidFirmware *self) +{ + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_COSWID_FIRMWARE(self), NULL); + return priv->persistent_id; +} + +/** + * fu_coswid_firmware_get_device_id: + * @self: a #FuCoswidFirmware + * + * Gets the device ID. + * + * Returns: string, or %NULL for unset + * + * Since: 2.0.17 + **/ +const gchar * +fu_coswid_firmware_get_device_id(FuCoswidFirmware *self) +{ + FuCoswidFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_COSWID_FIRMWARE(self), NULL); + return priv->device_id; +} + #ifdef HAVE_CBOR -static void -fu_coswid_firmware_write_hash(cbor_item_t *root, FuCoswidFirmwareHash *hash) +static gboolean +fu_coswid_firmware_write_hash(cbor_item_t *root, FuCoswidFirmwareHash *hash, GError **error) { g_autoptr(cbor_item_t) item_hash = cbor_new_definite_array(2); g_autoptr(cbor_item_t) item_hash_alg_id = cbor_build_uint8(hash->alg_id); g_autoptr(cbor_item_t) item_hash_value = cbor_build_bytestring(hash->value->data, hash->value->len); - if (!cbor_array_push(item_hash, item_hash_alg_id)) - g_critical("failed to push to definite array"); - if (!cbor_array_push(item_hash, item_hash_value)) - g_critical("failed to push to definite array"); - if (!cbor_array_push(root, item_hash)) - g_critical("failed to push to indefinite array"); + if (!cbor_array_push(item_hash, item_hash_alg_id)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to push hash alg-id to definite array"); + return FALSE; + } + if (!cbor_array_push(item_hash, item_hash_value)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to push hash value to definite array"); + return FALSE; + } + if (!cbor_array_push(root, item_hash)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to push hash to indefinite array"); + return FALSE; + } + + /* success */ + return TRUE; } -static void -fu_coswid_firmware_write_payload(cbor_item_t *root, FuCoswidFirmwarePayload *payload) +static gboolean +fu_coswid_firmware_write_payload(cbor_item_t *root, + FuCoswidFirmwarePayload *payload, + GError **error) { g_autoptr(cbor_item_t) item_payload = cbor_new_indefinite_map(); g_autoptr(cbor_item_t) item_file = cbor_new_indefinite_map(); - if (payload->name != NULL) { + if (payload->name != NULL) fu_coswid_write_tag_string(item_file, FU_COSWID_TAG_FS_NAME, payload->name); - } - if (payload->size != 0) { + if (payload->size != 0) fu_coswid_write_tag_u64(item_file, FU_COSWID_TAG_SIZE, payload->size); - } if (payload->hashes->len > 0) { g_autoptr(cbor_item_t) item_hashes = cbor_new_indefinite_array(); for (guint j = 0; j < payload->hashes->len; j++) { FuCoswidFirmwareHash *hash = g_ptr_array_index(payload->hashes, j); - fu_coswid_firmware_write_hash(item_hashes, hash); + if (!fu_coswid_firmware_write_hash(item_hashes, hash, error)) { + g_prefix_error_literal(error, "failed to add payload: "); + return FALSE; + } } fu_coswid_write_tag_item(item_file, FU_COSWID_TAG_HASH, item_hashes); } fu_coswid_write_tag_item(item_payload, FU_COSWID_TAG_FILE, item_file); - if (!cbor_array_push(root, item_payload)) - g_critical("failed to push to indefinite array"); + if (!cbor_array_push(root, item_payload)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to push payload to indefinite array"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_coswid_firmware_write_entity(cbor_item_t *root, FuCoswidFirmwareEntity *entity, GError **error) +{ + g_autoptr(cbor_item_t) item_entity = cbor_new_indefinite_map(); + g_autoptr(cbor_item_t) item_roles = cbor_new_indefinite_array(); + if (entity->name != NULL) { + fu_coswid_write_tag_string(item_entity, FU_COSWID_TAG_ENTITY_NAME, entity->name); + } + if (entity->regid != NULL) { + fu_coswid_write_tag_string(item_entity, FU_COSWID_TAG_REG_ID, entity->regid); + } + for (guint j = 0; j < FU_COSWID_ENTITY_ROLE_LAST; j++) { + if (FU_BIT_IS_SET(entity->roles, j)) { + g_autoptr(cbor_item_t) item_role = cbor_build_uint8(j); + if (!cbor_array_push(item_roles, item_role)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to push entity to indefinite array"); + return FALSE; + } + } + } + fu_coswid_write_tag_item(item_entity, FU_COSWID_TAG_ROLE, item_roles); + if (!cbor_array_push(root, item_entity)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to push entity to indefinite array"); + return FALSE; + } + + /* success */ + return TRUE; } + +static gboolean +fu_coswid_firmware_write_evidence(cbor_item_t *root, const gchar *device_id, GError **error) +{ + g_autoptr(cbor_item_t) item_entity = cbor_new_indefinite_map(); + + fu_coswid_write_tag_string(item_entity, FU_COSWID_TAG_DEVICE_ID, device_id); + if (!cbor_array_push(root, item_entity)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to push evidence to indefinite array"); + return FALSE; + } + + /* success */ + return TRUE; +} + #endif static GByteArray * @@ -833,34 +1034,27 @@ FU_COSWID_TAG_COLLOQUIAL_VERSION, priv->colloquial_version); } + if (priv->persistent_id != NULL) { + fu_coswid_write_tag_string(item_meta, + FU_COSWID_TAG_PERSISTENT_ID, + priv->persistent_id); + } + + /* add evidence */ + if (priv->device_id != NULL) { + g_autoptr(cbor_item_t) items = cbor_new_indefinite_array(); + if (!fu_coswid_firmware_write_evidence(items, priv->device_id, error)) + return NULL; + fu_coswid_write_tag_item(root, FU_COSWID_TAG_EVIDENCE, items); + } /* add entities */ if (priv->entities->len > 0) { g_autoptr(cbor_item_t) item_entities = cbor_new_indefinite_array(); for (guint i = 0; i < priv->entities->len; i++) { FuCoswidFirmwareEntity *entity = g_ptr_array_index(priv->entities, i); - g_autoptr(cbor_item_t) item_entity = cbor_new_indefinite_map(); - g_autoptr(cbor_item_t) item_roles = cbor_new_indefinite_array(); - if (entity->name != NULL) { - fu_coswid_write_tag_string(item_entity, - FU_COSWID_TAG_ENTITY_NAME, - entity->name); - } - if (entity->regid != NULL) { - fu_coswid_write_tag_string(item_entity, - FU_COSWID_TAG_REG_ID, - entity->regid); - } - for (guint j = 0; j < FU_COSWID_ENTITY_ROLE_LAST; j++) { - if (FU_BIT_IS_SET(entity->roles, j)) { - g_autoptr(cbor_item_t) item_role = cbor_build_uint8(j); - if (!cbor_array_push(item_roles, item_role)) - g_critical("failed to push to indefinite array"); - } - } - fu_coswid_write_tag_item(item_entity, FU_COSWID_TAG_ROLE, item_roles); - if (!cbor_array_push(item_entities, item_entity)) - g_critical("failed to push to indefinite array"); + if (!fu_coswid_firmware_write_entity(item_entities, entity, error)) + return NULL; } fu_coswid_write_tag_item(root, FU_COSWID_TAG_ENTITY, item_entities); } @@ -877,8 +1071,13 @@ link->href); } fu_coswid_write_tag_s8(item_link, FU_COSWID_TAG_REL, link->rel); - if (!cbor_array_push(item_links, item_link)) - g_critical("failed to push to indefinite array"); + if (!cbor_array_push(item_links, item_link)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to push link to indefinite array"); + return NULL; + } } fu_coswid_write_tag_item(root, FU_COSWID_TAG_LINK, item_links); } @@ -888,7 +1087,8 @@ g_autoptr(cbor_item_t) item_payloads = cbor_new_indefinite_array(); for (guint i = 0; i < priv->payloads->len; i++) { FuCoswidFirmwarePayload *payload = g_ptr_array_index(priv->payloads, i); - fu_coswid_firmware_write_payload(item_payloads, payload); + if (!fu_coswid_firmware_write_payload(item_payloads, payload, error)) + return NULL; } fu_coswid_write_tag_item(root, FU_COSWID_TAG_PAYLOAD, item_payloads); } @@ -1084,6 +1284,12 @@ tmp = xb_node_query_text(n, "colloquial_version", NULL); if (tmp != NULL) priv->colloquial_version = g_strdup(tmp); + tmp = xb_node_query_text(n, "persistent_id", NULL); + if (tmp != NULL) + priv->persistent_id = g_strdup(tmp); + tmp = xb_node_query_text(n, "device_id", NULL); + if (tmp != NULL) + priv->device_id = g_strdup(tmp); tmp = xb_node_query_text(n, "version_scheme", NULL); if (tmp != NULL) { @@ -1145,6 +1351,8 @@ fu_xmlb_builder_insert_kv(bn, "product", priv->product); fu_xmlb_builder_insert_kv(bn, "summary", priv->summary); fu_xmlb_builder_insert_kv(bn, "colloquial_version", priv->colloquial_version); + fu_xmlb_builder_insert_kv(bn, "persistent_id", priv->persistent_id); + fu_xmlb_builder_insert_kv(bn, "device_id", priv->device_id); for (guint i = 0; i < priv->links->len; i++) { FuCoswidFirmwareLink *link = g_ptr_array_index(priv->links, i); g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "link", NULL); @@ -1195,6 +1403,7 @@ g_ptr_array_new_with_free_func((GDestroyNotify)fu_coswid_firmware_payload_free); priv->entities = g_ptr_array_new_with_free_func((GDestroyNotify)fu_coswid_firmware_entity_free); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_ALLOW_LINEAR); } static void @@ -1206,6 +1415,8 @@ g_free(priv->product); g_free(priv->summary); g_free(priv->colloquial_version); + g_free(priv->persistent_id); + g_free(priv->device_id); g_ptr_array_unref(priv->links); g_ptr_array_unref(priv->payloads); g_ptr_array_unref(priv->entities); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-coswid-firmware.h fwupd-2.0.20/libfwupdplugin/fu-coswid-firmware.h --- fwupd-2.0.8/libfwupdplugin/fu-coswid-firmware.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-coswid-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -17,3 +17,9 @@ FuFirmware * fu_coswid_firmware_new(void); +const gchar * +fu_coswid_firmware_get_product(FuCoswidFirmware *self) G_GNUC_NON_NULL(1); +const gchar * +fu_coswid_firmware_get_persistent_id(FuCoswidFirmware *self) G_GNUC_NON_NULL(1); +const gchar * +fu_coswid_firmware_get_device_id(FuCoswidFirmware *self) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-coswid.rs fwupd-2.0.20/libfwupdplugin/fu-coswid.rs --- fwupd-2.0.8/libfwupdplugin/fu-coswid.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-coswid.rs 2026-02-26 11:36:18.000000000 +0000 @@ -105,7 +105,7 @@ #[derive(ToString, FromString)] enum FuCoswidHashAlg { Unknown = 0, - SHA256 = 1, - SHA384 = 7, - SHA512 = 8, + Sha256 = 1, + Sha384 = 7, + Sha512 = 8, } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-crc.c fwupd-2.0.20/libfwupdplugin/fu-crc.c --- fwupd-2.0.8/libfwupdplugin/fu-crc.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-crc.c 2026-02-26 11:36:18.000000000 +0000 @@ -28,21 +28,22 @@ {FU_CRC_KIND_B32_POSIX, 32, 0x04C11DB7, 0x00000000, FALSE, 0xFFFFFFFF}, {FU_CRC_KIND_B32_SATA, 32, 0x04C11DB7, 0x52325032, FALSE, 0x00000000}, {FU_CRC_KIND_B32_XFER, 32, 0x000000AF, 0x00000000, FALSE, 0x00000000}, - {FU_CRC_KIND_B32_C, 32, 0x1EDC6F41, 0xFFFFFFFF, TRUE, 0xFFFFFFFF}, - {FU_CRC_KIND_B32_D, 32, 0xA833982B, 0xFFFFFFFF, TRUE, 0xFFFFFFFF}, - {FU_CRC_KIND_B32_Q, 32, 0x814141AB, 0x00000000, FALSE, 0x00000000}, + {FU_CRC_KIND_B32C, 32, 0x1EDC6F41, 0xFFFFFFFF, TRUE, 0xFFFFFFFF}, + {FU_CRC_KIND_B32D, 32, 0xA833982B, 0xFFFFFFFF, TRUE, 0xFFFFFFFF}, + {FU_CRC_KIND_B32Q, 32, 0x814141AB, 0x00000000, FALSE, 0x00000000}, {FU_CRC_KIND_B16_XMODEM, 16, 0x1021, 0x0000, FALSE, 0x0000}, + {FU_CRC_KIND_B16_KERMIT, 16, 0x1021, 0x0000, TRUE, 0x0000}, {FU_CRC_KIND_B16_USB, 16, 0x8005, 0xFFFF, TRUE, 0xFFFF}, {FU_CRC_KIND_B16_UMTS, 16, 0x8005, 0x0000, FALSE, 0x0000}, {FU_CRC_KIND_B16_TMS37157, 16, 0x1021, 0x89ec, TRUE, 0x0000}, {FU_CRC_KIND_B16_BNR, 16, 0x8005, 0xFFFF, FALSE, 0x0000}, {FU_CRC_KIND_B8_WCDMA, 8, 0x9B, 0x00, TRUE, 0x00}, - {FU_CRC_KIND_B8_TECH_3250, 8, 0x1D, 0xFF, TRUE, 0x00}, + {FU_CRC_KIND_B8_TECH3250, 8, 0x1D, 0xFF, TRUE, 0x00}, {FU_CRC_KIND_B8_STANDARD, 8, 0x07, 0x00, FALSE, 0x00}, {FU_CRC_KIND_B8_SAE_J1850, 8, 0x1D, 0xFF, FALSE, 0xFF}, {FU_CRC_KIND_B8_ROHC, 8, 0x07, 0xFF, TRUE, 0x00}, {FU_CRC_KIND_B8_OPENSAFETY, 8, 0x2F, 0x00, FALSE, 0x00}, - {FU_CRC_KIND_B8_NRSC_5, 8, 0x31, 0xFF, FALSE, 0x00}, + {FU_CRC_KIND_B8_NRSC5, 8, 0x31, 0xFF, FALSE, 0x00}, {FU_CRC_KIND_B8_MIFARE_MAD, 8, 0x1D, 0xC7, FALSE, 0x00}, {FU_CRC_KIND_B8_MAXIM_DOW, 8, 0x31, 0x00, TRUE, 0x00}, {FU_CRC_KIND_B8_LTE, 8, 0x9B, 0x00, FALSE, 0x00}, @@ -76,13 +77,30 @@ guint32 val = 0; for (guint bit = 0; bit < bitwidth; bit++) { if (data & 0x01) - val |= 1ul << ((bitwidth - 1) - bit); + val |= 1ul << ((bitwidth - 1) - bit); /* nocheck:blocked */ data = (data >> 1); } return val; } /** + * fu_crc_size: + * @kind: a #FuCrcKind + * + * Returns the size of the CRC in bits. + * + * Returns: integer, or 0 on error + * + * Since: 2.0.19 + **/ +guint +fu_crc_size(FuCrcKind kind) +{ + g_return_val_if_fail(kind < FU_CRC_KIND_LAST, 0x0); + return crc_map[kind].bitwidth; +} + +/** * fu_crc8_step: * @kind: a #FuCrcKind, typically %FU_CRC_KIND_B8_MAXIM_DOW * @buf: memory buffer @@ -394,11 +412,11 @@ if (crc_target == fu_crc32(crc_map[i].kind, buf, bufsz)) return crc_map[i].kind; } - if (crc_map[i].bitwidth == 16) { + if (crc_target <= G_MAXUINT16 && crc_map[i].bitwidth == 16) { if ((guint16)crc_target == fu_crc16(crc_map[i].kind, buf, bufsz)) return crc_map[i].kind; } - if (crc_map[i].bitwidth == 8) { + if (crc_target <= G_MAXUINT8 && crc_map[i].bitwidth == 8) { if ((guint8)crc_target == fu_crc8(crc_map[i].kind, buf, bufsz)) return crc_map[i].kind; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-crc.h fwupd-2.0.20/libfwupdplugin/fu-crc.h --- fwupd-2.0.8/libfwupdplugin/fu-crc.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-crc.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,51 +8,10 @@ #include -/** - * FuCrcKind: - * - * The type of CRC. - **/ -typedef enum { - FU_CRC_KIND_UNKNOWN, - FU_CRC_KIND_B32_STANDARD, - FU_CRC_KIND_B32_BZIP2, - FU_CRC_KIND_B32_JAMCRC, - FU_CRC_KIND_B32_MPEG2, - FU_CRC_KIND_B32_POSIX, - FU_CRC_KIND_B32_SATA, - FU_CRC_KIND_B32_XFER, - FU_CRC_KIND_B32_C, - FU_CRC_KIND_B32_D, - FU_CRC_KIND_B32_Q, - FU_CRC_KIND_B16_XMODEM, - FU_CRC_KIND_B16_USB, - FU_CRC_KIND_B16_UMTS, - FU_CRC_KIND_B16_TMS37157, - FU_CRC_KIND_B16_BNR, - FU_CRC_KIND_B8_WCDMA, - FU_CRC_KIND_B8_TECH_3250, - FU_CRC_KIND_B8_STANDARD, - FU_CRC_KIND_B8_SAE_J1850, - FU_CRC_KIND_B8_ROHC, - FU_CRC_KIND_B8_OPENSAFETY, - FU_CRC_KIND_B8_NRSC_5, - FU_CRC_KIND_B8_MIFARE_MAD, - FU_CRC_KIND_B8_MAXIM_DOW, - FU_CRC_KIND_B8_LTE, - FU_CRC_KIND_B8_I_CODE, - FU_CRC_KIND_B8_ITU, - FU_CRC_KIND_B8_HITAG, - FU_CRC_KIND_B8_GSM_B, - FU_CRC_KIND_B8_GSM_A, - FU_CRC_KIND_B8_DVB_S2, - FU_CRC_KIND_B8_DARC, - FU_CRC_KIND_B8_CDMA2000, - FU_CRC_KIND_B8_BLUETOOTH, - FU_CRC_KIND_B8_AUTOSAR, - /*< private >*/ - FU_CRC_KIND_LAST, -} FuCrcKind; +#include "fu-crc-struct.h" + +guint +fu_crc_size(FuCrcKind kind); guint32 fu_crc32(FuCrcKind kind, const guint8 *buf, gsize bufsz); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-crc.rs fwupd-2.0.20/libfwupdplugin/fu-crc.rs --- fwupd-2.0.8/libfwupdplugin/fu-crc.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-crc.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,43 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[derive(ToString, FromString)] +enum FuCrcKind { + Unknown, + B32Standard, + B32Bzip2, + B32Jamcrc, + B32Mpeg2, + B32Posix, + B32Sata, + B32Xfer, + B32C, + B32D, + B32Q, + B16Xmodem, + B16Kermit, + B16Usb, + B16Umts, + B16Tms37157, + B16Bnr, + B8Wcdma, + B8Tech3250, + B8Standard, + B8SaeJ1850, + B8Rohc, + B8Opensafety, + B8Nrsc5, + B8MifareMad, + B8MaximDow, + B8Lte, + B8ICode, + B8Itu, + B8Hitag, + B8GsmB, + B8GsmA, + B8DvbS2, + B8Darc, + B8Cdma2000, + B8Bluetooth, + B8Autosar, +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-csv-entry.c fwupd-2.0.20/libfwupdplugin/fu-csv-entry.c --- fwupd-2.0.8/libfwupdplugin/fu-csv-entry.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-csv-entry.c 2026-02-26 11:36:18.000000000 +0000 @@ -102,8 +102,7 @@ const gchar *str_value = fu_csv_entry_get_value_by_column_id(self, column_id); if (str_value == NULL) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "CSV value not found"); - + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "value not found"); return FALSE; } @@ -190,7 +189,7 @@ static gboolean fu_csv_entry_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuCsvEntry *self = FU_CSV_ENTRY(firmware); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-csv-firmware.c fwupd-2.0.20/libfwupdplugin/fu-csv-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-csv-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-csv-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -167,9 +167,9 @@ /* parse entry */ fw = g_bytes_new(token->str, token->len); fu_firmware_set_idx(entry, token_idx); - if (!fu_firmware_add_image_full(FU_FIRMWARE(self), entry, error)) + if (!fu_firmware_add_image(FU_FIRMWARE(self), entry, error)) return FALSE; - if (!fu_firmware_parse_bytes(entry, fw, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_bytes(entry, fw, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, error)) return FALSE; return TRUE; } @@ -177,7 +177,7 @@ static gboolean fu_csv_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuCsvFirmware *self = FU_CSV_FIRMWARE(firmware); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-device-event-private.h fwupd-2.0.20/libfwupdplugin/fu-device-event-private.h --- fwupd-2.0.8/libfwupdplugin/fu-device-event-private.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-device-event-private.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,9 +8,6 @@ #include "fu-device-event.h" -FuDeviceEvent * -fu_device_event_new(const gchar *id); - const gchar * fu_device_event_get_id(FuDeviceEvent *self) G_GNUC_NON_NULL(1); gchar * diff -Nru fwupd-2.0.8/libfwupdplugin/fu-device-event.c fwupd-2.0.20/libfwupdplugin/fu-device-event.c --- fwupd-2.0.8/libfwupdplugin/fu-device-event.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-device-event.c 2026-02-26 11:36:18.000000000 +0000 @@ -256,7 +256,10 @@ const gchar *message; g_return_val_if_fail(FU_IS_DEVICE_EVENT(self), FALSE); - g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* nothing to do */ + if (error == NULL) + return TRUE; /* anything set */ code = fu_device_event_get_i64(self, "Error", NULL); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-device-event.h fwupd-2.0.20/libfwupdplugin/fu-device-event.h --- fwupd-2.0.8/libfwupdplugin/fu-device-event.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-device-event.h 2026-02-26 11:36:18.000000000 +0000 @@ -11,6 +11,8 @@ #define FU_TYPE_DEVICE_EVENT (fu_device_event_get_type()) G_DECLARE_FINAL_TYPE(FuDeviceEvent, fu_device_event, FU, DEVICE_EVENT, GObject) +FuDeviceEvent * +fu_device_event_new(const gchar *id); void fu_device_event_set_str(FuDeviceEvent *self, const gchar *key, const gchar *value) G_GNUC_NON_NULL(1, 2); @@ -41,4 +43,4 @@ void fu_device_event_set_error(FuDeviceEvent *self, const GError *error) G_GNUC_NON_NULL(1, 2); gboolean -fu_device_event_check_error(FuDeviceEvent *self, GError **error) G_GNUC_NON_NULL(1, 2); +fu_device_event_check_error(FuDeviceEvent *self, GError **error) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-device-locker.c fwupd-2.0.20/libfwupdplugin/fu-device-locker.c --- fwupd-2.0.8/libfwupdplugin/fu-device-locker.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-device-locker.c 2026-02-26 11:36:18.000000000 +0000 @@ -24,7 +24,7 @@ struct _FuDeviceLocker { GObject parent_instance; - GObject *device; + FuDevice *device; gboolean device_open; FuDeviceLockerFunc open_func; FuDeviceLockerFunc close_func; @@ -96,7 +96,7 @@ /** * fu_device_locker_new: - * @device: a #GObject + * @device: a #FuDevice * @error: (nullable): optional return location for an error * * Opens the device for use. When the #FuDeviceLocker is deallocated the device @@ -105,9 +105,6 @@ * manually closed using g_clear_object(). * * The functions used for opening and closing the device are set automatically. - * If the @device is not a type or supertype of #FuDevice then this function will not work. - * - * For custom objects please use fu_device_locker_new_full(). * * NOTE: If the @open_func failed then the @close_func will not be called. * @@ -118,28 +115,18 @@ * Since: 1.0.0 **/ FuDeviceLocker * -fu_device_locker_new(gpointer device, GError **error) +fu_device_locker_new(FuDevice *device, GError **error) { - g_return_val_if_fail(device != NULL, NULL); + g_return_val_if_fail(FU_IS_DEVICE(device), NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); /* FuDevice */ - if (FU_IS_DEVICE(device)) { - return fu_device_locker_new_full(device, - (FuDeviceLockerFunc)fu_device_open, - (FuDeviceLockerFunc)fu_device_close, - error); - } - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "device object type not supported"); - return NULL; + return fu_device_locker_new_full(device, fu_device_open, fu_device_close, error); } /** * fu_device_locker_new_full: - * @device: a #GObject + * @device: a #FuDevice * @open_func: (scope async): a function to open the device * @close_func: (scope async): a function to close the device * @error: (nullable): optional return location for an error @@ -158,14 +145,14 @@ * Since: 1.0.0 **/ FuDeviceLocker * -fu_device_locker_new_full(gpointer device, +fu_device_locker_new_full(FuDevice *device, FuDeviceLockerFunc open_func, FuDeviceLockerFunc close_func, GError **error) { g_autoptr(FuDeviceLocker) self = NULL; - g_return_val_if_fail(device != NULL, NULL); + g_return_val_if_fail(FU_IS_DEVICE(device), NULL); g_return_val_if_fail(open_func != NULL, NULL); g_return_val_if_fail(close_func != NULL, NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); @@ -180,10 +167,7 @@ if (!self->open_func(device, error)) { g_autoptr(GError) error_local = NULL; if (!self->close_func(device, &error_local)) { - if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { - g_debug("ignoring close error on aborted open: %s", - error_local->message); - } + g_debug("ignoring close error on aborted open: %s", error_local->message); } return NULL; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-device-locker.h fwupd-2.0.20/libfwupdplugin/fu-device-locker.h --- fwupd-2.0.8/libfwupdplugin/fu-device-locker.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-device-locker.h 2026-02-26 11:36:18.000000000 +0000 @@ -6,7 +6,7 @@ #pragma once -#include +#include "fu-device.h" #define FU_TYPE_DEVICE_LOCKER (fu_device_locker_get_type()) @@ -17,12 +17,12 @@ * * Callback to use when opening and closing using [ctor@DeviceLocker.new_full]. **/ -typedef gboolean (*FuDeviceLockerFunc)(GObject *device, GError **error); +typedef gboolean (*FuDeviceLockerFunc)(FuDevice *device, GError **error); FuDeviceLocker * -fu_device_locker_new(gpointer device, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); +fu_device_locker_new(FuDevice *device, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); FuDeviceLocker * -fu_device_locker_new_full(gpointer device, +fu_device_locker_new_full(FuDevice *device, FuDeviceLockerFunc open_func, FuDeviceLockerFunc close_func, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-device-poll-locker.h fwupd-2.0.20/libfwupdplugin/fu-device-poll-locker.h --- fwupd-2.0.8/libfwupdplugin/fu-device-poll-locker.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-device-poll-locker.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-device-locker.h" +#include "fu-device.h" + +FuDeviceLocker * +fu_device_poll_locker_new(FuDevice *self, GError **error) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-device-private.h fwupd-2.0.20/libfwupdplugin/fu-device-private.h --- fwupd-2.0.8/libfwupdplugin/fu-device-private.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-device-private.h 2026-02-26 11:36:18.000000000 +0000 @@ -13,6 +13,10 @@ #define fu_device_set_plugin(d, v) fwupd_device_set_plugin(FWUPD_DEVICE(d), v) +gboolean +fu_device_has_private_flag_quark(FuDevice *self, GQuark flag_quark) G_GNUC_NON_NULL(1); +void +fu_device_remove_possible_plugin(FuDevice *self, const gchar *plugin) G_GNUC_NON_NULL(1, 2); void fu_device_remove_children(FuDevice *self) G_GNUC_NON_NULL(1); GPtrArray * @@ -37,8 +41,6 @@ fu_device_get_update_request_id(FuDevice *self) G_GNUC_NON_NULL(1); void fu_device_set_update_request_id(FuDevice *self, const gchar *update_request_id) G_GNUC_NON_NULL(1); -const gchar * -fu_device_get_fwupd_version(FuDevice *self) G_GNUC_NON_NULL(1); void fu_device_set_fwupd_version(FuDevice *self, const gchar *fwupd_version) G_GNUC_NON_NULL(1, 2); gboolean @@ -67,8 +69,6 @@ GError **error) G_GNUC_NON_NULL(1, 2, 3); void fu_device_set_specialized_gtype(FuDevice *self, GType gtype) G_GNUC_NON_NULL(1); -void -fu_device_set_proxy_gtype(FuDevice *self, GType gtype) G_GNUC_NON_NULL(1); GPtrArray * fu_device_get_counterpart_guids(FuDevice *self) G_GNUC_NON_NULL(1); gboolean @@ -77,15 +77,13 @@ fu_device_get_custom_flags(FuDevice *self) G_GNUC_NON_NULL(1); void fu_device_set_custom_flags(FuDevice *self, const gchar *custom_flags) G_GNUC_NON_NULL(1); -void -fu_device_register_private_flag_safe(FuDevice *self, const gchar *flag); +FuDevice * +fu_device_get_proxy_internal(FuDevice *self) G_GNUC_NON_NULL(1); +FuDevice * +fu_device_get_parent_internal(FuDevice *self) G_GNUC_NON_NULL(1); void -fu_device_add_event(FuDevice *self, FuDeviceEvent *event); -void fu_device_clear_events(FuDevice *self); -GPtrArray * -fu_device_get_events(FuDevice *self); void fu_device_set_target(FuDevice *self, FuDevice *target); @@ -93,3 +91,11 @@ fu_device_get_backend(FuDevice *self); void fu_device_set_backend(FuDevice *self, FuBackend *backend); + +void +fu_device_add_json(FuDevice *self, JsonBuilder *builder, FwupdCodecFlags flags) + G_GNUC_NON_NULL(1, 2); +gboolean +fu_device_from_json(FuDevice *self, JsonObject *json_object, GError **error) G_GNUC_NON_NULL(1, 2); +gchar * +fu_device_convert_version(FuDevice *self, guint64 version_raw, GError **error) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-device.c fwupd-2.0.20/libfwupdplugin/fu-device.c --- fwupd-2.0.8/libfwupdplugin/fu-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,10 +18,11 @@ #include "fu-byte-array.h" #include "fu-bytes.h" #include "fu-chunk-array.h" -#include "fu-common.h" #include "fu-device-event-private.h" +#include "fu-device-poll-locker.h" #include "fu-device-private.h" #include "fu-input-stream.h" +#include "fu-output-stream.h" #include "fu-quirks.h" #include "fu-security-attr.h" #include "fu-string.h" @@ -45,6 +46,8 @@ FwupdDeviceProblem problem, const gchar *inhibit_id, const gchar *reason); +static void +fu_device_ensure_exported_name(FuDevice *self); typedef struct { gchar *equivalent_id; @@ -56,6 +59,7 @@ gchar *update_image; gchar *fwupd_version; gchar *proxy_guid; + gchar *name; FuDevice *proxy; /* noref */ FuDevice *target; /* ref */ FuBackend *backend; /* noref */ @@ -83,7 +87,8 @@ gboolean device_id_valid; guint64 size_min; guint64 size_max; - gint open_refcount; /* atomic */ + guint64 required_free; /* bytes */ + gint open_refcount; /* atomic */ GType specialized_gtype; GType proxy_gtype; GType firmware_gtype; @@ -91,10 +96,9 @@ GPtrArray *instance_ids; /* (nullable) (element-type FuDeviceInstanceIdItem) */ GPtrArray *retry_recs; /* (nullable) (element-type FuDeviceRetryRecovery) */ guint retry_delay; - GPtrArray *private_flags_registered; /* (nullable) (element-type GRefString) */ - GPtrArray *private_flags; /* (nullable) (no-ref) (element-type GRefString) */ + GArray *private_flags_registered; /* (nullable) (element-type GQuark) */ + GArray *private_flags; /* (nullable) (element-type GQuark) */ gchar *custom_flags; - gulong notify_flags_handler_id; gulong notify_flags_proxy_id; GHashTable *instance_hash; /* (nullable) */ FuProgress *progress; /* provided for FuDevice notify callbacks */ @@ -115,7 +119,7 @@ typedef struct { gchar *instance_id; gchar *guid; - FuDeviceInstanceFlag flags; + FuDeviceInstanceFlags flags; } FuDeviceInstanceIdItem; enum { @@ -133,6 +137,7 @@ PROP_PRIVATE_FLAGS, PROP_VID, PROP_PID, + PROP_REQUIRED_FREE, PROP_LAST }; @@ -140,15 +145,19 @@ static guint signals[SIGNAL_LAST] = {0}; -static void -fu_device_codec_iface_init(FwupdCodecInterface *iface); +/* ordered for query speed; most frequent first */ +enum { + QUARK_NO_PROBE, + QUARK_REFCOUNTED_PROXY, + QUARK_NO_GENERIC_GUIDS, + QUARK_NO_SERIAL_NUMBER, + QUARK_IS_FAKE, + QUARK_LAST, +}; + +static guint quarks[QUARK_LAST] = {0}; -G_DEFINE_TYPE_EXTENDED(FuDevice, - fu_device, - FWUPD_TYPE_DEVICE, - 0, - G_ADD_PRIVATE(FuDevice) - G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, fu_device_codec_iface_init)); +G_DEFINE_TYPE_WITH_PRIVATE(FuDevice, fu_device, FWUPD_TYPE_DEVICE); #define GET_PRIVATE(o) (fu_device_get_instance_private(o)) @@ -186,7 +195,7 @@ g_value_set_object(value, priv->proxy); break; case PROP_PARENT: - g_value_set_object(value, fu_device_get_parent(self)); + g_value_set_object(value, fu_device_get_parent_internal(self)); break; case PROP_PRIVATE_FLAGS: g_value_set_uint64(value, priv->private_flags->len); @@ -197,6 +206,9 @@ case PROP_PID: g_value_set_uint(value, priv->pid); break; + case PROP_REQUIRED_FREE: + g_value_set_uint64(value, priv->required_free); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; @@ -244,114 +256,143 @@ case PROP_PID: fu_device_set_pid(self, g_value_get_uint(value)); break; + case PROP_REQUIRED_FREE: + fu_device_set_required_free(self, g_value_get_uint64(value)); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); break; } } -/* private */ -void -fu_device_register_private_flag_safe(FuDevice *self, const gchar *flag) +static void +fu_device_register_private_flags(FuDevice *self) { FuDevicePrivate *priv = GET_PRIVATE(self); - g_return_if_fail(FU_IS_DEVICE(self)); - g_return_if_fail(flag != NULL); - g_ptr_array_add(priv->private_flags_registered, g_ref_string_new_intern(flag)); + const gchar *flags[] = { + FU_DEVICE_PRIVATE_FLAG_MD_SET_ICON, + FU_DEVICE_PRIVATE_FLAG_MD_SET_NAME, + FU_DEVICE_PRIVATE_FLAG_MD_SET_NAME_CATEGORY, + FU_DEVICE_PRIVATE_FLAG_MD_SET_VERFMT, + FU_DEVICE_PRIVATE_FLAG_ONLY_SUPPORTED, + FU_DEVICE_PRIVATE_FLAG_NO_AUTO_INSTANCE_IDS, + FU_DEVICE_PRIVATE_FLAG_ENSURE_SEMVER, + FU_DEVICE_PRIVATE_FLAG_RETRY_OPEN, + FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID, + FU_DEVICE_PRIVATE_FLAG_INHERIT_ACTIVATION, + FU_DEVICE_PRIVATE_FLAG_IS_OPEN, + FU_DEVICE_PRIVATE_FLAG_AUTO_PARENT_CHILDREN, + FU_DEVICE_PRIVATE_FLAG_ATTACH_EXTRA_RESET, + FU_DEVICE_PRIVATE_FLAG_INHIBIT_CHILDREN, + FU_DEVICE_PRIVATE_FLAG_NO_AUTO_REMOVE_CHILDREN, + FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN, + FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN, + FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_BATTERY, + FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FALLBACK, + FU_DEVICE_PRIVATE_FLAG_NO_AUTO_REMOVE, + FU_DEVICE_PRIVATE_FLAG_MD_SET_VENDOR, + FU_DEVICE_PRIVATE_FLAG_NO_LID_CLOSED, + FU_DEVICE_PRIVATE_FLAG_MD_SET_SIGNED, + FU_DEVICE_PRIVATE_FLAG_AUTO_PAUSE_POLLING, + FU_DEVICE_PRIVATE_FLAG_DELAYED_REMOVAL, + FU_DEVICE_PRIVATE_FLAG_IGNORE_SYSTEM_POWER, + FU_DEVICE_PRIVATE_FLAG_SAVE_INTO_BACKUP_REMOTE, + FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS, + FU_DEVICE_PRIVATE_FLAG_MD_SET_VERSION, + FU_DEVICE_PRIVATE_FLAG_MD_ONLY_CHECKSUM, + FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV, + FU_DEVICE_PRIVATE_FLAG_UNCONNECTED, + FU_DEVICE_PRIVATE_FLAG_DISPLAY_REQUIRED, + FU_DEVICE_PRIVATE_FLAG_UPDATE_PENDING, + FU_DEVICE_PRIVATE_FLAG_ENFORCE_REQUIRES, + FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE, + FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD, + FU_DEVICE_PRIVATE_FLAG_HOST_CPU, + FU_DEVICE_PRIVATE_FLAG_HOST_CPU_CHILD, + FU_DEVICE_PRIVATE_FLAG_EXPLICIT_ORDER, + FU_DEVICE_PRIVATE_FLAG_INSTALL_PARENT_FIRST, + FU_DEVICE_PRIVATE_FLAG_REGISTERED, + FU_DEVICE_PRIVATE_FLAG_ADD_COUNTERPART_GUIDS, + FU_DEVICE_PRIVATE_FLAG_USE_RUNTIME_VERSION, + FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART, + FU_DEVICE_PRIVATE_FLAG_COUNTERPART_VISIBLE, + FU_DEVICE_PRIVATE_FLAG_DETACH_PREPARE_FIRMWARE, + FU_DEVICE_PRIVATE_FLAG_EMULATED_REQUIRE_SETUP, + FU_DEVICE_PRIVATE_FLAG_INSTALL_LOOP_RESTART, + FU_DEVICE_PRIVATE_FLAG_MD_SET_REQUIRED_FREE, + FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX, + FU_DEVICE_PRIVATE_FLAG_LAZY_VERFMT, + FU_DEVICE_PRIVATE_FLAG_NO_VERSION_EXPECTED, + }; + GQuark quarks_tmp[G_N_ELEMENTS(flags)] = {0}; + if (G_LIKELY(priv->private_flags_registered->len > 0)) + return; + for (guint i = 0; i < G_N_ELEMENTS(flags); i++) + quarks_tmp[i] = g_quark_from_static_string(flags[i]); + g_array_append_vals(priv->private_flags_registered, quarks, G_N_ELEMENTS(quarks)); + g_array_append_vals(priv->private_flags_registered, quarks_tmp, G_N_ELEMENTS(quarks_tmp)); } -static void -fu_device_register_flags(FuDevice *self) +/* private */ +gboolean +fu_device_has_private_flag_quark(FuDevice *self, GQuark flag_quark) { - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_ICON); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_NAME); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_NAME_CATEGORY); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_VERFMT); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_ONLY_SUPPORTED); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_NO_AUTO_INSTANCE_IDS); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_ENSURE_SEMVER); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_RETRY_OPEN); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_INHERIT_ACTIVATION); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_IS_OPEN); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_NO_SERIAL_NUMBER); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_AUTO_PARENT_CHILDREN); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_ATTACH_EXTRA_RESET); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_INHIBIT_CHILDREN); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_NO_AUTO_REMOVE_CHILDREN); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_BATTERY); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FALLBACK); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_NO_AUTO_REMOVE); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_VENDOR); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_NO_LID_CLOSED); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_NO_PROBE); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_SIGNED); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_AUTO_PAUSE_POLLING); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_DELAYED_REMOVAL); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_IGNORE_SYSTEM_POWER); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_SAVE_INTO_BACKUP_REMOTE); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_VERSION); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_MD_ONLY_CHECKSUM); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_UNCONNECTED); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_DISPLAY_REQUIRED); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_UPDATE_PENDING); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_NO_GENERIC_GUIDS); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_ENFORCE_REQUIRES); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_HOST_CPU); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_HOST_CPU_CHILD); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_EXPLICIT_ORDER); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_INSTALL_PARENT_FIRST); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_REGISTERED); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_ADD_COUNTERPART_GUIDS); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_USE_RUNTIME_VERSION); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_IS_FAKE); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_COUNTERPART_VISIBLE); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_DETACH_PREPARE_FIRMWARE); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_EMULATED_REQUIRE_SETUP); - fu_device_register_private_flag_safe(self, FU_DEVICE_PRIVATE_FLAG_INSTALL_LOOP_RESTART); + FuDevicePrivate *priv = GET_PRIVATE(self); + for (guint i = 0; i < priv->private_flags->len; i++) { + GQuark flag_tmp = g_array_index(priv->private_flags, GQuark, i); + if (flag_quark == flag_tmp) + return TRUE; + } + return FALSE; } -static void -fu_device_ensure_private_flags(FuDevice *self) +static gboolean +fu_device_private_flags_has_registered_quark(FuDevice *self, GQuark flag_quark) { FuDevicePrivate *priv = GET_PRIVATE(self); - FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self); - - if (priv->private_flags_registered != NULL) - return; - - priv->private_flags_registered = - g_ptr_array_new_with_free_func((GDestroyNotify)g_ref_string_release); - priv->private_flags = g_ptr_array_new(); - - /* subclassed */ - if (device_class->register_flags != NULL) - device_class->register_flags(self); + for (guint i = 0; i < priv->private_flags_registered->len; i++) { + GQuark flag_tmp = g_array_index(priv->private_flags_registered, GQuark, i); + if (flag_quark == flag_tmp) + return TRUE; + } + return FALSE; } -static GRefString * -fu_device_find_private_flag_registered(FuDevice *self, const gchar *flag) +static gboolean +fu_device_add_private_flag_quark(FuDevice *self, GQuark flag_quark) { FuDevicePrivate *priv = GET_PRIVATE(self); - g_autoptr(GRefString) flag_ref = g_ref_string_new_intern(flag); + if (fu_device_has_private_flag_quark(self, flag_quark)) + return FALSE; + g_array_append_val(priv->private_flags, flag_quark); + return TRUE; +} - /* make sure base private flags are registered */ - fu_device_ensure_private_flags(self); +static gboolean +fu_device_remove_private_flag_quark(FuDevice *self, GQuark flag_quark) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + for (guint i = 0; i < priv->private_flags->len; i++) { + GQuark flag_tmp = g_array_index(priv->private_flags, GQuark, i); + if (flag_quark == flag_tmp) { + g_array_remove_index(priv->private_flags, i); + return TRUE; + } + } + return FALSE; +} +static GQuark +fu_device_find_private_flag_quark(FuDevice *self, const gchar *flag) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + GQuark flag_quark = g_quark_from_string(flag); for (guint i = 0; i < priv->private_flags_registered->len; i++) { - GRefString *flag_tmp = g_ptr_array_index(priv->private_flags_registered, i); - if (flag_ref == flag_tmp) + GQuark flag_tmp = g_array_index(priv->private_flags_registered, GQuark, i); + if (flag_quark == flag_tmp) return flag_tmp; } - return NULL; + return 0; } /** @@ -367,14 +408,17 @@ fu_device_add_private_flag(FuDevice *self, const gchar *flag) { FuDevicePrivate *priv = GET_PRIVATE(self); - GRefString *flag_registered; + GQuark flag_quark; g_return_if_fail(FU_IS_DEVICE(self)); g_return_if_fail(flag != NULL); + /* ensure */ + fu_device_register_private_flags(self); + /* do not let devices be updated until re-connected */ if (g_strcmp0(flag, FU_DEVICE_PRIVATE_FLAG_UNCONNECTED) == 0) - fu_device_inhibit(self, "unconnected", "Device has been removed"); + fu_device_add_problem(self, FWUPD_DEVICE_PROBLEM_UNREACHABLE); /* add counterpart GUIDs already added */ if (g_strcmp0(flag, FU_DEVICE_PRIVATE_FLAG_COUNTERPART_VISIBLE) == 0) { @@ -397,8 +441,8 @@ } /* check exists */ - flag_registered = fu_device_find_private_flag_registered(self, flag); - if (flag_registered == NULL) { + flag_quark = fu_device_find_private_flag_quark(self, flag); + if (G_UNLIKELY(flag_quark == 0)) { #ifndef SUPPORTED_BUILD g_critical("%s flag %s is unknown -- use fu_device_register_private_flag()", G_OBJECT_TYPE_NAME(self), @@ -406,14 +450,8 @@ #endif return; } - - /* already set */ - if (g_ptr_array_find(priv->private_flags, flag_registered, NULL)) - return; - - /* add */ - g_ptr_array_add(priv->private_flags, (gpointer)flag_registered); /* no ref */ - g_object_notify(G_OBJECT(self), "private-flags"); + if (fu_device_add_private_flag_quark(self, flag_quark)) + g_object_notify(G_OBJECT(self), "private-flags"); } /** @@ -428,17 +466,19 @@ void fu_device_remove_private_flag(FuDevice *self, const gchar *flag) { - FuDevicePrivate *priv = GET_PRIVATE(self); - GRefString *flag_registered; + GQuark flag_quark; g_return_if_fail(FU_IS_DEVICE(self)); g_return_if_fail(flag != NULL); + /* ensure */ + fu_device_register_private_flags(self); + if (g_strcmp0(flag, FU_DEVICE_PRIVATE_FLAG_UNCONNECTED) == 0) - fu_device_uninhibit(self, "unconnected"); + fu_device_remove_problem(self, FWUPD_DEVICE_PROBLEM_UNREACHABLE); - flag_registered = fu_device_find_private_flag_registered(self, flag); - if (flag_registered == NULL) { + flag_quark = fu_device_find_private_flag_quark(self, flag); + if (G_UNLIKELY(flag_quark == 0)) { #ifndef SUPPORTED_BUILD g_critical("%s flag %s is unknown -- use fu_device_register_private_flag()", G_OBJECT_TYPE_NAME(self), @@ -446,8 +486,8 @@ #endif return; } - g_ptr_array_remove(priv->private_flags, (gpointer)flag_registered); - g_object_notify(G_OBJECT(self), "private-flags"); + if (fu_device_remove_private_flag_quark(self, flag_quark)) + g_object_notify(G_OBJECT(self), "private-flags"); } /** @@ -462,14 +502,16 @@ gboolean fu_device_has_private_flag(FuDevice *self, const gchar *flag) { - FuDevicePrivate *priv = GET_PRIVATE(self); - GRefString *flag_registered; + GQuark flag_quark; g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); g_return_val_if_fail(flag != NULL, FALSE); - flag_registered = fu_device_find_private_flag_registered(self, flag); - if (flag_registered == NULL) { + /* ensure */ + fu_device_register_private_flags(self); + + flag_quark = fu_device_find_private_flag_quark(self, flag); + if (G_UNLIKELY(flag_quark == 0)) { #ifndef SUPPORTED_BUILD g_critical("%s flag %s is unknown -- use fu_device_register_private_flag()", G_OBJECT_TYPE_NAME(self), @@ -477,7 +519,7 @@ #endif return FALSE; } - return g_ptr_array_find(priv->private_flags, flag_registered, NULL); + return fu_device_has_private_flag_quark(self, flag_quark); } /** @@ -546,6 +588,36 @@ } /** + * fu_device_remove_possible_plugin: + * @self: a #FuDevice + * @plugin: a plugin name, e.g. `dfu` + * + * Removes a plugin name to the list of plugins that *might* be able to handle this + * device. This is typically called from a quirk handler. + * + * Missing plugin names are ignored. + * + * Since: 2.0.18 + **/ +void +fu_device_remove_possible_plugin(FuDevice *self, const gchar *plugin) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + + g_return_if_fail(FU_IS_DEVICE(self)); + g_return_if_fail(plugin != NULL); + + /* remove if exists */ + for (guint i = 0; i < priv->possible_plugins->len; i++) { + const gchar *plugin_tmp = g_ptr_array_index(priv->possible_plugins, i); + if (g_strcmp0(plugin, plugin_tmp) == 0) { + g_ptr_array_remove_index(priv->possible_plugins, i); + break; + } + } +} + +/** * fu_device_retry_add_recovery: * @self: a #FuDevice * @domain: a #GQuark, or %0 for all domains @@ -645,10 +717,10 @@ /* sanity check */ if (error_local == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "exec failed but no error set!"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "exec failed but no error set!"); return FALSE; } @@ -855,7 +927,6 @@ fu_progress_set_id(progress, G_STRLOC); fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - gssize wrote; g_autoptr(FuChunk) chk = NULL; g_autoptr(GBytes) blob = NULL; @@ -863,19 +934,8 @@ if (chk == NULL) return FALSE; blob = fu_chunk_get_bytes(chk); - - wrote = g_output_stream_write_bytes(ostr, blob, NULL, error); - if (wrote < 0) - return FALSE; - if ((gsize)wrote != g_bytes_get_size(blob)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "only wrote 0x%x bytes of 0x%x", - (guint)wrote, - (guint)g_bytes_get_size(blob)); + if (!fu_output_stream_write_bytes(ostr, blob, NULL, error)) return FALSE; - } /* save */ if (event != NULL) @@ -930,10 +990,11 @@ * fu_device_get_contents_bytes: * @self: a #FuDevice * @filename: full path to a file + * @count: maximum number of bytes to read * @progress: (nullable): optional #FuProgress * @error: (nullable): optional return location for an error * - * Writes @blob to @filename, emulating if required. + * Reads a blob of data from the file, emulating if required. * * Returns: (transfer full): a #GBytes, or %NULL on error * @@ -942,6 +1003,7 @@ GBytes * fu_device_get_contents_bytes(FuDevice *self, const gchar *filename, + gsize count, FuProgress *progress, GError **error) { @@ -978,7 +1040,7 @@ istr = fu_input_stream_from_path(filename, error); if (istr == NULL) return NULL; - blob = fu_input_stream_read_bytes(istr, 0, G_MAXSIZE, progress, error); + blob = fu_input_stream_read_bytes(istr, 0, count, progress, error); if (blob == NULL) return NULL; @@ -991,6 +1053,158 @@ } /** + * fu_device_get_contents: + * @self: a #FuDevice + * @filename: full path to a file + * @count: maximum number of bytes to read + * @progress: (nullable): optional #FuProgress + * @error: (nullable): optional return location for an error + * + * Reads a blob of ASCII text from the file, emulating if required. + * + * Returns: (transfer full): a #GBytes, or %NULL on error + * + * Since: 2.0.12 + **/ +gchar * +fu_device_get_contents(FuDevice *self, + const gchar *filename, + gsize count, + FuProgress *progress, + GError **error) +{ + FuDeviceEvent *event = NULL; + g_autofree gchar *event_id = NULL; + g_autofree gchar *str = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GInputStream) istr = NULL; + + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + g_return_val_if_fail(filename != NULL, NULL); + g_return_val_if_fail(progress == NULL || FU_IS_PROGRESS(progress), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* need event ID */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || + fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)), + FU_CONTEXT_FLAG_SAVE_EVENTS)) { + event_id = g_strdup_printf("GetContents:Filename=%s", filename); + } + + /* emulated */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) { + event = fu_device_load_event(FU_DEVICE(self), event_id, error); + if (event == NULL) + return NULL; + return g_strdup(fu_device_event_get_str(event, "Data", error)); + } + + /* save */ + if (event_id != NULL) + event = fu_device_save_event(FU_DEVICE(self), event_id); + + /* open for reading */ + istr = fu_input_stream_from_path(filename, error); + if (istr == NULL) + return NULL; + blob = fu_input_stream_read_bytes(istr, 0, count, progress, error); + if (blob == NULL) + return NULL; + str = fu_strsafe_bytes(blob, G_MAXSIZE); + if (str == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "invalid ASCII data"); + return NULL; + } + + /* save response */ + if (event != NULL) + fu_device_event_set_str(event, "Data", str); + + /* success */ + return g_steal_pointer(&str); +} + +/** + * fu_device_get_smbios_string: + * @self: a #FuDevice + * @type: a SMBIOS structure type, e.g. %FU_SMBIOS_STRUCTURE_TYPE_BIOS + * @length: expected length of the structure, or %FU_SMBIOS_STRUCTURE_LENGTH_ANY + * @offset: a SMBIOS offset + * @error: (nullable): optional return location for an error + * + * Gets a hardware SMBIOS string. + * + * The @type and @offset can be referenced from the DMTF SMBIOS specification: + * https://www.dmtf.org/sites/default/files/standards/documents/DSP0134_3.1.1.pdf + * + * Returns: a string, or %NULL + * + * Since: 2.0.10 + **/ +const gchar * +fu_device_get_smbios_string(FuDevice *self, + guint8 type, + guint8 length, + guint8 offset, + GError **error) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + FuDeviceEvent *event = NULL; + const gchar *str; + g_autofree gchar *event_id = NULL; + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* need event ID */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || + fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)), + FU_CONTEXT_FLAG_SAVE_EVENTS)) { + event_id = g_strdup_printf("GetSmbiosString:" + "Type=0x%02x," + "Length=0x%02x," + "Offset=0x%02x", + type, + length, + offset); + } + + /* emulated */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) { + event = fu_device_load_event(FU_DEVICE(self), event_id, error); + if (event == NULL) + return NULL; + if (!fu_device_event_check_error(event, error)) + return NULL; + return fu_device_event_get_str(event, "Data", error); + } + + /* save */ + if (event_id != NULL) + event = fu_device_save_event(FU_DEVICE(self), event_id); + + /* use context */ + str = fu_context_get_smbios_string(priv->ctx, type, length, offset, &error_local); + if (str == NULL) { + if (event != NULL) + fu_device_event_set_error(event, error_local); + g_propagate_error(error, g_steal_pointer(&error_local)); + return NULL; + } + + /* save response */ + if (event != NULL) + fu_device_event_set_str(event, "Data", str); + + /* success */ + return str; +} + +/** * fu_device_query_file_exists: * @self: a #FuDevice * @filename: filename @@ -1048,18 +1262,16 @@ } static gboolean -fu_device_poll_locker_open_cb(GObject *device, GError **error) +fu_device_poll_locker_open_cb(FuDevice *self, GError **error) { - FuDevice *self = FU_DEVICE(device); FuDevicePrivate *priv = GET_PRIVATE(self); g_atomic_int_inc(&priv->poll_locker_cnt); return TRUE; } static gboolean -fu_device_poll_locker_close_cb(GObject *device, GError **error) +fu_device_poll_locker_close_cb(FuDevice *self, GError **error) { - FuDevice *self = FU_DEVICE(device); FuDevicePrivate *priv = GET_PRIVATE(self); g_atomic_int_dec_and_test(&priv->poll_locker_cnt); return TRUE; @@ -1296,8 +1508,19 @@ } /** + * fu_device_get_parent_internal: (skip): + **/ +FuDevice * +fu_device_get_parent_internal(FuDevice *self) +{ + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return FU_DEVICE(fwupd_device_get_parent(FWUPD_DEVICE(self))); +} + +/** * fu_device_get_parent: * @self: a #FuDevice + * @error: (nullable): optional return location for an error * * Gets any parent device. An parent device is logically "above" the current * device and this may be reflected in client tools. @@ -1310,12 +1533,20 @@ * * Returns: (transfer none): a device or %NULL * - * Since: 1.0.8 + * Since: 2.0.18 **/ FuDevice * -fu_device_get_parent(FuDevice *self) +fu_device_get_parent(FuDevice *self, GError **error) { g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + if (fwupd_device_get_parent(FWUPD_DEVICE(self)) == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no parent device"); + return NULL; + } return FU_DEVICE(fwupd_device_get_parent(FWUPD_DEVICE(self))); } @@ -1338,7 +1569,7 @@ FuDevice *parent; g_return_val_if_fail(FU_IS_DEVICE(self), NULL); do { - parent = fu_device_get_parent(self); + parent = fu_device_get_parent_internal(self); if (parent != NULL) self = parent; } while (parent != NULL); @@ -1381,11 +1612,9 @@ /* debug */ if (parent != NULL) { - g_info("setting parent of %s [%s] to be %s [%s]", - fu_device_get_name(self), - fu_device_get_id(self), - fu_device_get_name(parent), - fu_device_get_id(parent)); + g_autofree gchar *id_display = fu_device_get_id_display(self); + g_autofree gchar *id_display_parent = fu_device_get_id_display(parent); + g_debug("setting parent of %s to be %s", id_display, id_display_parent); } /* set the composite ID on the children and grandchildren */ @@ -1400,18 +1629,30 @@ fwupd_device_set_parent(FWUPD_DEVICE(self), FWUPD_DEVICE(parent)); g_object_notify(G_OBJECT(self), "parent"); + + /* fix the exported name */ + fu_device_ensure_exported_name(self); } /* if the proxy sets this flag copy it to the logical device */ static void fu_device_incorporate_from_proxy_flags(FuDevice *self, FuDevice *proxy) { - const FwupdDeviceFlags flags[] = {FWUPD_DEVICE_FLAG_EMULATED, - FWUPD_DEVICE_FLAG_UNREACHABLE}; + const FwupdDeviceFlags flags[] = { + FWUPD_DEVICE_FLAG_EMULATED, + FWUPD_DEVICE_FLAG_INTERNAL, + FWUPD_DEVICE_FLAG_REQUIRE_AC, + FWUPD_DEVICE_FLAG_UNREACHABLE, + }; for (guint i = 0; i < G_N_ELEMENTS(flags); i++) { - if (fu_device_has_flag(proxy, flags[i])) { + if (fu_device_has_flag(proxy, flags[i]) && !fu_device_has_flag(self, flags[i])) { g_debug("propagating %s from proxy", fwupd_device_flag_to_string(flags[i])); fu_device_add_flag(self, flags[i]); + } else if (!fu_device_has_flag(proxy, flags[i]) && + fu_device_has_flag(self, flags[i])) { + g_debug("unpropagating %s from proxy", + fwupd_device_flag_to_string(flags[i])); + fu_device_remove_flag(self, flags[i]); } } } @@ -1466,7 +1707,7 @@ } /* sometimes strong, sometimes weak */ - if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY)) { + if (fu_device_has_private_flag_quark(self, quarks[QUARK_REFCOUNTED_PROXY])) { g_set_object(&priv->proxy, proxy); fu_device_set_target(self, proxy); } else { @@ -1481,8 +1722,20 @@ } /** + * fu_device_get_proxy_internal: (skip): + **/ +FuDevice * +fu_device_get_proxy_internal(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + return priv->proxy; +} + +/** * fu_device_get_proxy: * @self: a #FuDevice + * @error: (nullable): optional return location for an error * * Gets any proxy device. A proxy device can be used to perform an action on * behalf of another device, for instance attach()ing it after a successful @@ -1493,14 +1746,39 @@ * * Returns: (transfer none): a device or %NULL * - * Since: 1.4.1 + * Since: 2.0.18 **/ FuDevice * -fu_device_get_proxy(FuDevice *self) +fu_device_get_proxy(FuDevice *self, GError **error) { FuDevicePrivate *priv = GET_PRIVATE(self); g_return_val_if_fail(FU_IS_DEVICE(self), NULL); - return priv->proxy; + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + if (priv->proxy_gtype == G_TYPE_INVALID) { +#ifndef SUPPORTED_BUILD + g_critical("plugin used fu_device_get_proxy() but did not define proxy GType -- " + "use fu_device_set_proxy_gtype() or ProxyGType="); +#endif + } else if (priv->proxy == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no proxy device assigned"); + return NULL; + } + + /* check is the correct type */ + if (!g_type_is_a(G_OBJECT_TYPE(priv->proxy), priv->proxy_gtype)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "wrong proxy GType, got %s and expected %s", + G_OBJECT_TYPE_NAME(priv->proxy), + g_type_name(priv->proxy_gtype)); + return NULL; + } + return fu_device_get_proxy_internal(self); } /** @@ -1544,6 +1822,70 @@ } /** + * fu_device_get_child_by_logical_id: + * @self: a #FuDevice + * @logical_id: (nullable): an ID, e.g. `CONFIG` + * @error: (nullable): optional return location for an error + * + * Gets a child device by the logical ID. + * + * Returns: (transfer full): a device, or %NULL + * + * Since: 2.0.17 + **/ +FuDevice * +fu_device_get_child_by_logical_id(FuDevice *self, const gchar *logical_id, GError **error) +{ + GPtrArray *children; + g_autofree gchar *str = NULL; + g_autoptr(GPtrArray) logical_ids = g_ptr_array_new(); + + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* find the first that matches */ + children = fu_device_get_children(self); + if (children->len == 0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no children"); + return NULL; + } + for (guint i = 0; i < children->len; i++) { + FuDevice *device_tmp = g_ptr_array_index(children, i); + if (g_strcmp0(fu_device_get_logical_id(device_tmp), logical_id) == 0) + return g_object_ref(device_tmp); + + /* save this for the error message */ + g_ptr_array_add(logical_ids, (gpointer)fu_device_get_logical_id(device_tmp)); + } + + /* nothing found */ + str = fu_strjoin(",", logical_ids); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no child with logical ID %s, only have %s", + logical_id, + str); + return NULL; +} + +static void +fu_device_ensure_exported_name(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + FuDevice *parent = fu_device_get_parent_internal(self); + + if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX) && + priv->name != NULL && parent != NULL && fu_device_get_name(parent) != NULL) { + g_autofree gchar *name_parent = + g_strdup_printf("%s (%s)", fu_device_get_name(parent), priv->name); + fwupd_device_set_name(FWUPD_DEVICE(self), name_parent); + } else { + fwupd_device_set_name(FWUPD_DEVICE(self), priv->name); + } +} + +/** * fu_device_add_child: * @self: a #FuDevice * @child: Another #FuDevice @@ -1563,6 +1905,10 @@ g_return_if_fail(FU_IS_DEVICE(self)); g_return_if_fail(FU_IS_DEVICE(child)); + /* if parent is emulated, child must be too */ + if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_EMULATED)) + fu_device_add_flag(child, FWUPD_DEVICE_FLAG_EMULATED); + /* make tests easier */ fu_device_convert_instance_ids(child); @@ -2050,8 +2396,14 @@ if (g_strcmp0(key, FU_QUIRKS_PLUGIN) == 0) { g_auto(GStrv) sections = g_strsplit(value, ",", -1); - for (guint i = 0; sections[i] != NULL; i++) - fu_device_add_possible_plugin(self, sections[i]); + for (guint i = 0; sections[i] != NULL; i++) { + const gchar *plugin = sections[i]; + if (g_str_has_prefix(plugin, "~")) { + fu_device_remove_possible_plugin(self, plugin + 1); + continue; + } + fu_device_add_possible_plugin(self, plugin); + } return TRUE; } if (g_strcmp0(key, FU_QUIRKS_FLAGS) == 0) { @@ -2394,6 +2746,11 @@ priv->firmware_gtype = firmware_gtype; } +typedef struct { + FuDevice *self; + FuDeviceInstanceFlags flags; +} FuDeviceQuirksIterHelper; + static void fu_device_quirks_iter_cb(FuContext *ctx, const gchar *key, @@ -2401,18 +2758,23 @@ FuContextQuirkSource source, gpointer user_data) { - FuDevice *self = FU_DEVICE(user_data); + FuDeviceQuirksIterHelper *helper = (FuDeviceQuirksIterHelper *)user_data; g_autoptr(GError) error = NULL; - if (!fu_device_set_quirk_kv(self, key, value, source, &error)) { +#ifndef SUPPORTED_BUILD + if (helper->flags & FU_DEVICE_INSTANCE_FLAG_DEPRECATED) + g_critical("matched deprecated instance ID %s -> %s", key, value); +#endif + if (!fu_device_set_quirk_kv(helper->self, key, value, source, &error)) { if (!g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) g_warning("failed to set quirk key %s=%s: %s", key, value, error->message); } } static void -fu_device_add_guid_quirks(FuDevice *self, const gchar *guid) +fu_device_add_guid_quirks(FuDevice *self, const gchar *guid, FuDeviceInstanceFlags flags) { FuDevicePrivate *priv = GET_PRIVATE(self); + FuDeviceQuirksIterHelper helper = {.self = self, .flags = flags}; g_return_if_fail(FU_IS_DEVICE(self)); g_return_if_fail(guid != NULL); @@ -2424,7 +2786,11 @@ } /* run the query */ - fu_context_lookup_quirk_by_id_iter(priv->ctx, guid, NULL, fu_device_quirks_iter_cb, self); + fu_context_lookup_quirk_by_id_iter(priv->ctx, + guid, + NULL, + fu_device_quirks_iter_cb, + &helper); } /** @@ -2516,6 +2882,49 @@ } /** + * fu_device_get_required_free: + * @self: a #FuDevice + * + * Returns the required amount of free firmware space. + * + * Returns: size in bytes + * + * Since: 2.0.12 + **/ +guint64 +fu_device_get_required_free(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_DEVICE(self), 0); + return priv->required_free; +} + +/** + * fu_device_set_required_free: + * @self: a #FuDevice + * @required_free: size in bytes + * + * Sets the required amount of free firmware size. + * + * NOTE: What we really want to do for EFI devices is check if a *contiguous* block of the right + * size can be written, but on most machines this causes an SMI which causes all cores to halt. + * On my desktop this causes **ALL** CPU processes to stop for ~1s, which is clearly unacceptable + * at every boot. Instead, check for free space at least as big as needed, plus a little extra. + * + * Since: 2.0.12 + **/ +void +fu_device_set_required_free(FuDevice *self, guint64 required_free) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DEVICE(self)); + if (priv->required_free == required_free) + return; + priv->required_free = required_free; + g_object_notify(G_OBJECT(self), "required-free"); +} + +/** * fu_device_has_guid: * @self: a #FuDevice * @guid: a GUID, e.g. `WacomAES` @@ -2584,7 +2993,7 @@ * Since: 2.0.4 **/ gboolean -fu_device_has_instance_id(FuDevice *self, const gchar *instance_id, FuDeviceInstanceFlag flags) +fu_device_has_instance_id(FuDevice *self, const gchar *instance_id, FuDeviceInstanceFlags flags) { FuDevicePrivate *priv = GET_PRIVATE(self); @@ -2596,8 +3005,13 @@ if ((item->flags & flags) == 0) continue; if (g_strcmp0(item->instance_id, instance_id) == 0 || - g_strcmp0(item->guid, instance_id) == 0) + g_strcmp0(item->guid, instance_id) == 0) { +#ifndef SUPPORTED_BUILD + if (item->flags & FU_DEVICE_INSTANCE_FLAG_DEPRECATED) + g_critical("using deprecated instance ID %s", instance_id); +#endif return TRUE; + } } return FALSE; } @@ -2616,7 +3030,9 @@ * Since: 1.2.9 **/ void -fu_device_add_instance_id_full(FuDevice *self, const gchar *instance_id, FuDeviceInstanceFlag flags) +fu_device_add_instance_id_full(FuDevice *self, + const gchar *instance_id, + FuDeviceInstanceFlags flags) { FuDevicePrivate *priv = GET_PRIVATE(self); FuDeviceInstanceIdItem *item; @@ -2637,7 +3053,7 @@ if ((item->flags & FU_DEVICE_INSTANCE_FLAG_QUIRKS) == 0 && (flags & FU_DEVICE_INSTANCE_FLAG_QUIRKS) > 0) { /* visible -> visible+quirks */ - fu_device_add_guid_quirks(self, item->guid); + fu_device_add_guid_quirks(self, item->guid, item->flags); } item->flags |= flags; } else { @@ -2656,14 +3072,15 @@ /* we want the quirks to match so the plugin is set */ if (flags & FU_DEVICE_INSTANCE_FLAG_QUIRKS) - fu_device_add_guid_quirks(self, item->guid); + fu_device_add_guid_quirks(self, item->guid, item->flags); } /* already done by ->setup(), so this must be ->registered() */ if (priv->done_setup) { if (item->instance_id != NULL) fwupd_device_add_instance_id(FWUPD_DEVICE(self), item->instance_id); - fwupd_device_add_guid(FWUPD_DEVICE(self), item->guid); + if (flags & FU_DEVICE_INSTANCE_FLAG_VISIBLE) + fwupd_device_add_guid(FWUPD_DEVICE(self), item->guid); } } @@ -2885,25 +3302,26 @@ static void fu_device_fixup_vendor_name(FuDevice *self) { - const gchar *name = fu_device_get_name(self); + FuDevicePrivate *priv = GET_PRIVATE(self); const gchar *vendor = fu_device_get_vendor(self); - if (name != NULL && vendor != NULL) { - g_autofree gchar *name_up = g_utf8_strup(name, -1); + if (priv->name != NULL && vendor != NULL) { + g_autofree gchar *name_up = g_utf8_strup(priv->name, -1); g_autofree gchar *vendor_up = g_utf8_strup(vendor, -1); if (g_strcmp0(name_up, vendor_up) == 0) { #ifndef SUPPORTED_BUILD - g_warning("name and vendor are the same for %s [%s]", - fu_device_get_name(self), - fu_device_get_id(self)); + g_autofree gchar *id_display = fu_device_get_id_display(self); + g_warning("name and vendor are the same for %s", id_display); #endif return; } if (g_str_has_prefix(name_up, vendor_up)) { gsize vendor_len = strlen(vendor); - g_autofree gchar *name1 = g_strdup(name + vendor_len); + g_autofree gchar *name1 = g_strdup(priv->name + vendor_len); g_autofree gchar *name2 = fu_strstrip(name1); - g_debug("removing vendor prefix of '%s' from '%s'", vendor, name); - fwupd_device_set_name(FWUPD_DEVICE(self), name2); + g_debug("removing vendor prefix of '%s' from '%s'", vendor, priv->name); + g_free(priv->name); + priv->name = g_steal_pointer(&name2); + fu_device_ensure_exported_name(self); } } } @@ -2915,6 +3333,11 @@ * * Sets the vendor name on the device. * + * NOTE: The vendor is frequently used as a prefix to the device name in front-end tools, and so + * @vendor should be as short as possible. + * This should also be the "nice name" the user is familiar with and should not be the legal name, + * for example use `AMD` not `Advanced Micro Devices, Inc.`. + * * Since: 1.6.2 **/ void @@ -2957,6 +3380,8 @@ } } g_string_truncate(new, last_non_space); + g_string_replace(new, "[", "(", 0); + g_string_replace(new, "]", ")", 0); g_string_replace(new, "(TM)", "™", 0); g_string_replace(new, "(R)", "", 0); if (new->len == 0) @@ -2976,6 +3401,7 @@ void fu_device_set_name(FuDevice *self, const gchar *value) { + FuDevicePrivate *priv = GET_PRIVATE(self); g_autofree gchar *value_safe = NULL; g_return_if_fail(FU_IS_DEVICE(self)); @@ -2987,20 +3413,25 @@ g_info("ignoring name value: '%s'", value); return; } - if (g_strcmp0(value_safe, fu_device_get_name(self)) == 0) + if (g_strcmp0(value_safe, priv->name) == 0) return; /* changing */ - if (fu_device_get_name(self) != NULL) { + if (priv->name != NULL) { const gchar *id = fu_device_get_id(self); g_debug("%s device overwriting name value: %s->%s", id != NULL ? id : "unknown", - fu_device_get_name(self), + priv->name, value_safe); } - fwupd_device_set_name(FWUPD_DEVICE(self), value_safe); + /* this is the name set by quirks or plugin */ + g_free(priv->name); + priv->name = g_steal_pointer(&value_safe); fu_device_fixup_vendor_name(self); + + /* set the exported parent name */ + fu_device_ensure_exported_name(self); } /** @@ -3202,39 +3633,17 @@ void fu_device_set_version_bootloader(FuDevice *self, const gchar *version) { - g_autofree gchar *version_safe = NULL; - g_autoptr(GError) error = NULL; - g_return_if_fail(FU_IS_DEVICE(self)); /* sanitize if required */ if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_ENSURE_SEMVER)) { - version_safe = + g_autofree gchar *version_safe = fu_version_ensure_semver(version, fu_device_get_version_format(self)); if (g_strcmp0(version, version_safe) != 0) g_debug("converted '%s' to '%s'", version, version_safe); - } else { - version_safe = g_strdup(version); - } - - /* print a console warning for an invalid version, if semver */ - if (version_safe != NULL && - !fu_version_verify_format(version_safe, fu_device_get_version_format(self), &error)) -#ifdef SUPPORTED_BUILD - g_warning("%s", error->message); -#else - g_critical("%s", error->message); -#endif - - /* if different */ - if (g_strcmp0(fu_device_get_version_bootloader(self), version_safe) != 0) { - if (fu_device_get_version_bootloader(self) != NULL) { - g_debug("changing version for %s: %s->%s", - fu_device_get_id(self), - fu_device_get_version_bootloader(self), - version_safe); - } fwupd_device_set_version_bootloader(FWUPD_DEVICE(self), version_safe); + } else { + fwupd_device_set_version_bootloader(FWUPD_DEVICE(self), version); } } @@ -3284,6 +3693,34 @@ } } +/** + * fu_device_convert_version: + * @self: a #FuDevice + * @version_raw: an integer + * @error: (nullable): optional return location for an error + * + * Converts the integer version to a string version, using the device-specific converter. + * + * Returns: a string value, or %NULL for error. + * + * Since: 2.0.18 + **/ +gchar * +fu_device_convert_version(FuDevice *self, guint64 version_raw, GError **error) +{ + FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self); + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + + if (device_class->convert_version == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "->convert_version not implemented"); + return NULL; + } + return device_class->convert_version(self, version_raw); +} + /* private */ gboolean fu_device_is_updatable(FuDevice *self) @@ -3308,13 +3745,8 @@ FwupdDeviceProblem problems = FWUPD_DEVICE_PROBLEM_NONE; guint nr_inhibits = g_hash_table_size(priv->inhibits); - /* disable */ - if (priv->notify_flags_handler_id != 0) - g_signal_handler_block(self, priv->notify_flags_handler_id); - /* was okay -> not okay */ if (nr_inhibits > 0) { - g_autofree gchar *reasons_str = NULL; g_autoptr(GList) values = g_hash_table_get_values(priv->inhibits); g_autoptr(GPtrArray) reasons = g_ptr_array_new(); @@ -3322,31 +3754,33 @@ * inhibits and *not* be automatically updatable */ if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE)) { fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN); + fwupd_device_add_flag(FWUPD_DEVICE(self), + FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN); } /* update the update error */ for (GList *l = values; l != NULL; l = l->next) { FuDeviceInhibit *inhibit = (FuDeviceInhibit *)l->data; - g_ptr_array_add(reasons, inhibit->reason); + if (inhibit->problem == FWUPD_DEVICE_PROBLEM_NONE) + g_ptr_array_add(reasons, inhibit->reason); problems |= inhibit->problem; } - reasons_str = fu_strjoin(", ", reasons); - fu_device_set_update_error(self, reasons_str); + if (reasons->len > 0) { + g_autofree gchar *reasons_str = fu_strjoin(", ", reasons); + fu_device_set_update_error(self, reasons_str); + } else { + fu_device_set_update_error(self, NULL); + } } else { if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)) { fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN); - fu_device_add_flag(self, FWUPD_DEVICE_FLAG_UPDATABLE); + fwupd_device_add_flag(FWUPD_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); } fu_device_set_update_error(self, NULL); } /* sync with baseclass */ fwupd_device_set_problems(FWUPD_DEVICE(self), problems); - - /* enable */ - if (priv->notify_flags_handler_id != 0) - g_signal_handler_unblock(self, priv->notify_flags_handler_id); } static gchar * @@ -3443,7 +3877,7 @@ GPtrArray *children = fu_device_get_children(self); for (guint i = 0; i < children->len; i++) { FuDevice *child = g_ptr_array_index(children, i); - fu_device_inhibit(child, inhibit_id, reason); + fu_device_inhibit_full(child, problem, inhibit_id, reason); } } } @@ -3843,10 +4277,7 @@ const gchar *id; /* we might have to propagate this to preserve compat with older emulation files */ - if (priv->fwupd_version != NULL && - fu_version_compare(priv->fwupd_version, - "2.0.8", - FWUPD_VERSION_FORMAT_TRIPLET) >= 0) { + if (fu_device_check_fwupd_version(self, "2.0.8")) { event = fu_device_load_event(FU_DEVICE(self), event_id, error); if (event == NULL) return NULL; @@ -4093,21 +4524,27 @@ } /** - * fu_device_get_fwupd_version: + * fu_device_check_fwupd_version: * @self: a #FuDevice + * @fwupd_version: (not nullable): the version, e.g. `2.0.8` * - * Gets the fwupd version that created the emulation. + * Checks the fwupd version that created the emulation. * - * Returns: a version, e.g. `2.0.8`, or %NULL if unset + * Returns: %TRUE if @fwupd_version version is >= to the emulation version * - * Since: 2.0.8 + * Since: 2.0.13 **/ -const gchar * -fu_device_get_fwupd_version(FuDevice *self) +gboolean +fu_device_check_fwupd_version(FuDevice *self, const gchar *fwupd_version) { FuDevicePrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FU_IS_DEVICE(self), NULL); - return priv->fwupd_version; + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(fwupd_version != NULL, FALSE); + if (priv->fwupd_version == NULL) + return FALSE; + return fu_version_compare(priv->fwupd_version, + fwupd_version, + FWUPD_VERSION_FORMAT_TRIPLET) >= 0; } /** @@ -4276,10 +4713,29 @@ void fu_device_add_flag(FuDevice *self, FwupdDeviceFlags flag) { + FuDevicePrivate *priv = GET_PRIVATE(self); + /* none is not used as an "exported" flag */ if (flag == FWUPD_DEVICE_FLAG_NONE) return; + /* emulated device reinstalling do not need a replug or shutdown */ + if (flag == FWUPD_DEVICE_FLAG_EMULATED) { + if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) { + g_debug("removing needs-reboot for emulated device"); + fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + } + if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) { + g_debug("removing needs-shutdown for emulated device"); + fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN); + } + } + + /* we only inhibit when the flags contains UPDATABLE, and that might be discovered by + * probing the hardware *after* the battery level has been set */ + if (flag == FWUPD_DEVICE_FLAG_UPDATABLE && priv->inhibits != NULL) + fu_device_ensure_inhibits(self); + /* an emulated device cannot be used to record emulation events, * and an emulated device cannot be tagged for emulation */ if (flag == FWUPD_DEVICE_FLAG_EMULATED) @@ -4315,6 +4771,17 @@ /* do not let devices be updated until back in range */ if (flag & FWUPD_DEVICE_FLAG_UNREACHABLE) fu_device_add_problem(self, FWUPD_DEVICE_PROBLEM_UNREACHABLE); + + /* fixup and maybe show a warning if the remove delay was forgotten */ + if ((flag & FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG) && priv->remove_delay == 0) { + priv->remove_delay = FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE; +#ifndef SUPPORTED_BUILD + g_critical("FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG added but remove delay is unset! -- " + "add something like fu_device_set_remove_delay(FU_DEVICE(self), " + "FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE) to the %s _init()", + G_OBJECT_TYPE_NAME(self)); +#endif + } } /** @@ -4330,7 +4797,8 @@ void fu_device_register_private_flag(FuDevice *self, const gchar *flag) { - GRefString *flag_registered; + FuDevicePrivate *priv = GET_PRIVATE(self); + GQuark flag_quark; g_return_if_fail(FU_IS_DEVICE(self)); g_return_if_fail(flag != NULL); @@ -4344,23 +4812,26 @@ return; } #endif + /* ensure */ + fu_device_register_private_flags(self); /* sanity check */ - flag_registered = fu_device_find_private_flag_registered(self, flag); - if (flag_registered != NULL) { + flag_quark = fu_device_find_private_flag_quark(self, flag); + if (G_UNLIKELY(flag_quark != 0)) { g_critical("already registered private %s flag %s", G_OBJECT_TYPE_NAME(self), flag); return; } /* add new */ - fu_device_register_private_flag_safe(self, flag); + flag_quark = g_quark_from_static_string(flag); + g_array_append_val(priv->private_flags_registered, flag_quark); } static void fu_device_set_custom_flag(FuDevice *self, const gchar *hint) { FwupdDeviceFlags flag; - GRefString *private_flag; + GQuark flag_quark; g_return_if_fail(hint != NULL); @@ -4371,9 +4842,9 @@ fu_device_remove_flag(self, flag); return; } - private_flag = fu_device_find_private_flag_registered(self, hint + 1); - if (private_flag != NULL) { - fu_device_remove_private_flag(self, private_flag); + flag_quark = fu_device_find_private_flag_quark(self, hint + 1); + if (flag_quark != 0) { + fu_device_remove_private_flag_quark(self, flag_quark); return; } return; @@ -4385,9 +4856,9 @@ fu_device_add_flag(self, flag); return; } - private_flag = fu_device_find_private_flag_registered(self, hint); - if (private_flag != NULL) { - fu_device_add_private_flag(self, private_flag); + flag_quark = fu_device_find_private_flag_quark(self, hint); + if (flag_quark != 0) { + fu_device_add_private_flag_quark(self, flag_quark); return; } } @@ -4576,7 +5047,7 @@ /* use the parent if the child is unset */ if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_BATTERY) && fwupd_device_get_battery_level(FWUPD_DEVICE(self)) == FWUPD_BATTERY_LEVEL_INVALID) { - FuDevice *parent = fu_device_get_parent(self); + FuDevice *parent = fu_device_get_parent_internal(self); if (parent != NULL) return fu_device_get_battery_level(parent); } @@ -4621,12 +5092,12 @@ guint fu_device_get_battery_threshold(FuDevice *self) { - g_return_val_if_fail(FU_IS_DEVICE(self), FWUPD_BATTERY_LEVEL_INVALID); + g_return_val_if_fail(FU_IS_DEVICE(self), G_MAXUINT); /* use the parent if the child is unset */ if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_BATTERY) && fwupd_device_get_battery_threshold(FWUPD_DEVICE(self)) == FWUPD_BATTERY_LEVEL_INVALID) { - FuDevice *parent = fu_device_get_parent(self); + FuDevice *parent = fu_device_get_parent_internal(self); if (parent != NULL) return fu_device_get_battery_threshold(parent); } @@ -4823,9 +5294,9 @@ } static gchar * -fu_device_instance_flag_to_string_trunc(FuDeviceInstanceFlag flags) +fu_device_instance_flag_to_string_trunc(FuDeviceInstanceFlags flags) { - g_autofree gchar *tmp = fu_device_instance_flag_to_string(flags); + g_autofree gchar *tmp = fu_device_instance_flags_to_string(flags); g_auto(GStrv) split = g_strsplit(tmp, ",", -1); for (guint i = 0; split[i] != NULL; i++) { if (strlen(split[i]) > 2) @@ -4877,6 +5348,7 @@ } fwupd_codec_string_append_size(str, idt, "FirmwareSizeMin", priv->size_min); fwupd_codec_string_append_size(str, idt, "FirmwareSizeMax", priv->size_max); + fwupd_codec_string_append_int(str, idt, "RequiredFree", priv->required_free); if (priv->order != G_MAXINT) { g_autofree gchar *order = g_strdup_printf("%i", priv->order); fwupd_codec_string_append(str, idt, "Order", order); @@ -4905,20 +5377,32 @@ if (priv->private_flags != NULL && priv->private_flags->len != 0) { g_autoptr(GPtrArray) tmpv = g_ptr_array_new(); for (guint64 i = 0; i < priv->private_flags->len; i++) { - GRefString *private_flag = g_ptr_array_index(priv->private_flags, i); - g_ptr_array_add(tmpv, (gpointer)private_flag); + GQuark flag_quark = g_array_index(priv->private_flags, GQuark, i); + g_ptr_array_add(tmpv, (gpointer)g_quark_to_string(flag_quark)); } if (tmpv->len > 0) { g_autofree gchar *tmps = fu_strjoin(",", tmpv); fwupd_codec_string_append(str, idt, "PrivateFlags", tmps); } } + if (priv->instance_hash != NULL) { + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, priv->instance_hash); + while (g_hash_table_iter_next(&iter, &key, &value)) { + g_autofree gchar *title = + g_strdup_printf("InstanceKey[%s]", (const gchar *)key); + fwupd_codec_string_append(str, idt, title, value); + } + } if (priv->inhibits != NULL) { g_autoptr(GList) values = g_hash_table_get_values(priv->inhibits); for (GList *l = values; l != NULL; l = l->next) { FuDeviceInhibit *inhibit = (FuDeviceInhibit *)l->data; g_autofree gchar *val = - g_strdup_printf("[%s] %s", inhibit->inhibit_id, inhibit->reason); + g_strdup_printf("[%s] %s", + inhibit->inhibit_id, + inhibit->reason != NULL ? inhibit->reason : ""); fwupd_codec_string_append(str, idt, "Inhibit", val); } } @@ -5022,6 +5506,44 @@ } /** + * fu_device_get_id_display: + * @self: a #FuDevice + * + * This gets the device ID suffixed with the name if set. + * + * Returns: a string value, or %NULL for invalid. + * + * Since: 2.0.17 + **/ +gchar * +fu_device_get_id_display(FuDevice *self) +{ + FuDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GString) str = g_string_new(NULL); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_DEVICE(self), NULL); + + if (!fu_device_ensure_id(self, &error_local)) + g_debug("ignoring: %s", error_local->message); + if (fu_device_get_id(self) != NULL) + g_string_append(str, fu_device_get_id(self)); + /* nocheck:blocked */ + if (priv->name != NULL) { + if (str->len > 0) + g_string_append(str, " "); + g_string_append_printf(str, "[%s]", priv->name); + } else if (fu_device_get_plugin(self) != NULL) { + if (str->len > 0) + g_string_append(str, " "); + g_string_append_printf(str, "{%s}", fu_device_get_plugin(self)); + } + if (str->len == 0) + return NULL; + return g_string_free(g_steal_pointer(&str), FALSE); +} + +/** * fu_device_set_context: * @self: a #FuDevice * @ctx: (nullable): optional #FuContext @@ -5041,9 +5563,8 @@ #ifndef SUPPORTED_BUILD if (priv->ctx != NULL && ctx == NULL) { - g_critical("clearing device context for %s [%s]", - fu_device_get_name(self), - fu_device_get_id(self)); + g_autofree gchar *id_display = fu_device_get_id_display(self); + g_critical("clearing device context for %s", id_display); return; } #endif @@ -5143,7 +5664,7 @@ /* call vfunc */ str = fu_firmware_to_string(firmware); - g_info("installing onto %s:\n%s", fu_device_get_id(self), str); + g_info("%s", str); g_set_object(&priv->progress, progress); if (!device_class->write_firmware(self, firmware, priv->progress, flags, error)) return FALSE; @@ -5176,7 +5697,7 @@ * fu_device_prepare_firmware: * @self: a #FuDevice * @stream: a #GInputStream - * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @flags: #FuFirmwareParseFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE * @error: (nullable): optional return location for an error * * Prepares the firmware by calling an optional device-specific vfunc for the @@ -5195,7 +5716,7 @@ fu_device_prepare_firmware(FuDevice *self, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self); @@ -5256,6 +5777,7 @@ * fu_device_read_firmware: * @self: a #FuDevice * @progress: a #FuProgress + * @flags: #FuFirmwareParseFlags, e.g. %FU_FIRMWARE_PARSE_FLAG_NONE * @error: (nullable): optional return location for an error * * Reads firmware from the device by calling a plugin-specific vfunc. @@ -5267,10 +5789,13 @@ * * Returns: (transfer full): a #FuFirmware, or %NULL for error * - * Since: 1.0.8 + * Since: 2.0.11, although a simpler version was added in 1.0.8 **/ FuFirmware * -fu_device_read_firmware(FuDevice *self, FuProgress *progress, GError **error) +fu_device_read_firmware(FuDevice *self, + FuProgress *progress, + FuFirmwareParseFlags flags, + GError **error) { FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self); FuDevicePrivate *priv = GET_PRIVATE(self); @@ -5280,15 +5805,6 @@ g_return_val_if_fail(FU_IS_PROGRESS(progress), NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); - /* device does not support reading for verification CRCs */ - if (!fu_device_has_flag(self, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "reading firmware is not supported by device"); - return NULL; - } - /* call vfunc */ g_set_object(&priv->progress, progress); if (device_class->read_firmware != NULL) @@ -5300,7 +5816,7 @@ return NULL; if (priv->firmware_gtype != G_TYPE_INVALID) { g_autoptr(FuFirmware) firmware = g_object_new(priv->firmware_gtype, NULL); - if (!fu_firmware_parse_bytes(firmware, fw, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_bytes(firmware, fw, 0x0, flags, error)) return NULL; return g_steal_pointer(&firmware); } @@ -5557,13 +6073,13 @@ /* probe */ if (!fu_device_probe(self, error)) { - g_prefix_error(error, "failed to probe: "); + g_prefix_error_literal(error, "failed to probe: "); return FALSE; } /* ensure the device ID is already setup */ if (!fu_device_ensure_id(self, error)) { - g_prefix_error(error, "failed to ensure ID: "); + g_prefix_error_literal(error, "failed to ensure ID: "); return FALSE; } @@ -5576,12 +6092,12 @@ FU_DEVICE_RETRY_OPEN_DELAY, NULL, error)) { - g_prefix_error(error, "failed to retry subclass open: "); + g_prefix_error_literal(error, "failed to retry subclass open: "); return FALSE; } } else { if (!device_class->open(self, error)) { - g_prefix_error(error, "failed to subclass open: "); + g_prefix_error_literal(error, "failed to subclass open: "); return FALSE; } } @@ -5589,13 +6105,13 @@ /* setup */ if (!fu_device_setup(self, error)) { - g_prefix_error(error, "failed to setup: "); + g_prefix_error_literal(error, "failed to setup: "); return FALSE; } /* ensure the device ID is still valid */ if (!fu_device_ensure_id(self, error)) { - g_prefix_error(error, "failed to ensure ID: "); + g_prefix_error_literal(error, "failed to ensure ID: "); return FALSE; } @@ -5635,7 +6151,7 @@ g_return_val_if_fail(error == NULL || *error == NULL, FALSE); /* skip */ - if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_IS_FAKE)) { + if (fu_device_has_private_flag_quark(self, quarks[QUARK_IS_FAKE])) { fu_device_add_private_flag(self, FU_DEVICE_PRIVATE_FLAG_IS_OPEN); if (!fu_device_probe(self, error)) return FALSE; @@ -5646,25 +6162,15 @@ /* use parent */ if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN)) { - FuDevice *parent = fu_device_get_parent(self); - if (parent == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no parent device"); + FuDevice *parent = fu_device_get_parent(self, error); + if (parent == NULL) return FALSE; - } return fu_device_open_internal(parent, error); } if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN)) { - FuDevice *proxy = fu_device_get_proxy(self); - if (proxy == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no proxy device"); + FuDevice *proxy = fu_device_get_proxy(self, error); + if (proxy == NULL) return FALSE; - } if (!fu_device_open_internal(proxy, error)) return FALSE; } @@ -5678,13 +6184,8 @@ FuDevicePrivate *priv = GET_PRIVATE(self); /* not yet open */ - if (priv->open_refcount == 0) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "cannot close device, refcount already zero"); - return FALSE; - } + if (priv->open_refcount == 0) + return TRUE; if (!g_atomic_int_dec_and_test(&priv->open_refcount)) return TRUE; @@ -5729,7 +6230,7 @@ g_return_val_if_fail(error == NULL || *error == NULL, FALSE); /* skip */ - if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_IS_FAKE)) { + if (fu_device_has_private_flag_quark(self, quarks[QUARK_IS_FAKE])) { fu_device_remove_private_flag(self, FU_DEVICE_PRIVATE_FLAG_IS_OPEN); return TRUE; } @@ -5740,25 +6241,15 @@ /* use parent */ if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN)) { - FuDevice *parent = fu_device_get_parent(self); - if (parent == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no parent device"); + FuDevice *parent = fu_device_get_parent(self, error); + if (parent == NULL) return FALSE; - } return fu_device_close_internal(parent, error); } if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN)) { - FuDevice *proxy = fu_device_get_proxy(self); - if (proxy == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no proxy device"); + FuDevice *proxy = fu_device_get_proxy(self, error); + if (proxy == NULL) return FALSE; - } if (!fu_device_close_internal(proxy, error)) return FALSE; } @@ -5794,7 +6285,7 @@ return TRUE; /* device self-assigned */ - if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_NO_PROBE)) { + if (fu_device_has_private_flag_quark(self, quarks[QUARK_NO_PROBE])) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not probing"); return FALSE; } @@ -5806,7 +6297,7 @@ } /* vfunc skipped device */ - if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_NO_PROBE)) { + if (fu_device_has_private_flag_quark(self, quarks[QUARK_NO_PROBE])) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not probing"); return FALSE; } @@ -5915,6 +6406,7 @@ fu_device_convert_instance_ids(FuDevice *self) { FuDevicePrivate *priv = GET_PRIVATE(self); + gboolean no_generic_guids; g_return_if_fail(FU_IS_DEVICE(self)); @@ -5923,14 +6415,13 @@ return; /* convert the FuDevice IDs to FwupdDevice IDs */ + no_generic_guids = fu_device_has_private_flag_quark(self, quarks[QUARK_NO_GENERIC_GUIDS]); if (priv->instance_ids != NULL) { for (guint i = 0; i < priv->instance_ids->len; i++) { FuDeviceInstanceIdItem *item = g_ptr_array_index(priv->instance_ids, i); if ((item->flags & FU_DEVICE_INSTANCE_FLAG_VISIBLE) == 0) continue; - if ((item->flags & FU_DEVICE_INSTANCE_FLAG_GENERIC) > 0 && - fu_device_has_private_flag(self, - FU_DEVICE_PRIVATE_FLAG_NO_GENERIC_GUIDS)) + if ((item->flags & FU_DEVICE_INSTANCE_FLAG_GENERIC) > 0 && no_generic_guids) continue; if (item->instance_id != NULL) fwupd_device_add_instance_id(FWUPD_DEVICE(self), item->instance_id); @@ -5967,7 +6458,7 @@ g_return_val_if_fail(error == NULL || *error == NULL, FALSE); /* skip */ - if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_IS_FAKE)) { + if (fu_device_has_private_flag_quark(self, quarks[QUARK_IS_FAKE])) { fu_device_convert_instance_ids(self); return TRUE; } @@ -5987,7 +6478,7 @@ } /* vfunc skipped device */ - if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_NO_PROBE)) { + if (fu_device_has_private_flag_quark(self, quarks[QUARK_NO_PROBE])) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not probing"); return FALSE; } @@ -6295,15 +6786,15 @@ fu_device_incorporate_instance_ids(FuDevice *self, FuDevice *donor) { FuDevicePrivate *priv_donor = GET_PRIVATE(donor); + gboolean no_generic_guids; if (priv_donor->instance_ids == NULL) return; + no_generic_guids = fu_device_has_private_flag_quark(self, quarks[QUARK_NO_GENERIC_GUIDS]); for (guint i = 0; i < priv_donor->instance_ids->len; i++) { FuDeviceInstanceIdItem *item = g_ptr_array_index(priv_donor->instance_ids, i); - if ((item->flags & FU_DEVICE_INSTANCE_FLAG_GENERIC) > 0 && - fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_NO_GENERIC_GUIDS)) { + if ((item->flags & FU_DEVICE_INSTANCE_FLAG_GENERIC) > 0 && no_generic_guids) continue; - } if (item->instance_id != NULL) fu_device_add_instance_id_full(self, item->instance_id, item->flags); else @@ -6342,7 +6833,7 @@ if (fu_device_get_id(self) != NULL) priv->device_id_valid = TRUE; /* remove the baseclass-added serial number and GUIDs if set */ - if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_NO_SERIAL_NUMBER)) + if (fu_device_has_private_flag_quark(self, quarks[QUARK_NO_SERIAL_NUMBER])) fwupd_device_set_serial(FWUPD_DEVICE(self), NULL); } if (flag & FU_DEVICE_INCORPORATE_FLAG_VENDOR) { @@ -6451,6 +6942,17 @@ fu_device_add_possible_plugin(self, possible_plugin); } } + if (flag & FU_DEVICE_INCORPORATE_FLAG_INSTANCE_KEYS) { + if (priv_donor->instance_hash != NULL) { + GHashTableIter iter; + gpointer key, value; + g_hash_table_iter_init(&iter, priv_donor->instance_hash); + while (g_hash_table_iter_next(&iter, &key, &value)) { + if (fu_device_get_instance_str(self, key) == NULL) + fu_device_add_instance_str(self, key, value); + } + } + } /* everything else */ if (flag == FU_DEVICE_INCORPORATE_FLAG_ALL) { @@ -6460,13 +6962,13 @@ /* copy from donor FuDevice if has not already been set */ if (priv_donor->private_flags != NULL) { + fu_device_register_private_flags(self); for (guint i = 0; i < priv_donor->private_flags->len; i++) { - GRefString *flag_tmp = - g_ptr_array_index(priv_donor->private_flags, i); - if (g_ptr_array_find(priv->private_flags_registered, - flag_tmp, - NULL)) { - fu_device_add_private_flag(self, flag_tmp); + GQuark flag_quark = + g_array_index(priv_donor->private_flags, GQuark, i); + if (fu_device_private_flags_has_registered_quark(self, + flag_quark)) { + fu_device_add_private_flag_quark(self, flag_quark); } } } @@ -6476,15 +6978,20 @@ fu_device_set_modified_usec(self, priv_donor->modified_usec); if (priv->equivalent_id == NULL && fu_device_get_equivalent_id(donor) != NULL) fu_device_set_equivalent_id(self, fu_device_get_equivalent_id(donor)); - if (priv->fwupd_version == NULL && fu_device_get_fwupd_version(donor) != NULL) - fu_device_set_fwupd_version(self, fu_device_get_fwupd_version(donor)); + if (priv->fwupd_version == NULL && priv_donor->fwupd_version != NULL) + fu_device_set_fwupd_version(self, priv_donor->fwupd_version); + if (priv_donor->required_free > 0) + fu_device_set_required_free(self, priv_donor->required_free); if (priv->update_request_id == NULL && priv_donor->update_request_id != NULL) fu_device_set_update_request_id(self, priv_donor->update_request_id); - if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY) && - fu_device_has_private_flag(donor, FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY)) { + if (fu_device_has_private_flag_quark(self, quarks[QUARK_REFCOUNTED_PROXY]) && + fu_device_has_private_flag_quark(donor, quarks[QUARK_REFCOUNTED_PROXY])) { if (priv->proxy == NULL && priv_donor->proxy != NULL) fu_device_set_proxy(self, priv_donor->proxy); } + if (priv->proxy_gtype == G_TYPE_INVALID && + priv_donor->proxy_gtype != G_TYPE_INVALID) + fu_device_set_proxy_gtype(self, priv_donor->proxy_gtype); if (priv->proxy_guid == NULL && priv_donor->proxy_guid != NULL) fu_device_set_proxy_guid(self, priv_donor->proxy_guid); if (priv->custom_flags == NULL && priv_donor->custom_flags != NULL) @@ -6517,22 +7024,11 @@ } } - /* copy all instance ID keys if not already set */ - if (priv_donor->instance_hash != NULL) { - GHashTableIter iter; - gpointer key, value; - g_hash_table_iter_init(&iter, priv_donor->instance_hash); - while (g_hash_table_iter_next(&iter, &key, &value)) { - if (fu_device_get_instance_str(self, key) == NULL) - fu_device_add_instance_str(self, key, value); - } - } - /* 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); + fu_device_add_guid_quirks(self, guid, FU_DEVICE_INSTANCE_FLAG_NONE); } } } @@ -6878,6 +7374,19 @@ g_return_if_fail(FU_IS_DEVICE(self)); g_return_if_fail(XB_IS_NODE(rel)); + /* set the required free */ + if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_SET_REQUIRED_FREE)) { + guint64 size; + size = xb_node_query_text_as_uint(rel, + "artifacts/artifact/size[@type='installed']", + NULL); + if (size != G_MAXUINT64) { + fu_device_set_required_free(self, size); + fu_device_remove_private_flag(self, + FU_DEVICE_PRIVATE_FLAG_MD_SET_REQUIRED_FREE); + } + } + /* optionally filter by device checksum */ if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_MD_ONLY_CHECKSUM)) { gboolean valid = FALSE; @@ -6934,26 +7443,26 @@ /* nag the developer */ if (fwupd_request_has_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE) && !fu_device_has_request_flag(self, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE)) { + g_autofree gchar *id_display = fu_device_get_id_display(self); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "request %s emitted but device %s [%s] does not set " + "request %s emitted but device %s does not set " "FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE", fwupd_request_get_id(request), - fu_device_get_id(self), - fu_device_get_plugin(self)); + id_display); return FALSE; } if (!fwupd_request_has_flag(request, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE) && !fu_device_has_request_flag(self, FWUPD_REQUEST_FLAG_NON_GENERIC_MESSAGE)) { + g_autofree gchar *id_display = fu_device_get_id_display(self); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "request %s is not a GENERIC_MESSAGE and device %s [%s] does not set " + "request %s is not a GENERIC_MESSAGE and device %s does not set " "FWUPD_REQUEST_FLAG_NON_GENERIC_MESSAGE", fwupd_request_get_id(request), - fu_device_get_id(self), - fu_device_get_plugin(self)); + id_display); return FALSE; } #endif @@ -7024,29 +7533,6 @@ } static void -fu_device_flags_notify_cb(FuDevice *self, GParamSpec *pspec, gpointer user_data) -{ - FuDevicePrivate *priv = GET_PRIVATE(self); - - /* emulated device reinstalling do not need a replug or shutdown */ - if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_EMULATED) && - fu_device_has_flag(self, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) { - g_debug("removing needs-reboot for emulated device"); - fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); - } - if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_EMULATED) && - fu_device_has_flag(self, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) { - g_debug("removing needs-shutdown for emulated device"); - fu_device_remove_flag(self, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN); - } - - /* we only inhibit when the flags contains UPDATABLE, and that might be discovered by - * probing the hardware *after* the battery level has been set */ - if (priv->inhibits != NULL) - fu_device_ensure_inhibits(self); -} - -static void fu_device_ensure_instance_hash(FuDevice *self) { FuDevicePrivate *priv = GET_PRIVATE(self); @@ -7124,6 +7610,9 @@ if (tmp->len > 0 && tmp->str[tmp->len - 1] == '-') g_string_truncate(tmp, tmp->len - 1); + /* trim both ends */ + fu_string_strip(tmp); + /* nothing left! */ if (tmp->len == 0) return NULL; @@ -7257,44 +7746,44 @@ } /** - * fu_device_build_instance_id: + * fu_device_build_instance_id_strv: * @self: a #FuDevice - * @error: (nullable): optional return location for an error * @subsystem: (not nullable): subsystem, e.g. `NVME` - * @...: pairs of string key values, ending with %NULL + * @keys: (nullable): %NULL terminated array of string keys + * @error: (nullable): optional return location for an error * - * Creates an instance ID from a prefix and some key values. + * Creates an instance ID from a prefix and an array of key values. * If the key value cannot be found, the parent and then proxy is also queried. * * If any of the key values remain unset then no instance ID is added. * * fu_device_add_instance_str(dev, "VID", "1234"); * fu_device_add_instance_u16(dev, "PID", 5678); - * if (!fu_device_build_instance_id(dev, &error, "NVME", "VID", "PID", NULL)) + * const gchar *keys[] = {"VID", "PID", NULL}; + * if (!fu_device_build_instance_id_strv(dev, "NVME", keys, &error)) * g_warning("failed to add ID: %s", error->message); * * Returns: %TRUE if the instance ID was added. * - * Since: 1.7.7 + * Since: 2.0.14 **/ gboolean -fu_device_build_instance_id(FuDevice *self, GError **error, const gchar *subsystem, ...) +fu_device_build_instance_id_strv(FuDevice *self, + const gchar *subsystem, + gchar **keys, + GError **error) { - FuDevice *parent = fu_device_get_parent(self); + FuDevice *parent = fu_device_get_parent_internal(self); FuDevicePrivate *priv = GET_PRIVATE(self); - gboolean ret = TRUE; - va_list args; g_autoptr(GString) str = g_string_new(subsystem); g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); g_return_val_if_fail(subsystem != NULL, FALSE); - va_start(args, subsystem); - for (guint i = 0;; i++) { - const gchar *key = va_arg(args, const gchar *); + for (guint i = 0; keys != NULL && keys[i] != NULL; i++) { + const gchar *key = keys[i]; const gchar *value; - if (key == NULL) - break; + value = fu_device_get_instance_str(self, key); if (value == NULL && parent != NULL) value = fu_device_get_instance_str(parent, key); @@ -7306,17 +7795,11 @@ FWUPD_ERROR_INVALID_DATA, "no value for %s", key); - ret = FALSE; - break; + return FALSE; } g_string_append(str, i == 0 ? "\\" : "&"); g_string_append_printf(str, "%s_%s", key, value); } - va_end(args); - - /* we set an error above */ - if (!ret) - return FALSE; /* success */ fu_device_add_instance_id(self, str->str); @@ -7324,6 +7807,52 @@ } /** + * fu_device_build_instance_id: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * @subsystem: (not nullable): subsystem, e.g. `NVME` + * @...: pairs of string key values, ending with %NULL + * + * Creates an instance ID from a prefix and some key values. + * If the key value cannot be found, the parent and then proxy is also queried. + * + * If any of the key values remain unset then no instance ID is added. + * + * fu_device_add_instance_str(dev, "VID", "1234"); + * fu_device_add_instance_u16(dev, "PID", 5678); + * if (!fu_device_build_instance_id(dev, &error, "NVME", "VID", "PID", NULL)) + * g_warning("failed to add ID: %s", error->message); + * + * Returns: %TRUE if the instance ID was added. + * + * Since: 1.7.7 + **/ +gboolean +fu_device_build_instance_id(FuDevice *self, GError **error, const gchar *subsystem, ...) +{ + va_list args; + g_autoptr(GStrvBuilder) keys_builder = g_strv_builder_new(); + g_auto(GStrv) keys = NULL; + + g_return_val_if_fail(FU_IS_DEVICE(self), FALSE); + g_return_val_if_fail(subsystem != NULL, FALSE); + + /* convert varargs to GStrv */ + va_start(args, subsystem); + for (;;) { + const gchar *key = va_arg(args, const gchar *); + if (key == NULL) + break; + g_strv_builder_add(keys_builder, key); + } + va_end(args); + keys = g_strv_builder_end(keys_builder); + + /* call the strv version */ + return fu_device_build_instance_id_strv(self, subsystem, keys, error); +} + +/** * fu_device_build_instance_id_full: * @self: a #FuDevice * @flags: instance ID flags, e.g. %FU_DEVICE_INSTANCE_FLAG_QUIRKS @@ -7340,7 +7869,7 @@ **/ gboolean fu_device_build_instance_id_full(FuDevice *self, - FuDeviceInstanceFlag flags, + FuDeviceInstanceFlags flags, GError **error, const gchar *subsystem, ...) @@ -7416,7 +7945,7 @@ /* if the device is a child of the host firmware then add those GUIDs too */ if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD)) { - FuDevice *msf_device = fu_device_get_parent(self); + FuDevice *msf_device = fu_device_get_parent_internal(self); if (msf_device != NULL) { GPtrArray *guids = fu_device_get_guids(msf_device); for (guint i = 0; i < guids->len; i++) { @@ -7524,7 +8053,11 @@ /* sanity check */ if (priv->events == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no events loaded"); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no event with ID %s: no events loaded", + id); return NULL; } @@ -7540,26 +8073,18 @@ FuDeviceEvent *event = g_ptr_array_index(priv->events, i); if (g_strcmp0(fu_device_event_get_id(event), id_hash) == 0) { priv->event_idx = i + 1; + g_debug("found event with ID %s [%s]", id, id_hash); return event; } } - /* look for *any* event that matches */ - for (guint i = 0; i < priv->events->len; i++) { - FuDeviceEvent *event = g_ptr_array_index(priv->events, i); - if (g_strcmp0(fu_device_event_get_id(event), id_hash) == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "found out-of-order event %s at position %u", - id, - i); - return NULL; - } - } - /* nothing found */ - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no event with ID %s", id); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no event with ID %s [%s]", + id, + id_hash); return NULL; } @@ -7642,10 +8167,11 @@ g_set_object(&priv->target, target); } -static void -fu_device_add_json(FwupdCodec *codec, JsonBuilder *builder, FwupdCodecFlags flags) +/* private; used to save an emulated device */ +void +fu_device_add_json(FuDevice *self, JsonBuilder *builder, FwupdCodecFlags flags) { - FuDevice *self = FU_DEVICE(codec); + FuDevicePrivate *priv = GET_PRIVATE(self); FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self); if (fu_device_get_created_usec(self) != 0) { @@ -7662,27 +8188,26 @@ } /* subclassed */ - if (device_class->add_json != NULL) + if (device_class->add_json != NULL) { device_class->add_json(self, builder, flags); + return; + } + + /* proxy */ + if (priv->proxy != NULL) { + device_class = FU_DEVICE_GET_CLASS(priv->proxy); + if (device_class->add_json != NULL) + device_class->add_json(priv->proxy, builder, flags); + } } -static gboolean -fu_device_from_json(FwupdCodec *codec, JsonNode *json_node, GError **error) +/* private; used to load an emulated device */ +gboolean +fu_device_from_json(FuDevice *self, JsonObject *json_object, GError **error) { - FuDevice *self = FU_DEVICE(codec); - JsonObject *json_object; const gchar *tmp; FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(self); - - /* sanity check */ - if (!JSON_NODE_HOLDS_OBJECT(json_node)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "not JSON object"); - return FALSE; - } - json_object = json_node_get_object(json_node); + FuDevicePrivate *priv = GET_PRIVATE(self); tmp = json_object_get_string_member_with_default(json_object, "Created", NULL); if (tmp != NULL) { @@ -7698,9 +8223,14 @@ } /* subclassed */ - if (device_class->from_json != NULL) { - if (!device_class->from_json(self, json_object, error)) - return FALSE; + if (device_class->from_json != NULL) + return device_class->from_json(self, json_object, error); + + /* proxy */ + if (priv->proxy != NULL) { + device_class = FU_DEVICE_GET_CLASS(priv->proxy); + if (device_class->from_json != NULL) + return device_class->from_json(priv->proxy, json_object, error); } /* success */ @@ -7708,13 +8238,6 @@ } static void -fu_device_codec_iface_init(FwupdCodecInterface *iface) -{ - iface->add_json = fu_device_add_json; - iface->from_json = fu_device_from_json; -} - -static void fu_device_dispose(GObject *object) { FuDevice *self = FU_DEVICE(object); @@ -7730,6 +8253,13 @@ GObjectClass *object_class = G_OBJECT_CLASS(klass); FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); GParamSpec *pspec; + const gchar *quark_flags[] = { + FU_DEVICE_PRIVATE_FLAG_NO_PROBE, + FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY, + FU_DEVICE_PRIVATE_FLAG_NO_GENERIC_GUIDS, + FU_DEVICE_PRIVATE_FLAG_NO_SERIAL_NUMBER, + FU_DEVICE_PRIVATE_FLAG_IS_FAKE, + }; object_class->dispose = fu_device_dispose; object_class->finalize = fu_device_finalize; @@ -7737,7 +8267,10 @@ object_class->set_property = fu_device_set_property; device_class->to_string = fu_device_to_string_impl; - device_class->register_flags = fu_device_register_flags; + + /* used as device flags, order is important! */ + for (guint i = 0; i < G_N_ELEMENTS(quark_flags); i++) + quarks[i] = g_quark_from_static_string(quark_flags[i]); /** * FuDevice::child-added: @@ -7983,6 +8516,22 @@ 0, G_PARAM_READWRITE | G_PARAM_STATIC_NAME); g_object_class_install_property(object_class, PROP_PID, pspec); + + /** + * FuDevice:required-free: + * + * The required amount of free firmware space. + * + * Since: 2.0.12 + */ + pspec = g_param_spec_uint64("required-free", + NULL, + NULL, + 0, + G_MAXUINT64, + 0, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_REQUIRED_FREE, pspec); } static void @@ -7992,10 +8541,9 @@ priv->order = G_MAXINT; priv->possible_plugins = g_ptr_array_new_with_free_func(g_free); priv->acquiesce_delay = 50; /* ms */ - priv->notify_flags_handler_id = g_signal_connect(FWUPD_DEVICE(self), - "notify::flags", - G_CALLBACK(fu_device_flags_notify_cb), - NULL); + priv->private_flags_registered = g_array_new(FALSE, FALSE, sizeof(GQuark)); + priv->private_flags = g_array_new(FALSE, FALSE, sizeof(GQuark)); + priv->proxy_gtype = G_TYPE_INVALID; } static void @@ -8009,7 +8557,7 @@ if (priv->proxy != NULL) { if (priv->notify_flags_proxy_id != 0) g_signal_handler_disconnect(priv->proxy, priv->notify_flags_proxy_id); - if (fu_device_has_private_flag(self, FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY)) { + if (fu_device_has_private_flag_quark(self, quarks[QUARK_REFCOUNTED_PROXY])) { g_object_unref(priv->proxy); } else { g_object_remove_weak_pointer(G_OBJECT(priv->proxy), @@ -8038,10 +8586,8 @@ g_ptr_array_unref(priv->instance_ids); if (priv->parent_guids != NULL) g_ptr_array_unref(priv->parent_guids); - if (priv->private_flags != NULL) - g_ptr_array_unref(priv->private_flags); - if (priv->private_flags_registered != NULL) - g_ptr_array_unref(priv->private_flags_registered); + g_array_unref(priv->private_flags); + g_array_unref(priv->private_flags_registered); g_ptr_array_unref(priv->possible_plugins); g_free(priv->equivalent_id); g_free(priv->physical_id); @@ -8052,6 +8598,7 @@ g_free(priv->update_image); g_free(priv->fwupd_version); g_free(priv->proxy_guid); + g_free(priv->name); g_free(priv->custom_flags); G_OBJECT_CLASS(fu_device_parent_class)->finalize(object); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-device.h fwupd-2.0.20/libfwupdplugin/fu-device.h --- fwupd-2.0.8/libfwupdplugin/fu-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,6 @@ #include "fu-context.h" #include "fu-device-event.h" -#include "fu-device-locker.h" #include "fu-device-struct.h" #include "fu-firmware.h" #include "fu-progress.h" @@ -45,7 +44,7 @@ FuFirmware *(*prepare_firmware)(FuDevice *self, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error)G_GNUC_WARN_UNUSED_RESULT; gboolean (*set_quirk_kv)(FuDevice *self, const gchar *key, @@ -90,7 +89,6 @@ void (*set_progress)(FuDevice *self, FuProgress *progress); void (*invalidate)(FuDevice *self); gchar *(*convert_version)(FuDevice *self, guint64 version_raw); - void (*register_flags)(FuDevice *self); void (*add_json)(FuDevice *self, JsonBuilder *builder, FwupdCodecFlags flags); gboolean (*from_json)(FuDevice *self, JsonObject *json_object, @@ -264,6 +262,14 @@ * Since: 2.0.6 **/ FU_DEVICE_INCORPORATE_FLAG_GTYPE = 1ull << 19, + /** + * FU_DEVICE_INCORPORATE_FLAG_INSTANCE_KEYS: + * + * Set the device instance keys. + * + * Since: 2.0.9 + **/ + FU_DEVICE_INCORPORATE_FLAG_INSTANCE_KEYS = 1ull << 20, /*< private >*/ FU_DEVICE_INCORPORATE_FLAG_ALL = G_MAXUINT64, } FuDeviceIncorporateFlags; @@ -831,12 +837,306 @@ * Since: 2.0.7 */ #define FU_DEVICE_PRIVATE_FLAG_INSTALL_LOOP_RESTART "install-loop-restart" +/** + * FU_DEVICE_PRIVATE_FLAG_MD_SET_REQUIRED_FREE: + * + * Set the device required free size from the metadata if available. + * + * Since: 2.0.12 + */ +#define FU_DEVICE_PRIVATE_FLAG_MD_SET_REQUIRED_FREE "md-set-required-free" +/** + * FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX: + * + * Use the parent device as the name prefix. + * + * Since: 2.0.15 + */ +#define FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX "parent-name-prefix" +/** + * FU_DEVICE_PRIVATE_FLAG_LAZY_VERFMT: + * + * Allow the device version to be specified as an integer in the release XML. + * + * NOTE: This has only ever been a best-effort fallback for ESRT-derived UEFI devices. + * + * Since: 2.0.18 + */ +#define FU_DEVICE_PRIVATE_FLAG_LAZY_VERFMT "lazy-verfmt" +/** + * FU_DEVICE_PRIVATE_FLAG_NO_VERSION_EXPECTED: + * + * Allow the device version to be unset. + * + * NOTE: This should only be used when writing to "raw" CFI devices, such as standalone SPI + * programmers such as CH341A. CFI devices should be used as a proxy devices rather than + * installable targets in most fwupd plugins. + * + * Since: 2.0.18 + */ +#define FU_DEVICE_PRIVATE_FLAG_NO_VERSION_EXPECTED "no-version-expected" + +/* standard icons */ + +/** + * FU_DEVICE_ICON_COMPUTER: + * + * A generic icon of a computer. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_COMPUTER "computer" + +/** + * FU_DEVICE_ICON_GPU: + * + * A generic icon of a gpu. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_GPU "gpu" + +/** + * FU_DEVICE_ICON_AC_ADAPTER: + * + * A generic icon of an AC adapter. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_AC_ADAPTER "ac-adapter" + +/** + * FU_DEVICE_ICON_DOCK: + * + * A generic icon of a dock. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_DOCK "dock" + +/** + * FU_DEVICE_ICON_DOCK_USB: + * + * An icon of a dock attached via USB. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_DOCK_USB "dock-usb" + +/** + * FU_DEVICE_ICON_USB_HUB: + * + * An icon of a hub of USB devices. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_USB_HUB "usb-hub" + +/** + * FU_DEVICE_ICON_THUNDERBOLT: + * + * A generic icon of a thunderbolt device. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_THUNDERBOLT "thunderbolt" + +/** + * FU_DEVICE_ICON_MEMORY: + * + * A generic icon of a memory card. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_MEMORY "media-memory" + +/** + * FU_DEVICE_ICON_MODEM: + * + * An icon of a modem. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_MODEM "modem" + +/** + * FU_DEVICE_ICON_NETWORK_WIRED: + * + * A generic icon of a wired connection. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_NETWORK_WIRED "network-wired" + +/** + * FU_DEVICE_ICON_NETWORK_WIRELESS: + * + * A generic icon of a wireless connection. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_NETWORK_WIRELESS "network-wireless" + +/** + * FU_DEVICE_ICON_DRIVE_HARDDISK: + * + * A generic icon of a hard disk drive. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_DRIVE_HARDDISK "drive-harddisk" + +/** + * FU_DEVICE_ICON_DRIVE_SSD: + * + * A generic icon of a hard disk drive. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_DRIVE_SSD "drive-harddisk-solidstate" + +/** + * FU_DEVICE_ICON_DRIVE_MULTIDISK: + * + * A generic icon of a multidisk drive. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_DRIVE_MULTIDISK "drive-multidisk" + +/** + * FU_DEVICE_ICON_AUDIO_CARD: + * + * An icon of an audio card. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_AUDIO_CARD "audio-card" + +/** + * FU_DEVICE_ICON_VIDEO_DISPLAY: + * + * A generic icon of a display. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_VIDEO_DISPLAY "video-display" + +/** + * FU_DEVICE_ICON_INPUT_TOUCHPAD: + * + * A generic icon of a touchpad. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_INPUT_TOUCHPAD "input-touchpad" + +/** + * FU_DEVICE_ICON_INPUT_KEYBOARD: + * + * A generic icon of a keyboard. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_INPUT_KEYBOARD "input-keyboard" + +/** + * FU_DEVICE_ICON_INPUT_MOUSE: + * + * A generic icon of a mouse. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_INPUT_MOUSE "input-mouse" + +/** + * FU_DEVICE_ICON_INPUT_DIALPAD: + * + * A generic icon of a dialpad. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_INPUT_DIALPAD "input-dialpad" + +/** + * FU_DEVICE_ICON_INPUT_GAMING: + * + * A generic icon of a gaming controller. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_INPUT_GAMING "input-gaming" + +/** + * FU_DEVICE_ICON_INPUT_TABLET: + * + * A generic icon of a drawing tablet. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_INPUT_TABLET "input-tablet" + +/** + * FU_DEVICE_ICON_AUTH_FINGERPRINT: + * + * A generic icon of a fingerprint reader. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_AUTH_FINGERPRINT "auth-fingerprint" + +/** + * FU_DEVICE_ICON_USB_RECEIVER: + * + * A generic icon of an USB receiver. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_USB_RECEIVER "usb-receiver" + +/** + * FU_DEVICE_ICON_PDA: + * + * A generic icon of a PDA. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_PDA "pda" + +/** + * FU_DEVICE_ICON_WEB_CAMERA: + * + * A generic icon of a web camera. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_WEB_CAMERA "camera-web" + +/** + * FU_DEVICE_ICON_VIDEO_CAMERA: + * + * A generic icon of a video camera. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_VIDEO_CAMERA "camera-video" + +/** + * FU_DEVICE_ICON_APPLICATION_CERTIFICATE: + * + * A generic icon of a certificate. + * + * Since: 2.0.10 + */ +#define FU_DEVICE_ICON_APPLICATION_CERTIFICATE "application-certificate" /* accessors */ gchar * fu_device_to_string(FuDevice *self) G_GNUC_NON_NULL(1); void fu_device_add_string(FuDevice *self, guint idt, GString *str) G_GNUC_NON_NULL(1, 3); +gchar * +fu_device_get_id_display(FuDevice *self) G_GNUC_NON_NULL(1); const gchar * fu_device_get_equivalent_id(FuDevice *self) G_GNUC_NON_NULL(1); void @@ -846,15 +1146,16 @@ void fu_device_add_instance_id(FuDevice *self, const gchar *instance_id) G_GNUC_NON_NULL(1, 2); gboolean -fu_device_has_instance_id(FuDevice *self, const gchar *instance_id, FuDeviceInstanceFlag flags) +fu_device_has_instance_id(FuDevice *self, const gchar *instance_id, FuDeviceInstanceFlags flags) G_GNUC_NON_NULL(1, 2); void -fu_device_add_instance_id_full(FuDevice *self, const gchar *instance_id, FuDeviceInstanceFlag flags) - G_GNUC_NON_NULL(1, 2); +fu_device_add_instance_id_full(FuDevice *self, + const gchar *instance_id, + FuDeviceInstanceFlags flags) G_GNUC_NON_NULL(1, 2); FuDevice * fu_device_get_root(FuDevice *self) G_GNUC_NON_NULL(1); FuDevice * -fu_device_get_parent(FuDevice *self) G_GNUC_NON_NULL(1); +fu_device_get_parent(FuDevice *self, GError **error) G_GNUC_NON_NULL(1); FuDevice * fu_device_get_backend_parent(FuDevice *self, GError **error) G_GNUC_NON_NULL(1); FuDevice * @@ -862,6 +1163,9 @@ G_GNUC_NON_NULL(1); GPtrArray * fu_device_get_children(FuDevice *self) G_GNUC_NON_NULL(1); +FuDevice * +fu_device_get_child_by_logical_id(FuDevice *self, const gchar *logical_id, GError **error) + G_GNUC_NON_NULL(1); void fu_device_add_child(FuDevice *self, FuDevice *child) G_GNUC_NON_NULL(1, 2); void @@ -873,7 +1177,7 @@ void fu_device_add_parent_backend_id(FuDevice *self, const gchar *backend_id) G_GNUC_NON_NULL(1, 2); FuDevice * -fu_device_get_proxy(FuDevice *self) G_GNUC_NON_NULL(1); +fu_device_get_proxy(FuDevice *self, GError **error) G_GNUC_NON_NULL(1); void fu_device_set_proxy(FuDevice *self, FuDevice *proxy) G_GNUC_NON_NULL(1); FuDevice * @@ -966,6 +1270,10 @@ fu_device_get_firmware_size_min(FuDevice *self) G_GNUC_NON_NULL(1); guint64 fu_device_get_firmware_size_max(FuDevice *self) G_GNUC_NON_NULL(1); +guint64 +fu_device_get_required_free(FuDevice *self) G_GNUC_NON_NULL(1); +void +fu_device_set_required_free(FuDevice *self, guint64 required_free) G_GNUC_NON_NULL(1); guint fu_device_get_battery_level(FuDevice *self) G_GNUC_NON_NULL(1); void @@ -1011,6 +1319,8 @@ fu_device_get_specialized_gtype(FuDevice *self) G_GNUC_NON_NULL(1); GType fu_device_get_proxy_gtype(FuDevice *self) G_GNUC_NON_NULL(1); +void +fu_device_set_proxy_gtype(FuDevice *self, GType gtype) G_GNUC_NON_NULL(1); GType fu_device_get_firmware_gtype(FuDevice *self) G_GNUC_NON_NULL(1); void @@ -1027,11 +1337,12 @@ fu_device_prepare_firmware(FuDevice *self, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2, 3); FuFirmware * fu_device_read_firmware(FuDevice *self, FuProgress *progress, + FuFirmwareParseFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); GBytes * fu_device_dump_firmware(FuDevice *self, @@ -1144,16 +1455,31 @@ GBytes *blob, FuProgress *progress, GError **error) G_GNUC_NON_NULL(1, 2, 3); +gchar * +fu_device_get_contents(FuDevice *self, + const gchar *filename, + gsize count, + FuProgress *progress, + GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); GBytes * fu_device_get_contents_bytes(FuDevice *self, const gchar *filename, + gsize count, FuProgress *progress, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); gboolean fu_device_query_file_exists(FuDevice *self, const gchar *filename, gboolean *exists, GError **error) G_GNUC_NON_NULL(1, 2, 3); +const gchar * +fu_device_get_smbios_string(FuDevice *self, + guint8 type, + guint8 length, + guint8 offset, + GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); void fu_device_add_possible_plugin(FuDevice *self, const gchar *plugin) G_GNUC_NON_NULL(1, 2); +gboolean +fu_device_check_fwupd_version(FuDevice *self, const gchar *fwupd_version) G_GNUC_NON_NULL(1, 2); const gchar * fu_device_get_instance_str(FuDevice *self, const gchar *key) G_GNUC_NON_NULL(1, 2); @@ -1175,11 +1501,16 @@ void fu_device_add_instance_u32(FuDevice *self, const gchar *key, guint32 value) G_GNUC_NON_NULL(1, 2); gboolean +fu_device_build_instance_id_strv(FuDevice *self, + const gchar *subsystem, + gchar **keys, + GError **error) G_GNUC_NON_NULL(1, 2); +gboolean fu_device_build_instance_id(FuDevice *self, GError **error, const gchar *subsystem, ...) G_GNUC_NULL_TERMINATED G_GNUC_NON_NULL(1, 3); gboolean fu_device_build_instance_id_full(FuDevice *self, - FuDeviceInstanceFlag flags, + FuDeviceInstanceFlags flags, GError **error, const gchar *subsystem, ...) G_GNUC_NULL_TERMINATED G_GNUC_NON_NULL(1, 4); @@ -1187,10 +1518,12 @@ fu_device_build_vendor_id(FuDevice *self, const gchar *prefix, const gchar *value); void fu_device_build_vendor_id_u16(FuDevice *self, const gchar *prefix, guint16 value); -FuDeviceLocker * -fu_device_poll_locker_new(FuDevice *self, GError **error) G_GNUC_NON_NULL(1); FuDeviceEvent * fu_device_save_event(FuDevice *self, const gchar *id); FuDeviceEvent * fu_device_load_event(FuDevice *self, const gchar *id, GError **error); +void +fu_device_add_event(FuDevice *self, FuDeviceEvent *event); +GPtrArray * +fu_device_get_events(FuDevice *self); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-device.rs fwupd-2.0.20/libfwupdplugin/fu-device.rs --- fwupd-2.0.8/libfwupdplugin/fu-device.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-device.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,11 +1,12 @@ // Copyright 2024 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later -#[derive(ToBitString)] -enum FuDeviceInstanceFlag { +#[derive(ToString)] +enum FuDeviceInstanceFlags { None = 0, Visible = 1 << 0, Quirks = 1 << 1, Generic = 1 << 2, // added by a baseclass Counterpart = 1 << 3, + Deprecated = 1 << 4, } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-dfu-firmware-private.h fwupd-2.0.20/libfwupdplugin/fu-dfu-firmware-private.h --- fwupd-2.0.8/libfwupdplugin/fu-dfu-firmware-private.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-dfu-firmware-private.h 2026-02-26 11:36:18.000000000 +0000 @@ -16,5 +16,5 @@ gboolean fu_dfu_firmware_parse_footer(FuDfuFirmware *self, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-dfu-firmware.c fwupd-2.0.20/libfwupdplugin/fu-dfu-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-dfu-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-dfu-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,6 @@ #include #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-crc.h" #include "fu-dfu-firmware-private.h" @@ -216,13 +215,13 @@ gboolean fu_dfu_firmware_parse_footer(FuDfuFirmware *self, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); gsize bufsz; const guint8 *buf; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructDfuFtr) st = NULL; g_autoptr(GBytes) fw = NULL; fw = fu_input_stream_read_bytes(stream, 0, G_MAXSIZE, NULL, error); @@ -241,7 +240,7 @@ priv->footer_len = fu_struct_dfu_ftr_get_len(st); /* verify the checksum */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint32 crc_new = fu_crc32(FU_CRC_KIND_B32_JAMCRC, buf, bufsz - 4); if (fu_struct_dfu_ftr_get_crc(st) != crc_new) { g_set_error(error, @@ -272,7 +271,7 @@ static gboolean fu_dfu_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuDfuFirmware *self = FU_DFU_FIRMWARE(firmware); @@ -299,7 +298,7 @@ { FuDfuFirmwarePrivate *priv = GET_PRIVATE(self); g_autoptr(GByteArray) buf = g_byte_array_new(); - g_autoptr(GByteArray) st = fu_struct_dfu_ftr_new(); + g_autoptr(FuStructDfuFtr) st = fu_struct_dfu_ftr_new(); /* add the raw firmware data, the footer-less-CRC, and only then the CRC */ fu_byte_array_append_bytes(buf, contents); @@ -307,7 +306,7 @@ fu_struct_dfu_ftr_set_pid(st, priv->pid); fu_struct_dfu_ftr_set_vid(st, priv->vid); fu_struct_dfu_ftr_set_ver(st, priv->dfu_version); - g_byte_array_append(buf, st->data, st->len - sizeof(guint32)); + g_byte_array_append(buf, st->buf->data, st->buf->len - sizeof(guint32)); fu_byte_array_append_uint32(buf, fu_crc32(FU_CRC_KIND_B32_JAMCRC, buf->data, buf->len), G_LITTLE_ENDIAN); @@ -323,10 +322,10 @@ /* can only contain one image */ if (images->len > 1) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "DFU only supports writing one image"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "DFU only supports writing one image"); return NULL; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-dfu-firmware.rs fwupd-2.0.20/libfwupdplugin/fu-dfu-firmware.rs --- fwupd-2.0.8/libfwupdplugin/fu-dfu-firmware.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-dfu-firmware.rs 2026-02-26 11:36:18.000000000 +0000 @@ -13,7 +13,7 @@ crc: u32le, } -#[derive(New, ValidateStream, ParseStream, Default)] +#[derive(New, ValidateStream, ParseStream, ToBytes, Default)] #[repr(C, packed)] struct FuStructDfuseHdr { sig: [char; 5] == "DfuSe", @@ -22,7 +22,7 @@ targets: u8, } -#[derive(New, Validate, ParseStream, Default)] +#[derive(New, Validate, ParseStream, ToBytes, Default)] #[repr(C, packed)] struct FuStructDfuseImage { sig: [char; 6] == "Target", @@ -33,7 +33,7 @@ chunks: u32le, } -#[derive(New, ParseStream)] +#[derive(New, ParseStream, ToBytes)] #[repr(C, packed)] struct FuStructDfuseElement { address: u32le, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-dfuse-firmware.c fwupd-2.0.20/libfwupdplugin/fu-dfuse-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-dfuse-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-dfuse-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,12 +8,7 @@ #include "config.h" -#include - #include "fu-byte-array.h" -#include "fu-bytes.h" -#include "fu-chunk-private.h" -#include "fu-common.h" #include "fu-dfu-firmware-private.h" #include "fu-dfu-firmware-struct.h" #include "fu-dfuse-firmware.h" @@ -36,14 +31,14 @@ GError **error) { g_autoptr(FuChunk) chk = NULL; - g_autoptr(GByteArray) st_ele = NULL; + g_autoptr(FuStructDfuseElement) st_ele = NULL; g_autoptr(GBytes) blob = NULL; /* create new chunk */ st_ele = fu_struct_dfuse_element_parse_stream(stream, *offset, error); if (st_ele == NULL) return NULL; - *offset += st_ele->len; + *offset += st_ele->buf->len; blob = fu_input_stream_read_bytes(stream, *offset, fu_struct_dfuse_element_get_size(st_ele), @@ -67,7 +62,7 @@ { guint chunks; g_autoptr(FuFirmware) image = fu_firmware_new(); - g_autoptr(GByteArray) st_img = NULL; + g_autoptr(FuStructDfuseImage) st_img = NULL; /* verify image signature */ st_img = fu_struct_dfuse_image_parse_stream(stream, *offset, error); @@ -92,7 +87,7 @@ } /* parse chunks */ - *offset += st_img->len; + *offset += st_img->buf->len; for (guint j = 0; j < chunks; j++) { g_autoptr(FuChunk) chk = NULL; chk = fu_dfuse_firmware_image_chunk_parse(self, stream, offset, error); @@ -114,14 +109,14 @@ static gboolean fu_dfuse_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuDfuFirmware *dfu_firmware = FU_DFU_FIRMWARE(firmware); gsize offset = 0; gsize streamsz = 0; guint8 targets = 0; - g_autoptr(GByteArray) st_hdr = NULL; + g_autoptr(FuStructDfuseHdr) st_hdr = NULL; /* DFU footer first */ if (!fu_dfu_firmware_parse_footer(dfu_firmware, stream, flags, error)) @@ -150,7 +145,7 @@ /* parse the image targets */ targets = fu_struct_dfuse_hdr_get_targets(st_hdr); - offset += st_hdr->len; + offset += st_hdr->buf->len; for (guint i = 0; i < targets; i++) { g_autoptr(FuFirmware) image = NULL; image = fu_dfuse_firmware_image_parse_stream(FU_DFUSE_FIRMWARE(firmware), @@ -159,7 +154,7 @@ error); if (image == NULL) return FALSE; - if (!fu_firmware_add_image_full(firmware, image, error)) + if (!fu_firmware_add_image(firmware, image, error)) return FALSE; } return TRUE; @@ -168,18 +163,18 @@ static GBytes * fu_dfuse_firmware_chunk_write(FuDfuseFirmware *self, FuChunk *chk) { - g_autoptr(GByteArray) st_ele = fu_struct_dfuse_element_new(); + g_autoptr(FuStructDfuseElement) st_ele = fu_struct_dfuse_element_new(); fu_struct_dfuse_element_set_address(st_ele, fu_chunk_get_address(chk)); fu_struct_dfuse_element_set_size(st_ele, fu_chunk_get_data_sz(chk)); - g_byte_array_append(st_ele, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); - return g_bytes_new(st_ele->data, st_ele->len); + g_byte_array_append(st_ele->buf, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + return fu_struct_dfuse_element_to_bytes(st_ele); } static GBytes * fu_dfuse_firmware_write_image(FuDfuseFirmware *self, FuFirmware *image, GError **error) { gsize totalsz = 0; - g_autoptr(GByteArray) st_img = fu_struct_dfuse_image_new(); + g_autoptr(FuStructDfuseImage) st_img = fu_struct_dfuse_image_new(); g_autoptr(GPtrArray) blobs = NULL; g_autoptr(GPtrArray) chunks = NULL; @@ -210,9 +205,9 @@ /* copy data */ for (guint i = 0; i < blobs->len; i++) { GBytes *blob = g_ptr_array_index(blobs, i); - fu_byte_array_append_bytes(st_img, blob); + fu_byte_array_append_bytes(st_img->buf, blob); } - return g_bytes_new(st_img->data, st_img->len); + return fu_struct_dfuse_image_to_bytes(st_img); } static GByteArray * @@ -220,7 +215,7 @@ { FuDfuseFirmware *self = FU_DFUSE_FIRMWARE(firmware); gsize totalsz = 0; - g_autoptr(GByteArray) st_hdr = fu_struct_dfuse_hdr_new(); + g_autoptr(FuStructDfuseHdr) st_hdr = fu_struct_dfuse_hdr_new(); g_autoptr(GBytes) blob_noftr = NULL; g_autoptr(GPtrArray) blobs = NULL; g_autoptr(GPtrArray) images = NULL; @@ -239,7 +234,7 @@ } /* DfuSe header */ - fu_struct_dfuse_hdr_set_image_size(st_hdr, st_hdr->len + totalsz); + fu_struct_dfuse_hdr_set_image_size(st_hdr, st_hdr->buf->len + totalsz); if (images->len > G_MAXUINT8) { g_set_error(error, FWUPD_ERROR, @@ -253,11 +248,11 @@ /* copy images */ for (guint i = 0; i < blobs->len; i++) { GBytes *blob = g_ptr_array_index(blobs, i); - fu_byte_array_append_bytes(st_hdr, blob); + fu_byte_array_append_bytes(st_hdr->buf, blob); } /* return blob */ - blob_noftr = g_bytes_new(st_hdr->data, st_hdr->len); + blob_noftr = fu_struct_dfuse_hdr_to_bytes(st_hdr); return fu_dfu_firmware_append_footer(FU_DFU_FIRMWARE(firmware), blob_noftr, error); } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-dpaux-device.c fwupd-2.0.20/libfwupdplugin/fu-dpaux-device.c --- fwupd-2.0.8/libfwupdplugin/fu-dpaux-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-dpaux-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,6 @@ #include "fu-dpaux-struct.h" #include "fu-dump.h" #include "fu-io-channel.h" -#include "fu-string.h" /** * FuDpauxDevice @@ -100,7 +99,8 @@ FuDpauxDevice *self = FU_DPAUX_DEVICE(device); FuDpauxDevicePrivate *priv = GET_PRIVATE(self); guint8 buf[FU_STRUCT_DPAUX_DPCD_SIZE] = {0x0}; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructDpauxDpcd) st = NULL; + /* ignore all Framework FRANDGCP07 BIOS version 3.02 */ if (fu_device_get_name(device) != NULL && g_str_has_prefix(fu_device_get_name(device), "AMDGPU DM") && @@ -120,7 +120,7 @@ sizeof(buf), FU_DPAUX_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "DPCD read failed: "); + g_prefix_error_literal(error, "DPCD read failed: "); return FALSE; } st = fu_struct_dpaux_dpcd_parse(buf, sizeof(buf), 0x0, error); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-drm-device-private.h fwupd-2.0.20/libfwupdplugin/fu-drm-device-private.h --- fwupd-2.0.8/libfwupdplugin/fu-drm-device-private.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-drm-device-private.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * Copyright 2024 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-drm-device.h" + +void +fu_drm_device_set_edid(FuDrmDevice *self, FuEdid *edid) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-drm-device.c fwupd-2.0.20/libfwupdplugin/fu-drm-device.c --- fwupd-2.0.8/libfwupdplugin/fu-drm-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-drm-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,8 +14,7 @@ #include "fu-bytes.h" #include "fu-common-struct.h" -#include "fu-drm-device.h" -#include "fu-string.h" +#include "fu-drm-device-private.h" /** * FuDrmDevice @@ -60,6 +59,10 @@ idt, "State", fu_display_state_to_string(priv->display_state)); + if (priv->edid != NULL) { + g_autofree gchar *xml = fu_firmware_to_string(FU_FIRMWARE(priv->edid)); + fwupd_codec_string_append(str, idt, "Edid", xml); + } } /** @@ -206,6 +209,23 @@ return priv->edid; } +/** + * fu_drm_device_set_edid: + * @self: a #FuDrmDevice + * @edid: a #FuEdid, or %NULL + * + * Sets the cached EDID onto the DRM device. + * + * Since: 2.0.14 + **/ +void +fu_drm_device_set_edid(FuDrmDevice *self, FuEdid *edid) +{ + FuDrmDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_DRM_DEVICE(self)); + g_set_object(&priv->edid, edid); +} + static gboolean fu_drm_device_probe(FuDevice *device, GError **error) { @@ -220,7 +240,10 @@ /* check if "card" is in the sysfs_path string */ if (g_strstr_len(sysfs_path, -1, "card") == NULL) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not a DRM card device"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not a DRM card device"); return FALSE; } @@ -272,10 +295,10 @@ if (!fu_firmware_parse_bytes(FU_FIRMWARE(edid), blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, error)) return FALSE; - g_set_object(&priv->edid, edid); + fu_drm_device_set_edid(self, edid); /* add instance ID */ fu_device_add_instance_str(device, "VEN", fu_edid_get_pnp_id(edid)); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-dummy-efivars.c fwupd-2.0.20/libfwupdplugin/fu-dummy-efivars.c --- fwupd-2.0.8/libfwupdplugin/fu-dummy-efivars.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-dummy-efivars.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,7 +13,7 @@ typedef struct { gchar *guid; gchar *name; - guint32 attr; + FuEfiVariableAttrs attr; GByteArray *buf; } FuDummyEfivarsKey; @@ -24,6 +24,8 @@ G_DEFINE_TYPE(FuDummyEfivars, fu_dummy_efivars, FU_TYPE_EFIVARS) +#define FU_DUMMY_EFIVARS_NVRAM_SIZE 10240 /* bytes */ + static void fu_dummy_efivars_key_free(FuDummyEfivarsKey *key) { @@ -109,7 +111,7 @@ const gchar *name, guint8 **data, gsize *data_sz, - guint32 *attr, + FuEfiVariableAttrs *attr, GError **error) { FuDummyEfivars *self = FU_DUMMY_EFIVARS(efivars); @@ -157,13 +159,24 @@ return total; } +static guint64 +fu_dummy_efivars_space_free(FuEfivars *efivars, GError **error) +{ + guint64 total; + + total = fu_dummy_efivars_space_used(efivars, error); + if (total == G_MAXUINT64) + return G_MAXUINT64; + return FU_DUMMY_EFIVARS_NVRAM_SIZE - total; +} + static gboolean fu_dummy_efivars_set_data(FuEfivars *efivars, const gchar *guid, const gchar *name, const guint8 *data, gsize sz, - guint32 attr, + FuEfiVariableAttrs attr, GError **error) { FuDummyEfivars *self = FU_DUMMY_EFIVARS(efivars); @@ -202,6 +215,7 @@ FuEfivarsClass *efivars_class = FU_EFIVARS_CLASS(klass); efivars_class->supported = fu_dummy_efivars_supported; efivars_class->space_used = fu_dummy_efivars_space_used; + efivars_class->space_free = fu_dummy_efivars_space_free; efivars_class->exists = fu_dummy_efivars_exists; efivars_class->get_data = fu_dummy_efivars_get_data; efivars_class->set_data = fu_dummy_efivars_set_data; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-dump.h fwupd-2.0.20/libfwupdplugin/fu-dump.h --- fwupd-2.0.8/libfwupdplugin/fu-dump.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-dump.h 2026-02-26 11:36:18.000000000 +0000 @@ -22,7 +22,7 @@ FU_DUMP_FLAGS_SHOW_ADDRESSES = 1 << 1, /*< private >*/ FU_DUMP_FLAGS_LAST -} FuDumpFlags; +} G_GNUC_FLAG_ENUM FuDumpFlags; void fu_dump_raw(const gchar *log_domain, const gchar *title, const guint8 *data, gsize len) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-edid.c fwupd-2.0.20/libfwupdplugin/fu-edid.c --- fwupd-2.0.8/libfwupdplugin/fu-edid.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-edid.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,7 +8,6 @@ #include "config.h" -#include "fu-byte-array.h" #include "fu-common.h" #include "fu-edid-struct.h" #include "fu-edid.h" @@ -135,6 +134,42 @@ } /** + * fu_edid_get_product_name: + * @self: a #FuEdid + * + * Gets the product name. + * + * Returns: string value, or %NULL for unset + * + * Since: 1.9.6 + **/ +const gchar * +fu_edid_get_product_name(FuEdid *self) +{ + g_return_val_if_fail(FU_IS_EDID(self), NULL); + return self->product_name; +} + +/** + * fu_edid_set_product_name: + * @self: a #FuEdid + * @product_name: (nullable): string value, or %NULL + * + * Sets the product name, e.g. `LG HDR WQHD`. + * + * Since: 2.0.14 + **/ +void +fu_edid_set_product_name(FuEdid *self, const gchar *product_name) +{ + g_return_if_fail(FU_IS_EDID(self)); + if (g_strcmp0(self->product_name, product_name) == 0) + return; + g_free(self->product_name); + self->product_name = g_strdup(product_name); +} + +/** * fu_edid_get_product_code: * @self: a #FuEdid * @@ -187,7 +222,7 @@ { gsize buf2sz = 0; const guint8 *buf2; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructEdidDescriptor) st = NULL; st = fu_struct_edid_descriptor_parse_stream(stream, offset, error); if (st == NULL) @@ -217,13 +252,16 @@ } static gboolean -fu_edid_parse(FuFirmware *firmware, GInputStream *stream, FwupdInstallFlags flags, GError **error) +fu_edid_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) { FuEdid *self = FU_EDID(firmware); const guint8 *manu_id; gsize offset = 0; g_autofree gchar *pnp_id = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructEdid) st = NULL; /* parse header */ st = fu_struct_edid_parse_stream(stream, offset, error); @@ -265,7 +303,7 @@ fu_edid_write(FuFirmware *firmware, GError **error) { FuEdid *self = FU_EDID(firmware); - g_autoptr(GByteArray) st = fu_struct_edid_new(); + g_autoptr(FuStructEdid) st = fu_struct_edid_new(); guint64 serial_number = 0; gsize offset_desc = FU_STRUCT_EDID_OFFSET_DATA_BLOCKS; @@ -282,21 +320,23 @@ /* store descriptors */ if (self->product_name != NULL) { - g_autoptr(GByteArray) st_desc = fu_struct_edid_descriptor_new(); + g_autoptr(FuStructEdidDescriptor) st_desc = fu_struct_edid_descriptor_new(); fu_struct_edid_descriptor_set_tag(st_desc, FU_EDID_DESCRIPTOR_TAG_DISPLAY_PRODUCT_NAME); if (!fu_struct_edid_descriptor_set_data(st_desc, (const guint8 *)self->product_name, strlen(self->product_name), error)) { - g_prefix_error(error, "cannot write product name: "); + g_prefix_error_literal(error, "cannot write product name: "); return NULL; } - memcpy(st->data + offset_desc, st_desc->data, st_desc->len); /* nocheck:blocked */ - offset_desc += st_desc->len; + memcpy(st->buf->data + offset_desc, + st_desc->buf->data, + st_desc->buf->len); /* nocheck:blocked */ + offset_desc += st_desc->buf->len; } if (self->serial_number != NULL) { - g_autoptr(GByteArray) st_desc = fu_struct_edid_descriptor_new(); + g_autoptr(FuStructEdidDescriptor) st_desc = fu_struct_edid_descriptor_new(); fu_struct_edid_descriptor_set_tag( st_desc, FU_EDID_DESCRIPTOR_TAG_DISPLAY_PRODUCT_SERIAL_NUMBER); @@ -304,29 +344,33 @@ (const guint8 *)self->serial_number, strlen(self->serial_number), error)) { - g_prefix_error(error, "cannot write serial number: "); + g_prefix_error_literal(error, "cannot write serial number: "); return NULL; } - memcpy(st->data + offset_desc, st_desc->data, st_desc->len); /* nocheck:blocked */ - offset_desc += st_desc->len; + memcpy(st->buf->data + offset_desc, + st_desc->buf->data, + st_desc->buf->len); /* nocheck:blocked */ + offset_desc += st_desc->buf->len; } if (self->eisa_id != NULL) { - g_autoptr(GByteArray) st_desc = fu_struct_edid_descriptor_new(); + g_autoptr(FuStructEdidDescriptor) st_desc = fu_struct_edid_descriptor_new(); fu_struct_edid_descriptor_set_tag(st_desc, FU_EDID_DESCRIPTOR_TAG_ALPHANUMERIC_DATA_STRING); if (!fu_struct_edid_descriptor_set_data(st_desc, (const guint8 *)self->eisa_id, strlen(self->eisa_id), error)) { - g_prefix_error(error, "cannot write EISA ID: "); + g_prefix_error_literal(error, "cannot write EISA ID: "); return NULL; } - memcpy(st->data + offset_desc, st_desc->data, st_desc->len); /* nocheck:blocked */ - offset_desc += st_desc->len; + memcpy(st->buf->data + offset_desc, + st_desc->buf->data, + st_desc->buf->len); /* nocheck:blocked */ + offset_desc += st_desc->buf->len; } /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static gboolean diff -Nru fwupd-2.0.8/libfwupdplugin/fu-edid.h fwupd-2.0.20/libfwupdplugin/fu-edid.h --- fwupd-2.0.8/libfwupdplugin/fu-edid.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-edid.h 2026-02-26 11:36:18.000000000 +0000 @@ -24,6 +24,10 @@ fu_edid_get_serial_number(FuEdid *self) G_GNUC_NON_NULL(1); void fu_edid_set_serial_number(FuEdid *self, const gchar *serial_number) G_GNUC_NON_NULL(1); +const gchar * +fu_edid_get_product_name(FuEdid *self) G_GNUC_NON_NULL(1); +void +fu_edid_set_product_name(FuEdid *self, const gchar *product_name) G_GNUC_NON_NULL(1); guint16 fu_edid_get_product_code(FuEdid *self) G_GNUC_NON_NULL(1); void diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-common.c fwupd-2.0.20/libfwupdplugin/fu-efi-common.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,12 +6,10 @@ #include "config.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-efi-common.h" #include "fu-efi-section.h" #include "fu-input-stream.h" -#include "fu-lzma-common.h" #include "fu-partial-input-stream.h" /** @@ -67,11 +65,12 @@ return "Section:Acpi2Table"; return NULL; } + /** * fu_efi_parse_sections: * @firmware: #FuFirmware * @stream: a #GInputStream - * @flags: flags + * @flags: #FuFirmwareParseFlags * @error: (nullable): optional return location for an error * * Parses a UEFI section. @@ -84,7 +83,7 @@ fu_efi_parse_sections(FuFirmware *firmware, GInputStream *stream, gsize offset, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize streamsz = 0; @@ -99,13 +98,13 @@ partial_stream = fu_partial_input_stream_new(stream, offset, streamsz - offset, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut payload: "); + g_prefix_error_literal(error, "failed to cut payload: "); return FALSE; } if (!fu_firmware_parse_stream(img, partial_stream, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) { g_prefix_error(error, "failed to parse section of size 0x%x: ", @@ -123,7 +122,7 @@ } fu_firmware_set_offset(img, offset); - if (!fu_firmware_add_image_full(firmware, img, error)) + if (!fu_firmware_add_image(firmware, img, error)) return FALSE; /* next! */ @@ -133,3 +132,63 @@ /* success */ return TRUE; } + +/** + * fu_efi_timestamp_export: + * @st: a #FuStructEfiTime + * @bn: a #XbBuilderNode + * + * Exports an `EFI_TIME` to XML. + * + * Since: 2.0.17 + **/ +void +fu_efi_timestamp_export(FuStructEfiTime *st, XbBuilderNode *bn) +{ + if (fu_struct_efi_time_get_year(st) != 0) + fu_xmlb_builder_insert_kx(bn, "year", fu_struct_efi_time_get_year(st)); + if (fu_struct_efi_time_get_month(st) != 0) + fu_xmlb_builder_insert_kx(bn, "month", fu_struct_efi_time_get_month(st)); + if (fu_struct_efi_time_get_day(st) != 0) + fu_xmlb_builder_insert_kx(bn, "day", fu_struct_efi_time_get_day(st)); + if (fu_struct_efi_time_get_hour(st) != 0) + fu_xmlb_builder_insert_kx(bn, "hour", fu_struct_efi_time_get_hour(st)); + if (fu_struct_efi_time_get_minute(st) != 0) + fu_xmlb_builder_insert_kx(bn, "minute", fu_struct_efi_time_get_minute(st)); + if (fu_struct_efi_time_get_second(st) != 0) + fu_xmlb_builder_insert_kx(bn, "second", fu_struct_efi_time_get_second(st)); +} + +/** + * fu_efi_timestamp_build: + * @st: a #FuStructEfiTime + * @n: a #XbNode + * + * Imports an `EFI_TIME` from XML. + * + * Since: 2.0.17 + **/ +void +fu_efi_timestamp_build(FuStructEfiTime *st, XbNode *n) +{ + guint64 tmp; + + tmp = xb_node_query_text_as_uint(n, "year", NULL); + if (tmp != G_MAXUINT64) + fu_struct_efi_time_set_year(st, tmp); + tmp = xb_node_query_text_as_uint(n, "month", NULL); + if (tmp != G_MAXUINT64) + fu_struct_efi_time_set_month(st, tmp); + tmp = xb_node_query_text_as_uint(n, "day", NULL); + if (tmp != G_MAXUINT64) + fu_struct_efi_time_set_day(st, tmp); + tmp = xb_node_query_text_as_uint(n, "hour", NULL); + if (tmp != G_MAXUINT64) + fu_struct_efi_time_set_hour(st, tmp); + tmp = xb_node_query_text_as_uint(n, "minute", NULL); + if (tmp != G_MAXUINT64) + fu_struct_efi_time_set_minute(st, tmp); + tmp = xb_node_query_text_as_uint(n, "second", NULL); + if (tmp != G_MAXUINT64) + fu_struct_efi_time_set_second(st, tmp); +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-common.h fwupd-2.0.20/libfwupdplugin/fu-efi-common.h --- fwupd-2.0.8/libfwupdplugin/fu-efi-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -6,6 +6,7 @@ #pragma once +#include "fu-efi-struct.h" #include "fu-firmware.h" #define FU_EFI_VOLUME_GUID_FFS1 "7a9354d9-0468-444a-81ce-0bf617d890df" @@ -36,5 +37,9 @@ fu_efi_parse_sections(FuFirmware *firmware, GInputStream *stream, gsize offset, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) G_GNUC_NON_NULL(1, 2); +void +fu_efi_timestamp_export(FuStructEfiTime *st, XbBuilderNode *bn) G_GNUC_NON_NULL(1, 2); +void +fu_efi_timestamp_build(FuStructEfiTime *st, XbNode *n) G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-device-path-list.c fwupd-2.0.20/libfwupdplugin/fu-efi-device-path-list.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-device-path-list.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-device-path-list.c 2026-02-26 11:36:18.000000000 +0000 @@ -68,7 +68,7 @@ static gboolean fu_efi_device_path_list_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize offset = 0; @@ -77,7 +77,7 @@ return FALSE; while (offset < streamsz) { g_autoptr(FuEfiDevicePath) efi_dp = NULL; - g_autoptr(GByteArray) st_dp = NULL; + g_autoptr(FuStructEfiDevicePath) st_dp = NULL; /* parse the header so we can work out what GType to create */ st_dp = fu_struct_efi_device_path_parse_stream(stream, offset, error); @@ -100,7 +100,7 @@ fu_firmware_set_offset(FU_FIRMWARE(efi_dp), offset); if (!fu_firmware_parse_stream(FU_FIRMWARE(efi_dp), stream, offset, flags, error)) return FALSE; - if (!fu_firmware_add_image_full(firmware, FU_FIRMWARE(efi_dp), error)) + if (!fu_firmware_add_image(firmware, FU_FIRMWARE(efi_dp), error)) return FALSE; offset += fu_firmware_get_size(FU_FIRMWARE(efi_dp)); } @@ -114,7 +114,7 @@ { g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); g_autoptr(GByteArray) buf = g_byte_array_new(); - g_autoptr(GByteArray) st_dp_end = NULL; + g_autoptr(FuStructEfiDevicePath) st_dp_end = NULL; /* add each image */ for (guint i = 0; i < imgs->len; i++) { @@ -129,7 +129,7 @@ st_dp_end = fu_struct_efi_device_path_new(); fu_struct_efi_device_path_set_type(st_dp_end, FU_EFI_DEVICE_PATH_TYPE_END); fu_struct_efi_device_path_set_subtype(st_dp_end, 0xFF); - g_byte_array_append(buf, st_dp_end->data, st_dp_end->len); + fu_byte_array_append_array(buf, st_dp_end->buf); /* success */ return g_steal_pointer(&buf); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-device-path.c fwupd-2.0.20/libfwupdplugin/fu-efi-device-path.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-device-path.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-device-path.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,6 @@ #include "config.h" #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-efi-device-path.h" #include "fu-efi-struct.h" @@ -91,14 +90,14 @@ static gboolean fu_efi_device_path_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuEfiDevicePath *self = FU_EFI_DEVICE_PATH(firmware); FuEfiDevicePathPrivate *priv = GET_PRIVATE(self); gsize dp_length; gsize streamsz = 0; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructEfiDevicePath) st = NULL; /* parse */ st = fu_struct_efi_device_path_parse_stream(stream, 0x0, error); @@ -125,9 +124,13 @@ fu_struct_efi_device_path_get_length(st), (guint)dp_length); } - if (dp_length > st->len) { - g_autoptr(GBytes) payload = - fu_input_stream_read_bytes(stream, st->len, dp_length - st->len, NULL, error); + if (dp_length > st->buf->len) { + g_autoptr(GBytes) payload = NULL; + payload = fu_input_stream_read_bytes(stream, + st->buf->len, + dp_length - st->buf->len, + NULL, + error); if (payload == NULL) return FALSE; fu_firmware_set_bytes(firmware, payload); @@ -143,7 +146,7 @@ { FuEfiDevicePath *self = FU_EFI_DEVICE_PATH(firmware); FuEfiDevicePathPrivate *priv = GET_PRIVATE(self); - g_autoptr(GByteArray) st = fu_struct_efi_device_path_new(); + g_autoptr(FuStructEfiDevicePath) st = fu_struct_efi_device_path_new(); g_autoptr(GBytes) payload = NULL; /* required */ @@ -152,11 +155,11 @@ return NULL; fu_struct_efi_device_path_set_type(st, fu_firmware_get_idx(firmware)); fu_struct_efi_device_path_set_subtype(st, priv->subtype); - fu_struct_efi_device_path_set_length(st, st->len + g_bytes_get_size(payload)); - fu_byte_array_append_bytes(st, payload); + fu_struct_efi_device_path_set_length(st, st->buf->len + g_bytes_get_size(payload)); + fu_byte_array_append_bytes(st->buf, payload); /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static gboolean diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-file.c fwupd-2.0.20/libfwupdplugin/fu-efi-file.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-file.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-file.c 2026-02-26 11:36:18.000000000 +0000 @@ -7,7 +7,6 @@ #include "config.h" #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-efi-common.h" #include "fu-efi-file.h" @@ -72,14 +71,14 @@ static gboolean fu_efi_file_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuEfiFile *self = FU_EFI_FILE(firmware); FuEfiFilePrivate *priv = GET_PRIVATE(self); guint32 size = 0x0; g_autofree gchar *guid_str = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructEfiFile) st = NULL; g_autoptr(GInputStream) partial_stream = NULL; /* parse */ @@ -103,14 +102,14 @@ return FALSE; } fu_struct_efi_file_unref(st); - st = fu_struct_efi_file2_parse_stream(stream, 0x0, error); + st = (FuStructEfiFile *)fu_struct_efi_file2_parse_stream(stream, 0x0, error); if (st == NULL) return FALSE; - size = fu_struct_efi_file2_get_extended_size(st); + size = fu_struct_efi_file2_get_extended_size((FuStructEfiFile2 *)st); } else { size = fu_struct_efi_file_get_size(st); } - if (size < st->len) { + if (size < st->buf->len) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -120,11 +119,11 @@ } /* verify header checksum */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint8 hdr_checksum_verify; g_autoptr(GBytes) hdr_blob = NULL; - hdr_blob = fu_input_stream_read_bytes(stream, 0x0, st->len, NULL, error); + hdr_blob = fu_input_stream_read_bytes(stream, 0x0, st->buf->len, NULL, error); if (hdr_blob == NULL) return FALSE; hdr_checksum_verify = fu_efi_file_hdr_checksum8(hdr_blob); @@ -140,15 +139,16 @@ } /* add simple blob */ - partial_stream = fu_partial_input_stream_new(stream, st->len, size - st->len, error); + partial_stream = + fu_partial_input_stream_new(stream, st->buf->len, size - st->buf->len, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut EFI blob: "); + g_prefix_error_literal(error, "failed to cut EFI blob: "); return FALSE; } /* verify data checksum */ if ((priv->attrib & FU_EFI_FILE_ATTRIB_CHECKSUM) > 0 && - (flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + (flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint8 data_checksum_verify = 0; if (!fu_input_stream_compute_sum8(partial_stream, &data_checksum_verify, error)) return FALSE; @@ -166,7 +166,7 @@ /* add sections */ if (priv->type != FU_EFI_FILE_TYPE_FFS_PAD && priv->type != FU_EFI_FILE_TYPE_RAW) { if (!fu_efi_parse_sections(firmware, partial_stream, 0, flags, error)) { - g_prefix_error(error, "failed to add firmware image: "); + g_prefix_error_literal(error, "failed to add firmware image: "); return FALSE; } } else { @@ -183,24 +183,24 @@ } static GBytes * -fu_efi_file_write_sections(FuFirmware *firmware, GError **error) +fu_efi_file_write_sections(FuEfiFile *self, GError **error) { - g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + g_autoptr(GPtrArray) images = fu_firmware_get_images(FU_FIRMWARE(self)); g_autoptr(GByteArray) buf = g_byte_array_new(); /* sanity check */ - if (fu_firmware_get_alignment(firmware) > FU_FIRMWARE_ALIGNMENT_1M) { + if (fu_firmware_get_alignment(FU_FIRMWARE(self)) > FU_FIRMWARE_ALIGNMENT_1M) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "alignment invalid, got 0x%02x", - fu_firmware_get_alignment(firmware)); + fu_firmware_get_alignment(FU_FIRMWARE(self))); return NULL; } /* no sections defined */ if (images->len == 0) - return fu_firmware_get_bytes_with_patches(firmware, error); + return fu_firmware_get_bytes_with_patches(FU_FIRMWARE(self), error); /* add each section */ for (guint i = 0; i < images->len; i++) { @@ -235,12 +235,12 @@ FuEfiFile *self = FU_EFI_FILE(firmware); FuEfiFilePrivate *priv = GET_PRIVATE(self); fwupd_guid_t guid = {0x0}; - g_autoptr(GByteArray) st = fu_struct_efi_file_new(); + g_autoptr(FuStructEfiFile) st = fu_struct_efi_file_new(); g_autoptr(GBytes) blob = NULL; g_autoptr(GBytes) hdr_blob = NULL; /* simple blob for now */ - blob = fu_efi_file_write_sections(firmware, error); + blob = fu_efi_file_write_sections(self, error); if (blob == NULL) return NULL; if (fu_firmware_get_id(firmware) != NULL) { @@ -255,15 +255,15 @@ fu_struct_efi_file_set_data_checksum(st, 0x100 - fu_sum8_bytes(blob)); fu_struct_efi_file_set_type(st, priv->type); fu_struct_efi_file_set_attrs(st, priv->attrib); - fu_struct_efi_file_set_size(st, g_bytes_get_size(blob) + st->len); + fu_struct_efi_file_set_size(st, g_bytes_get_size(blob) + st->buf->len); /* fix up header checksum */ - hdr_blob = g_bytes_new_static(st->data, st->len); + hdr_blob = g_bytes_new_static(st->buf->data, st->buf->len); fu_struct_efi_file_set_hdr_checksum(st, fu_efi_file_hdr_checksum8(hdr_blob)); /* success */ - fu_byte_array_append_bytes(st, blob); - return g_steal_pointer(&st); + fu_byte_array_append_bytes(st->buf, blob); + return g_steal_pointer(&st->buf); } static gboolean diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-filesystem.c fwupd-2.0.20/libfwupdplugin/fu-efi-filesystem.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-filesystem.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-filesystem.c 2026-02-26 11:36:18.000000000 +0000 @@ -7,7 +7,6 @@ #include "config.h" #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-efi-file.h" #include "fu-efi-filesystem.h" #include "fu-input-stream.h" @@ -29,7 +28,7 @@ static gboolean fu_efi_filesystem_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize offset = 0; @@ -59,19 +58,19 @@ } stream_tmp = fu_partial_input_stream_new(stream, offset, streamsz - offset, error); if (stream_tmp == NULL) { - g_prefix_error(error, "failed to cut EFI file: "); + g_prefix_error_literal(error, "failed to cut EFI file: "); return FALSE; } if (!fu_firmware_parse_stream(img, stream_tmp, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) { g_prefix_error(error, "failed to parse EFI file at 0x%x: ", (guint)offset); return FALSE; } fu_firmware_set_offset(firmware, offset); - if (!fu_firmware_add_image_full(firmware, img, error)) + if (!fu_firmware_add_image(firmware, img, error)) return FALSE; /* next! */ @@ -128,11 +127,13 @@ static void fu_efi_filesystem_init(FuEfiFilesystem *self) { +#ifdef HAVE_FUZZER /* if fuzzing, artificially limit the number of files to avoid using large amounts of RSS * when printing the FuEfiFilesystem XML output */ - fu_firmware_set_images_max( - FU_FIRMWARE(self), - g_getenv("FWUPD_FUZZER_RUNNING") == NULL ? FU_EFI_FILESYSTEM_FILES_MAX : 50); + fu_firmware_set_images_max(FU_FIRMWARE(self), 50); +#else + fu_firmware_set_images_max(FU_FIRMWARE(self), FU_EFI_FILESYSTEM_FILES_MAX); +#endif fu_firmware_set_alignment(FU_FIRMWARE(self), FU_FIRMWARE_ALIGNMENT_8); g_type_ensure(FU_TYPE_EFI_FILE); } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-ftw-store.c fwupd-2.0.20/libfwupdplugin/fu-efi-ftw-store.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-ftw-store.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-ftw-store.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,160 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#define G_LOG_DOMAIN "FuEfiFtwStore" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-common.h" +#include "fu-efi-ftw-store.h" +#include "fu-efi-struct.h" + +/** + * FuEfiFtwStore: + * + * A fault tolerant working block store, as found in EDK2 NVRAM blocks. + * + * See also: [class@FuFirmware] + */ + +struct _FuEfiFtwStore { + FuFirmware parent_instance; + FuEfiVariableStoreState state; +}; + +G_DEFINE_TYPE(FuEfiFtwStore, fu_efi_ftw_store, FU_TYPE_FIRMWARE) + +static void +fu_efi_ftw_store_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuEfiFtwStore *self = FU_EFI_FTW_STORE(firmware); + if (self->state != FU_EFI_VARIABLE_STORE_STATE_UNSET) { + const gchar *str = fu_efi_variable_store_state_to_string(self->state); + fu_xmlb_builder_insert_kv(bn, "state", str); + } +} + +static gboolean +fu_efi_ftw_store_validate(FuFirmware *firmware, GInputStream *stream, gsize offset, GError **error) +{ + return fu_struct_efi_fault_tolerant_working_block_header64_validate_stream(stream, + offset, + error); +} + +static gboolean +fu_efi_ftw_store_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ + FuEfiFtwStore *self = FU_EFI_FTW_STORE(firmware); + g_autoptr(FuStructEfiFaultTolerantWorkingBlockHeader64) st = NULL; + g_autoptr(GBytes) blob = NULL; + + st = fu_struct_efi_fault_tolerant_working_block_header64_parse_stream(stream, 0x0, error); + if (st == NULL) + return FALSE; + + /* sanity check */ + if (fu_struct_efi_fault_tolerant_working_block_header64_get_write_queue_size(st) > + fu_firmware_get_size_max(firmware)) { + g_set_error( + error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "FTW store larger than max size: 0x%x > 0x%x", + (guint)fu_struct_efi_fault_tolerant_working_block_header64_get_write_queue_size( + st), + (guint)fu_firmware_get_size_max(firmware)); + return FALSE; + } + + /* attributes we care about */ + self->state = fu_struct_efi_fault_tolerant_working_block_header64_get_state(st); + + /* data area */ + blob = fu_input_stream_read_bytes( + stream, + st->buf->len, + fu_struct_efi_fault_tolerant_working_block_header64_get_write_queue_size(st), + NULL, + error); + if (blob == NULL) + return FALSE; + fu_firmware_set_bytes(firmware, blob); + fu_firmware_set_size(firmware, st->buf->len + g_bytes_get_size(blob)); + + /* success */ + return TRUE; +} + +static GByteArray * +fu_efi_ftw_store_write(FuFirmware *firmware, GError **error) +{ + FuEfiFtwStore *self = FU_EFI_FTW_STORE(firmware); + g_autoptr(FuStructEfiFaultTolerantWorkingBlockHeader64) st = + fu_struct_efi_fault_tolerant_working_block_header64_new(); + g_autoptr(GBytes) blob = NULL; + + /* get blob */ + blob = fu_firmware_get_bytes(firmware, error); + if (blob == NULL) + return NULL; + + /* CRC32 */ + fu_struct_efi_fault_tolerant_working_block_header64_set_write_queue_size( + st, + g_bytes_get_size(blob)); + fu_struct_efi_fault_tolerant_working_block_header64_set_crc( + st, + fu_crc32(FU_CRC_KIND_B32_STANDARD, st->buf->data, st->buf->len)); + + /* attrs + data area */ + fu_struct_efi_fault_tolerant_working_block_header64_set_state(st, self->state); + fu_byte_array_append_bytes(st->buf, blob); + + /* success */ + return g_steal_pointer(&st->buf); +} + +static gboolean +fu_efi_ftw_store_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuEfiFtwStore *self = FU_EFI_FTW_STORE(firmware); + const gchar *tmp; + + /* simple properties */ + tmp = xb_node_query_text(n, "state", NULL); + if (tmp != NULL) + self->state = fu_efi_variable_store_state_from_string(tmp); + + /* success */ + return TRUE; +} + +static void +fu_efi_ftw_store_init(FuEfiFtwStore *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE); +#ifdef HAVE_FUZZER + fu_firmware_set_size_max(FU_FIRMWARE(self), 0x1000); /* 4KB */ +#else + fu_firmware_set_size_max(FU_FIRMWARE(self), 0x1000000); /* 16MB */ +#endif +} + +static void +fu_efi_ftw_store_class_init(FuEfiFtwStoreClass *klass) +{ + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + firmware_class->validate = fu_efi_ftw_store_validate; + firmware_class->parse = fu_efi_ftw_store_parse; + firmware_class->export = fu_efi_ftw_store_export; + firmware_class->write = fu_efi_ftw_store_write; + firmware_class->build = fu_efi_ftw_store_build; +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-ftw-store.h fwupd-2.0.20/libfwupdplugin/fu-efi-ftw-store.h --- fwupd-2.0.8/libfwupdplugin/fu-efi-ftw-store.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-ftw-store.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_EFI_FTW_STORE (fu_efi_ftw_store_get_type()) +G_DECLARE_FINAL_TYPE(FuEfiFtwStore, fu_efi_ftw_store, FU, EFI_FTW_STORE, FuFirmware) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-hard-drive-device-path.c fwupd-2.0.20/libfwupdplugin/fu-efi-hard-drive-device-path.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-hard-drive-device-path.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-hard-drive-device-path.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,8 +8,6 @@ #include "config.h" -#include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-efi-hard-drive-device-path.h" #include "fu-efi-struct.h" @@ -93,11 +91,11 @@ static gboolean fu_efi_hard_drive_device_path_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuEfiHardDriveDevicePath *self = FU_EFI_HARD_DRIVE_DEVICE_PATH(firmware); - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructEfiHardDriveDevicePath) st = NULL; /* re-parse */ st = fu_struct_efi_hard_drive_device_path_parse_stream(stream, 0x0, error); @@ -113,7 +111,7 @@ self->signature_type = fu_struct_efi_hard_drive_device_path_get_signature_type(st); /* success */ - fu_firmware_set_size(firmware, fu_struct_efi_device_path_get_length(st)); + fu_firmware_set_size(firmware, st->buf->len); return TRUE; } @@ -121,7 +119,7 @@ fu_efi_hard_drive_device_path_write(FuFirmware *firmware, GError **error) { FuEfiHardDriveDevicePath *self = FU_EFI_HARD_DRIVE_DEVICE_PATH(firmware); - g_autoptr(GByteArray) st = fu_struct_efi_hard_drive_device_path_new(); + g_autoptr(FuStructEfiHardDriveDevicePath) st = fu_struct_efi_hard_drive_device_path_new(); /* required */ fu_struct_efi_hard_drive_device_path_set_partition_number(st, self->partition_number); @@ -133,7 +131,7 @@ fu_struct_efi_hard_drive_device_path_set_signature_type(st, self->signature_type); /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static gboolean diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-load-option.c fwupd-2.0.20/libfwupdplugin/fu-efi-load-option.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-load-option.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-load-option.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,11 +11,8 @@ #include "fu-byte-array.h" #include "fu-common.h" #include "fu-efi-device-path-list.h" -#include "fu-efi-file-path-device-path.h" -#include "fu-efi-hard-drive-device-path.h" #include "fu-efi-load-option.h" #include "fu-input-stream.h" -#include "fu-mem.h" #include "fu-string.h" struct _FuEfiLoadOption { @@ -191,7 +188,7 @@ st_item = fu_struct_shim_hive_item_parse_stream(stream, offset, error); if (st_item == NULL) return FALSE; - offset += st_item->len; + offset += st_item->buf->len; /* key */ keysz = fu_struct_shim_hive_item_get_key_length(st_item); @@ -292,7 +289,7 @@ static gboolean fu_efi_load_option_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(firmware); @@ -301,14 +298,14 @@ g_autofree gchar *id = NULL; g_autoptr(FuEfiDevicePathList) device_path_list = fu_efi_device_path_list_new(); g_autoptr(GByteArray) buf_utf16 = g_byte_array_new(); - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructEfiLoadOption) st = NULL; /* parse header */ st = fu_struct_efi_load_option_parse_stream(stream, offset, error); if (st == NULL) return FALSE; self->attrs = fu_struct_efi_load_option_get_attrs(st); - offset += st->len; + offset += st->buf->len; /* parse UTF-16 description */ if (!fu_input_stream_size(stream, &streamsz, error)) @@ -338,7 +335,7 @@ /* parse dp blob */ if (!fu_firmware_parse_stream(FU_FIRMWARE(device_path_list), stream, offset, flags, error)) return FALSE; - if (!fu_firmware_add_image_full(firmware, FU_FIRMWARE(device_path_list), error)) + if (!fu_firmware_add_image(firmware, FU_FIRMWARE(device_path_list), error)) return FALSE; offset += fu_struct_efi_load_option_get_dp_size(st); @@ -377,22 +374,24 @@ fu_struct_shim_hive_item_set_key_length(st_item, keysz); fu_struct_shim_hive_item_set_value_length(st_item, value_safe->len); if (keysz > 0) - g_byte_array_append(st_item, (const guint8 *)key, keysz); + g_byte_array_append(st_item->buf, (const guint8 *)key, keysz); if (value_safe->len > 0) { - g_byte_array_append(st_item, + g_byte_array_append(st_item->buf, (const guint8 *)value_safe->str, value_safe->len); } /* add to hive */ - g_byte_array_append(st, st_item->data, st_item->len); + fu_byte_array_append_array(st->buf, st_item->buf); } /* this covers all items, and so has to be done last */ - fu_struct_shim_hive_set_crc32(st, fu_crc32(FU_CRC_KIND_B32_STANDARD, st->data, st->len)); + fu_struct_shim_hive_set_crc32( + st, + fu_crc32(FU_CRC_KIND_B32_STANDARD, st->buf->data, st->buf->len)); /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static GByteArray * @@ -419,7 +418,7 @@ { FuEfiLoadOption *self = FU_EFI_LOAD_OPTION(firmware); g_autoptr(GByteArray) buf_utf16 = NULL; - g_autoptr(GByteArray) st = fu_struct_efi_load_option_new(); + g_autoptr(FuStructEfiLoadOption) st = fu_struct_efi_load_option_new(); g_autoptr(GBytes) dpbuf = NULL; /* header */ @@ -439,14 +438,14 @@ error); if (buf_utf16 == NULL) return NULL; - g_byte_array_append(st, buf_utf16->data, buf_utf16->len); + g_byte_array_append(st->buf, buf_utf16->data, buf_utf16->len); /* dpbuf */ dpbuf = fu_firmware_get_image_by_gtype_bytes(firmware, FU_TYPE_EFI_DEVICE_PATH_LIST, error); if (dpbuf == NULL) return NULL; fu_struct_efi_load_option_set_dp_size(st, g_bytes_get_size(dpbuf)); - fu_byte_array_append_bytes(st, dpbuf); + fu_byte_array_append_bytes(st->buf, dpbuf); /* hive, path or data */ if (self->kind == FU_EFI_LOAD_OPTION_KIND_HIVE) { @@ -454,20 +453,20 @@ buf_hive = fu_efi_load_option_write_hive(self, error); if (buf_hive == NULL) return NULL; - g_byte_array_append(st, buf_hive->data, buf_hive->len); - fu_byte_array_align_up(st, FU_FIRMWARE_ALIGNMENT_512, 0x0); /* make atomic */ + g_byte_array_append(st->buf, buf_hive->data, buf_hive->len); + fu_byte_array_align_up(st->buf, FU_FIRMWARE_ALIGNMENT_512, 0x0); /* make atomic */ } else if (self->kind == FU_EFI_LOAD_OPTION_KIND_PATH) { g_autoptr(GByteArray) buf_path = NULL; buf_path = fu_efi_load_option_write_path(self, error); if (buf_path == NULL) return NULL; - g_byte_array_append(st, buf_path->data, buf_path->len); + g_byte_array_append(st->buf, buf_path->data, buf_path->len); } else if (self->kind == FU_EFI_LOAD_OPTION_KIND_DATA && self->optional_data != NULL) { - fu_byte_array_append_bytes(st, self->optional_data); + fu_byte_array_append_bytes(st->buf, self->optional_data); } /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static gboolean @@ -618,7 +617,7 @@ static void fu_efi_load_option_init(FuEfiLoadOption *self) { - self->attrs = FU_EFI_LOAD_OPTION_ATTRS_ACTIVE; + self->attrs = FU_EFI_LOAD_OPTION_ATTR_ACTIVE; self->metadata = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); g_type_ensure(FU_TYPE_EFI_DEVICE_PATH_LIST); } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-lz77-decompressor.c fwupd-2.0.20/libfwupdplugin/fu-efi-lz77-decompressor.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-lz77-decompressor.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-lz77-decompressor.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,6 @@ #include "fu-byte-array.h" #include "fu-efi-lz77-decompressor.h" #include "fu-input-stream.h" -#include "fu-string.h" struct _FuEfiLz77Decompressor { FuFirmware parent_instance; @@ -172,9 +171,9 @@ } for (index = 1; index <= 16; index++) { - guint16 WordOfStart = start[index]; - guint16 WordOfCount = count[index]; - start[index + 1] = (guint16)(WordOfStart + (WordOfCount << (16 - index))); + guint16 word_of_start = start[index]; + guint16 word_of_count = count[index]; + start[index + 1] = (guint16)(word_of_start + (word_of_count << (16 - index))); } if (start[17] != 0) { @@ -350,6 +349,13 @@ if (index == special_symbol) { if (!fu_efi_lz77_decompressor_get_bits(helper, 2, &char_c, error)) return FALSE; + if (char_c == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "bad table"); + return FALSE; + } while ((gint16)(--char_c) >= 0 && index < NPT) { helper->pt_len[index++] = 0; } @@ -423,6 +429,13 @@ return FALSE; char_c += 20; } + if (char_c == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "bad table"); + return FALSE; + } while ((gint16)(--char_c) >= 0 && index < NC) helper->c_len[index++] = 0; } else { @@ -453,7 +466,7 @@ /* read in the extra set code length array */ if (!fu_efi_lz77_decompressor_read_pt_len(helper, NT, TBIT, 3, error)) { - g_prefix_error( + g_prefix_error_literal( error, "failed to generate the Huffman code mapping table for extra set: "); return FALSE; @@ -461,8 +474,9 @@ /* read in and decode the char&len set code length array */ if (!fu_efi_lz77_decompressor_read_c_len(helper, error)) { - g_prefix_error(error, - "failed to generate the code mapping table for char&len: "); + g_prefix_error_literal( + error, + "failed to generate the code mapping table for char&len: "); return FALSE; } @@ -472,9 +486,10 @@ helper->p_bit, (guint16)(-1), error)) { - g_prefix_error(error, - "failed to generate the Huffman code mapping table for the " - "position set: "); + g_prefix_error_literal( + error, + "failed to generate the Huffman code mapping table for the " + "position set: "); return FALSE; } } @@ -587,13 +602,13 @@ static gboolean fu_efi_lz77_decompressor_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize streamsz = 0; guint32 dst_bufsz; guint32 src_bufsz; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructEfiLz77DecompressorHeader) st = NULL; g_autoptr(GError) error_all = NULL; g_autoptr(GByteArray) dst = g_byte_array_new(); FuEfiLz77DecompressorVersion decompressor_versions[] = { @@ -608,7 +623,7 @@ if (st == NULL) return FALSE; src_bufsz = fu_struct_efi_lz77_decompressor_header_get_src_size(st); - if (streamsz < src_bufsz + st->len) { + if (streamsz < src_bufsz + st->buf->len) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, @@ -644,7 +659,7 @@ }; g_autoptr(GError) error_local = NULL; - if (!g_seekable_seek(G_SEEKABLE(stream), st->len, G_SEEK_SET, NULL, error)) + if (!g_seekable_seek(G_SEEKABLE(stream), st->buf->len, G_SEEK_SET, NULL, error)) return FALSE; if (fu_efi_lz77_decompressor_internal(&helper, decompressor_versions[i], @@ -665,7 +680,7 @@ fu_efi_lz77_decompressor_version_to_string(decompressor_versions[i])); continue; } - g_prefix_error(&error_all, + g_prefix_error(&error_all, /* nocheck:error */ "failed to parse %s: %s: ", fu_efi_lz77_decompressor_version_to_string(decompressor_versions[i]), error_local->message); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-section.c fwupd-2.0.20/libfwupdplugin/fu-efi-section.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-section.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-section.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,6 @@ #include "config.h" #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-efi-common.h" #include "fu-efi-lz77-decompressor.h" @@ -18,7 +17,6 @@ #include "fu-efi-volume.h" #include "fu-input-stream.h" #include "fu-lzma-common.h" -#include "fu-mem.h" #include "fu-partial-input-stream.h" #include "fu-string.h" @@ -60,25 +58,24 @@ static gboolean fu_efi_section_parse_volume_image(FuEfiSection *self, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) img = fu_efi_volume_new(); if (!fu_firmware_parse_stream(img, stream, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) { return FALSE; } - fu_firmware_add_image(FU_FIRMWARE(self), img); - return TRUE; + return fu_firmware_add_image(FU_FIRMWARE(self), img, error); } static gboolean fu_efi_section_parse_lzma_sections(FuEfiSection *self, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(GBytes) blob = NULL; @@ -91,12 +88,12 @@ return FALSE; blob_uncomp = fu_lzma_decompress_bytes(blob, 128 * 1024 * 1024, error); if (blob_uncomp == NULL) { - g_prefix_error(error, "failed to decompress: "); + g_prefix_error_literal(error, "failed to decompress: "); return FALSE; } stream_uncomp = g_memory_input_stream_new_from_bytes(blob_uncomp); if (!fu_efi_parse_sections(FU_FIRMWARE(self), stream_uncomp, 0, flags, error)) { - g_prefix_error(error, "failed to parse sections: "); + g_prefix_error_literal(error, "failed to parse sections: "); return FALSE; } return TRUE; @@ -105,7 +102,7 @@ static gboolean fu_efi_section_parse_user_interface(FuEfiSection *self, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuEfiSectionPrivate *priv = GET_PRIVATE(self); @@ -131,7 +128,7 @@ static gboolean fu_efi_section_parse_version(FuEfiSection *self, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint16 version_raw = 0; @@ -139,18 +136,18 @@ g_autoptr(GByteArray) buf = NULL; if (!fu_input_stream_read_u16(stream, 0x0, &version_raw, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to read raw version: "); + g_prefix_error_literal(error, "failed to read raw version: "); return FALSE; } fu_firmware_set_version_raw(FU_FIRMWARE(self), version_raw); buf = fu_input_stream_read_byte_array(stream, sizeof(guint16), G_MAXSIZE, NULL, error); if (buf == NULL) { - g_prefix_error(error, "failed to read version buffer: "); + g_prefix_error_literal(error, "failed to read version buffer: "); return FALSE; } version = fu_utf16_to_utf8_byte_array(buf, G_LITTLE_ENDIAN, error); if (version == NULL) { - g_prefix_error(error, "failed to convert to UTF-16: "); + g_prefix_error_literal(error, "failed to convert to UTF-16: "); return FALSE; } fu_firmware_set_version(FU_FIRMWARE(self), version); /* nocheck:set-version */ @@ -160,29 +157,33 @@ static gboolean fu_efi_section_parse_compression_sections(FuEfiSection *self, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructEfiSectionCompression) st = NULL; st = fu_struct_efi_section_compression_parse_stream(stream, 0x0, error); if (st == NULL) return FALSE; if (fu_struct_efi_section_compression_get_compression_type(st) == FU_EFI_COMPRESSION_TYPE_NOT_COMPRESSED) { - if (!fu_efi_parse_sections(FU_FIRMWARE(self), stream, st->len, flags, error)) { - g_prefix_error(error, "failed to parse sections: "); + if (!fu_efi_parse_sections(FU_FIRMWARE(self), stream, st->buf->len, flags, error)) { + g_prefix_error_literal(error, "failed to parse sections: "); return FALSE; } } else { g_autoptr(FuFirmware) lz77_decompressor = fu_efi_lz77_decompressor_new(); g_autoptr(GInputStream) lz77_stream = NULL; - if (!fu_firmware_parse_stream(lz77_decompressor, stream, st->len, flags, error)) + if (!fu_firmware_parse_stream(lz77_decompressor, + stream, + st->buf->len, + flags, + error)) return FALSE; lz77_stream = fu_firmware_get_stream(lz77_decompressor, error); if (lz77_stream == NULL) return FALSE; if (!fu_efi_parse_sections(FU_FIRMWARE(self), lz77_stream, 0, flags, error)) { - g_prefix_error(error, "failed to parse sections: "); + g_prefix_error_literal(error, "failed to parse sections: "); return FALSE; } } @@ -222,12 +223,12 @@ static gboolean fu_efi_section_parse_freeform_subtype_guid(FuEfiSection *self, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { const gchar *guid_ui; g_autofree gchar *guid_str = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructEfiSectionFreeformSubtypeGuid) st = NULL; st = fu_struct_efi_section_freeform_subtype_guid_parse_stream(stream, 0x0, error); if (st == NULL) @@ -248,15 +249,16 @@ static gboolean fu_efi_section_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuEfiSection *self = FU_EFI_SECTION(firmware); FuEfiSectionPrivate *priv = GET_PRIVATE(self); gsize offset = 0; gsize streamsz = 0; + gsize stlen = 0; guint32 size; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructEfiSection) st = NULL; g_autoptr(GInputStream) partial_stream = NULL; /* parse */ @@ -266,13 +268,17 @@ /* use extended size */ if (fu_struct_efi_section_get_size(st) == 0xFFFFFF) { - fu_struct_efi_section_unref(st); - st = fu_struct_efi_section2_parse_stream(stream, offset, error); - if (st == NULL) - return FALSE; - size = fu_struct_efi_section2_get_extended_size(st); + g_autoptr(FuStructEfiSection2) st2 = NULL; + st2 = fu_struct_efi_section2_parse_stream(stream, offset, error); + if (st2 == NULL) + return FALSE; + priv->type = fu_struct_efi_section2_get_type(st2); + size = fu_struct_efi_section2_get_extended_size(st2); + stlen = st2->buf->len; } else { + priv->type = fu_struct_efi_section_get_type(st); size = fu_struct_efi_section_get_size(st); + stlen = st->buf->len; } if (size < FU_STRUCT_EFI_SECTION_SIZE) { g_set_error(error, @@ -297,17 +303,16 @@ } /* name */ - priv->type = fu_struct_efi_section_get_type(st); if (priv->type == FU_EFI_SECTION_TYPE_GUID_DEFINED) { g_autofree gchar *guid_str = NULL; - g_autoptr(GByteArray) st_def = NULL; - st_def = fu_struct_efi_section_guid_defined_parse_stream(stream, st->len, error); + g_autoptr(FuStructEfiSectionGuidDefined) st_def = NULL; + st_def = fu_struct_efi_section_guid_defined_parse_stream(stream, stlen, error); if (st_def == NULL) return FALSE; guid_str = fwupd_guid_to_string(fu_struct_efi_section_guid_defined_get_name(st_def), FWUPD_GUID_FLAG_MIXED_ENDIAN); fu_firmware_set_id(firmware, guid_str); - if (fu_struct_efi_section_guid_defined_get_offset(st_def) < st_def->len) { + if (fu_struct_efi_section_guid_defined_get_offset(st_def) < st_def->buf->len) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -315,14 +320,14 @@ (guint)fu_struct_efi_section_guid_defined_get_offset(st_def)); return FALSE; } - offset += fu_struct_efi_section_guid_defined_get_offset(st_def) - st->len; + offset += fu_struct_efi_section_guid_defined_get_offset(st_def) - stlen; } /* create blob */ - offset += st->len; + offset += stlen; partial_stream = fu_partial_input_stream_new(stream, offset, size - offset, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut data: "); + g_prefix_error_literal(error, "failed to cut data: "); return FALSE; } fu_firmware_set_offset(firmware, offset); @@ -333,14 +338,14 @@ /* nested volume */ if (priv->type == FU_EFI_SECTION_TYPE_VOLUME_IMAGE) { if (!fu_efi_section_parse_volume_image(self, partial_stream, flags, error)) { - g_prefix_error(error, "failed to parse nested volume: "); + g_prefix_error_literal(error, "failed to parse nested volume: "); return FALSE; } } else if (priv->type == FU_EFI_SECTION_TYPE_GUID_DEFINED && g_strcmp0(fu_firmware_get_id(firmware), FU_EFI_SECTION_GUID_LZMA_COMPRESS) == 0) { if (!fu_efi_section_parse_lzma_sections(self, partial_stream, flags, error)) { - g_prefix_error(error, "failed to parse lzma section: "); + g_prefix_error_literal(error, "failed to parse lzma section: "); return FALSE; } } else if (priv->type == FU_EFI_SECTION_TYPE_GUID_DEFINED && @@ -354,12 +359,12 @@ fu_firmware_get_id(firmware)); } else if (priv->type == FU_EFI_SECTION_TYPE_USER_INTERFACE) { if (!fu_efi_section_parse_user_interface(self, partial_stream, flags, error)) { - g_prefix_error(error, "failed to parse user interface: "); + g_prefix_error_literal(error, "failed to parse user interface: "); return FALSE; } } else if (priv->type == FU_EFI_SECTION_TYPE_VERSION) { if (!fu_efi_section_parse_version(self, partial_stream, flags, error)) { - g_prefix_error(error, "failed to parse version: "); + g_prefix_error_literal(error, "failed to parse version: "); return FALSE; } } else if (priv->type == FU_EFI_SECTION_TYPE_COMPRESSION) { @@ -367,7 +372,7 @@ partial_stream, flags, error)) { - g_prefix_error(error, "failed to parse compression: "); + g_prefix_error_literal(error, "failed to parse compression: "); return FALSE; } } else if (priv->type == FU_EFI_SECTION_TYPE_FREEFORM_SUBTYPE_GUID) { @@ -375,7 +380,7 @@ partial_stream, flags, error)) { - g_prefix_error(error, "failed to parse compression: "); + g_prefix_error_literal(error, "failed to parse compression: "); return FALSE; } } else if (priv->type == FU_EFI_SECTION_TYPE_PEI_DEPEX || @@ -401,7 +406,7 @@ { FuEfiSection *self = FU_EFI_SECTION(firmware); FuEfiSectionPrivate *priv = GET_PRIVATE(self); - g_autoptr(GByteArray) buf = fu_struct_efi_section_new(); + g_autoptr(FuStructEfiSection) st = fu_struct_efi_section_new(); g_autoptr(GBytes) blob = NULL; /* simple blob for now */ @@ -412,22 +417,24 @@ /* header */ if (priv->type == FU_EFI_SECTION_TYPE_GUID_DEFINED) { fwupd_guid_t guid = {0x0}; - g_autoptr(GByteArray) st_def = fu_struct_efi_section_guid_defined_new(); + g_autoptr(FuStructEfiSectionGuidDefined) st_def = + fu_struct_efi_section_guid_defined_new(); if (!fwupd_guid_from_string(fu_firmware_get_id(firmware), &guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, error)) return NULL; fu_struct_efi_section_guid_defined_set_name(st_def, &guid); - fu_struct_efi_section_guid_defined_set_offset(st_def, buf->len + st_def->len); - g_byte_array_append(buf, st_def->data, st_def->len); + fu_struct_efi_section_guid_defined_set_offset(st_def, + st->buf->len + st_def->buf->len); + fu_byte_array_append_array(st->buf, st_def->buf); } - fu_struct_efi_section_set_type(buf, priv->type); - fu_struct_efi_section_set_size(buf, buf->len + g_bytes_get_size(blob)); + fu_struct_efi_section_set_type(st, priv->type); + fu_struct_efi_section_set_size(st, st->buf->len + g_bytes_get_size(blob)); /* blob */ - fu_byte_array_append_bytes(buf, blob); - return g_steal_pointer(&buf); + fu_byte_array_append_bytes(st->buf, blob); + return g_steal_pointer(&st->buf); } static gboolean @@ -464,8 +471,11 @@ { FuEfiSectionPrivate *priv = GET_PRIVATE(self); priv->type = FU_EFI_SECTION_TYPE_RAW; - fu_firmware_set_images_max(FU_FIRMWARE(self), - g_getenv("FWUPD_FUZZER_RUNNING") != NULL ? 10 : 2000); +#ifdef HAVE_FUZZER + fu_firmware_set_images_max(FU_FIRMWARE(self), 10); +#else + fu_firmware_set_images_max(FU_FIRMWARE(self), 2000); +#endif fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION); // fu_firmware_set_alignment (FU_FIRMWARE (self), FU_FIRMWARE_ALIGNMENT_8); g_type_ensure(FU_TYPE_EFI_VOLUME); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-signature-list.c fwupd-2.0.20/libfwupdplugin/fu-efi-signature-list.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-signature-list.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-signature-list.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,16 +9,13 @@ #include "config.h" #include -#include #include "fu-byte-array.h" -#include "fu-common.h" #include "fu-efi-signature-list.h" #include "fu-efi-signature-private.h" #include "fu-efi-struct.h" #include "fu-efi-x509-signature-private.h" #include "fu-input-stream.h" -#include "fu-mem.h" /** * FuEfiSignatureList: @@ -28,14 +25,13 @@ * See also: [class@FuFirmware] */ -struct _FuEfiSignatureList { - FuFirmware parent_instance; -}; - G_DEFINE_TYPE(FuEfiSignatureList, fu_efi_signature_list, FU_TYPE_FIRMWARE) const guint8 FU_EFI_SIGLIST_HEADER_MAGIC[] = {0x26, 0x16, 0xC4, 0xC1, 0x4C}; +#define FU_EFI_SIGNATURE_LIST_GUID_SHA256 "c1c41626-504c-4092-aca9-41f936934328" +#define FU_EFI_SIGNATURE_LIST_GUID_X509 "a5c059a1-94e4-4aa7-87b5-ab155c2bf072" + /** * fu_efi_signature_list_get_newest: * @self: a #FuEfiSignatureList @@ -66,10 +62,7 @@ g_autofree gchar *key = NULL; if (fu_efi_signature_get_kind(sig) == FU_EFI_SIGNATURE_KIND_X509) { - key = g_strdup_printf( - "%s:%s", - fu_efi_x509_signature_get_subject_vendor(FU_EFI_X509_SIGNATURE(sig)), - fu_efi_x509_signature_get_subject_name(FU_EFI_X509_SIGNATURE(sig))); + key = fu_efi_x509_signature_build_dedupe_key(FU_EFI_X509_SIGNATURE(sig)); } else { key = fu_firmware_get_checksum(FU_FIRMWARE(sig), G_CHECKSUM_SHA256, NULL); } @@ -90,7 +83,7 @@ sigs_newest = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); sigs_values = g_hash_table_get_values(hash); for (GList *l = sigs_values; l != NULL; l = l->next) { - FuEfiX509Signature *sig = FU_EFI_X509_SIGNATURE(l->data); + FuEfiSignature *sig = FU_EFI_SIGNATURE(l->data); g_ptr_array_add(sigs_newest, g_object_ref(sig)); } @@ -102,6 +95,7 @@ fu_efi_signature_list_parse_list(FuEfiSignatureList *self, GInputStream *stream, gsize *offset, + FuFirmwareParseFlags flags, GError **error) { FuEfiSignatureKind sig_kind = FU_EFI_SIGNATURE_KIND_UNKNOWN; @@ -110,7 +104,7 @@ guint32 list_size; guint32 size; g_autofree gchar *sig_type = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructEfiSignatureList) st = NULL; /* read EFI_SIGNATURE_LIST */ st = fu_struct_efi_signature_list_parse_stream(stream, *offset, error); @@ -118,9 +112,9 @@ return FALSE; sig_type = fwupd_guid_to_string(fu_struct_efi_signature_list_get_type(st), FWUPD_GUID_FLAG_MIXED_ENDIAN); - if (g_strcmp0(sig_type, "c1c41626-504c-4092-aca9-41f936934328") == 0) { + if (g_strcmp0(sig_type, FU_EFI_SIGNATURE_LIST_GUID_SHA256) == 0) { sig_kind = FU_EFI_SIGNATURE_KIND_SHA256; - } else if (g_strcmp0(sig_type, "a5c059a1-94e4-4aa7-87b5-ab155c2bf072") == 0) { + } else if (g_strcmp0(sig_type, FU_EFI_SIGNATURE_LIST_GUID_X509) == 0) { sig_kind = FU_EFI_SIGNATURE_KIND_X509; } list_size = fu_struct_efi_signature_list_get_list_size(st); @@ -162,13 +156,9 @@ sig = fu_efi_signature_new(sig_kind); } fu_firmware_set_size(FU_FIRMWARE(sig), size); - if (!fu_firmware_parse_stream(FU_FIRMWARE(sig), - stream, - offset_tmp, - FWUPD_INSTALL_FLAG_NONE, - error)) + if (!fu_firmware_parse_stream(FU_FIRMWARE(sig), stream, offset_tmp, flags, error)) return FALSE; - if (!fu_firmware_add_image_full(FU_FIRMWARE(self), FU_FIRMWARE(sig), error)) + if (!fu_firmware_add_image(FU_FIRMWARE(self), FU_FIRMWARE(sig), error)) return FALSE; offset_tmp += size; } @@ -192,12 +182,12 @@ offset, /* seek */ sizeof(guid), error)) { - g_prefix_error(error, "failed to read magic: "); + g_prefix_error_literal(error, "failed to read magic: "); return FALSE; } sig_type = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); - if (g_strcmp0(sig_type, "c1c41626-504c-4092-aca9-41f936934328") != 0 && - g_strcmp0(sig_type, "a5c059a1-94e4-4aa7-87b5-ab155c2bf072") != 0) { + if (g_strcmp0(sig_type, FU_EFI_SIGNATURE_LIST_GUID_SHA256) != 0 && + g_strcmp0(sig_type, FU_EFI_SIGNATURE_LIST_GUID_X509) != 0) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, @@ -212,7 +202,7 @@ static gboolean fu_efi_signature_list_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuEfiSignatureList *self = FU_EFI_SIGNATURE_LIST(firmware); @@ -223,7 +213,7 @@ if (!fu_input_stream_size(stream, &streamsz, error)) return FALSE; while (offset < streamsz) { - if (!fu_efi_signature_list_parse_list(self, stream, &offset, error)) + if (!fu_efi_signature_list_parse_list(self, stream, &offset, flags, error)) return FALSE; } @@ -232,45 +222,125 @@ } static GByteArray * -fu_efi_signature_list_write(FuFirmware *firmware, GError **error) +fu_efi_signature_list_write_x509(FuEfiSignature *sig, GError **error) { fwupd_guid_t guid = {0}; g_autoptr(FuStructEfiSignatureList) st = fu_struct_efi_signature_list_new(); - g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + g_autoptr(GBytes) blob = NULL; /* entry */ - if (!fwupd_guid_from_string("c1c41626-504c-4092-aca9-41f936934328", + if (!fwupd_guid_from_string(FU_EFI_SIGNATURE_LIST_GUID_X509, &guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, error)) return NULL; fu_struct_efi_signature_list_set_type(st, &guid); - fu_struct_efi_signature_list_set_header_size(st, 0); + + /* SignatureOwner + SignatureData */ + blob = fu_firmware_write(FU_FIRMWARE(sig), error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(st->buf, blob); + fu_struct_efi_signature_list_set_size(st, g_bytes_get_size(blob)); fu_struct_efi_signature_list_set_list_size(st, FU_STRUCT_EFI_SIGNATURE_LIST_SIZE + - (images->len * (16 + 32))); - fu_struct_efi_signature_list_set_size(st, sizeof(fwupd_guid_t) + 32); /* SHA256 */ + g_bytes_get_size(blob)); + + /* success */ + return g_steal_pointer(&st->buf); +} + +static GByteArray * +fu_efi_signature_list_write_sha256(GPtrArray *sigs, GError **error) +{ + fwupd_guid_t guid = {0}; + g_autoptr(FuStructEfiSignatureList) st = fu_struct_efi_signature_list_new(); + + /* entry */ + if (!fwupd_guid_from_string(FU_EFI_SIGNATURE_LIST_GUID_SHA256, + &guid, + FWUPD_GUID_FLAG_MIXED_ENDIAN, + error)) + return NULL; + fu_struct_efi_signature_list_set_type(st, &guid); /* SignatureOwner + SignatureData */ - for (guint i = 0; i < images->len; i++) { - FuFirmware *img = g_ptr_array_index(images, i); + for (guint i = 0; i < sigs->len; i++) { + FuEfiSignature *sig = g_ptr_array_index(sigs, i); g_autoptr(GBytes) img_blob = NULL; - img_blob = fu_firmware_write(img, error); + + img_blob = fu_firmware_write(FU_FIRMWARE(sig), error); if (img_blob == NULL) return NULL; - if (g_bytes_get_size(img_blob) != sizeof(fwupd_guid_t) + 32) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "expected SHA256 hash as signature data, got 0x%x", - (guint)(g_bytes_get_size(img_blob) - sizeof(fwupd_guid_t))); - return NULL; + fu_byte_array_append_bytes(st->buf, img_blob); + } + + /* fix up header */ + fu_struct_efi_signature_list_set_size(st, sizeof(fwupd_guid_t) + 32); /* SHA256 */ + fu_struct_efi_signature_list_set_list_size(st, st->buf->len); + + /* success */ + return g_steal_pointer(&st->buf); +} + +static GByteArray * +fu_efi_signature_list_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(GPtrArray) sigs = fu_firmware_get_images(firmware); + g_autoptr(GPtrArray) sigs_sha256 = g_ptr_array_new(); + g_autoptr(GPtrArray) sigs_x509 = g_ptr_array_new(); + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* look for each type */ + for (guint i = 0; i < sigs->len; i++) { + FuEfiSignature *sig = g_ptr_array_index(sigs, i); + if (fu_efi_signature_get_kind(sig) == FU_EFI_SIGNATURE_KIND_SHA256) { + g_ptr_array_add(sigs_sha256, sig); + continue; + } + if (fu_efi_signature_get_kind(sig) == FU_EFI_SIGNATURE_KIND_X509) { + g_ptr_array_add(sigs_x509, sig); + continue; } - fu_byte_array_append_bytes(st, img_blob); + } + + /* write SHA256 hashes in one block */ + if (sigs_sha256->len > 0) { + g_autoptr(GByteArray) buf_tmp = NULL; + buf_tmp = fu_efi_signature_list_write_sha256(sigs_sha256, error); + if (buf_tmp == NULL) + return NULL; + g_byte_array_append(buf, buf_tmp->data, buf_tmp->len); + } + + /* write certs as a new siglist for each signature */ + for (guint i = 0; i < sigs_x509->len; i++) { + FuEfiSignature *sig = g_ptr_array_index(sigs_x509, i); + g_autoptr(GByteArray) buf_tmp = NULL; + buf_tmp = fu_efi_signature_list_write_x509(sig, error); + if (buf_tmp == NULL) + return NULL; + g_byte_array_append(buf, buf_tmp->data, buf_tmp->len); } /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&buf); +} + +static void +fu_efi_signature_list_add_magic(FuFirmware *firmware) +{ + fwupd_guid_t guid = {0}; + (void)fwupd_guid_from_string(FU_EFI_SIGNATURE_LIST_GUID_SHA256, + &guid, + FWUPD_GUID_FLAG_MIXED_ENDIAN, + NULL); + fu_firmware_add_magic(firmware, guid, sizeof(guid), 0x0); + (void)fwupd_guid_from_string(FU_EFI_SIGNATURE_LIST_GUID_X509, + &guid, + FWUPD_GUID_FLAG_MIXED_ENDIAN, + NULL); + fu_firmware_add_magic(firmware, guid, sizeof(guid), 0x0); } /** @@ -293,6 +363,7 @@ firmware_class->validate = fu_efi_signature_list_validate; firmware_class->parse = fu_efi_signature_list_parse; firmware_class->write = fu_efi_signature_list_write; + firmware_class->add_magic = fu_efi_signature_list_add_magic; } static void @@ -301,4 +372,5 @@ fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_ALWAYS_SEARCH); fu_firmware_set_images_max(FU_FIRMWARE(self), 2000); g_type_ensure(FU_TYPE_EFI_SIGNATURE); + g_type_ensure(FU_TYPE_EFI_X509_SIGNATURE); } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-signature-list.h fwupd-2.0.20/libfwupdplugin/fu-efi-signature-list.h --- fwupd-2.0.8/libfwupdplugin/fu-efi-signature-list.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-signature-list.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,15 @@ #include "fu-firmware.h" #define FU_TYPE_EFI_SIGNATURE_LIST (fu_efi_signature_list_get_type()) -G_DECLARE_FINAL_TYPE(FuEfiSignatureList, fu_efi_signature_list, FU, EFI_SIGNATURE_LIST, FuFirmware) +G_DECLARE_DERIVABLE_TYPE(FuEfiSignatureList, + fu_efi_signature_list, + FU, + EFI_SIGNATURE_LIST, + FuFirmware) + +struct _FuEfiSignatureListClass { + FuFirmwareClass parent_class; +}; FuFirmware * fu_efi_signature_list_new(void); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-signature.c fwupd-2.0.20/libfwupdplugin/fu-efi-signature.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-signature.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-signature.c 2026-02-26 11:36:18.000000000 +0000 @@ -113,7 +113,7 @@ static gboolean fu_efi_signature_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuEfiSignature *self = FU_EFI_SIGNATURE(firmware); @@ -140,7 +140,7 @@ 0x0, /* offset */ sizeof(guid), error)) { - g_prefix_error(error, "failed to read signature GUID: "); + g_prefix_error_literal(error, "failed to read signature GUID: "); return FALSE; } priv->owner = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); @@ -152,7 +152,7 @@ NULL, error); if (data == NULL) { - g_prefix_error(error, "failed to read signature data: "); + g_prefix_error_literal(error, "failed to read signature data: "); return FALSE; } fu_firmware_set_bytes(firmware, data); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-signature.h fwupd-2.0.20/libfwupdplugin/fu-efi-signature.h --- fwupd-2.0.8/libfwupdplugin/fu-efi-signature.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-signature.h 2026-02-26 11:36:18.000000000 +0000 @@ -20,6 +20,7 @@ #define FU_EFI_SIGNATURE_GUID_ZERO "00000000-0000-0000-0000-000000000000" #define FU_EFI_SIGNATURE_GUID_MICROSOFT "77fa9abd-0359-4d32-bd60-28f4e78f784b" +#define FU_EFI_SIGNATURE_GUID_FRAMEWORK "6841bdb0-b2af-4079-918f-fe458325d5cd" #define FU_EFI_SIGNATURE_GUID_OVMF "a0baa8a3-041d-48a8-bc87-c36d121b5e3d" #define FU_EFI_SIGNATURE_GUID_OVMF_LEGACY "d5c1df0b-1bac-4edf-ba48-08834009ca5a" diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-variable-authentication2.c fwupd-2.0.20/libfwupdplugin/fu-efi-variable-authentication2.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-variable-authentication2.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-variable-authentication2.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,263 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#define G_LOG_DOMAIN "FuEfiVariableAuthentication2" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-efi-struct.h" +#include "fu-efi-variable-authentication2.h" +#include "fu-mem.h" +#include "fu-partial-input-stream.h" +#include "fu-pkcs7.h" + +/** + * FuEfiVariableAuthentication2: + * + * A UEFI signature list typically found in the `KEKUpdate.bin` and `DBXUpdate.bin` files. + * + * See also: [class@FuFirmware] + */ + +struct _FuEfiVariableAuthentication2 { + FuEfiSignatureList parent_instance; + GPtrArray *signers; +}; + +G_DEFINE_TYPE(FuEfiVariableAuthentication2, + fu_efi_variable_authentication2, + FU_TYPE_EFI_SIGNATURE_LIST) + +/** + * fu_efi_variable_authentication2_get_signers: + * @self: A #FuEfiVariableAuthentication2 + * + * Returns the certificates that signed the variable. + * + * Returns: (transfer full) (element-type FuX509Certificate): certificates + * + * Since: 2.0.9 + **/ +GPtrArray * +fu_efi_variable_authentication2_get_signers(FuEfiVariableAuthentication2 *self) +{ + g_return_val_if_fail(FU_IS_EFI_VARIABLE_AUTHENTICATION2(self), NULL); + return g_ptr_array_ref(self->signers); +} + +static void +fu_efi_variable_authentication2_export(FuFirmware *firmware, + FuFirmwareExportFlags flags, + XbBuilderNode *bn) +{ + g_autoptr(XbBuilderNode) bn_signers = NULL; + FuEfiVariableAuthentication2 *self = FU_EFI_VARIABLE_AUTHENTICATION2(firmware); + + bn_signers = xb_builder_node_insert(bn, "signers", NULL); + for (guint i = 0; i < self->signers->len; i++) { + FuFirmware *img = g_ptr_array_index(self->signers, i); + g_autoptr(XbBuilderNode) bn_firmware = NULL; + bn_firmware = xb_builder_node_insert(bn_signers, "firmware", NULL); + fu_firmware_export(img, flags, bn_firmware); + } +} + +static gboolean +fu_efi_variable_authentication2_validate(FuFirmware *firmware, + GInputStream *stream, + gsize offset, + GError **error) +{ + return fu_struct_efi_variable_authentication2_validate_stream(stream, offset, error); +} + +/* + * with ContentInfo: + * 30 82 05 90 -- SEQUENCE (1424 BYTES) -- ContentInfo + * 06 09 -- OBJECT-IDENTIFIER (9 BYTES) -- ContentType + * 2a 86 48 86 f7 0d 01 07 02 -- signedData [1.2.840.113549.1.7.2] + * a0 82 05 81 -- CONTEXT-SPECIFIC CONSTRUCTED TAG 0 (1409 BYTES) -- content + * + * without ContentInfo: + * 30 82 05 7d -- SEQUENCE (1405 BYTES) -- SignedData + * 02 01 01 -- INTEGER 1 -- Version + * 31 0f -- SET (1 element) (15 BYTES) -- DigestAlgorithmIdentifiers + * 30 0d -- SEQUENCE (13 BYTES) -- AlgorithmIdentifier + * 06 09 -- OBJECT-IDENTIFIER (9 BYTES) -- algorithm + * 60 86 48 01 65 03 04 02 01 -- sha256 [2.16.840.1.101.3.4.2.1] + * 05 00 -- NULL (0 BYTES) -- parameters + */ +static gboolean +fu_efi_variable_authentication2_add_content_info_prefix(GByteArray *buf, GError **error) +{ + guint16 sz = 0; + const guint8 buf_algorithm[] = {0x2A, 0x86, 0x48, 0x86, 0xF7, 0x0D, 0x01, 0x07, 0x02}; + g_autoptr(GByteArray) buf_prefix = g_byte_array_new(); + + /* check is ASN.1 SEQUENCE */ + if (!fu_memread_uint16_safe(buf->data, buf->len, 0x0, &sz, G_BIG_ENDIAN, error)) { + g_prefix_error_literal(error, "not ASN.1 SEQUENCE: "); + return FALSE; + } + if (sz != 0x3082) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "not ASN.1 SEQUENCE, got 0x%x", + sz); + return FALSE; + } + + /* get size of SignedData */ + if (!fu_memread_uint16_safe(buf->data, buf->len, 0x2, &sz, G_BIG_ENDIAN, error)) + return FALSE; + + /* add SEQUENCE */ + fu_byte_array_append_uint16(buf_prefix, 0x3082, G_BIG_ENDIAN); + fu_byte_array_append_uint16(buf_prefix, sz + 19, G_BIG_ENDIAN); + + /* add OBJECT-IDENTIFIER */ + fu_byte_array_append_uint16(buf_prefix, 0x0609, G_BIG_ENDIAN); + g_byte_array_append(buf_prefix, buf_algorithm, sizeof(buf_algorithm)); + + /* add CONTEXT-SPECIFIC CONSTRUCTED TAG */ + fu_byte_array_append_uint16(buf_prefix, 0xA082, G_BIG_ENDIAN); + fu_byte_array_append_uint16(buf_prefix, sz + 4, G_BIG_ENDIAN); + + /* fix this up */ + g_byte_array_prepend(buf, buf_prefix->data, buf_prefix->len); + return TRUE; +} + +static gboolean +fu_efi_variable_authentication2_parse_pkcs7_certs(FuEfiVariableAuthentication2 *self, + GByteArray *buf, + GError **error) +{ + g_autoptr(FuPkcs7) pkcs7 = fu_pkcs7_new(); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GPtrArray) imgs = NULL; + + /* parse PKCS#7 blob */ + blob = g_bytes_new(buf->data, buf->len); + if (!fu_firmware_parse_bytes(FU_FIRMWARE(pkcs7), + blob, + 0x0, + FU_FIRMWARE_PARSE_FLAG_NONE, + error)) + return FALSE; + + /* add certificates that signed this variable */ + imgs = fu_firmware_get_images(FU_FIRMWARE(pkcs7)); + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + g_ptr_array_add(self->signers, g_object_ref(img)); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_efi_variable_authentication2_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ + FuEfiVariableAuthentication2 *self = FU_EFI_VARIABLE_AUTHENTICATION2(firmware); + gsize offset = FU_STRUCT_EFI_TIME_SIZE; + g_autoptr(FuStructEfiVariableAuthentication2) st = NULL; + g_autoptr(FuStructEfiWinCertificate) st_wincert = NULL; + g_autoptr(GInputStream) partial_stream = NULL; + gboolean offset_tmp = 0; + + st = fu_struct_efi_variable_authentication2_parse_stream(stream, 0x0, error); + if (st == NULL) + return FALSE; + + /* parse the EFI_SIGNATURE_LIST blob past the EFI_TIME + WIN_CERTIFICATE */ + st_wincert = fu_struct_efi_variable_authentication2_get_auth_info(st); + if (fu_struct_efi_win_certificate_get_length(st_wincert) > st_wincert->buf->len) { + g_autoptr(GByteArray) buf = NULL; + buf = fu_input_stream_read_byte_array( + stream, + offset + st_wincert->buf->len + offset_tmp, + fu_struct_efi_win_certificate_get_length(st_wincert) - st_wincert->buf->len, + NULL, + error); + if (buf == NULL) + return FALSE; + if (!fu_efi_variable_authentication2_add_content_info_prefix(buf, error)) + return FALSE; + if (!fu_efi_variable_authentication2_parse_pkcs7_certs(self, buf, error)) + return FALSE; + } + + offset += fu_struct_efi_win_certificate_get_length(st_wincert); + partial_stream = fu_partial_input_stream_new(stream, offset, G_MAXSIZE, error); + if (partial_stream == NULL) + return FALSE; + return FU_FIRMWARE_CLASS(fu_efi_variable_authentication2_parent_class) + ->parse(firmware, partial_stream, flags, error); +} + +static GByteArray * +fu_efi_variable_authentication2_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(FuStructEfiVariableAuthentication2) st = + fu_struct_efi_variable_authentication2_new(); + g_autoptr(GByteArray) buf = NULL; + + /* append EFI_SIGNATURE_LIST */ + buf = + FU_FIRMWARE_CLASS(fu_efi_variable_authentication2_parent_class)->write(firmware, error); + if (buf == NULL) + return NULL; + fu_byte_array_append_array(st->buf, buf); + + /* success */ + return g_steal_pointer(&st->buf); +} + +static void +fu_efi_variable_authentication2_add_magic(FuFirmware *firmware) +{ + fu_firmware_add_magic(firmware, + (const guint8 *)FU_STRUCT_EFI_WIN_CERTIFICATE_DEFAULT_GUID, + sizeof(fwupd_guid_t), + FU_STRUCT_EFI_VARIABLE_AUTHENTICATION2_OFFSET_AUTH_INFO + + FU_STRUCT_EFI_WIN_CERTIFICATE_OFFSET_GUID); +} + +static void +fu_efi_variable_authentication2_init(FuEfiVariableAuthentication2 *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_ALWAYS_SEARCH); + g_type_ensure(FU_TYPE_EFI_SIGNATURE_LIST); + self->signers = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); +} + +static void +fu_efi_variable_authentication2_finalize(GObject *obj) +{ + FuEfiVariableAuthentication2 *self = FU_EFI_VARIABLE_AUTHENTICATION2(obj); + g_ptr_array_unref(self->signers); + G_OBJECT_CLASS(fu_efi_variable_authentication2_parent_class)->finalize(obj); +} + +static void +fu_efi_variable_authentication2_class_init(FuEfiVariableAuthentication2Class *klass) +{ + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_efi_variable_authentication2_finalize; + firmware_class->validate = fu_efi_variable_authentication2_validate; + firmware_class->parse = fu_efi_variable_authentication2_parse; + firmware_class->export = fu_efi_variable_authentication2_export; + firmware_class->write = fu_efi_variable_authentication2_write; + firmware_class->add_magic = fu_efi_variable_authentication2_add_magic; +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-variable-authentication2.h fwupd-2.0.20/libfwupdplugin/fu-efi-variable-authentication2.h --- fwupd-2.0.8/libfwupdplugin/fu-efi-variable-authentication2.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-variable-authentication2.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-efi-signature-list.h" + +#define FU_TYPE_EFI_VARIABLE_AUTHENTICATION2 (fu_efi_variable_authentication2_get_type()) +G_DECLARE_FINAL_TYPE(FuEfiVariableAuthentication2, + fu_efi_variable_authentication2, + FU, + EFI_VARIABLE_AUTHENTICATION2, + FuEfiSignatureList) + +GPtrArray * +fu_efi_variable_authentication2_get_signers(FuEfiVariableAuthentication2 *self) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-volume.c fwupd-2.0.20/libfwupdplugin/fu-efi-volume.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-volume.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-volume.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,11 +10,14 @@ #include "fu-byte-array.h" #include "fu-bytes.h" +#include "fu-chunk-array.h" #include "fu-common.h" #include "fu-efi-common.h" #include "fu-efi-filesystem.h" +#include "fu-efi-ftw-store.h" #include "fu-efi-struct.h" #include "fu-efi-volume.h" +#include "fu-efi-vss2-variable-store.h" #include "fu-input-stream.h" #include "fu-partial-input-stream.h" #include "fu-sum.h" @@ -54,9 +57,88 @@ } static gboolean +fu_efi_volume_parse_nvram_evsa(FuEfiVolume *self, + GInputStream *stream, + gsize offset, + FuFirmwareParseFlags flags, + GError **error) +{ + gsize streamsz = 0; + guint found_cnt = 0; + gsize offset_last = offset; + + if (!fu_input_stream_size(stream, &streamsz, error)) + return FALSE; + while (offset < streamsz) { + g_autoptr(FuFirmware) img = NULL; + g_autoptr(GError) error_local = NULL; + + /* try to find a NVRAM store */ + img = fu_firmware_new_from_gtypes(stream, + offset, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, + &error_local, + FU_TYPE_EFI_VSS2_VARIABLE_STORE, + FU_TYPE_EFI_FTW_STORE, + G_TYPE_INVALID); + if (img == NULL) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA)) { + g_debug("ignoring EFI NVRAM @0x%x: %s", + (guint)offset, + error_local->message); + } + offset += 0x1000; + continue; + } + + /* sanity check */ + if (fu_firmware_get_size(img) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "NVRAM store entry has zero size"); + return FALSE; + } + + /* preserve the exact padding between EVSA stores */ + if (offset != offset_last) { + g_autoptr(GBytes) blob = g_bytes_new(NULL, 0); + g_autoptr(GBytes) blob_padded = + fu_bytes_pad(blob, offset - offset_last, 0xFF); + g_autoptr(FuFirmware) img_padded = fu_firmware_new_from_bytes(blob_padded); + if (!fu_firmware_add_image(FU_FIRMWARE(self), img_padded, error)) + return FALSE; + } + + /* we found something */ + fu_firmware_set_offset(img, offset); + if (!fu_firmware_add_image(FU_FIRMWARE(self), img, error)) + return FALSE; + offset += fu_firmware_get_size(img); + offset = fu_common_align_up(offset, FU_FIRMWARE_ALIGNMENT_4K); + found_cnt += 1; + + /* the last thing we found */ + offset_last = offset; + } + + /* we found nothing */ + if (found_cnt == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no NVRAM stores found"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean fu_efi_volume_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuEfiVolume *self = FU_EFI_VOLUME(firmware); @@ -69,7 +151,7 @@ guint64 fv_length = 0; guint8 alignment; g_autofree gchar *guid_str = NULL; - g_autoptr(GByteArray) st_hdr = NULL; + g_autoptr(FuStructEfiVolume) st_hdr = NULL; g_autoptr(GInputStream) partial_stream = NULL; /* parse */ @@ -93,7 +175,15 @@ "invalid volume length"); return FALSE; } - fu_firmware_set_size(firmware, fv_length); + if (fv_length > fu_firmware_get_size_max(firmware)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "volume length larger than max size: 0x%x > 0x%x", + (guint)fv_length, + (guint)fu_firmware_get_size_max(firmware)); + return FALSE; + } attrs = fu_struct_efi_volume_get_attrs(st_hdr); alignment = (attrs & 0x00ff0000) >> 16; if (alignment > FU_FIRMWARE_ALIGNMENT_2G) { @@ -108,7 +198,7 @@ fu_firmware_set_alignment(firmware, alignment); priv->attrs = attrs & 0xffff; hdr_length = fu_struct_efi_volume_get_hdr_len(st_hdr); - if (hdr_length < st_hdr->len || hdr_length > fv_length || hdr_length > streamsz || + if (hdr_length < st_hdr->buf->len || hdr_length > fv_length || hdr_length > streamsz || hdr_length % 2 != 0) { g_set_error(error, FWUPD_ERROR, @@ -119,7 +209,7 @@ } /* verify checksum */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint16 checksum_verify; g_autoptr(GBytes) blob_hdr = NULL; @@ -140,7 +230,7 @@ /* extended header items */ if (fu_struct_efi_volume_get_ext_hdr(st_hdr) != 0) { - g_autoptr(GByteArray) st_ext_hdr = NULL; + g_autoptr(FuStructEfiVolumeExtHeader) st_ext_hdr = NULL; goffset offset_ext = fu_struct_efi_volume_get_ext_hdr(st_hdr); st_ext_hdr = fu_struct_efi_volume_ext_header_parse_stream(stream, offset_ext, error); @@ -148,7 +238,7 @@ return FALSE; offset_ext += fu_struct_efi_volume_ext_header_get_size(st_ext_hdr); do { - g_autoptr(GByteArray) st_ext_entry = NULL; + g_autoptr(FuStructEfiVolumeExtEntry) st_ext_entry = NULL; st_ext_entry = fu_struct_efi_volume_ext_entry_parse_stream(stream, offset_ext, error); if (st_ext_entry == NULL) @@ -170,7 +260,7 @@ partial_stream = fu_partial_input_stream_new(stream, hdr_length, fv_length - hdr_length, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut EFI volume: "); + g_prefix_error_literal(error, "failed to cut EFI volume: "); return FALSE; } fu_firmware_set_id(firmware, guid_str); @@ -184,15 +274,26 @@ if (!fu_firmware_parse_stream(img, partial_stream, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return FALSE; - fu_firmware_add_image(firmware, img); + if (!fu_firmware_add_image(firmware, img, error)) + return FALSE; } else if (g_strcmp0(guid_str, FU_EFI_VOLUME_GUID_NVRAM_EVSA) == 0 || g_strcmp0(guid_str, FU_EFI_VOLUME_GUID_NVRAM_EVSA2) == 0) { - g_debug("ignoring %s [%s] EFI FV", guid_str, fu_efi_guid_to_name(guid_str)); - if (!fu_firmware_set_stream(firmware, partial_stream, error)) - return FALSE; + g_autoptr(GError) error_local = NULL; + if (!fu_efi_volume_parse_nvram_evsa(self, + stream, + hdr_length, + flags, + &error_local)) { + g_debug("ignoring %s [%s] EFI FV: %s", + guid_str, + fu_efi_guid_to_name(guid_str), + error_local->message); + if (!fu_firmware_set_stream(firmware, partial_stream, error)) + return FALSE; + } } else { g_warning("no idea how to parse %s [%s] EFI volume", guid_str, @@ -202,17 +303,17 @@ } /* skip the blockmap */ - offset += st_hdr->len; + offset += st_hdr->buf->len; while (offset < streamsz) { guint32 num_blocks; guint32 length; - g_autoptr(GByteArray) st_blk = NULL; + g_autoptr(FuStructEfiVolumeBlockMap) st_blk = NULL; st_blk = fu_struct_efi_volume_block_map_parse_stream(stream, offset, error); if (st_blk == NULL) return FALSE; num_blocks = fu_struct_efi_volume_block_map_get_num_blocks(st_blk); length = fu_struct_efi_volume_block_map_get_length(st_blk); - offset += st_blk->len; + offset += st_blk->buf->len; if (num_blocks == 0x0 && length == 0x0) break; blockmap_sz += (gsize)num_blocks * (gsize)length; @@ -234,13 +335,14 @@ { FuEfiVolume *self = FU_EFI_VOLUME(firmware); FuEfiVolumePrivate *priv = GET_PRIVATE(self); - g_autoptr(GByteArray) buf = fu_struct_efi_volume_new(); - g_autoptr(GByteArray) st_blk = fu_struct_efi_volume_block_map_new(); + g_autoptr(FuStructEfiVolume) st_vol = fu_struct_efi_volume_new(); + g_autoptr(FuStructEfiVolumeBlockMap) st_blk = fu_struct_efi_volume_block_map_new(); fwupd_guid_t guid = {0x0}; guint32 hdr_length = 0x48; guint64 fv_length; + g_autoptr(FuChunkArray) chunks = NULL; g_autoptr(GBytes) img_blob = NULL; - g_autoptr(FuFirmware) img = NULL; + g_autoptr(GPtrArray) images = NULL; /* sanity check */ if (fu_firmware_get_alignment(firmware) > FU_FIRMWARE_ALIGNMENT_1M) { @@ -254,7 +356,7 @@ /* GUID */ if (fu_firmware_get_id(firmware) == NULL) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no GUID set for EFI FV"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no GUID set for FV"); return NULL; } if (!fwupd_guid_from_string(fu_firmware_get_id(firmware), @@ -264,50 +366,72 @@ return NULL; /* length */ - img = fu_firmware_get_image_by_id(firmware, NULL, NULL); - if (img != NULL) { - img_blob = fu_firmware_write(img, error); + images = fu_firmware_get_images(firmware); + if (images->len == 0) { + img_blob = fu_firmware_get_bytes_with_patches(firmware, error); if (img_blob == NULL) { - g_prefix_error(error, "no EFI FV child payload: "); + g_prefix_error_literal(error, "no EFI FV payload: "); return NULL; } } else { - img_blob = fu_firmware_get_bytes_with_patches(firmware, error); - if (img_blob == NULL) { - g_prefix_error(error, "no EFI FV payload: "); - return NULL; + g_autoptr(GByteArray) buf_tmp = g_byte_array_new(); + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) img_blob_tmp = NULL; + + img_blob_tmp = fu_firmware_write(img, error); + if (img_blob_tmp == NULL) { + g_prefix_error_literal(error, "no EFI FV child payload: "); + return NULL; + } + fu_byte_array_append_bytes(buf_tmp, img_blob_tmp); } + img_blob = + g_byte_array_free_to_bytes(g_steal_pointer(&buf_tmp)); /* nocheck:blocked */ } /* pack */ - fu_struct_efi_volume_set_guid(buf, &guid); + fu_struct_efi_volume_set_guid(st_vol, &guid); fv_length = fu_common_align_up(hdr_length + g_bytes_get_size(img_blob), fu_firmware_get_alignment(firmware)); - fu_struct_efi_volume_set_length(buf, fv_length); - fu_struct_efi_volume_set_attrs(buf, + + /* we want a minimum size of volume */ + if (fu_firmware_get_size(firmware) > fv_length) { + g_debug("padding FV from 0x%x to 0x%x", + (guint)fv_length, + (guint)fu_firmware_get_size(firmware)); + fv_length = fu_firmware_get_size(firmware); + } + + fu_struct_efi_volume_set_length(st_vol, fv_length); + fu_struct_efi_volume_set_attrs(st_vol, priv->attrs | ((guint32)fu_firmware_get_alignment(firmware) << 16)); - fu_struct_efi_volume_set_hdr_len(buf, hdr_length); + fu_struct_efi_volume_set_hdr_len(st_vol, hdr_length); /* blockmap */ - fu_struct_efi_volume_block_map_set_num_blocks(st_blk, fv_length); - fu_struct_efi_volume_block_map_set_length(st_blk, 0x1); - g_byte_array_append(buf, st_blk->data, st_blk->len); + chunks = fu_chunk_array_new_virtual(fv_length, + FU_CHUNK_ADDR_OFFSET_NONE, + FU_CHUNK_PAGESZ_NONE, + 0x1000); + fu_struct_efi_volume_block_map_set_num_blocks(st_blk, fu_chunk_array_length(chunks)); + fu_struct_efi_volume_block_map_set_length(st_blk, 0x1000); + fu_byte_array_append_array(st_vol->buf, st_blk->buf); fu_struct_efi_volume_block_map_set_num_blocks(st_blk, 0x0); fu_struct_efi_volume_block_map_set_length(st_blk, 0x0); - g_byte_array_append(buf, st_blk->data, st_blk->len); + fu_byte_array_append_array(st_vol->buf, st_blk->buf); /* fix up checksum */ - fu_struct_efi_volume_set_checksum(buf, - 0x10000 - - fu_sum16w(buf->data, buf->len, G_LITTLE_ENDIAN)); + fu_struct_efi_volume_set_checksum( + st_vol, + 0x10000 - fu_sum16w(st_vol->buf->data, st_vol->buf->len, G_LITTLE_ENDIAN)); /* pad contents to alignment */ - fu_byte_array_append_bytes(buf, img_blob); - fu_byte_array_set_size(buf, fv_length, 0xFF); + fu_byte_array_append_bytes(st_vol->buf, img_blob); + fu_byte_array_set_size(st_vol->buf, fv_length, 0xFF); /* success */ - return g_steal_pointer(&buf); + return g_steal_pointer(&st_vol->buf); } static void @@ -315,7 +439,16 @@ { FuEfiVolumePrivate *priv = GET_PRIVATE(self); priv->attrs = 0xfeff; +#ifdef HAVE_FUZZER + fu_firmware_set_size_max(FU_FIRMWARE(self), 0x100000); /* 1MB */ + fu_firmware_set_images_max(FU_FIRMWARE(self), 10); +#else + fu_firmware_set_size_max(FU_FIRMWARE(self), 0x10000000); /* 256MB */ + fu_firmware_set_images_max(FU_FIRMWARE(self), 1000); +#endif g_type_ensure(FU_TYPE_EFI_FILESYSTEM); + g_type_ensure(FU_TYPE_EFI_VSS2_VARIABLE_STORE); + g_type_ensure(FU_TYPE_EFI_FTW_STORE); } static void diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-vss-auth-variable.c fwupd-2.0.20/libfwupdplugin/fu-efi-vss-auth-variable.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-vss-auth-variable.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-vss-auth-variable.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,319 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#define G_LOG_DOMAIN "FuEfiVssAuthVariable" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-common.h" +#include "fu-efi-common.h" +#include "fu-efi-signature-list.h" +#include "fu-efi-struct.h" +#include "fu-efi-vss-auth-variable.h" +#include "fu-efivars.h" +#include "fu-partial-input-stream.h" +#include "fu-string.h" + +/** + * FuEfiVssAuthVariable: + * + * A NVRAM authenticated variable + * + * See also: [class@FuFirmware] + */ + +struct _FuEfiVssAuthVariable { + FuFirmware parent_instance; + gchar *vendor_guid; + FuEfiVariableAttrs attributes; + FuEfiVariableState state; + FuStructEfiTime *timestamp; /* nullable */ +}; + +G_DEFINE_TYPE(FuEfiVssAuthVariable, fu_efi_vss_auth_variable, FU_TYPE_FIRMWARE) + +/** + * fu_efi_vss_auth_variable_get_state: + * @self: #FuEfiVssAuthVariable + * + * Gets the VSS variable state. + * + * Returns: a #FuEfiVariableState, e.g. %FU_EFI_VARIABLE_STATE_VARIABLE_ADDED + * + * Since: 2.0.17 + **/ +FuEfiVariableState +fu_efi_vss_auth_variable_get_state(FuEfiVssAuthVariable *self) +{ + g_return_val_if_fail(FU_IS_FIRMWARE(self), FU_EFI_VARIABLE_STATE_UNSET); + return self->state; +} + +static void +fu_efi_vss_auth_variable_export(FuFirmware *firmware, + FuFirmwareExportFlags flags, + XbBuilderNode *bn) +{ + FuEfiVssAuthVariable *self = FU_EFI_VSS_AUTH_VARIABLE(firmware); + fu_xmlb_builder_insert_kv(bn, "vendor_guid", self->vendor_guid); + if (self->state != FU_EFI_VARIABLE_STATE_UNSET) { + const gchar *str = fu_efi_variable_state_to_string(self->state); + fu_xmlb_builder_insert_kv(bn, "state", str); + } + if (self->attributes != FU_EFI_VARIABLE_ATTR_NONE) { + g_autofree gchar *str = fu_efi_variable_attrs_to_string(self->attributes); + fu_xmlb_builder_insert_kv(bn, "attributes", str); + } + if (self->timestamp != NULL) { + g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "timestamp", NULL); + fu_efi_timestamp_export(self->timestamp, bc); + } +} + +static GType +fu_efi_vss_auth_variable_lookup_image_gtype(FuEfiVssAuthVariable *self) +{ + struct { + const gchar *guid; + const gchar *name; + GType gtype; + } gtypes[] = { + {FU_EFIVARS_GUID_EFI_GLOBAL, "PK", FU_TYPE_EFI_SIGNATURE_LIST}, + {FU_EFIVARS_GUID_EFI_GLOBAL, "KEK", FU_TYPE_EFI_SIGNATURE_LIST}, + {FU_EFIVARS_GUID_SECURITY_DATABASE, "db", FU_TYPE_EFI_SIGNATURE_LIST}, + {FU_EFIVARS_GUID_SECURITY_DATABASE, "dbx", FU_TYPE_EFI_SIGNATURE_LIST}, + }; + for (guint i = 0; i < G_N_ELEMENTS(gtypes); i++) { + if (g_strcmp0(gtypes[i].guid, self->vendor_guid) == 0 && + g_strcmp0(gtypes[i].name, fu_firmware_get_id(FU_FIRMWARE(self))) == 0) + return gtypes[i].gtype; + } + return G_TYPE_INVALID; +} + +static gboolean +fu_efi_vss_auth_variable_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ + FuEfiVssAuthVariable *self = FU_EFI_VSS_AUTH_VARIABLE(firmware); + gsize offset = 0x0; + GType img_gtype; + g_autoptr(FuStructEfiVssAuthVariableHeader) st = NULL; + g_autoptr(GByteArray) buf_name = NULL; + g_autofree gchar *name = NULL; + + st = fu_struct_efi_vss_auth_variable_header_parse_stream(stream, offset, error); + if (st == NULL) + return FALSE; + if (fu_struct_efi_vss_auth_variable_header_get_start_id(st) == 0xFFFF) { + fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_IS_LAST_IMAGE); + return TRUE; + } + if (fu_struct_efi_vss_auth_variable_header_get_start_id(st) != + FU_STRUCT_EFI_VSS_AUTH_VARIABLE_HEADER_DEFAULT_START_ID) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid VSS variable start ID, expected 0x%x and got 0x%x", + (guint)FU_STRUCT_EFI_VSS_AUTH_VARIABLE_HEADER_DEFAULT_START_ID, + fu_struct_efi_vss_auth_variable_header_get_start_id(st)); + return FALSE; + } + + /* attributes we care about */ + self->vendor_guid = + fwupd_guid_to_string(fu_struct_efi_vss_auth_variable_header_get_vendor_guid(st), + FWUPD_GUID_FLAG_MIXED_ENDIAN); + self->attributes = fu_struct_efi_vss_auth_variable_header_get_attributes(st); + self->state = fu_struct_efi_vss_auth_variable_header_get_state(st); + self->timestamp = fu_struct_efi_vss_auth_variable_header_get_timestamp(st); + + /* read name */ + offset += st->buf->len; + buf_name = fu_input_stream_read_byte_array( + stream, + offset, + fu_struct_efi_vss_auth_variable_header_get_name_size(st), + NULL, + error); + if (buf_name == NULL) + return FALSE; + name = fu_utf16_to_utf8_byte_array(buf_name, G_LITTLE_ENDIAN, error); + if (name == NULL) + return FALSE; + fu_firmware_set_id(firmware, name); + g_debug("added %s: %s", self->vendor_guid, name); + + /* read data */ + offset += fu_struct_efi_vss_auth_variable_header_get_name_size(st); + + /* if this is a well known key then parse it as a child type */ + img_gtype = fu_efi_vss_auth_variable_lookup_image_gtype(self); + if (img_gtype != G_TYPE_INVALID) { + g_autoptr(FuFirmware) img = g_object_new(img_gtype, NULL); + g_autoptr(GInputStream) partial_stream = NULL; + partial_stream = fu_partial_input_stream_new( + stream, + offset, + fu_struct_efi_vss_auth_variable_header_get_data_size(st), + error); + if (partial_stream == NULL) + return FALSE; + if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error)) + return FALSE; + if (!fu_firmware_add_image(firmware, img, error)) + return FALSE; + } else { + g_autoptr(GBytes) data = NULL; + data = fu_input_stream_read_bytes( + stream, + offset, + fu_struct_efi_vss_auth_variable_header_get_data_size(st), + NULL, + error); + if (data == NULL) + return FALSE; + fu_firmware_set_bytes(firmware, data); + } + + /* next header */ + offset += fu_struct_efi_vss_auth_variable_header_get_data_size(st); + + /* success */ + fu_firmware_set_size(firmware, offset); + return TRUE; +} + +static GByteArray * +fu_efi_vss_auth_variable_write(FuFirmware *firmware, GError **error) +{ + FuEfiVssAuthVariable *self = FU_EFI_VSS_AUTH_VARIABLE(firmware); + g_autoptr(FuStructEfiVssAuthVariableHeader) st = + fu_struct_efi_vss_auth_variable_header_new(); + g_autoptr(GBytes) name = NULL; + g_autoptr(GBytes) blob = NULL; + + /* attrs */ + fu_struct_efi_vss_auth_variable_header_set_attributes(st, self->attributes); + fu_struct_efi_vss_auth_variable_header_set_state(st, self->state); + if (self->timestamp != NULL) { + if (!fu_struct_efi_vss_auth_variable_header_set_timestamp(st, + self->timestamp, + error)) + return NULL; + } + + /* name */ + name = fu_utf8_to_utf16_bytes(fu_firmware_get_id(firmware), + G_LITTLE_ENDIAN, + FU_UTF_CONVERT_FLAG_APPEND_NUL, + error); + if (name == NULL) + return NULL; + fu_struct_efi_vss_auth_variable_header_set_name_size(st, g_bytes_get_size(name)); + + /* data */ + blob = fu_firmware_get_image_by_id_bytes(firmware, NULL, NULL); + if (blob == NULL) { + blob = fu_firmware_get_bytes(firmware, error); + if (blob == NULL) + return NULL; + } + fu_struct_efi_vss_auth_variable_header_set_data_size(st, g_bytes_get_size(blob)); + + /* guid */ + if (self->vendor_guid != NULL) { + fwupd_guid_t guid = {0}; + if (!fwupd_guid_from_string(self->vendor_guid, + &guid, + FWUPD_GUID_FLAG_MIXED_ENDIAN, + error)) + return NULL; + fu_struct_efi_vss_auth_variable_header_set_vendor_guid(st, &guid); + } + + /* concat */ + fu_byte_array_append_bytes(st->buf, name); + fu_byte_array_append_bytes(st->buf, blob); + + /* success */ + return g_steal_pointer(&st->buf); +} + +static gboolean +fu_efi_vss_auth_variable_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuEfiVssAuthVariable *self = FU_EFI_VSS_AUTH_VARIABLE(firmware); + const gchar *tmp; + g_autoptr(XbNode) n_timestamp = NULL; + + /* simple properties */ + tmp = xb_node_query_text(n, "vendor_guid", NULL); + if (tmp != NULL) + self->vendor_guid = g_strdup(tmp); + tmp = xb_node_query_text(n, "attributes", NULL); + if (tmp != NULL) + self->attributes = fu_efi_variable_attrs_from_string(tmp); + tmp = xb_node_query_text(n, "state", NULL); + if (tmp != NULL) + self->state = fu_efi_variable_state_from_string(tmp); + + /* EFI_TIME */ + n_timestamp = xb_node_query_first(n, "timestamp", NULL); + if (n_timestamp != NULL) { + self->timestamp = fu_struct_efi_time_new(); + fu_efi_timestamp_build(self->timestamp, n_timestamp); + } + + /* success */ + return TRUE; +} + +static void +fu_efi_vss_auth_variable_init(FuEfiVssAuthVariable *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE); +} + +static void +fu_efi_vss_auth_variable_finalize(GObject *obj) +{ + FuEfiVssAuthVariable *self = FU_EFI_VSS_AUTH_VARIABLE(obj); + if (self->timestamp != NULL) + fu_struct_efi_time_unref(self->timestamp); + g_free(self->vendor_guid); + G_OBJECT_CLASS(fu_efi_vss_auth_variable_parent_class)->finalize(obj); +} + +static void +fu_efi_vss_auth_variable_class_init(FuEfiVssAuthVariableClass *klass) +{ + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_efi_vss_auth_variable_finalize; + firmware_class->parse = fu_efi_vss_auth_variable_parse; + firmware_class->export = fu_efi_vss_auth_variable_export; + firmware_class->write = fu_efi_vss_auth_variable_write; + firmware_class->build = fu_efi_vss_auth_variable_build; +} + +/** + * fu_efi_vss_auth_variable_new: + * + * Creates an empty VSS variable store. + * + * Returns: a #FuFirmware + * + * Since: 2.0.17 + **/ +FuFirmware * +fu_efi_vss_auth_variable_new(void) +{ + return g_object_new(FU_TYPE_EFI_VSS_AUTH_VARIABLE, NULL); +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-vss-auth-variable.h fwupd-2.0.20/libfwupdplugin/fu-efi-vss-auth-variable.h --- fwupd-2.0.8/libfwupdplugin/fu-efi-vss-auth-variable.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-vss-auth-variable.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_EFI_VSS_AUTH_VARIABLE (fu_efi_vss_auth_variable_get_type()) +G_DECLARE_FINAL_TYPE(FuEfiVssAuthVariable, + fu_efi_vss_auth_variable, + FU, + EFI_VSS_AUTH_VARIABLE, + FuFirmware) + +FuEfiVariableState +fu_efi_vss_auth_variable_get_state(FuEfiVssAuthVariable *self) G_GNUC_NON_NULL(1); + +FuFirmware * +fu_efi_vss_auth_variable_new(void); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-vss2-variable-store.c fwupd-2.0.20/libfwupdplugin/fu-efi-vss2-variable-store.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-vss2-variable-store.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-vss2-variable-store.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,183 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#define G_LOG_DOMAIN "FuEfiVss2VariableStore" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-common.h" +#include "fu-efi-struct.h" +#include "fu-efi-vss-auth-variable.h" +#include "fu-efi-vss2-variable-store.h" +#include "fu-string.h" + +/** + * FuEfiVss2VariableStore: + * + * A NVRAM variable store. + * + * See also: [class@FuFirmware] + */ + +struct _FuEfiVss2VariableStore { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE(FuEfiVss2VariableStore, fu_efi_vss2_variable_store, FU_TYPE_FIRMWARE) + +static gboolean +fu_efi_vss2_variable_store_validate(FuFirmware *firmware, + GInputStream *stream, + gsize offset, + GError **error) +{ + return fu_struct_efi_vss2_variable_store_header_validate_stream(stream, offset, error); +} + +static gboolean +fu_efi_vss2_variable_store_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ + gsize offset = 0x0; + g_autoptr(FuStructEfiVss2VariableStoreHeader) st = NULL; + + st = fu_struct_efi_vss2_variable_store_header_parse_stream(stream, offset, error); + if (st == NULL) + return FALSE; + + /* sanity check */ + if (fu_struct_efi_vss2_variable_store_header_get_size(st) > + fu_firmware_get_size_max(firmware)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "VSS store larger than max size: 0x%x > 0x%x", + (guint)fu_struct_efi_vss2_variable_store_header_get_size(st), + (guint)fu_firmware_get_size_max(firmware)); + return FALSE; + } + + /* parse each attr */ + offset += st->buf->len; + while (offset < fu_struct_efi_vss2_variable_store_header_get_size(st)) { + g_autoptr(FuFirmware) img = fu_efi_vss_auth_variable_new(); + + if (!fu_firmware_parse_stream(img, stream, offset, flags, error)) { + g_prefix_error(error, "offset @0x%x: ", (guint)offset); + return FALSE; + } + if (fu_firmware_has_flag(img, FU_FIRMWARE_FLAG_IS_LAST_IMAGE)) + break; + if (fu_firmware_get_size(img) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "VSS2 store entry has zero size"); + return FALSE; + } + if (fu_efi_vss_auth_variable_get_state(FU_EFI_VSS_AUTH_VARIABLE(img)) == + FU_EFI_VARIABLE_STATE_VARIABLE_ADDED) { + fu_firmware_set_offset(img, offset); + if (!fu_firmware_add_image(firmware, img, error)) + return FALSE; + } + offset += fu_firmware_get_size(img); + offset = fu_common_align_up(offset, FU_FIRMWARE_ALIGNMENT_4); + } + + /* success */ + fu_firmware_set_size(firmware, fu_struct_efi_vss2_variable_store_header_get_size(st)); + return TRUE; +} + +static GByteArray * +fu_efi_vss2_variable_store_write(FuFirmware *firmware, GError **error) +{ + g_autoptr(FuStructEfiVss2VariableStoreHeader) st = + fu_struct_efi_vss2_variable_store_header_new(); + g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); + + /* sanity check */ + if (fu_firmware_get_size(firmware) < st->buf->len) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "VSS2 variable store has zero size"); + return NULL; + } + + /* each attr */ + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + g_autoptr(GBytes) fw = NULL; + + fw = fu_firmware_write(img, error); + if (fw == NULL) + return NULL; + fu_byte_array_append_bytes(st->buf, fw); + fu_byte_array_align_up(st->buf, FU_FIRMWARE_ALIGNMENT_4, 0xFF); + } + + /* sanity check */ + if (st->buf->len > fu_firmware_get_size(firmware)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "VSS2 store is too small, needed 0x%x but defined as 0x%x", + st->buf->len, + (guint)(fu_firmware_get_size(firmware))); + return NULL; + } + + /* fix up header and attrs */ + fu_byte_array_set_size(st->buf, fu_firmware_get_size(firmware), 0xFF); + fu_struct_efi_vss2_variable_store_header_set_size(st, fu_firmware_get_size(firmware)); + + /* success */ + return g_steal_pointer(&st->buf); +} + +static void +fu_efi_vss2_variable_store_init(FuEfiVss2VariableStore *self) +{ + g_type_ensure(FU_TYPE_EFI_VSS_AUTH_VARIABLE); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_DEDUPE_ID); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE); +#ifdef HAVE_FUZZER + fu_firmware_set_images_max(FU_FIRMWARE(self), 10); + fu_firmware_set_size_max(FU_FIRMWARE(self), 0x1000); /* 4KB */ +#else + fu_firmware_set_images_max(FU_FIRMWARE(self), 10000); + fu_firmware_set_size_max(FU_FIRMWARE(self), 0x1000000); /* 16MB */ +#endif +} + +static void +fu_efi_vss2_variable_store_class_init(FuEfiVss2VariableStoreClass *klass) +{ + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + firmware_class->validate = fu_efi_vss2_variable_store_validate; + firmware_class->parse = fu_efi_vss2_variable_store_parse; + firmware_class->write = fu_efi_vss2_variable_store_write; +} + +/** + * fu_efi_vss2_variable_store_new: + * + * Creates an empty VSS variable store. + * + * Returns: a #FuFirmware + * + * Since: 2.0.17 + **/ +FuFirmware * +fu_efi_vss2_variable_store_new(void) +{ + return g_object_new(FU_TYPE_EFI_VSS2_VARIABLE_STORE, NULL); +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-vss2-variable-store.h fwupd-2.0.20/libfwupdplugin/fu-efi-vss2-variable-store.h --- fwupd-2.0.8/libfwupdplugin/fu-efi-vss2-variable-store.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-vss2-variable-store.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_EFI_VSS2_VARIABLE_STORE (fu_efi_vss2_variable_store_get_type()) +G_DECLARE_FINAL_TYPE(FuEfiVss2VariableStore, + fu_efi_vss2_variable_store, + FU, + EFI_VSS2_VARIABLE_STORE, + FuFirmware) + +FuFirmware * +fu_efi_vss2_variable_store_new(void); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-x509-device.c fwupd-2.0.20/libfwupdplugin/fu-efi-x509-device.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-x509-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-x509-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,6 +8,8 @@ #include "config.h" +#include "fu-archive-firmware.h" +#include "fu-efi-variable-authentication2.h" #include "fu-efi-x509-device.h" #include "fu-version-common.h" @@ -30,6 +32,9 @@ { FuEfiX509Device *self = FU_EFI_X509_DEVICE(device); FuEfiX509DevicePrivate *priv = GET_PRIVATE(self); + const gchar *subject_name; + const gchar *subject_vendor; + g_autofree gchar *logical_id = NULL; /* sanity check */ if (priv->sig == NULL) { @@ -37,34 +42,62 @@ return FALSE; } - /* these have to exist */ - fu_device_add_instance_strsafe(device, - "VENDOR", - fu_efi_x509_signature_get_subject_vendor(priv->sig)); - fu_device_add_instance_strsafe(device, - "NAME", - fu_efi_x509_signature_get_subject_name(priv->sig)); - if (!fu_device_build_instance_id(device, error, "UEFI", "VENDOR", "NAME", NULL)) - return FALSE; - fu_device_set_name(device, fu_efi_x509_signature_get_subject_name(priv->sig)); - fu_device_set_vendor(device, fu_efi_x509_signature_get_subject_vendor(priv->sig)); + /* the O= key may not exist */ + subject_name = fu_efi_x509_signature_get_subject_name(priv->sig); + subject_vendor = fu_efi_x509_signature_get_subject_vendor(priv->sig); + fu_device_add_instance_strsafe(device, "VENDOR", subject_vendor); + fu_device_add_instance_strsafe(device, "NAME", subject_name); + fu_device_build_instance_id(device, NULL, "UEFI", "VENDOR", "NAME", NULL); + fu_device_set_name(device, subject_name != NULL ? subject_name : "Unknown"); + if (subject_vendor != NULL) { + /* build this here as well as from notify::vendor to ensure + * we get the "from hardware" and "quirk simplified" values */ + fu_device_build_vendor_id(device, "UEFI", subject_vendor); + fu_device_set_vendor(device, subject_vendor); + } fu_device_set_version_raw(device, fu_firmware_get_version_raw(FU_FIRMWARE(priv->sig))); - fu_device_set_logical_id(device, fu_firmware_get_id(FU_FIRMWARE(priv->sig))); - fu_device_build_vendor_id(device, - "UEFI", - fu_efi_x509_signature_get_subject_vendor(priv->sig)); + + /* the device ID (and thus the logical ID) needs to stay the same between versions */ + logical_id = g_strdup_printf("%s:%s", + subject_name != NULL ? subject_name : "UNKNOWN", + subject_vendor != NULL ? subject_vendor : "UNKNOWN"); + fu_device_set_logical_id(device, logical_id); /* success */ fu_device_add_instance_strup(device, "CRT", fu_firmware_get_id(FU_FIRMWARE(priv->sig))); return fu_device_build_instance_id(device, error, "UEFI", "CRT", NULL); } +static void +fu_efi_x509_device_vendor_notify_cb(FuDevice *device, GParamSpec *pspec, gpointer user_data) +{ + const gchar *subject_vendor = fu_device_get_vendor(device); + if (subject_vendor != NULL) + fu_device_build_vendor_id(device, "UEFI", subject_vendor); +} + static gchar * fu_efi_x509_device_convert_version(FuDevice *device, guint64 version_raw) { return fu_version_from_uint64(version_raw, fu_device_get_version_format(device)); } +static FuFirmware * +fu_efi_x509_device_prepare_firmware(FuDevice *device, + GInputStream *stream, + FuProgress *progress, + FuFirmwareParseFlags flags, + GError **error) +{ + return fu_firmware_new_from_gtypes(stream, + 0x0, + flags, + error, + FU_TYPE_EFI_VARIABLE_AUTHENTICATION2, + FU_TYPE_ARCHIVE_FIRMWARE, + G_TYPE_INVALID); +} + static gboolean fu_efi_x509_device_write_firmware(FuDevice *device, FuFirmware *firmware, @@ -74,28 +107,55 @@ { FuDeviceClass *device_class; FuDevice *proxy; + g_autoptr(GPtrArray) imgs = NULL; + + /* not an archive */ + if (FU_IS_EFI_VARIABLE_AUTHENTICATION2(firmware)) { + imgs = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + g_ptr_array_add(imgs, g_object_ref(firmware)); + } else { + imgs = fu_firmware_get_images(firmware); + } + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + fu_progress_set_steps(progress, imgs->len); /* process by the parent */ - proxy = fu_device_get_proxy(device); - if (proxy == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no proxy device assigned"); + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) return FALSE; - } device_class = FU_DEVICE_GET_CLASS(proxy); - return device_class->write_firmware(proxy, firmware, progress, flags, error); + + /* install each blob */ + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + g_autoptr(GBytes) fw = NULL; + + g_debug("installing %s", fu_firmware_get_id(img)); + fw = fu_firmware_get_bytes(img, error); + if (fw == NULL) + return FALSE; + if (!device_class->write_firmware(proxy, img, progress, flags, error)) { + g_prefix_error(error, "failed to write %s: ", fu_firmware_get_id(img)); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success! */ + return TRUE; } static void -fu_efi_x509_device_set_progress(FuDevice *self, FuProgress *progress) +fu_efi_x509_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 80, "prepare-fw"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 20, "write"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); } @@ -104,12 +164,18 @@ fu_efi_x509_device_init(FuEfiX509Device *self) { fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_DEVICE); fu_device_add_protocol(FU_DEVICE(self), "org.uefi.dbx2"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); fu_device_add_icon(FU_DEVICE(self), "application-certificate"); + g_signal_connect(FU_DEVICE(self), + "notify::vendor", + G_CALLBACK(fu_efi_x509_device_vendor_notify_cb), + NULL); } static void @@ -130,6 +196,7 @@ object_class->finalize = fu_efi_x509_device_finalize; device_class->probe = fu_efi_x509_device_probe; device_class->convert_version = fu_efi_x509_device_convert_version; + device_class->prepare_firmware = fu_efi_x509_device_prepare_firmware; device_class->write_firmware = fu_efi_x509_device_write_firmware; device_class->set_progress = fu_efi_x509_device_set_progress; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-x509-signature-private.h fwupd-2.0.20/libfwupdplugin/fu-efi-x509-signature-private.h --- fwupd-2.0.8/libfwupdplugin/fu-efi-x509-signature-private.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-x509-signature-private.h 2026-02-26 11:36:18.000000000 +0000 @@ -15,3 +15,5 @@ void fu_efi_x509_signature_set_subject(FuEfiX509Signature *self, const gchar *subject) G_GNUC_NON_NULL(1); +gchar * +fu_efi_x509_signature_build_dedupe_key(FuEfiX509Signature *self) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi-x509-signature.c fwupd-2.0.20/libfwupdplugin/fu-efi-x509-signature.c --- fwupd-2.0.8/libfwupdplugin/fu-efi-x509-signature.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi-x509-signature.c 2026-02-26 11:36:18.000000000 +0000 @@ -16,21 +16,7 @@ #include "fu-efi-x509-signature-private.h" #include "fu-string.h" #include "fu-version-common.h" - -#ifdef HAVE_GNUTLS -static void -fu_efi_x509_signature_gnutls_datum_deinit(gnutls_datum_t *d) -{ - gnutls_free(d->data); - gnutls_free(d); -} - -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(gnutls_datum_t, fu_efi_x509_signature_gnutls_datum_deinit) -G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_crt_t, gnutls_x509_crt_deinit, NULL) -#pragma clang diagnostic pop -#endif +#include "fu-x509-certificate.h" /** * FuEfiX509Signature: @@ -41,7 +27,7 @@ */ struct _FuEfiX509Signature { - FuFirmware parent_instance; + FuEfiSignature parent_instance; gchar *issuer; gchar *subject; gchar *subject_name; @@ -143,6 +129,20 @@ } /* private */ +gchar * +fu_efi_x509_signature_build_dedupe_key(FuEfiX509Signature *self) +{ + g_return_val_if_fail(FU_IS_EFI_X509_SIGNATURE(self), NULL); + + /* in 2023 Microsoft renamed "Microsoft Windows Production PCA" -> "Windows UEFI CA" */ + if (g_strcmp0(self->subject_vendor, "Microsoft") == 0 && + g_strcmp0(self->subject_name, "Microsoft Windows Production PCA") == 0) { + return g_strdup("Microsoft:Windows UEFI CA"); + } + return g_strdup_printf("%s:%s", self->subject_vendor, self->subject_name); +} + +/* private */ void fu_efi_x509_signature_set_subject(FuEfiX509Signature *self, const gchar *subject) { @@ -222,21 +222,11 @@ static gboolean fu_efi_x509_signature_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { -#ifdef HAVE_GNUTLS FuEfiX509Signature *self = FU_EFI_X509_SIGNATURE(firmware); - gchar buf[1024] = {'\0'}; - guchar key_id[20] = {'\0'}; - gsize key_idsz = sizeof(key_id); - gnutls_datum_t d = {0}; - gnutls_x509_dn_t dn = {0x0}; - gsize bufsz = sizeof(buf); - int rc; - g_autofree gchar *key_idstr = NULL; - g_auto(gnutls_x509_crt_t) crt = NULL; - g_autoptr(gnutls_datum_t) subject = NULL; + g_autoptr(FuX509Certificate) crt = fu_x509_certificate_new(); g_autoptr(GBytes) blob = NULL; /* set bytes */ @@ -248,72 +238,33 @@ blob = fu_firmware_get_bytes(firmware, error); if (blob == NULL) return FALSE; - d.size = g_bytes_get_size(blob); - d.data = (unsigned char *)g_bytes_get_data(blob, NULL); - rc = gnutls_x509_crt_init(&crt); - if (rc < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "crt_init: %s [%i]", - gnutls_strerror(rc), - rc); - return FALSE; - } - rc = gnutls_x509_crt_import(crt, &d, GNUTLS_X509_FMT_DER); - if (rc < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "crt_import: %s [%i]", - gnutls_strerror(rc), - rc); + if (!fu_firmware_parse_bytes(FU_FIRMWARE(crt), blob, 0x0, flags, error)) return FALSE; + fu_firmware_set_id(firmware, fu_firmware_get_id(FU_FIRMWARE(crt))); + fu_efi_x509_signature_set_issuer(self, fu_x509_certificate_get_issuer(crt)); + fu_efi_x509_signature_set_subject(self, fu_x509_certificate_get_subject(crt)); + + /* no year in the subject, fall back */ + if (fu_firmware_get_version_raw(FU_FIRMWARE(self)) == 0) { + g_autoptr(GDateTime) dt = fu_x509_certificate_get_activation_time(crt); + if (dt != NULL) { + g_debug("falling back to activation time %u", + (guint)g_date_time_get_year(dt)); + fu_firmware_set_version_raw(FU_FIRMWARE(self), g_date_time_get_year(dt)); + } } - /* issuer */ - if (gnutls_x509_crt_get_issuer_dn(crt, buf, &bufsz) == GNUTLS_E_SUCCESS) { - g_autofree gchar *str = fu_strsafe((const gchar *)buf, bufsz); - fu_efi_x509_signature_set_issuer(self, str); - } - - /* subject */ - subject = (gnutls_datum_t *)gnutls_malloc(sizeof(gnutls_datum_t)); - if (gnutls_x509_crt_get_subject(crt, &dn) == GNUTLS_E_SUCCESS) { - g_autofree gchar *str = NULL; - gnutls_x509_dn_get_str(dn, subject); - str = fu_strsafe((const gchar *)subject->data, subject->size); - fu_efi_x509_signature_set_subject(self, str); - } - - /* key ID */ - rc = gnutls_x509_crt_get_key_id(crt, 0, key_id, &key_idsz); - if (rc < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "failed to get key ID: %s [%i]", - gnutls_strerror(rc), - rc); - return FALSE; - } - key_idstr = g_compute_checksum_for_data(G_CHECKSUM_SHA1, key_id, key_idsz); - if (key_idstr == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "failed to calculate key ID for 0x%x bytes", - (guint)key_idsz); - return FALSE; + /* set something plausible */ + if (fu_firmware_get_filename(firmware) == NULL && + fu_x509_certificate_get_subject(crt) != NULL) { + g_autofree gchar *filename = g_strdup_printf("%s_%s.der", + fu_firmware_get_id(FU_FIRMWARE(crt)), + fu_x509_certificate_get_subject(crt)); + fu_firmware_set_filename(firmware, filename); } - fu_firmware_set_id(firmware, key_idstr); /* success */ return TRUE; -#else - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no GnuTLS support"); - return FALSE; -#endif } static gchar * diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efi.rs fwupd-2.0.20/libfwupdplugin/fu-efi.rs --- fwupd-2.0.8/libfwupdplugin/fu-efi.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efi.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,46 @@ // Copyright 2023 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later +// EFI_STATUS is u64le on 64-bit and u32le on 32-bit -- but be pragmatic +#[repr(u64le)] +#[derive(ToString)] +enum FuEfiStatus { + Success = 0, + LoadError = 1, + InvalidParameter = 2, + Unsupported = 3, + BadBufferSize = 4, + BufferTooSmall = 5, + NotReady = 6, + DeviceError = 7, + WriteProtected = 8, + OutOfResources = 9, + VolumeCorrupted = 10, + VolumeFull = 11, + NoMedia = 12, + MediaChanged = 13, + NotFound = 14, + AccessDenied = 15, + NoResponse = 16, + NoMapping = 17, + Timeout = 18, + NotStarted = 19, + AlreadyStarted = 20, + Aborted = 21, + IcmpError = 22, + TftpError = 23, + ProtocolError = 24, + IncompatibleVersion = 25, + SecurityViolation = 26, + CrcError = 27, + EndOfMedia = 28, + EndOfFile = 31, + InvalidLanguage = 32, + CompromisedData = 33, + IpAddressConflict = 34, + HttpError = 35, +} + #[derive(ToString, FromString)] enum FuEfiSignatureKind { Unknown, @@ -118,10 +158,11 @@ type: FuEfiSectionType, } -#[derive(ParseStream)] +#[derive(ParseStream, Default)] #[repr(C, packed)] struct FuStructEfiSection2 { - _base: FuStructEfiSection, + size: u24le == 0xFFFFFF, + type: FuEfiSectionType, extended_size: u32le, } @@ -183,6 +224,38 @@ length: u32le, } +#[derive(Getters, New, ToString, NewInternal, ValidateInternal)] +#[repr(C, packed)] +struct FuStructEfiTime { + year: u16le, + month: u8, + day: u8, + hour: u8, + minute: u8, + second: u8, + _pad1: u8, + nanosecond: u32le, + timezone: u16le, + daylight: u8, + _pad2: u8, +} + +#[derive(Default, Setters)] +#[repr(C, packed)] +struct FuStructEfiWinCertificate { + length: u32le = $struct_size, + revision: u16le == 0x0200, + certificate_type: u16le == 0x0EF1, + guid: Guid == "4aafd29d-68df-49ee-8aa9-347d375665a7", +} + +#[derive(New, ParseStream, ValidateStream)] +#[repr(C, packed)] +struct FuStructEfiVariableAuthentication2 { + timestamp: FuStructEfiTime, + auth_info: FuStructEfiWinCertificate, +} + #[derive(New, ParseStream)] #[repr(C, packed)] struct FuStructEfiSignatureList { @@ -277,6 +350,81 @@ signature_type: FuEfiHardDriveDevicePathSignatureType = Guid, } +#[repr(u8)] +#[derive(ToString, FromString)] +enum FuEfiVariableStoreState { + Unset = 0x00, + Healthy = 0xFE, + Empty = 0xFF, +} + +#[repr(u8)] +enum FuEfiVariableStoreFormat { + Formatted = 0x5A, +} + +#[derive(ParseStream, ValidateStream, Default, New)] +#[repr(C, packed)] +struct FuStructEfiVss2VariableStoreHeader { + signature: Guid == "aaf32c78-947b-439a-a180-2e144ec37792", + size: u32le, // size of variable store, including store header + format: FuEfiVariableStoreFormat == Formatted, + state: FuEfiVariableStoreState == Healthy, + _reserved: u16le, + _reserved1: u32le, +} + +#[repr(u8)] +#[derive(ToString, FromString)] +enum FuEfiVariableState { + Unset = 0x00, + VariableInDeletedTransition = 0xFE, + VariableDeleted = 0xFD, + VariableHeaderValid = 0x7F, + VariableAdded = 0x3F, + IntelVariableValid = 0xFC, + IntelVariableInvalid = 0xF8, +} + +#[derive(ToString, FromString, Bitfield)] +#[repr(u32le)] +enum FuEfiVariableAttrs { + None = 0x00000000, + NonVolatile = 0x00000001, + BootserviceAccess = 0x00000002, + RuntimeAccess = 0x00000004, + HardwareErrorRecord = 0x00000008, + AuthenticatedWriteAccess = 0x00000010, + TimeBasedAuthenticatedWriteAccess = 0x00000020, + AppendWrite = 0x00000040, +} + +// authenticated variable header, used for SecureBoot vars +#[derive(ParseStream, Default, New)] +#[repr(C, packed)] +struct FuStructEfiVssAuthVariableHeader { + start_id: u16le = 0x55AA, + state: FuEfiVariableState, + reserved: u8, + attributes: FuEfiVariableAttrs, + monotonic_counter: u64le, + timestamp: FuStructEfiTime, + pubkey_index: u32le, + name_size: u32le, // null-terminated UCS2 string + data_size: u32le, // size of variable data without header and name + vendor_guid: Guid, +} + +#[derive(ParseStream, ValidateStream, Default, New)] +#[repr(C, packed)] +struct FuStructEfiFaultTolerantWorkingBlockHeader64 { + signature: Guid == "9e58292b-7c68-497d-a0ce-6500fd9f1b95", + crc: u32le = 0xFFFFFFFF, + state: FuEfiVariableStoreState = Empty, + reserved: [u8; 3] = [0xFF; 3], + write_queue_size: u64le, +} + #[derive(ParseStream, New, Default)] #[repr(C, packed)] struct FuStructShimHive { @@ -296,3 +444,10 @@ // key string, no trailing NUL // value string, no trailing NUL } + +#[repr(u32le)] +enum FuEfiCapsuleHeaderFlags { + PersistAcrossReset = 1 << 16, + PopulateSystemTable = 1 << 17, + InitiateReset = 1 << 18, +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efivars.c fwupd-2.0.20/libfwupdplugin/fu-efivars.c --- fwupd-2.0.8/libfwupdplugin/fu-efivars.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efivars.c 2026-02-26 11:36:18.000000000 +0000 @@ -16,7 +16,6 @@ #include "fu-efivars-private.h" #include "fu-mem.h" #include "fu-pefile-firmware.h" -#include "fu-volume-private.h" G_DEFINE_TYPE(FuEfivars, fu_efivars, G_TYPE_OBJECT) @@ -141,7 +140,7 @@ * @name: Variable name * @data: Data to set * @data_sz: size of data - * @attr: Attributes + * @attr: (nullable) (out): #FuEfiVariableAttrs, e.g. %FU_EFI_VARIABLE_ATTR_NON_VOLATILE * @error: (nullable): optional return location for an error * * Gets the data from a UEFI variable in NVRAM @@ -156,7 +155,7 @@ const gchar *name, guint8 **data, gsize *data_sz, - guint32 *attr, + FuEfiVariableAttrs *attr, GError **error) { FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self); @@ -178,7 +177,7 @@ * @self: a #FuEfivars * @guid: Globally unique identifier * @name: Variable name - * @attr: (nullable): Attributes + * @attr: (out): #FuEfiVariableAttrs, e.g. %FU_EFI_VARIABLE_ATTR_NON_VOLATILE * @error: (nullable): optional return location for an error * * Gets the data from a UEFI variable in NVRAM @@ -191,7 +190,7 @@ fu_efivars_get_data_bytes(FuEfivars *self, const gchar *guid, const gchar *name, - guint32 *attr, + FuEfiVariableAttrs *attr, GError **error) { guint8 *data = NULL; @@ -293,13 +292,39 @@ } /** + * fu_efivars_space_free: + * @self: a #FuEfivars + * @error: (nullable): optional return location for an error + * + * Gets the free size available for new EFI variables, as reported from QueryVariableInfo. + * + * Returns: free space in bytes, or %G_MAXUINT64 on error + * + * Since: 2.0.12 + **/ +guint64 +fu_efivars_space_free(FuEfivars *self, GError **error) +{ + FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self); + + g_return_val_if_fail(FU_IS_EFIVARS(self), G_MAXUINT64); + g_return_val_if_fail(error == NULL || *error == NULL, G_MAXUINT64); + + if (efivars_class->space_free == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported"); + return G_MAXUINT64; + } + return efivars_class->space_free(self, error); +} + +/** * fu_efivars_set_data: * @self: a #FuEfivars * @guid: Globally unique identifier * @name: Variable name * @data: Data to set * @sz: size of @data - * @attr: Attributes + * @attr: #FuEfiVariableAttrs, e.g. %FU_EFI_VARIABLE_ATTR_NON_VOLATILE * @error: (nullable): optional return location for an error * * Sets the data to a UEFI variable in NVRAM @@ -314,7 +339,7 @@ const gchar *name, const guint8 *data, gsize sz, - guint32 attr, + FuEfiVariableAttrs attr, GError **error) { FuEfivarsClass *efivars_class = FU_EFIVARS_GET_CLASS(self); @@ -338,7 +363,7 @@ * @guid: globally unique identifier * @name: variable name * @bytes: data blob - * @attr: attributes + * @attr: #FuEfiVariableAttrs, e.g. %FU_EFI_VARIABLE_ATTR_NON_VOLATILE * @error: (nullable): optional return location for an error * * Sets the data to a UEFI variable in NVRAM @@ -352,7 +377,7 @@ const gchar *guid, const gchar *name, GBytes *bytes, - guint32 attr, + FuEfiVariableAttrs attr, GError **error) { gsize bufsz = 0; @@ -432,7 +457,7 @@ "SecureBoot", &value, sizeof(value), - FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS, + FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS, error); } @@ -500,9 +525,9 @@ "BootNext", buf, sizeof(buf), - FU_EFIVARS_ATTR_NON_VOLATILE | - FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVARS_ATTR_RUNTIME_ACCESS, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE | + FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS, error); } @@ -563,7 +588,8 @@ "BootCurrent", buf, sizeof(buf), - FU_EFIVARS_ATTR_NON_VOLATILE | FU_EFIVARS_ATTR_RUNTIME_ACCESS, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS, error); } @@ -639,9 +665,9 @@ "BootOrder", buf->data, buf->len, - FU_EFIVARS_ATTR_NON_VOLATILE | - FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVARS_ATTR_RUNTIME_ACCESS, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE | + FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS, error); } @@ -724,7 +750,8 @@ g_autoptr(GBytes) img_blob = g_bytes_new_static("hello", 5); fu_firmware_set_id(img_text, ".text"); fu_firmware_set_bytes(img_text, img_blob); - fu_firmware_add_image(pefile, img_text); + if (!fu_firmware_add_image(pefile, img_text, error)) + return FALSE; if (!fu_firmware_write_file(pefile, file, error)) return FALSE; } @@ -735,11 +762,14 @@ dp_fp = fu_efi_file_path_device_path_new(); if (!fu_efi_file_path_device_path_set_name(dp_fp, target, error)) return FALSE; - fu_firmware_add_image(FU_FIRMWARE(devpath_list), FU_FIRMWARE(dp_hdd)); - fu_firmware_add_image(FU_FIRMWARE(devpath_list), FU_FIRMWARE(dp_fp)); + if (!fu_firmware_add_image(FU_FIRMWARE(devpath_list), FU_FIRMWARE(dp_hdd), error)) + return FALSE; + if (!fu_firmware_add_image(FU_FIRMWARE(devpath_list), FU_FIRMWARE(dp_fp), error)) + return FALSE; fu_firmware_set_id(FU_FIRMWARE(entry), name); - fu_firmware_add_image(FU_FIRMWARE(entry), FU_FIRMWARE(devpath_list)); + if (!fu_firmware_add_image(FU_FIRMWARE(entry), FU_FIRMWARE(devpath_list), error)) + return FALSE; return fu_efivars_set_boot_entry(self, idx, entry, error); } @@ -790,9 +820,9 @@ FU_EFIVARS_GUID_EFI_GLOBAL, name, blob, - FU_EFIVARS_ATTR_NON_VOLATILE | - FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVARS_ATTR_RUNTIME_ACCESS, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE | + FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS, error); } @@ -825,7 +855,7 @@ if (!fu_firmware_parse_bytes(FU_FIRMWARE(loadopt), blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, error)) return NULL; fu_firmware_set_idx(FU_FIRMWARE(loadopt), idx); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-efivars.h fwupd-2.0.20/libfwupdplugin/fu-efivars.h --- fwupd-2.0.8/libfwupdplugin/fu-efivars.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-efivars.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,6 +8,7 @@ #pragma once #include "fu-efi-load-option.h" +#include "fu-efi-struct.h" #include "fu-volume.h" #define FU_TYPE_EFIVARS (fu_efivars_get_type()) @@ -17,6 +18,7 @@ GObjectClass parent_class; gboolean (*supported)(FuEfivars *self, GError **error) G_GNUC_NON_NULL(1); guint64 (*space_used)(FuEfivars *self, GError **error) G_GNUC_NON_NULL(1); + guint64 (*space_free)(FuEfivars *self, GError **error) G_GNUC_NON_NULL(1); gboolean (*exists)(FuEfivars *self, const gchar *guid, const gchar *name) G_GNUC_NON_NULL(1, 2); GFileMonitor *(*get_monitor)(FuEfivars *self, @@ -28,14 +30,14 @@ const gchar *name, guint8 **data, gsize *data_sz, - guint32 *attr, + FuEfiVariableAttrs *attr, GError **error) G_GNUC_NON_NULL(1, 2, 3); gboolean (*set_data)(FuEfivars *self, const gchar *guid, const gchar *name, const guint8 *data, gsize sz, - guint32 attr, + FuEfiVariableAttrs attr, GError **error) G_GNUC_NON_NULL(1, 2, 3); gboolean (*delete)(FuEfivars *self, const gchar *guid, const gchar *name, GError **error) G_GNUC_NON_NULL(1, 2, 3); @@ -52,24 +54,17 @@ #define FU_EFIVARS_GUID_FWUPDATE "0abba7dc-e516-4167-bbf5-4d9d1c739416" #define FU_EFIVARS_GUID_UX_CAPSULE "3b8c8162-188c-46a4-aec9-be43f1d65697" #define FU_EFIVARS_GUID_SECURITY_DATABASE "d719b2cb-3d3a-4596-a3bc-dad00e67656f" -#define FU_EFIVARS_GUID_UX_CAPSULE "3b8c8162-188c-46a4-aec9-be43f1d65697" #define FU_EFIVARS_GUID_EFI_CAPSULE_REPORT "39b68c46-f7fb-441b-b6ec-16b0f69821f3" #define FU_EFIVARS_GUID_SHIM "605dab50-e046-4300-abb6-3dd810dd8b23" -#define FU_EFIVARS_ATTR_NON_VOLATILE (1 << 0) -#define FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS (1 << 1) -#define FU_EFIVARS_ATTR_RUNTIME_ACCESS (1 << 2) -#define FU_EFIVARS_ATTR_HARDWARE_ERROR_RECORD (1 << 3) -#define FU_EFIVARS_ATTR_AUTHENTICATED_WRITE_ACCESS (1 << 4) -#define FU_EFIVARS_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS (1 << 5) -#define FU_EFIVARS_ATTR_APPEND_WRITE (1 << 6) - FuEfivars * fu_efivars_new(void); gboolean fu_efivars_supported(FuEfivars *self, GError **error) G_GNUC_NON_NULL(1); guint64 fu_efivars_space_used(FuEfivars *self, GError **error) G_GNUC_NON_NULL(1); +guint64 +fu_efivars_space_free(FuEfivars *self, GError **error) G_GNUC_NON_NULL(1); gboolean fu_efivars_exists(FuEfivars *self, const gchar *guid, const gchar *name) G_GNUC_NON_NULL(1, 2); GFileMonitor * @@ -81,13 +76,13 @@ const gchar *name, guint8 **data, gsize *data_sz, - guint32 *attr, + FuEfiVariableAttrs *attr, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2, 3); GBytes * fu_efivars_get_data_bytes(FuEfivars *self, const gchar *guid, const gchar *name, - guint32 *attr, + FuEfiVariableAttrs *attr, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2, 3); gboolean fu_efivars_set_data(FuEfivars *self, @@ -95,14 +90,14 @@ const gchar *name, const guint8 *data, gsize sz, - guint32 attr, + FuEfiVariableAttrs attr, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2, 3); gboolean fu_efivars_set_data_bytes(FuEfivars *self, const gchar *guid, const gchar *name, GBytes *bytes, - guint32 attr, + FuEfiVariableAttrs attr, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2, 3); gboolean fu_efivars_delete(FuEfivars *self, const gchar *guid, const gchar *name, GError **error) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-elf-firmware.c fwupd-2.0.20/libfwupdplugin/fu-elf-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-elf-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-elf-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,6 @@ #include "config.h" #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-elf-firmware.h" #include "fu-elf-struct.h" #include "fu-input-stream.h" @@ -37,7 +36,7 @@ static gboolean fu_elf_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize offset_secthdr = 0; @@ -45,10 +44,10 @@ guint16 phentsize; guint16 phnum; guint16 shnum; - g_autoptr(GByteArray) st_fhdr = NULL; + g_autoptr(FuStructElfFileHeader64le) st_fhdr = NULL; g_autoptr(GByteArray) shstrndx_buf = NULL; g_autoptr(GPtrArray) sections = - g_ptr_array_new_with_free_func((GDestroyNotify)g_byte_array_unref); + g_ptr_array_new_with_free_func((GDestroyNotify)fu_struct_elf_section_header64le_unref); /* file header */ st_fhdr = fu_struct_elf_file_header64le_parse_stream(stream, 0x0, error); @@ -60,7 +59,7 @@ phentsize = fu_struct_elf_file_header64le_get_phentsize(st_fhdr); phnum = fu_struct_elf_file_header64le_get_phnum(st_fhdr); for (guint i = 0; i < phnum; i++) { - g_autoptr(GByteArray) st_phdr = + g_autoptr(FuStructElfProgramHeader64le) st_phdr = fu_struct_elf_program_header64le_parse_stream(stream, offset_proghdr, error); if (st_phdr == NULL) return FALSE; @@ -118,14 +117,14 @@ g_autoptr(GInputStream) img_stream = fu_partial_input_stream_new(stream, sect_offset, sect_size, error); if (img_stream == NULL) { - g_prefix_error(error, "failed to cut EFI image: "); + g_prefix_error_literal(error, "failed to cut EFI image: "); return FALSE; } if (!fu_firmware_parse_stream(img, img_stream, 0x0, flags, error)) return FALSE; } fu_firmware_set_idx(img, i); - if (!fu_firmware_add_image_full(firmware, img, error)) + if (!fu_firmware_add_image(firmware, img, error)) return FALSE; } @@ -289,7 +288,7 @@ } /* calculate the offset of each section */ - section_offset = st_filehdr->len + st_proghdr->len + shstrtab->len; + section_offset = st_filehdr->buf->len + st_proghdr->buf->len + shstrtab->len; for (guint i = 0; i < imgs->len; i++) { FuFirmware *img = g_ptr_array_index(imgs, i); fu_firmware_set_offset(img, section_offset); @@ -306,7 +305,7 @@ if (imgs->len > 0) { g_autoptr(FuStructElfSectionHeader64le) st_secthdr = fu_struct_elf_section_header64le_new(); - g_byte_array_append(section_hdr, st_secthdr->data, st_secthdr->len); + fu_byte_array_append_array(section_hdr, st_secthdr->buf); } for (guint i = 0; i < imgs->len; i++) { FuFirmware *img = g_ptr_array_index(imgs, i); @@ -324,7 +323,7 @@ fu_struct_elf_section_header64le_set_offset(st_secthdr, fu_firmware_get_offset(img)); fu_struct_elf_section_header64le_set_size(st_secthdr, fu_firmware_get_size(img)); - g_byte_array_append(section_hdr, st_secthdr->data, st_secthdr->len); + fu_byte_array_append_array(section_hdr, st_secthdr->buf); } if (shstrtab->len > 0) { g_autoptr(FuStructElfSectionHeader64le) st_secthdr = @@ -334,15 +333,16 @@ fu_struct_elf_section_header64le_set_type(st_secthdr, FU_ELF_SECTION_HEADER_TYPE_STRTAB); fu_struct_elf_section_header64le_set_offset(st_secthdr, - st_filehdr->len + st_proghdr->len); + st_filehdr->buf->len + + st_proghdr->buf->len); fu_struct_elf_section_header64le_set_size(st_secthdr, shstrtab->len); - g_byte_array_append(section_hdr, st_secthdr->data, st_secthdr->len); + fu_byte_array_append_array(section_hdr, st_secthdr->buf); } /* update with the new totals */ fu_struct_elf_file_header64le_set_entry(st_filehdr, physical_addr + 0x60); fu_struct_elf_file_header64le_set_shoff(st_filehdr, - st_filehdr->len + st_proghdr->len + + st_filehdr->buf->len + st_proghdr->buf->len + section_data->len); fu_struct_elf_file_header64le_set_phentsize(st_filehdr, FU_STRUCT_ELF_PROGRAM_HEADER64LE_SIZE); @@ -354,17 +354,17 @@ fu_struct_elf_program_header64le_set_vaddr(st_proghdr, physical_addr); fu_struct_elf_program_header64le_set_paddr(st_proghdr, physical_addr); fu_struct_elf_program_header64le_set_filesz(st_proghdr, - st_filehdr->len + st_proghdr->len + + st_filehdr->buf->len + st_proghdr->buf->len + section_data->len + section_hdr->len); fu_struct_elf_program_header64le_set_memsz(st_proghdr, - st_filehdr->len + st_proghdr->len + + st_filehdr->buf->len + st_proghdr->buf->len + section_data->len + section_hdr->len); /* add file header, sections, then section headers */ - g_byte_array_append(buf, st_filehdr->data, st_filehdr->len); - g_byte_array_append(buf, st_proghdr->data, st_proghdr->len); - g_byte_array_append(buf, section_data->data, section_data->len); - g_byte_array_append(buf, section_hdr->data, section_hdr->len); + fu_byte_array_append_array(buf, st_filehdr->buf); + fu_byte_array_append_array(buf, st_proghdr->buf); + fu_byte_array_append_array(buf, section_data); + fu_byte_array_append_array(buf, section_hdr); return g_steal_pointer(&buf); } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-elf.rs fwupd-2.0.20/libfwupdplugin/fu-elf.rs --- fwupd-2.0.8/libfwupdplugin/fu-elf.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-elf.rs 2026-02-26 11:36:18.000000000 +0000 @@ -19,7 +19,7 @@ ei_version: u8 == 0x1, ei_osabi: u8 = 0x3, ei_abiversion: u8, - _ei_padding: [u8; 7] = 0x00000000000000, + _ei_padding: [u8; 7], type: FuElfFileHeaderType, machine: u16le, version: u32le == 0x1, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-fdt-firmware.c fwupd-2.0.20/libfwupdplugin/fu-fdt-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-fdt-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-fdt-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,6 @@ #include "fu-byte-array.h" #include "fu-bytes.h" #include "fu-common.h" -#include "fu-crc.h" #include "fu-dump.h" #include "fu-fdt-firmware.h" #include "fu-fdt-image.h" @@ -150,15 +149,19 @@ const guint8 *buf = g_bytes_get_data(fw, &bufsz); g_autoptr(FuFirmware) firmware_current = g_object_ref(FU_FIRMWARE(self)); - /* debug */ - fu_dump_bytes(G_LOG_DOMAIN, "dt_struct", fw); - /* parse */ while (offset < bufsz) { guint32 token = 0; /* read tag from aligned offset */ offset = fu_common_align_up(offset, FU_FIRMWARE_ALIGNMENT_4); + if (offset > G_MAXUINT32) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "offset bigger than 4GB"); + return FALSE; + } if (!fu_memread_uint32_safe(buf, bufsz, offset, &token, G_BIG_ENDIAN, error)) return FALSE; offset += sizeof(guint32); @@ -203,7 +206,7 @@ if (str->len > 0) fu_firmware_set_id(image, str->str); fu_firmware_set_offset(image, offset); - if (!fu_firmware_add_image_full(firmware_current, image, error)) + if (!fu_firmware_add_image(firmware_current, image, error)) return FALSE; g_set_object(&firmware_current, image); continue; @@ -230,7 +233,7 @@ guint32 prop_nameoff; g_autoptr(GBytes) blob = NULL; g_autoptr(GString) str = NULL; - g_autoptr(GByteArray) st_prp = NULL; + g_autoptr(FuStructFdtProp) st_prp = NULL; /* sanity check */ if (firmware_current == FU_FIRMWARE(self)) { @@ -247,7 +250,7 @@ return FALSE; prop_len = fu_struct_fdt_prop_get_len(st_prp); prop_nameoff = fu_struct_fdt_prop_get_nameoff(st_prp); - offset += st_prp->len; + offset += st_prp->buf->len; /* add property */ str = fu_fdt_firmware_string_new_safe(strtab->data, @@ -298,7 +301,7 @@ for (; offset < streamsz; offset += FU_STRUCT_FDT_RESERVE_ENTRY_SIZE) { guint64 address = 0; guint64 size = 0; - g_autoptr(GByteArray) st_res = NULL; + g_autoptr(FuStructFdtReserveEntry) st_res = NULL; st_res = fu_struct_fdt_reserve_entry_parse_stream(stream, offset, error); if (st_res == NULL) return FALSE; @@ -322,7 +325,7 @@ static gboolean fu_fdt_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuFdtFirmware *self = FU_FDT_FIRMWARE(firmware); @@ -330,7 +333,7 @@ guint32 totalsize; gsize streamsz = 0; guint32 off_mem_rsvmap = 0; - g_autoptr(GByteArray) st_hdr = NULL; + g_autoptr(FuStructFdt) st_hdr = NULL; /* sanity check */ st_hdr = fu_struct_fdt_parse_stream(stream, 0x0, error); @@ -391,10 +394,10 @@ if (dt_struct == NULL) return FALSE; if (dt_struct->len != fu_struct_fdt_get_size_dt_struct(st_hdr)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid firmware -- dt_struct invalid"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "invalid firmware -- dt_struct invalid"); return FALSE; } dt_struct_buf = @@ -464,7 +467,7 @@ for (guint i = 0; i < attrs->len; i++) { const gchar *key = g_ptr_array_index(attrs, i); g_autoptr(GBytes) blob = NULL; - g_autoptr(GByteArray) st_prp = fu_struct_fdt_prop_new(); + g_autoptr(FuStructFdtProp) st_prp = fu_struct_fdt_prop_new(); blob = fu_fdt_image_get_attr(img, key, error); if (blob == NULL) @@ -473,7 +476,7 @@ fu_struct_fdt_prop_set_len(st_prp, g_bytes_get_size(blob)); fu_struct_fdt_prop_set_nameoff(st_prp, fu_fdt_firmware_append_to_strtab(helper, key)); - g_byte_array_append(helper->dt_struct, st_prp->data, st_prp->len); + fu_byte_array_append_array(helper->dt_struct, st_prp->buf); fu_byte_array_append_bytes(helper->dt_struct, blob); fu_byte_array_align_up(helper->dt_struct, FU_FIRMWARE_ALIGNMENT_4, 0x0); } @@ -502,20 +505,20 @@ g_autoptr(GByteArray) dt_struct = g_byte_array_new(); g_autoptr(GHashTable) strtab = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); - g_autoptr(GByteArray) st_hdr = fu_struct_fdt_new(); - g_autoptr(GByteArray) mem_rsvmap = fu_struct_fdt_reserve_entry_new(); + g_autoptr(FuStructFdt) st_hdr = fu_struct_fdt_new(); + g_autoptr(FuStructFdtReserveEntry) st_rsvmap = fu_struct_fdt_reserve_entry_new(); FuFdtFirmwareBuildHelper helper = { .dt_strings = dt_strings, .dt_struct = dt_struct, .strtab = strtab, }; - /* empty mem_rsvmap */ - off_mem_rsvmap = fu_common_align_up(st_hdr->len, FU_FIRMWARE_ALIGNMENT_4); + /* empty st_rsvmap */ + off_mem_rsvmap = fu_common_align_up(st_hdr->buf->len, FU_FIRMWARE_ALIGNMENT_4); /* dt_struct */ off_dt_struct = - fu_common_align_up(off_mem_rsvmap + mem_rsvmap->len, FU_FIRMWARE_ALIGNMENT_4); + fu_common_align_up(off_mem_rsvmap + st_rsvmap->buf->len, FU_FIRMWARE_ALIGNMENT_4); /* only one root node supported */ if (images->len != 1) { @@ -543,18 +546,18 @@ fu_struct_fdt_set_boot_cpuid_phys(st_hdr, priv->cpuid); fu_struct_fdt_set_size_dt_strings(st_hdr, dt_strings->len); fu_struct_fdt_set_size_dt_struct(st_hdr, dt_struct->len); - fu_byte_array_align_up(st_hdr, FU_FIRMWARE_ALIGNMENT_4, 0x0); + fu_byte_array_align_up(st_hdr->buf, FU_FIRMWARE_ALIGNMENT_4, 0x0); - /* write mem_rsvmap, dt_struct, dt_strings */ - g_byte_array_append(st_hdr, mem_rsvmap->data, mem_rsvmap->len); - fu_byte_array_align_up(st_hdr, FU_FIRMWARE_ALIGNMENT_4, 0x0); - g_byte_array_append(st_hdr, dt_struct->data, dt_struct->len); - fu_byte_array_align_up(st_hdr, FU_FIRMWARE_ALIGNMENT_4, 0x0); - g_byte_array_append(st_hdr, dt_strings->data, dt_strings->len); - fu_byte_array_align_up(st_hdr, FU_FIRMWARE_ALIGNMENT_4, 0x0); + /* write st_rsvmap, dt_struct, dt_strings */ + fu_byte_array_append_array(st_hdr->buf, st_rsvmap->buf); + fu_byte_array_align_up(st_hdr->buf, FU_FIRMWARE_ALIGNMENT_4, 0x0); + fu_byte_array_append_array(st_hdr->buf, dt_struct); + fu_byte_array_align_up(st_hdr->buf, FU_FIRMWARE_ALIGNMENT_4, 0x0); + fu_byte_array_append_array(st_hdr->buf, dt_strings); + fu_byte_array_align_up(st_hdr->buf, FU_FIRMWARE_ALIGNMENT_4, 0x0); /* success */ - return g_steal_pointer(&st_hdr); + return g_steal_pointer(&st_hdr->buf); } static gboolean diff -Nru fwupd-2.0.8/libfwupdplugin/fu-fdt-image.c fwupd-2.0.20/libfwupdplugin/fu-fdt-image.c --- fwupd-2.0.8/libfwupdplugin/fu-fdt-image.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-fdt-image.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,6 @@ #include "config.h" #include "fu-byte-array.h" -#include "fu-common.h" #include "fu-fdt-image.h" #include "fu-mem.h" #include "fu-string.h" @@ -526,7 +525,7 @@ key = xb_node_get_attr(n, "key"); if (key == NULL) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "key invalid"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "key invalid"); return FALSE; } format = xb_node_get_attr(n, "format"); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-firmware-common.c fwupd-2.0.20/libfwupdplugin/fu-firmware-common.c --- fwupd-2.0.8/libfwupdplugin/fu-firmware-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-firmware-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,9 +8,6 @@ #include "config.h" -#include - -#include "fu-common.h" #include "fu-firmware-common.h" #include "fu-mem.h" #include "fu-string.h" diff -Nru fwupd-2.0.8/libfwupdplugin/fu-firmware.c fwupd-2.0.20/libfwupdplugin/fu-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -48,6 +48,7 @@ guint depth; GPtrArray *chunks; /* nullable, element-type FuChunk */ GPtrArray *patches; /* nullable, element-type FuFirmwarePatch */ + GPtrArray *magic; /* nullable, element-type FuFirmwarePatch */ } FuFirmwarePrivate; G_DEFINE_TYPE_WITH_PRIVATE(FuFirmware, fu_firmware, G_TYPE_OBJECT) @@ -57,76 +58,6 @@ #define FU_FIRMWARE_IMAGE_DEPTH_MAX 50 -/** - * fu_firmware_flag_to_string: - * @flag: a #FuFirmwareFlags, e.g. %FU_FIRMWARE_FLAG_DEDUPE_ID - * - * Converts a #FuFirmwareFlags to a string. - * - * Returns: identifier string - * - * Since: 1.5.0 - **/ -const gchar * -fu_firmware_flag_to_string(FuFirmwareFlags flag) -{ - if (flag == FU_FIRMWARE_FLAG_NONE) - return "none"; - if (flag == FU_FIRMWARE_FLAG_DEDUPE_ID) - return "dedupe-id"; - if (flag == FU_FIRMWARE_FLAG_DEDUPE_IDX) - return "dedupe-idx"; - if (flag == FU_FIRMWARE_FLAG_HAS_CHECKSUM) - return "has-checksum"; - if (flag == FU_FIRMWARE_FLAG_HAS_VID_PID) - return "has-vid-pid"; - if (flag == FU_FIRMWARE_FLAG_DONE_PARSE) - return "done-parse"; - if (flag == FU_FIRMWARE_FLAG_HAS_STORED_SIZE) - return "has-stored-size"; - if (flag == FU_FIRMWARE_FLAG_ALWAYS_SEARCH) - return "always-search"; - if (flag == FU_FIRMWARE_FLAG_NO_AUTO_DETECTION) - return "no-auto-detection"; - if (flag == FU_FIRMWARE_FLAG_HAS_CHECK_COMPATIBLE) - return "has-check-compatible"; - return NULL; -} - -/** - * fu_firmware_flag_from_string: - * @flag: a string, e.g. `dedupe-id` - * - * Converts a string to a #FuFirmwareFlags. - * - * Returns: enumerated value - * - * Since: 1.5.0 - **/ -FuFirmwareFlags -fu_firmware_flag_from_string(const gchar *flag) -{ - if (g_strcmp0(flag, "dedupe-id") == 0) - return FU_FIRMWARE_FLAG_DEDUPE_ID; - if (g_strcmp0(flag, "dedupe-idx") == 0) - return FU_FIRMWARE_FLAG_DEDUPE_IDX; - if (g_strcmp0(flag, "has-checksum") == 0) - return FU_FIRMWARE_FLAG_HAS_CHECKSUM; - if (g_strcmp0(flag, "has-vid-pid") == 0) - return FU_FIRMWARE_FLAG_HAS_VID_PID; - if (g_strcmp0(flag, "done-parse") == 0) - return FU_FIRMWARE_FLAG_DONE_PARSE; - if (g_strcmp0(flag, "has-stored-size") == 0) - return FU_FIRMWARE_FLAG_HAS_STORED_SIZE; - if (g_strcmp0(flag, "always-search") == 0) - return FU_FIRMWARE_FLAG_ALWAYS_SEARCH; - if (g_strcmp0(flag, "no-auto-detection") == 0) - return FU_FIRMWARE_FLAG_NO_AUTO_DETECTION; - if (g_strcmp0(flag, "has-check-compatible") == 0) - return FU_FIRMWARE_FLAG_HAS_CHECK_COMPATIBLE; - return FU_FIRMWARE_FLAG_NONE; -} - typedef struct { gsize offset; GBytes *blob; @@ -141,7 +72,7 @@ /** * fu_firmware_add_flag: - * @firmware: a #FuFirmware + * @self: a #FuFirmware * @flag: the firmware flag * * Adds a specific firmware flag to the firmware. @@ -149,16 +80,16 @@ * Since: 1.5.0 **/ void -fu_firmware_add_flag(FuFirmware *firmware, FuFirmwareFlags flag) +fu_firmware_add_flag(FuFirmware *self, FuFirmwareFlags flag) { - FuFirmwarePrivate *priv = GET_PRIVATE(firmware); - g_return_if_fail(FU_IS_FIRMWARE(firmware)); + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_FIRMWARE(self)); priv->flags |= flag; } /** * fu_firmware_has_flag: - * @firmware: a #FuFirmware + * @self: a #FuFirmware * @flag: the firmware flag * * Finds if the firmware has a specific firmware flag. @@ -168,10 +99,10 @@ * Since: 1.5.0 **/ gboolean -fu_firmware_has_flag(FuFirmware *firmware, FuFirmwareFlags flag) +fu_firmware_has_flag(FuFirmware *self, FuFirmwareFlags flag) { - FuFirmwarePrivate *priv = GET_PRIVATE(firmware); - g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE); + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE); return (priv->flags & flag) > 0; } @@ -261,7 +192,7 @@ if (klass->convert_version != NULL) { g_autofree gchar *version = klass->convert_version(self, version_raw); if (version != NULL) - fu_firmware_set_version(self, version); + fu_firmware_set_version(self, version); /* nocheck:set-version */ } } @@ -308,7 +239,7 @@ /* convert this, now we know */ if (klass->convert_version != NULL && priv->version != NULL && priv->version_raw != 0) { g_autofree gchar *version = klass->convert_version(self, priv->version_raw); - fu_firmware_set_version(self, version); + fu_firmware_set_version(self, version); /* nocheck:set-version */ } } @@ -888,6 +819,35 @@ } /** + * fu_firmware_add_magic: + * @self: a #FuFirmware + * @buf: some data + * @bufsz: sizeof @buf + * @offset: offset to start parsing, typically 0x0 + * + * Adds a possible magic signature to the image. + * + * Since: 2.0.18 + **/ +void +fu_firmware_add_magic(FuFirmware *self, const guint8 *buf, gsize bufsz, gsize offset) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_autofree FuFirmwarePatch *patch = g_new0(FuFirmwarePatch, 1); + + g_return_if_fail(FU_IS_FIRMWARE(self)); + g_return_if_fail(buf != NULL); + g_return_if_fail(bufsz != 0); + + if (priv->magic == NULL) + priv->magic = + g_ptr_array_new_with_free_func((GDestroyNotify)fu_firmware_patch_free); + patch->blob = g_bytes_new(buf, bufsz); + patch->offset = offset; + g_ptr_array_add(priv->magic, g_steal_pointer(&patch)); +} + +/** * fu_firmware_get_checksum: * @self: a #FuPlugin * @csum_kind: a checksum type, e.g. %G_CHECKSUM_SHA256 @@ -921,24 +881,33 @@ } } + /* write */ + if (klass->write != NULL) { + blob = fu_firmware_write(self, error); + if (blob == NULL) + return NULL; + return g_compute_checksum_for_bytes(csum_kind, blob); + } + /* internal data */ if (priv->bytes != NULL) return g_compute_checksum_for_bytes(csum_kind, priv->bytes); if (priv->stream != NULL) return fu_input_stream_compute_checksum(priv->stream, csum_kind, error); - /* write */ - blob = fu_firmware_write(self, error); - if (blob == NULL) - return NULL; - return g_compute_checksum_for_bytes(csum_kind, blob); + /* nothing to do */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no input data, as no FuFirmware->write, stream or bytes"); + return NULL; } /** * fu_firmware_tokenize: * @self: a #FuFirmware * @stream: a #GInputStream - * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @flags: #FuFirmwareParseFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE * @error: (nullable): optional return location for an error * * Tokenizes a firmware, typically breaking the firmware into records. @@ -953,7 +922,7 @@ gboolean fu_firmware_tokenize(FuFirmware *self, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); @@ -972,7 +941,7 @@ * fu_firmware_check_compatible: * @self: a #FuFirmware * @other: a #FuFirmware - * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @flags: #FuFirmwareParseFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE * @error: (nullable): optional return location for an error * * Check a new firmware is compatible with the existing firmware. @@ -984,7 +953,7 @@ gboolean fu_firmware_check_compatible(FuFirmware *self, FuFirmware *other, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); @@ -1002,11 +971,13 @@ static gboolean fu_firmware_validate_for_offset(FuFirmware *self, GInputStream *stream, - gsize *offset, - FwupdInstallFlags flags, + gsize offset, + gsize *offset_found, + FuFirmwareParseFlags flags, GError **error) { FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); + FuFirmwarePrivate *priv = GET_PRIVATE(self); gsize streamsz = 0; /* not implemented */ @@ -1015,39 +986,51 @@ /* fuzzing */ if (!fu_firmware_has_flag(self, FU_FIRMWARE_FLAG_ALWAYS_SEARCH) && - (flags & FWUPD_INSTALL_FLAG_NO_SEARCH) > 0) { - if (!klass->validate(self, stream, *offset, error)) - return FALSE; - return TRUE; + (flags & FU_FIRMWARE_PARSE_FLAG_NO_SEARCH) > 0) { + return klass->validate(self, stream, offset, error); } - /* limit the size of firmware we search */ - if (!fu_input_stream_size(stream, &streamsz, error)) - return FALSE; - if (streamsz > FU_FIRMWARE_SEARCH_MAGIC_BUFSZ_MAX) { - if (!klass->validate(self, stream, *offset, error)) { - g_prefix_error(error, - "failed to search for magic as firmware size was 0x%x and " - "limit was 0x%x: ", - (guint)streamsz, - (guint)FU_FIRMWARE_SEARCH_MAGIC_BUFSZ_MAX); - return FALSE; + /* try all the magic values, if provided */ + if (priv->magic != NULL) { + for (guint i = 0; i < priv->magic->len; i++) { + FuFirmwarePatch *patch = g_ptr_array_index(priv->magic, i); + gsize offset_tmp = 0; + g_debug("searching for 0x%x bytes of magic", + (guint)g_bytes_get_size(patch->blob)); + if (fu_input_stream_find(stream, + g_bytes_get_data(patch->blob, NULL), + g_bytes_get_size(patch->blob), + offset, + &offset_tmp, + NULL)) { + offset_tmp -= patch->offset; + g_debug("found magic @0x%x", (guint)offset_tmp); + if (offset_found != NULL) + *offset_found = offset_tmp; + return klass->validate(self, stream, *offset_found, error); + } } - return TRUE; + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "failed to find magic bytes"); + return FALSE; } - /* increment the offset, looking for the magic */ - for (gsize offset_tmp = *offset; offset_tmp < streamsz; offset_tmp++) { - if (klass->validate(self, stream, offset_tmp, NULL)) { - fu_firmware_set_offset(self, offset_tmp); - *offset = offset_tmp; - return TRUE; + /* limit the size of firmware we search as brute force is expensive */ + if (!fu_input_stream_size(stream, &streamsz, error)) + return FALSE; + if (streamsz < FU_FIRMWARE_SEARCH_MAGIC_BUFSZ_MAX) { + for (gsize offset_tmp = offset; offset_tmp < streamsz; offset_tmp++) { + if (klass->validate(self, stream, offset_tmp, NULL)) { + *offset_found = offset_tmp; + return TRUE; + } } } - /* did not find what we were looking for */ - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "did not find magic"); - return FALSE; + /* check in more detail */ + return klass->validate(self, stream, offset, error); } /** @@ -1055,7 +1038,7 @@ * @self: a #FuFirmware * @stream: input stream * @offset: start offset - * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @flags: #FuFirmwareParseFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE * @error: (nullable): optional return location for an error * * Parses a firmware from a stream, typically breaking the firmware into images. @@ -1068,12 +1051,15 @@ fu_firmware_parse_stream(FuFirmware *self, GInputStream *stream, gsize offset, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); FuFirmwarePrivate *priv = GET_PRIVATE(self); gsize streamsz = 0; + g_autoptr(GInputStream) partial_stream = NULL; + g_autoptr(GInputStream) seekable_stream = NULL; + g_autoptr(GBytes) blob = NULL; g_return_val_if_fail(FU_IS_FIRMWARE(self), FALSE); g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE); @@ -1088,8 +1074,18 @@ return FALSE; } + /* ensure the stream is seekable */ + if (!G_IS_SEEKABLE(stream) || !g_seekable_can_seek(G_SEEKABLE(stream))) { + blob = fu_input_stream_read_bytes(stream, offset, G_MAXUINT32, NULL, error); + if (blob == NULL) + return FALSE; + seekable_stream = g_memory_input_stream_new_from_bytes(blob); + } else { + seekable_stream = g_object_ref(stream); + } + /* check size */ - if (!fu_input_stream_size(stream, &streamsz, error)) + if (!fu_input_stream_size(seekable_stream, &streamsz, error)) return FALSE; if (streamsz <= offset) { g_set_error(error, @@ -1102,8 +1098,9 @@ } /* optional */ - if (!fu_firmware_validate_for_offset(self, stream, &offset, flags, error)) + if (!fu_firmware_validate_for_offset(self, seekable_stream, offset, &offset, flags, error)) return FALSE; + fu_firmware_set_offset(self, offset); /* save stream size */ priv->streamsz = streamsz - offset; @@ -1136,26 +1133,43 @@ /* save stream */ if (offset == 0) { - g_set_object(&priv->stream, stream); + partial_stream = g_object_ref(seekable_stream); } else { - g_autoptr(GInputStream) partial_stream = - fu_partial_input_stream_new(stream, offset, priv->streamsz, error); + partial_stream = + fu_partial_input_stream_new(seekable_stream, offset, priv->streamsz, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut firmware: "); + g_prefix_error_literal(error, "failed to cut firmware: "); return FALSE; } - g_set_object(&priv->stream, partial_stream); } + /* cache */ + if (flags & FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB) { + if (blob == NULL) { + blob = fu_input_stream_read_bytes(partial_stream, + 0x0, + priv->streamsz, + NULL, + error); + if (blob == NULL) + return FALSE; + } + fu_firmware_set_bytes(self, blob); + } + if (flags & FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM) + g_set_object(&priv->stream, partial_stream); + /* optional */ if (klass->tokenize != NULL) { - if (!klass->tokenize(self, priv->stream, flags, error)) + if (!klass->tokenize(self, partial_stream, flags, error)) return FALSE; } /* optional */ - if (klass->parse != NULL) - return klass->parse(self, priv->stream, flags, error); + if (klass->parse_full != NULL) + return klass->parse_full(self, seekable_stream, offset, flags, error); + else if (klass->parse != NULL) + return klass->parse(self, partial_stream, flags, error); /* verify alignment */ if (streamsz % (1ull << priv->alignment) != 0) { @@ -1179,7 +1193,7 @@ * @self: a #FuFirmware * @fw: firmware blob * @offset: start offset, useful for ignoring a bootloader - * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @flags: #FuFirmwareParseFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE * @error: (nullable): optional return location for an error * * Parses a firmware, typically breaking the firmware into images. @@ -1192,7 +1206,7 @@ fu_firmware_parse_bytes(FuFirmware *self, GBytes *fw, gsize offset, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(GInputStream) stream = NULL; @@ -1259,6 +1273,7 @@ fu_firmware_build(FuFirmware *self, XbNode *n, GError **error) { FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); + FuFirmwarePrivate *priv = GET_PRIVATE(self); const gchar *tmp; guint64 tmpval; guint64 version_raw; @@ -1273,7 +1288,7 @@ /* set attributes */ tmp = xb_node_query_text(n, "version", NULL); if (tmp != NULL) - fu_firmware_set_version(self, tmp); + fu_firmware_set_version(self, tmp); /* nocheck:set-version */ tmp = xb_node_query_text(n, "version_format", NULL); if (tmp != NULL) { FwupdVersionFormat version_format = fwupd_version_format_from_string(tmp); @@ -1321,15 +1336,6 @@ } fu_firmware_set_alignment(self, (guint8)tmpval); } - tmp = xb_node_query_text(n, "filename", NULL); - if (tmp != NULL) { - g_autoptr(GBytes) blob = NULL; - blob = fu_bytes_get_contents(tmp, error); - if (blob == NULL) - return FALSE; - fu_firmware_set_bytes(self, blob); - fu_firmware_set_filename(self, tmp); - } data = xb_node_query_first(n, "data", NULL); if (data != NULL) { guint64 sz = xb_node_get_attr_as_uint(data, "size"); @@ -1353,6 +1359,17 @@ fu_firmware_set_bytes(self, blob_padded); } } + tmp = xb_node_query_text(n, "filename", NULL); + if (tmp != NULL) { + if (priv->bytes == NULL) { + g_autoptr(GBytes) blob = NULL; + blob = fu_bytes_get_contents(tmp, error); + if (blob == NULL) + return FALSE; + fu_firmware_set_bytes(self, blob); + } + fu_firmware_set_filename(self, tmp); + } /* optional chunks */ chunks = xb_node_query(n, "chunks/chunk", 0, NULL); @@ -1388,7 +1405,7 @@ } else { img = fu_firmware_new(); } - if (!fu_firmware_add_image_full(self, img, error)) + if (!fu_firmware_add_image(self, img, error)) return FALSE; if (!fu_firmware_build(img, xb_image, error)) return FALSE; @@ -1465,7 +1482,7 @@ /* parse XML */ if (!xb_builder_source_load_xml(source, xml, XB_BUILDER_SOURCE_FLAG_NONE, error)) { - g_prefix_error(error, "could not parse XML: "); + g_prefix_error_literal(error, "could not parse XML: "); fwupd_error_convert(error); return FALSE; } @@ -1515,7 +1532,7 @@ * fu_firmware_parse_file: * @self: a #FuFirmware * @file: a file - * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_FORCE + * @flags: #FuFirmwareParseFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE * @error: (nullable): optional return location for an error * * Parses a firmware file, typically breaking the firmware into images. @@ -1525,7 +1542,7 @@ * Since: 1.3.3 **/ gboolean -fu_firmware_parse_file(FuFirmware *self, GFile *file, FwupdInstallFlags flags, GError **error) +fu_firmware_parse_file(FuFirmware *self, GFile *file, FuFirmwareParseFlags flags, GError **error) { g_autoptr(GFileInputStream) stream = NULL; @@ -1535,7 +1552,7 @@ stream = g_file_read(file, NULL, error); if (stream == NULL) { - fu_error_convert(error); + fwupd_error_convert(error); return FALSE; } return fu_firmware_parse_stream(self, G_INPUT_STREAM(stream), 0, flags, error); @@ -1670,9 +1687,8 @@ /* if we have less data than requested */ chunk_left = g_bytes_get_size(priv->bytes) - offset; - if (chunk_sz_max > chunk_left) { + if (chunk_sz_max > chunk_left) return fu_bytes_new_offset(priv->bytes, offset, chunk_left, error); - } /* check chunk */ return fu_bytes_new_offset(priv->bytes, offset, chunk_sz_max, error); @@ -1746,7 +1762,7 @@ } /** - * fu_firmware_add_image_full: + * fu_firmware_add_image: * @self: a #FuPlugin * @img: a child firmware image * @error: (nullable): optional return location for an error @@ -1759,10 +1775,10 @@ * * Returns: %TRUE if the image was added * - * Since: 1.9.3 + * Since: 1.9.3, but @error was added in 2.0.17 **/ gboolean -fu_firmware_add_image_full(FuFirmware *self, FuFirmware *img, GError **error) +fu_firmware_add_image(FuFirmware *self, FuFirmware *img, GError **error) { FuFirmwarePrivate *priv = GET_PRIVATE(self); @@ -1781,15 +1797,18 @@ } /* dedupe */ - for (guint i = 0; i < priv->images->len; i++) { - FuFirmware *img_tmp = g_ptr_array_index(priv->images, i); - if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_ID) { + if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_ID) { + for (guint i = 0; i < priv->images->len; i++) { + FuFirmware *img_tmp = g_ptr_array_index(priv->images, i); if (g_strcmp0(fu_firmware_get_id(img_tmp), fu_firmware_get_id(img)) == 0) { g_ptr_array_remove_index(priv->images, i); break; } } - if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_IDX) { + } + if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_IDX) { + for (guint i = 0; i < priv->images->len; i++) { + FuFirmware *img_tmp = g_ptr_array_index(priv->images, i); if (fu_firmware_get_idx(img_tmp) == fu_firmware_get_idx(img)) { g_ptr_array_remove_index(priv->images, i); break; @@ -1818,33 +1837,6 @@ } /** - * fu_firmware_add_image: - * @self: a #FuPlugin - * @img: a child firmware image - * - * Adds an image to the firmware. - * - * NOTE: If adding images in a loop of any kind then fu_firmware_add_image_full() should be used - * instead, and fu_firmware_set_images_max() should be set before adding images. - * - * If %FU_FIRMWARE_FLAG_DEDUPE_ID is set, an image with the same ID is already - * present it is replaced. - * - * Since: 1.3.1 - **/ -void -fu_firmware_add_image(FuFirmware *self, FuFirmware *img) -{ - g_autoptr(GError) error_local = NULL; - - g_return_if_fail(FU_IS_FIRMWARE(self)); - g_return_if_fail(FU_IS_FIRMWARE(img)); - - if (!fu_firmware_add_image_full(self, img, &error_local)) - g_critical("failed to add image: %s", error_local->message); -} - -/** * fu_firmware_set_images_max: * @self: a #FuPlugin * @images_max: integer, or 0 for unlimited @@ -2010,38 +2002,53 @@ fu_firmware_get_image_by_id(FuFirmware *self, const gchar *id, GError **error) { FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_autofree gchar *id_str = NULL; + g_autoptr(GPtrArray) id_errmsg = g_ptr_array_new(); g_return_val_if_fail(FU_IS_FIRMWARE(self), NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); + /* sanity check */ + if (priv->images->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no images in firmware"); + return NULL; + } + /* non-NULL */ if (id != NULL) { g_auto(GStrv) split = g_strsplit(id, "|", 0); for (guint i = 0; i < priv->images->len; i++) { FuFirmware *img = g_ptr_array_index(priv->images, i); for (guint j = 0; split[j] != NULL; j++) { + if (fu_firmware_get_id(img) == NULL) + continue; if (g_pattern_match_simple(split[j], fu_firmware_get_id(img))) 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; + } else { + for (guint i = 0; i < priv->images->len; i++) { + FuFirmware *img = g_ptr_array_index(priv->images, i); + if (fu_firmware_get_id(img) == NULL) + return g_object_ref(img); + } } - /* NULL */ + /* build a useful error */ for (guint i = 0; i < priv->images->len; i++) { FuFirmware *img = g_ptr_array_index(priv->images, i); - if (fu_firmware_get_id(img) == NULL) - return g_object_ref(img); + g_ptr_array_add(id_errmsg, (gpointer)fu_firmware_get_id(img)); } - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "no NULL image id found in firmware"); + id_str = fu_strjoin(",", id_errmsg); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no image id %s found in firmware, only have %s", + id, + id_str); return NULL; } @@ -2261,6 +2268,40 @@ return NULL; } +static gint +fu_firmware_sort_by_id_cb(gconstpointer a, gconstpointer b) +{ + FuFirmware *firmware1 = *((FuFirmware **)a); + FuFirmware *firmware2 = *((FuFirmware **)b); + return g_strcmp0(fu_firmware_get_id(firmware1), fu_firmware_get_id(firmware2)); +} + +static gint +fu_firmware_sort_by_idx_cb(gconstpointer a, gconstpointer b) +{ + FuFirmware *firmware1 = *((FuFirmware **)a); + FuFirmware *firmware2 = *((FuFirmware **)b); + guint64 idx1 = fu_firmware_get_idx(firmware1); + guint64 idx2 = fu_firmware_get_idx(firmware2); + if (idx1 < idx2) + return -1; + if (idx1 > idx2) + return 1; + return 0; +} + +static GPtrArray * +fu_firmware_get_images_sorted(FuFirmware *self) +{ + FuFirmwarePrivate *priv = GET_PRIVATE(self); + g_autoptr(GPtrArray) images = g_ptr_array_copy(priv->images, (GCopyFunc)g_object_ref, NULL); + if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_IDX) + g_ptr_array_sort(images, fu_firmware_sort_by_idx_cb); + if (priv->flags & FU_FIRMWARE_FLAG_DEDUPE_ID) + g_ptr_array_sort(images, fu_firmware_sort_by_id_cb); + return g_steal_pointer(&images); +} + /** * fu_firmware_export: * @self: a #FuFirmware @@ -2269,6 +2310,10 @@ * * This allows us to build an XML object for the nested firmware. * + * The image children will have a predictable order if the firmware has + * %FU_FIRMWARE_FLAG_DEDUPE_ID or %FU_FIRMWARE_FLAG_DEDUPE_IDX and + * %FU_FIRMWARE_EXPORT_FLAG_SORTED is also used in @flags. + * * Since: 1.6.0 **/ void @@ -2284,19 +2329,8 @@ /* subclassed type */ if (priv->flags != FU_FIRMWARE_FLAG_NONE) { - g_autoptr(GString) tmp = g_string_new(""); - for (guint i = 0; i < 64; i++) { - guint64 flag = (guint64)1 << i; - if (flag == FU_FIRMWARE_FLAG_DONE_PARSE) - continue; - if ((priv->flags & flag) == 0) - continue; - g_string_append_printf(tmp, "%s|", fu_firmware_flag_to_string(flag)); - } - if (tmp->len > 0) { - g_string_truncate(tmp, tmp->len - 1); - fu_xmlb_builder_insert_kv(bn, "flags", tmp->str); - } + g_autofree gchar *str = fu_firmware_flags_to_string(priv->flags); + fu_xmlb_builder_insert_kv(bn, "flags", str); } fu_xmlb_builder_insert_kv(bn, "id", priv->id); fu_xmlb_builder_insert_kx(bn, "idx", priv->idx); @@ -2316,9 +2350,7 @@ if (priv->stream != NULL) { g_autofree gchar *dataszstr = g_strdup_printf("0x%x", (guint)priv->streamsz); g_autofree gchar *datastr = NULL; - if (priv->streamsz > 0x100) { - datastr = g_strdup("[GInputStream]"); - } else { + if (priv->streamsz <= 0x100) { g_autoptr(GByteArray) buf = fu_input_stream_read_byte_array(priv->stream, 0x0, priv->streamsz, @@ -2334,13 +2366,18 @@ } else { datastr = g_base64_encode(buf->data, buf->len); } - } else { - datastr = g_strdup("[??GInputStream??]"); } } - xb_builder_node_insert_text(bn, "data", datastr, "size", dataszstr, NULL); + xb_builder_node_insert_text(bn, + "data", + datastr, + "type", + "GInputStream", + "size", + dataszstr, + NULL); } else if (priv->bytes != NULL && g_bytes_get_size(priv->bytes) == 0) { - xb_builder_node_insert_text(bn, "data", NULL, NULL); + xb_builder_node_insert_text(bn, "data", NULL, "type", "GBytes", NULL); } else if (priv->bytes != NULL) { gsize bufsz = 0; const guint8 *buf = g_bytes_get_data(priv->bytes, &bufsz); @@ -2351,7 +2388,14 @@ } else { datastr = g_base64_encode(buf, bufsz); } - xb_builder_node_insert_text(bn, "data", datastr, "size", dataszstr, NULL); + xb_builder_node_insert_text(bn, + "data", + datastr, + "type", + "GBytes", + "size", + dataszstr, + NULL); } /* chunks */ @@ -2364,14 +2408,28 @@ } } + /* magic */ + if (priv->magic != NULL && priv->magic->len > 0) { + g_autoptr(XbBuilderNode) bp = xb_builder_node_insert(bn, "magic", NULL); + for (guint i = 0; i < priv->magic->len; i++) { + FuFirmwarePatch *patch = g_ptr_array_index(priv->magic, i); + g_autofree gchar *str = fu_bytes_to_string(patch->blob); + g_autofree gchar *offset = g_strdup_printf("0x%x", (guint)patch->offset); + xb_builder_node_insert_text(bp, "data", str, "offset", offset, NULL); + } + } + /* vfunc */ if (klass->export != NULL) klass->export(self, flags, bn); /* children */ if (priv->images->len > 0) { - for (guint i = 0; i < priv->images->len; i++) { - FuFirmware *img = g_ptr_array_index(priv->images, i); + g_autoptr(GPtrArray) images = flags & FU_FIRMWARE_EXPORT_FLAG_SORTED + ? fu_firmware_get_images_sorted(self) + : g_ptr_array_ref(priv->images); + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, "firmware", NULL); fu_firmware_export(img, flags, bc); } @@ -2464,6 +2522,15 @@ } static void +fu_firmware_constructed(GObject *obj) +{ + FuFirmware *self = FU_FIRMWARE(obj); + FuFirmwareClass *klass = FU_FIRMWARE_GET_CLASS(self); + if (klass->add_magic != NULL) + klass->add_magic(self); +} + +static void fu_firmware_finalize(GObject *object) { FuFirmware *self = FU_FIRMWARE(object); @@ -2479,6 +2546,8 @@ g_ptr_array_unref(priv->chunks); if (priv->patches != NULL) g_ptr_array_unref(priv->patches); + if (priv->magic != NULL) + g_ptr_array_unref(priv->magic); if (priv->parent != NULL) g_object_remove_weak_pointer(G_OBJECT(priv->parent), (gpointer *)&priv->parent); g_ptr_array_unref(priv->images); @@ -2494,6 +2563,7 @@ object_class->finalize = fu_firmware_finalize; object_class->get_property = fu_firmware_get_property; object_class->set_property = fu_firmware_set_property; + object_class->constructed = fu_firmware_constructed; /** * FuFirmware:parent: @@ -2548,7 +2618,7 @@ * fu_firmware_new_from_gtypes: * @stream: a #GInputStream * @offset: start offset, useful for ignoring a bootloader - * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM + * @flags: install flags, e.g. %FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM * @error: (nullable): optional return location for an error * @...: an array of #GTypes, ending with %G_TYPE_INVALID * @@ -2561,7 +2631,7 @@ FuFirmware * fu_firmware_new_from_gtypes(GInputStream *stream, gsize offset, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error, ...) { @@ -2597,10 +2667,11 @@ g_autoptr(FuFirmware) firmware = g_object_new(gtype, NULL); g_autoptr(GError) error_local = NULL; if (!fu_firmware_parse_stream(firmware, stream, offset, flags, &error_local)) { - g_debug("%s", error_local->message); + g_debug("@0x%x %s", (guint)offset, error_local->message); if (error_all == NULL) { g_propagate_error(&error_all, g_steal_pointer(&error_local)); } else { + /* nocheck:error */ g_prefix_error(&error_all, "%s: ", error_local->message); } continue; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-firmware.h fwupd-2.0.20/libfwupdplugin/fu-firmware.h --- fwupd-2.0.8/libfwupdplugin/fu-firmware.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -10,62 +10,32 @@ #include #include "fu-chunk.h" -#include "fu-firmware.h" +#include "fu-firmware-struct.h" #define FU_TYPE_FIRMWARE (fu_firmware_get_type()) G_DECLARE_DERIVABLE_TYPE(FuFirmware, fu_firmware, FU, FIRMWARE, GObject) -/** - * FuFirmwareExportFlags: - * - * The firmware export flags. - **/ -typedef enum { - /** - * FU_FIRMWARE_EXPORT_FLAG_NONE: - * - * No flags set. - * - * Since: 1.6.0 - **/ - FU_FIRMWARE_EXPORT_FLAG_NONE = 0u, - /** - * FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG: - * - * Include debug information when exporting. - * - * Since: 1.6.0 - **/ - FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG = 1u << 0, - /** - * FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA: - * - * Write the data as UTF-8 strings. - * - * Since: 1.6.0 - **/ - FU_FIRMWARE_EXPORT_FLAG_ASCII_DATA = 1u << 1, - /** - * FU_FIRMWARE_EXPORT_FLAG_UNKNOWN: - * - * Unknown flag value. - * - * Since: 2.0.0 - */ - FU_FIRMWARE_EXPORT_FLAG_UNKNOWN = G_MAXUINT64, -} FuFirmwareExportFlags; +#ifdef __GI_SCANNER__ +#define FuFirmwareExportFlags guint64 +#define FuFirmwareParseFlags guint64 +#endif struct _FuFirmwareClass { GObjectClass parent_class; gboolean (*parse)(FuFirmware *self, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT; + gboolean (*parse_full)(FuFirmware *self, + GInputStream *stream, + gsize offset, + FuFirmwareParseFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT; GByteArray *(*write)(FuFirmware *self, GError **error)G_GNUC_WARN_UNUSED_RESULT; void (*export)(FuFirmware *self, FuFirmwareExportFlags flags, XbBuilderNode *bn); gboolean (*tokenize)(FuFirmware *self, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT; gboolean (*build)(FuFirmware *self, XbNode *n, GError **error) G_GNUC_WARN_UNUSED_RESULT; gchar *(*get_checksum)(FuFirmware *self, @@ -74,112 +44,13 @@ gboolean (*validate)(FuFirmware *self, GInputStream *stream, gsize offset, GError **error); gboolean (*check_compatible)(FuFirmware *self, FuFirmware *other, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error); gchar *(*convert_version)(FuFirmware *self, guint64 version_raw); + void (*add_magic)(FuFirmware *self); }; /** - * FuFirmwareFlags: - * - * The firmware flags. - **/ -typedef enum { - /** - * FU_FIRMWARE_FLAG_NONE: - * - * No flags set. - * - * Since: 1.5.0 - **/ - FU_FIRMWARE_FLAG_NONE = 0u, - /** - * FU_FIRMWARE_FLAG_DEDUPE_ID: - * - * Dedupe images by ID. - * - * Since: 1.5.0 - **/ - FU_FIRMWARE_FLAG_DEDUPE_ID = 1u << 0, - /** - * FU_FIRMWARE_FLAG_DEDUPE_IDX: - * - * Dedupe images by IDX. - * - * Since: 1.5.0 - **/ - FU_FIRMWARE_FLAG_DEDUPE_IDX = 1u << 1, - /** - * FU_FIRMWARE_FLAG_HAS_CHECKSUM: - * - * Has a CRC or checksum to test internal consistency. - * - * Since: 1.5.6 - **/ - FU_FIRMWARE_FLAG_HAS_CHECKSUM = 1u << 2, - /** - * FU_FIRMWARE_FLAG_HAS_VID_PID: - * - * Has a vendor or product ID in the firmware. - * - * Since: 1.5.6 - **/ - FU_FIRMWARE_FLAG_HAS_VID_PID = 1u << 3, - /** - * FU_FIRMWARE_FLAG_DONE_PARSE: - * - * The firmware object has been used by fu_firmware_parse_bytes(). - * - * Since: 1.7.3 - **/ - FU_FIRMWARE_FLAG_DONE_PARSE = 1u << 4, - /** - * FU_FIRMWARE_FLAG_HAS_STORED_SIZE: - * - * Encodes the image size in the firmware. - * - * Since: 1.8.2 - **/ - FU_FIRMWARE_FLAG_HAS_STORED_SIZE = 1u << 5, - /** - * FU_FIRMWARE_FLAG_ALWAYS_SEARCH: - * - * Always searches for magic regardless of the install flags. - * This is useful for firmware that always has an *unparsed* variable-length - * header. - * - * Since: 1.8.6 - **/ - FU_FIRMWARE_FLAG_ALWAYS_SEARCH = 1u << 6, - /** - * FU_FIRMWARE_FLAG_NO_AUTO_DETECTION: - * - * Do not use this firmware type when auto-detecting firmware. - * This should be used when there is no valid signature or CRC to check validity when - *parsing. - * - * Since: 1.9.3 - **/ - FU_FIRMWARE_FLAG_NO_AUTO_DETECTION = 1u << 7, - /** - * FU_FIRMWARE_FLAG_HAS_CHECK_COMPATIBLE: - * - * The firmware subclass implements a compatibility check. - * - * Since: 1.9.20 - **/ - FU_FIRMWARE_FLAG_HAS_CHECK_COMPATIBLE = 1u << 8, - /** - * FU_FIRMWARE_FLAG_UNKNOWN: - * - * Unknown flag value. - * - * Since: 2.0.0 - */ - FU_FIRMWARE_FLAG_UNKNOWN = G_MAXUINT64, -} FuFirmwareFlags; - -/** * FU_FIRMWARE_ID_PAYLOAD: * * The usual firmware ID string for the payload. @@ -204,54 +75,7 @@ **/ #define FU_FIRMWARE_ID_HEADER "header" -/** - * FuFirmwareAlignment: - * - * The firmware alignment position. - **/ -typedef enum { - FU_FIRMWARE_ALIGNMENT_1 = 0x00, - FU_FIRMWARE_ALIGNMENT_2 = 0x01, - FU_FIRMWARE_ALIGNMENT_4 = 0x02, - FU_FIRMWARE_ALIGNMENT_8 = 0x03, - FU_FIRMWARE_ALIGNMENT_16 = 0x04, - FU_FIRMWARE_ALIGNMENT_32 = 0x05, - FU_FIRMWARE_ALIGNMENT_64 = 0x06, - FU_FIRMWARE_ALIGNMENT_128 = 0x07, - FU_FIRMWARE_ALIGNMENT_256 = 0x08, - FU_FIRMWARE_ALIGNMENT_512 = 0x09, - FU_FIRMWARE_ALIGNMENT_1K = 0x0A, - FU_FIRMWARE_ALIGNMENT_2K = 0x0B, - FU_FIRMWARE_ALIGNMENT_4K = 0x0C, - FU_FIRMWARE_ALIGNMENT_8K = 0x0D, - FU_FIRMWARE_ALIGNMENT_16K = 0x0E, - FU_FIRMWARE_ALIGNMENT_32K = 0x0F, - FU_FIRMWARE_ALIGNMENT_64K = 0x10, - FU_FIRMWARE_ALIGNMENT_128K = 0x11, - FU_FIRMWARE_ALIGNMENT_256K = 0x12, - FU_FIRMWARE_ALIGNMENT_512K = 0x13, - FU_FIRMWARE_ALIGNMENT_1M = 0x14, - FU_FIRMWARE_ALIGNMENT_2M = 0x15, - FU_FIRMWARE_ALIGNMENT_4M = 0x16, - FU_FIRMWARE_ALIGNMENT_8M = 0x17, - FU_FIRMWARE_ALIGNMENT_16M = 0x18, - FU_FIRMWARE_ALIGNMENT_32M = 0x19, - FU_FIRMWARE_ALIGNMENT_64M = 0x1A, - FU_FIRMWARE_ALIGNMENT_128M = 0x1B, - FU_FIRMWARE_ALIGNMENT_256M = 0x1C, - FU_FIRMWARE_ALIGNMENT_512M = 0x1D, - FU_FIRMWARE_ALIGNMENT_1G = 0x1E, - FU_FIRMWARE_ALIGNMENT_2G = 0x1F, - FU_FIRMWARE_ALIGNMENT_4G = 0x20, - FU_FIRMWARE_ALIGNMENT_LAST, -} FuFirmwareAlignment; - -#define FU_FIRMWARE_SEARCH_MAGIC_BUFSZ_MAX (32 * 1024 * 1024) - -const gchar * -fu_firmware_flag_to_string(FuFirmwareFlags flag); -FuFirmwareFlags -fu_firmware_flag_from_string(const gchar *flag); +#define FU_FIRMWARE_SEARCH_MAGIC_BUFSZ_MAX (64 * 1024) FuFirmware * fu_firmware_new(void); @@ -260,7 +84,7 @@ FuFirmware * fu_firmware_new_from_gtypes(GInputStream *stream, gsize offset, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error, ...) G_GNUC_NON_NULL(1); gchar * @@ -285,9 +109,10 @@ FwupdVersionFormat fu_firmware_get_version_format(FuFirmware *self) G_GNUC_NON_NULL(1); void -fu_firmware_add_flag(FuFirmware *firmware, FuFirmwareFlags flag) G_GNUC_NON_NULL(1); +fu_firmware_add_flag(FuFirmware *self, FuFirmwareFlags flag) G_GNUC_NON_NULL(1); gboolean -fu_firmware_has_flag(FuFirmware *firmware, FuFirmwareFlags flag) G_GNUC_NON_NULL(1); +fu_firmware_has_flag(FuFirmware *self, FuFirmwareFlags flag) G_GNUC_WARN_UNUSED_RESULT + G_GNUC_NON_NULL(1); const gchar * fu_firmware_get_filename(FuFirmware *self) G_GNUC_NON_NULL(1); void @@ -338,6 +163,9 @@ fu_firmware_set_alignment(FuFirmware *self, FuFirmwareAlignment alignment) G_GNUC_NON_NULL(1); void fu_firmware_add_chunk(FuFirmware *self, FuChunk *chk) G_GNUC_NON_NULL(1); +void +fu_firmware_add_magic(FuFirmware *self, const guint8 *buf, gsize bufsz, gsize offset) + G_GNUC_NON_NULL(1, 2); GPtrArray * fu_firmware_get_chunks(FuFirmware *self, GError **error) G_GNUC_NON_NULL(1); FuFirmware * @@ -348,7 +176,7 @@ gboolean fu_firmware_tokenize(FuFirmware *self, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); gboolean fu_firmware_build(FuFirmware *self, XbNode *n, GError **error) G_GNUC_WARN_UNUSED_RESULT @@ -365,16 +193,16 @@ fu_firmware_parse_stream(FuFirmware *self, GInputStream *stream, gsize offset, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); gboolean -fu_firmware_parse_file(FuFirmware *self, GFile *file, FwupdInstallFlags flags, GError **error) +fu_firmware_parse_file(FuFirmware *self, GFile *file, FuFirmwareParseFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); gboolean fu_firmware_parse_bytes(FuFirmware *self, GBytes *fw, gsize offset, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); GBytes * fu_firmware_write(FuFirmware *self, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); @@ -390,13 +218,11 @@ gboolean fu_firmware_check_compatible(FuFirmware *self, FuFirmware *other, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) G_GNUC_NON_NULL(1, 2); -void -fu_firmware_add_image(FuFirmware *self, FuFirmware *img) G_GNUC_NON_NULL(1, 2); gboolean -fu_firmware_add_image_full(FuFirmware *self, FuFirmware *img, GError **error) G_GNUC_NON_NULL(1, 2); +fu_firmware_add_image(FuFirmware *self, FuFirmware *img, GError **error) G_GNUC_NON_NULL(1, 2); gboolean fu_firmware_remove_image(FuFirmware *self, FuFirmware *img, GError **error) G_GNUC_NON_NULL(1, 2); gboolean diff -Nru fwupd-2.0.8/libfwupdplugin/fu-firmware.rs fwupd-2.0.20/libfwupdplugin/fu-firmware.rs --- fwupd-2.0.8/libfwupdplugin/fu-firmware.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-firmware.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,72 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +enum FuFirmwareExportFlags { + None = 0, + IncludeDebug = 1 << 0, + AsciiData = 1 << 1, // as UTF-8 strings + Sorted = 1 << 2, +} + +enum FuFirmwareParseFlags { + None = 0, + IgnoreChecksum = 1 << 6, + IgnoreVidPid = 1 << 7, + NoSearch = 1 << 8, // no heuristics + CacheStream = 1 << 10, + CacheBlob = 1 << 11, + OnlyTrustPqSignatures = 1 << 12, + OnlyPartitionLayout = 1 << 13, +} + +#[derive(ToString)] +enum FuFirmwareFlags { + None = 0, + DedupeId = 1 << 0, + DedupeIdx = 1 << 1, + HasChecksum = 1 << 2, // or CRC + HasVidPid = 1 << 3, + DoneParse = 1 << 4, + HasStoredSize = 1 << 5, + AlwaysSearch = 1 << 6, // useful has an *unparsed* variable-length header + NoAutoDetection = 1 << 7, // has no known header + HasCheckCompatible = 1 << 8, + IsLastImage = 1 << 9, // use for FuLinearFirmware when padding is present + AllowLinear = 1 << 10, // parse as an array of firmwares +} + +enum FuFirmwareAlignment { + 1, + 2, + 4, + 8, + 16, + 32, + 64, + 128, + 256, + 512, + 1K, + 2K, + 4K, + 8K, + 16K, + 32K, + 64K, + 128K, + 256K, + 512K, + 1M, + 2M, + 4M, + 8M, + 16M, + 32M, + 64M, + 128M, + 256M, + 512M, + 1G, + 2G, + 4G, +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-fit-firmware.c fwupd-2.0.20/libfwupdplugin/fu-fit-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-fit-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-fit-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,6 @@ #include "config.h" #include "fu-bytes.h" -#include "fu-common.h" #include "fu-crc.h" #include "fu-dump.h" #include "fu-fdt-image.h" @@ -40,7 +39,8 @@ fu_fdt_image_set_attr_uint32(FU_FDT_IMAGE(img), FU_FIT_FIRMWARE_ATTR_TIMESTAMP, 0x0); fu_fdt_image_set_attr_str(FU_FDT_IMAGE(img), "description", "Firmware image"); fu_fdt_image_set_attr_str(FU_FDT_IMAGE(img), "creator", "fwupd"); - fu_firmware_add_image(FU_FIRMWARE(self), img); + if (!fu_firmware_add_image(FU_FIRMWARE(self), img, NULL)) + return NULL; return FU_FDT_IMAGE(img); } @@ -62,6 +62,8 @@ g_return_val_if_fail(FU_IS_FIT_FIRMWARE(self), 0x0); + if (img_root == NULL) + return 0; /* this has to exist */ (void)fu_fdt_image_get_attr_u32(img_root, FU_FIT_FIRMWARE_ATTR_TIMESTAMP, &tmp, NULL); return tmp; @@ -81,11 +83,13 @@ { g_autoptr(FuFdtImage) img_root = fu_fit_firmware_get_image_root(self); g_return_if_fail(FU_IS_FIT_FIRMWARE(self)); + if (img_root == NULL) + return; fu_fdt_image_set_attr_uint32(img_root, FU_FIT_FIRMWARE_ATTR_TIMESTAMP, timestamp); } static gboolean -fu_fit_firmware_verify_crc32(FuFirmware *firmware, +fu_fit_firmware_verify_crc32(FuFitFirmware *self, FuFirmware *img, FuFirmware *img_hash, GBytes *blob, @@ -113,12 +117,12 @@ } /* success */ - fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_HAS_CHECKSUM); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); return TRUE; } static gboolean -fu_fit_firmware_verify_checksum(FuFirmware *firmware, +fu_fit_firmware_verify_checksum(FuFitFirmware *self, FuFirmware *img, FuFirmware *img_hash, GChecksumType checksum_type, @@ -154,12 +158,12 @@ return FALSE; /* success */ - fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_HAS_CHECKSUM); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); return TRUE; } static gboolean -fu_fit_firmware_verify_hash(FuFirmware *firmware, +fu_fit_firmware_verify_hash(FuFitFirmware *self, FuFirmware *img, FuFirmware *img_hash, GBytes *blob, @@ -176,9 +180,9 @@ return FALSE; } if (g_strcmp0(algo, "crc32") == 0) - return fu_fit_firmware_verify_crc32(firmware, img, img_hash, blob, error); + return fu_fit_firmware_verify_crc32(self, img, img_hash, blob, error); if (g_strcmp0(algo, "md5") == 0) { - return fu_fit_firmware_verify_checksum(firmware, + return fu_fit_firmware_verify_checksum(self, img, img_hash, G_CHECKSUM_MD5, @@ -186,7 +190,7 @@ error); } if (g_strcmp0(algo, "sha1") == 0) { - return fu_fit_firmware_verify_checksum(firmware, + return fu_fit_firmware_verify_checksum(self, img, img_hash, G_CHECKSUM_SHA1, @@ -194,7 +198,7 @@ error); } if (g_strcmp0(algo, "sha256") == 0) { - return fu_fit_firmware_verify_checksum(firmware, + return fu_fit_firmware_verify_checksum(self, img, img_hash, G_CHECKSUM_SHA256, @@ -207,10 +211,10 @@ } static gboolean -fu_fit_firmware_verify_image(FuFirmware *firmware, +fu_fit_firmware_verify_image(FuFitFirmware *self, GInputStream *stream, FuFirmware *img, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(GBytes) blob = NULL; @@ -245,7 +249,7 @@ fu_dump_bytes(G_LOG_DOMAIN, "data", blob); /* verify any hashes we recognize */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { g_autoptr(GPtrArray) img_hashes = fu_firmware_get_images(img); for (guint i = 0; i < img_hashes->len; i++) { FuFirmware *img_hash = g_ptr_array_index(img_hashes, i); @@ -257,11 +261,7 @@ return FALSE; } if (g_str_has_prefix(fu_firmware_get_id(img_hash), "hash")) { - if (!fu_fit_firmware_verify_hash(firmware, - img, - img_hash, - blob, - error)) + if (!fu_fit_firmware_verify_hash(self, img, img_hash, blob, error)) return FALSE; } } @@ -272,9 +272,9 @@ } static gboolean -fu_fit_firmware_verify_configuration(FuFirmware *firmware, +fu_fit_firmware_verify_configuration(FuFitFirmware *self, FuFirmware *img, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { /* sanity check */ @@ -291,9 +291,10 @@ static gboolean fu_fit_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { + FuFitFirmware *self = FU_FIT_FIRMWARE(firmware); g_autoptr(FuFirmware) img_cfgs = NULL; g_autoptr(FuFirmware) img_images = NULL; g_autoptr(FuFirmware) img_root = NULL; @@ -321,7 +322,7 @@ img_images_array = fu_firmware_get_images(img_images); for (guint i = 0; i < img_images_array->len; i++) { FuFirmware *img = g_ptr_array_index(img_images_array, i); - if (!fu_fit_firmware_verify_image(firmware, stream, img, flags, error)) + if (!fu_fit_firmware_verify_image(self, stream, img, flags, error)) return FALSE; } @@ -332,7 +333,7 @@ img_cfgs_array = fu_firmware_get_images(img_cfgs); for (guint i = 0; i < img_cfgs_array->len; i++) { FuFirmware *img = g_ptr_array_index(img_cfgs_array, i); - if (!fu_fit_firmware_verify_configuration(firmware, img, flags, error)) + if (!fu_fit_firmware_verify_configuration(self, img, flags, error)) return FALSE; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-fmap-firmware.c fwupd-2.0.20/libfwupdplugin/fu-fmap-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-fmap-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-fmap-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,47 +13,102 @@ #include "fu-fmap-struct.h" #include "fu-input-stream.h" #include "fu-partial-input-stream.h" +#include "fu-string.h" +#include "fu-uswid-firmware.h" /** * FuFmapFirmware: * * A FMAP firmware image. * + * NOTE: the `__FMAP__` header may point to sections lower than the stream offset. + * * See also: [class@FuFirmware] */ -#define FMAP_AREANAME "FMAP" +typedef struct { + gsize signature_offset; /* only for constructing the image */ + guint8 ver_major; + guint8 ver_minor; +} FuFmapFirmwarePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuFmapFirmware, fu_fmap_firmware, FU_TYPE_FIRMWARE) +#define GET_PRIVATE(o) (fu_fmap_firmware_get_instance_private(o)) + +static void +fu_fmap_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuFmapFirmware *self = FU_FMAP_FIRMWARE(firmware); + FuFmapFirmwarePrivate *priv = GET_PRIVATE(self); + fu_xmlb_builder_insert_kx(bn, "ver_major", priv->ver_major); + fu_xmlb_builder_insert_kx(bn, "ver_minor", priv->ver_minor); + fu_xmlb_builder_insert_kx(bn, "signature_offset", priv->signature_offset); +} + +static gboolean +fu_fmap_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuFmapFirmware *self = FU_FMAP_FIRMWARE(firmware); + FuFmapFirmwarePrivate *priv = GET_PRIVATE(self); + const gchar *tmp; + + /* simple properties */ + tmp = xb_node_query_text(n, "signature_offset", NULL); + if (tmp != NULL) { + guint64 tmp64 = 0; + if (!fu_strtoull(tmp, &tmp64, 0x0, G_MAXSIZE, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + priv->signature_offset = (gsize)tmp64; + } + tmp = xb_node_query_text(n, "ver_major", NULL); + if (tmp != NULL) { + guint64 tmp64 = 0; + if (!fu_strtoull(tmp, &tmp64, 0x0, G_MAXUINT8, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + priv->ver_major = (gsize)tmp64; + } + tmp = xb_node_query_text(n, "ver_minor", NULL); + if (tmp != NULL) { + guint64 tmp64 = 0; + if (!fu_strtoull(tmp, &tmp64, 0x0, G_MAXUINT8, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + priv->ver_minor = (gsize)tmp64; + } + + /* success */ + return TRUE; +} -G_DEFINE_TYPE(FuFmapFirmware, fu_fmap_firmware, FU_TYPE_FIRMWARE) +static gboolean +fu_fmap_firmware_validate(FuFirmware *firmware, GInputStream *stream, gsize offset, GError **error) +{ + return fu_struct_fmap_validate_stream(stream, offset, error); +} static gboolean fu_fmap_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + gsize offset, + FuFirmwareParseFlags flags, GError **error) { - gsize offset = 0; + FuFmapFirmware *self = FU_FMAP_FIRMWARE(firmware); + FuFmapFirmwarePrivate *priv = GET_PRIVATE(self); gsize streamsz = 0; guint32 nareas; - g_autoptr(GByteArray) st_hdr = NULL; - - /* find the magic token */ - if (!fu_input_stream_find(stream, - (const guint8 *)FU_STRUCT_FMAP_DEFAULT_SIGNATURE, - FU_STRUCT_FMAP_SIZE_SIGNATURE, - &offset, - error)) - return FALSE; + g_autoptr(FuStructFmap) st_hdr = NULL; /* parse */ st_hdr = fu_struct_fmap_parse_stream(stream, offset, error); if (st_hdr == NULL) return FALSE; fu_firmware_set_addr(firmware, fu_struct_fmap_get_base(st_hdr)); + priv->ver_major = fu_struct_fmap_get_ver_major(st_hdr); + priv->ver_minor = fu_struct_fmap_get_ver_minor(st_hdr); if (!fu_input_stream_size(stream, &streamsz, error)) return FALSE; - if (fu_struct_fmap_get_size(st_hdr) != streamsz) { + if (fu_struct_fmap_get_size(st_hdr) > streamsz) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, @@ -70,13 +125,13 @@ "number of areas invalid"); return FALSE; } - offset += st_hdr->len; + offset += st_hdr->buf->len; for (gsize i = 0; i < nareas; i++) { guint32 area_offset; guint32 area_size; g_autofree gchar *area_name = NULL; - g_autoptr(FuFirmware) img = fu_firmware_new(); - g_autoptr(GByteArray) st_area = NULL; + g_autoptr(FuFirmware) img = NULL; + g_autoptr(FuStructFmapArea) st_area = NULL; g_autoptr(GInputStream) img_stream = NULL; /* load area */ @@ -86,32 +141,39 @@ area_size = fu_struct_fmap_area_get_size(st_area); if (area_size == 0) continue; + + /* this is an absolute stream, referencing from the start of the base stream */ area_offset = fu_struct_fmap_area_get_offset(st_area); img_stream = fu_partial_input_stream_new(stream, (gsize)area_offset, (gsize)area_size, error); if (img_stream == NULL) { - g_prefix_error(error, "failed to cut FMAP area: "); + g_prefix_error_literal(error, "failed to cut FMAP area: "); return FALSE; } - if (!fu_firmware_parse_stream(img, img_stream, 0x0, flags, error)) - return FALSE; area_name = fu_struct_fmap_area_get_name(st_area); + if (g_strcmp0(area_name, "SBOM") == 0) { + img = fu_firmware_new_from_gtypes(img_stream, + 0x0, + flags, + error, + FU_TYPE_USWID_FIRMWARE, + FU_TYPE_FIRMWARE, + G_TYPE_INVALID); + if (img == NULL) + return FALSE; + } else { + img = fu_firmware_new(); + if (!fu_firmware_parse_stream(img, img_stream, 0x0, flags, error)) + return FALSE; + } fu_firmware_set_id(img, area_name); fu_firmware_set_idx(img, i + 1); fu_firmware_set_addr(img, area_offset); - if (!fu_firmware_add_image_full(firmware, img, error)) + if (!fu_firmware_add_image(firmware, img, error)) return FALSE; - - if (g_strcmp0(area_name, FMAP_AREANAME) == 0) { - g_autofree gchar *version = NULL; - version = g_strdup_printf("%d.%d", - fu_struct_fmap_get_ver_major(st_hdr), - fu_struct_fmap_get_ver_minor(st_hdr)); - fu_firmware_set_version(img, version); - } - offset += st_area->len; + offset += st_area->buf->len; } /* success */ @@ -121,51 +183,64 @@ static GByteArray * fu_fmap_firmware_write(FuFirmware *firmware, GError **error) { + FuFmapFirmware *self = FU_FMAP_FIRMWARE(firmware); + FuFmapFirmwarePrivate *priv = GET_PRIVATE(self); gsize total_sz; gsize offset; g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); g_autoptr(GByteArray) buf = g_byte_array_new(); - g_autoptr(GByteArray) st_hdr = fu_struct_fmap_new(); + g_autoptr(FuStructFmap) st_hdr = fu_struct_fmap_new(); /* pad to offset */ - if (fu_firmware_get_offset(firmware) > 0) - fu_byte_array_set_size(buf, fu_firmware_get_offset(firmware), 0x00); + fu_byte_array_set_size(buf, priv->signature_offset, 0x00); - /* add header */ - total_sz = offset = st_hdr->len + (FU_STRUCT_FMAP_AREA_SIZE * images->len); + /* write each image if not already a blob */ for (guint i = 0; i < images->len; i++) { FuFirmware *img = g_ptr_array_index(images, i); - g_autoptr(GBytes) fw = fu_firmware_get_bytes_with_patches(img, error); + g_autoptr(GBytes) fw = fu_firmware_get_bytes(img, NULL); + if (fw != NULL) + continue; + fw = fu_firmware_write(img, error); if (fw == NULL) return NULL; + fu_firmware_set_bytes(img, fw); + } + + /* add header */ + total_sz = offset = st_hdr->buf->len + (FU_STRUCT_FMAP_AREA_SIZE * images->len); + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autoptr(GBytes) fw = fu_firmware_get_bytes(img, NULL); total_sz += g_bytes_get_size(fw); } /* header */ + fu_struct_fmap_set_ver_major(st_hdr, priv->ver_major); + fu_struct_fmap_set_ver_minor(st_hdr, priv->ver_minor); fu_struct_fmap_set_base(st_hdr, fu_firmware_get_addr(firmware)); fu_struct_fmap_set_nareas(st_hdr, images->len); - fu_struct_fmap_set_size(st_hdr, fu_firmware_get_offset(firmware) + total_sz); - g_byte_array_append(buf, st_hdr->data, st_hdr->len); + fu_struct_fmap_set_size(st_hdr, priv->signature_offset + total_sz); + fu_byte_array_append_array(buf, st_hdr->buf); /* add each area */ for (guint i = 0; i < images->len; i++) { FuFirmware *img = g_ptr_array_index(images, i); - g_autoptr(GBytes) fw = fu_firmware_get_bytes_with_patches(img, NULL); - g_autoptr(GByteArray) st_area = fu_struct_fmap_area_new(); - fu_struct_fmap_area_set_offset(st_area, fu_firmware_get_offset(firmware) + offset); + g_autoptr(GBytes) fw = fu_firmware_get_bytes(img, NULL); + g_autoptr(FuStructFmapArea) st_area = fu_struct_fmap_area_new(); + fu_struct_fmap_area_set_offset(st_area, priv->signature_offset + offset); fu_struct_fmap_area_set_size(st_area, g_bytes_get_size(fw)); if (fu_firmware_get_id(img) != NULL) { if (!fu_struct_fmap_area_set_name(st_area, fu_firmware_get_id(img), error)) return NULL; } - g_byte_array_append(buf, st_area->data, st_area->len); + fu_byte_array_append_array(buf, st_area->buf); offset += g_bytes_get_size(fw); } /* add the images */ for (guint i = 0; i < images->len; i++) { FuFirmware *img = g_ptr_array_index(images, i); - g_autoptr(GBytes) fw = fu_firmware_get_bytes_with_patches(img, error); + g_autoptr(GBytes) fw = fu_firmware_get_bytes(img, error); if (fw == NULL) return NULL; fu_byte_array_append_bytes(buf, fw); @@ -176,8 +251,21 @@ } static void +fu_fmap_firmware_add_magic(FuFirmware *firmware) +{ + fu_firmware_add_magic(firmware, + (const guint8 *)FU_STRUCT_FMAP_DEFAULT_SIGNATURE, + FU_STRUCT_FMAP_SIZE_SIGNATURE, + 0x0); +} + +static void fu_fmap_firmware_init(FuFmapFirmware *self) { + FuFmapFirmwarePrivate *priv = GET_PRIVATE(self); + g_type_ensure(FU_TYPE_USWID_FIRMWARE); + priv->ver_major = FU_STRUCT_FMAP_DEFAULT_VER_MAJOR; + priv->ver_minor = FU_STRUCT_FMAP_DEFAULT_VER_MINOR; fu_firmware_set_images_max(FU_FIRMWARE(self), 1024); } @@ -185,8 +273,12 @@ fu_fmap_firmware_class_init(FuFmapFirmwareClass *klass) { FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); - firmware_class->parse = fu_fmap_firmware_parse; + firmware_class->parse_full = fu_fmap_firmware_parse; + firmware_class->validate = fu_fmap_firmware_validate; firmware_class->write = fu_fmap_firmware_write; + firmware_class->export = fu_fmap_firmware_export; + firmware_class->build = fu_fmap_firmware_build; + firmware_class->add_magic = fu_fmap_firmware_add_magic; } /** diff -Nru fwupd-2.0.8/libfwupdplugin/fu-freebsd-efivars.c fwupd-2.0.20/libfwupdplugin/fu-freebsd-efivars.c --- fwupd-2.0.8/libfwupdplugin/fu-freebsd-efivars.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-freebsd-efivars.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,7 @@ #include "fwupd-error.h" -#include "fu-common.h" +#include "fu-efivars.h" #include "fu-freebsd-efivars.h" struct _FuFreebsdEfivars { @@ -28,10 +28,10 @@ fu_freebsd_efivars_supported(FuEfivars *efivars, GError **error) { if (efi_variables_supported() == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "kernel efivars support missing"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "kernel efivars support missing"); return FALSE; } return TRUE; @@ -67,7 +67,7 @@ efi_str_to_guid(guid, &guid_to_delete); - while (efi_get_next_variable_name(&guidt, &name)) { + while (efi_get_next_variable_name(&guidt, &name) == 1) { if (memcmp(&guid_to_delete, guidt, sizeof(guid_to_delete)) != 0) continue; if (!g_pattern_match_simple(name, name_glob)) @@ -87,31 +87,20 @@ efi_guid_t test; efi_str_to_guid(guid, &test); - while (efi_get_next_variable_name(&guidt, &name)) { - if (memcmp(&test, guidt, sizeof(test)) == 0) { + while (efi_get_next_variable_name(&guidt, &name) == 1) { + if (memcmp(&test, guidt, sizeof(test)) == 0) return TRUE; - } } return FALSE; } static gboolean -fu_freebsd_efivars_exists(FuEfivars *efivars, const gchar *guid, const gchar *name) -{ - /* any name */ - if (name == NULL) - return fu_freebsd_efivars_exists_guid(guid); - - return fu_freebsd_efivars_get_data(efivars, guid, name, NULL, NULL, NULL, NULL); -} - -static gboolean fu_freebsd_efivars_get_data(FuEfivars *efivars, const gchar *guid, const gchar *name, guint8 **data, gsize *data_sz, - guint32 *attr, + FuEfiVariableAttrs *attr, GError **error) { efi_guid_t guidt; @@ -119,6 +108,16 @@ return (efi_get_variable(guidt, name, data, data_sz, attr) != 0); } +static gboolean +fu_freebsd_efivars_exists(FuEfivars *efivars, const gchar *guid, const gchar *name) +{ + /* any name */ + if (name == NULL) + return fu_freebsd_efivars_exists_guid(guid); + + return fu_freebsd_efivars_get_data(efivars, guid, name, NULL, NULL, NULL, NULL); +} + static GPtrArray * fu_freebsd_efivars_get_names(FuEfivars *efivars, const gchar *guid, GError **error) { @@ -129,19 +128,18 @@ efi_str_to_guid(guid, &test); /* find names with matching GUID */ - while (efi_get_next_variable_name(&guidt, &name)) { - if (memcmp(&test, guidt, sizeof(test)) == 0) { + while (efi_get_next_variable_name(&guidt, &name) == 1) { + if (memcmp(&test, guidt, sizeof(test)) == 0) g_ptr_array_add(names, g_strdup(name)); - } } /* nothing found */ if (names->len == 0) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "no names for GUID %s", - guid); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no names for GUID %s", + guid); return NULL; } @@ -156,13 +154,13 @@ efi_guid_t *guidt = NULL; char *name = NULL; - while (efi_get_next_variable_name(&guidt, &name)) { + while (efi_get_next_variable_name(&guidt, &name) == 1) { size_t size = 0; if (efi_get_variable_size(*guidt, name, &size) < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to get efivars size"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to get efivars size"); return G_MAXUINT64; } total += size; @@ -178,13 +176,13 @@ const gchar *name, const guint8 *data, gsize sz, - guint32 attr, + FuEfiVariableAttrs attr, GError **error) { efi_guid_t guidt; efi_str_to_guid(guid, &guidt); - if (efi_set_variable(guidt, name, (guint8 *)data, sz, attr) != 0) { + if (efi_set_variable(guidt, name, (guint8 *)data, sz, attr, 0644) != 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -209,7 +207,6 @@ efivars_class->supported = fu_freebsd_efivars_supported; efivars_class->space_used = fu_freebsd_efivars_space_used; efivars_class->exists = fu_freebsd_efivars_exists; - efivars_class->get_monitor = fu_freebsd_efivars_get_monitor; efivars_class->get_data = fu_freebsd_efivars_get_data; efivars_class->set_data = fu_freebsd_efivars_set_data; efivars_class->delete = fu_freebsd_efivars_delete; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-fuzzer-firmware.c.in fwupd-2.0.20/libfwupdplugin/fu-fuzzer-firmware.c.in --- fwupd-2.0.8/libfwupdplugin/fu-fuzzer-firmware.c.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-fuzzer-firmware.c.in 2026-02-26 11:36:18.000000000 +0000 @@ -17,16 +17,16 @@ (void)g_setenv("G_DEBUG", "fatal-criticals", TRUE); (void)g_setenv("FWUPD_FUZZER_RUNNING", "1", TRUE); - ret = fu_firmware_parse_bytes(firmware, fw, 0x0, FWUPD_INSTALL_FLAG_NONE, NULL); + ret = fu_firmware_parse_bytes(firmware, fw, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, NULL); if (!ret && fu_firmware_has_flag(firmware, FU_FIRMWARE_FLAG_HAS_CHECKSUM)) { g_clear_object(&firmware); firmware = FU_FIRMWARE(@FIRMWARENEW@); ret = fu_firmware_parse_bytes(firmware, fw, 0x0, - FWUPD_INSTALL_FLAG_NO_SEARCH | - FWUPD_INSTALL_FLAG_IGNORE_VID_PID | - FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH | + FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID | + FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM, NULL); } if (ret) { diff -Nru fwupd-2.0.8/libfwupdplugin/fu-fuzzer-main.c fwupd-2.0.20/libfwupdplugin/fu-fuzzer-main.c --- fwupd-2.0.8/libfwupdplugin/fu-fuzzer-main.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-fuzzer-main.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,9 +6,9 @@ #include -__attribute__((weak)) extern int +__attribute__((weak)) extern int /* nocheck:name */ LLVMFuzzerTestOneInput(const unsigned char *data, size_t size); -__attribute__((weak)) extern int +__attribute__((weak)) extern int /* nocheck:name */ LLVMFuzzerInitialize(int *argc, char ***argv); int diff -Nru fwupd-2.0.8/libfwupdplugin/fu-gcab.c fwupd-2.0.20/libfwupdplugin/fu-gcab.c --- fwupd-2.0.8/libfwupdplugin/fu-gcab.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-gcab.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,92 +8,53 @@ #include -#include - -#ifdef _WIN32 -#include -#endif - int main(int argc, char **argv) { - gboolean do_compression = FALSE; gboolean do_create = FALSE; - gboolean do_extract = FALSE; - gboolean do_list = FALSE; - gboolean no_path = FALSE; - gboolean verbose = FALSE; g_autoptr(FuCabFirmware) cab_firmware = fu_cab_firmware_new(); g_autoptr(GOptionContext) context = g_option_context_new(NULL); g_autoptr(GError) error = NULL; const GOptionEntry options[] = { - {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be verbose", NULL}, {"create", 'c', 0, G_OPTION_ARG_NONE, &do_create, "Create archive", NULL}, - {"extract", 'x', 0, G_OPTION_ARG_NONE, &do_extract, "Extract all files", NULL}, - {"list", 'l', 0, G_OPTION_ARG_NONE, &do_list, "List contents", NULL}, - {"zip", 'z', 0, G_OPTION_ARG_NONE, &do_compression, "Use zip compression", NULL}, - {"nopath", 'n', 0, G_OPTION_ARG_NONE, &no_path, "Do not include path", NULL}, {NULL}}; -#ifdef _WIN32 - /* required for Windows */ - SetConsoleOutputCP(CP_UTF8); - SetConsoleCP(CP_UTF8); - (void)g_setenv("LANG", "C.UTF-8", FALSE); -#endif - - setlocale(LC_ALL, ""); - g_option_context_add_main_entries(context, options, NULL); if (!g_option_context_parse(context, &argc, &argv, &error)) { g_printerr("Failed to parse arguments: %s\n", error->message); return EXIT_FAILURE; } - if (verbose) - (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); if (do_create && argc > 1) { g_autoptr(GFile) file = g_file_new_for_path(argv[1]); for (gint i = 2; i < argc; i++) { g_autoptr(GBytes) img_blob = NULL; g_autoptr(FuCabImage) img = fu_cab_image_new(); - if (no_path) { - g_autofree gchar *basename = g_path_get_basename(argv[i]); - fu_firmware_set_id(FU_FIRMWARE(img), basename); - } else { - fu_firmware_set_id(FU_FIRMWARE(img), argv[i]); - } + g_autofree gchar *basename = g_path_get_basename(argv[i]); + + fu_firmware_set_id(FU_FIRMWARE(img), basename); img_blob = fu_bytes_get_contents(argv[i], &error); if (img_blob == NULL) { g_printerr("Failed to load file %s: %s\n", argv[i], error->message); return EXIT_FAILURE; } fu_firmware_set_bytes(FU_FIRMWARE(img), img_blob); - fu_firmware_add_image(FU_FIRMWARE(cab_firmware), FU_FIRMWARE(img)); + if (!fu_firmware_add_image(FU_FIRMWARE(cab_firmware), + FU_FIRMWARE(img), + &error)) { + g_printerr("Failed to add image to %s: %s\n", + argv[1], + error->message); + return EXIT_FAILURE; + } } - if (do_compression) - fu_cab_firmware_set_compressed(cab_firmware, TRUE); if (!fu_firmware_write_file(FU_FIRMWARE(cab_firmware), file, &error)) { g_printerr("Failed to write file %s: %s\n", argv[1], error->message); return EXIT_FAILURE; } return EXIT_SUCCESS; } - if (do_list && argc > 1) { - g_autofree gchar *str = NULL; - g_autoptr(GFile) file = g_file_new_for_path(argv[1]); - if (!fu_firmware_parse_file(FU_FIRMWARE(cab_firmware), - file, - FWUPD_INSTALL_FLAG_NONE, - &error)) { - g_printerr("Failed to parse %s: %s\n", argv[1], error->message); - return EXIT_FAILURE; - } - str = fu_firmware_to_string(FU_FIRMWARE(cab_firmware)); - g_print("%s", str); - return EXIT_SUCCESS; - } g_printerr("Please specify a single operation\n"); return EXIT_FAILURE; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-heci-device.c fwupd-2.0.20/libfwupdplugin/fu-heci-device.c --- fwupd-2.0.8/libfwupdplugin/fu-heci-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-heci-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,288 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#define G_LOG_DOMAIN "FuHeciDevice" + +#include "config.h" + +#include "fu-byte-array.h" +#include "fu-heci-device.h" + +/** + * FuHeciDevice + * + * A HECI device. + * + * See also: #FuMeiDevice + */ + +G_DEFINE_TYPE(FuHeciDevice, fu_heci_device, FU_TYPE_MEI_DEVICE) + +#define FU_HECI_DEVICE_TIMEOUT 200 /* ms */ + +static gboolean +fu_heci_device_result_to_error(FuMkhiStatus result, GError **error) +{ + const gchar *msg = fu_mkhi_status_to_string(result); + const FuErrorMapEntry entries[] = { + {FU_MKHI_STATUS_SUCCESS, FWUPD_ERROR_LAST, NULL}, + {FU_MKHI_STATUS_UNKNOWN_PERHAPS_NOT_SUPPORTED, FWUPD_ERROR_NOT_SUPPORTED, msg}, + {FU_MKHI_STATUS_NOT_SUPPORTED, FWUPD_ERROR_NOT_SUPPORTED, msg}, + {FU_MKHI_STATUS_NOT_AVAILABLE, FWUPD_ERROR_NOT_SUPPORTED, msg}, + {FU_MKHI_STATUS_NOT_SET, FWUPD_ERROR_NOT_SUPPORTED, msg}, + }; + return fu_error_map_entry_to_gerror(result, entries, G_N_ELEMENTS(entries), error); +} + +/** + * fu_heci_device_read_file: + * @self: a #FuHeciDevice + * @filename: (not nullable): MFS filename + * @error: (nullable): optional return location for an error + * + * Reads a file from the MFS. + * + * Returns: (transfer container): file data + * + * Since: 2.0.9 + **/ +GByteArray * +fu_heci_device_read_file(FuHeciDevice *self, const gchar *filename, GError **error) +{ + guint32 data_size; + guint datasz_req = 0x80; + g_autoptr(GByteArray) bufout = g_byte_array_new(); + g_autoptr(GByteArray) buf_res = g_byte_array_new(); + g_autoptr(FuMkhiReadFileRequest) st_req = fu_mkhi_read_file_request_new(); + g_autoptr(FuMkhiReadFileResponse) st_res = NULL; + + g_return_val_if_fail(FU_IS_HECI_DEVICE(self), NULL); + g_return_val_if_fail(filename != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* request */ + if (!fu_mkhi_read_file_request_set_filename(st_req, filename, error)) + return NULL; + fu_mkhi_read_file_request_set_data_size(st_req, datasz_req); + fu_mkhi_read_file_request_set_flags(st_req, (1 << 3)); /* ?? */ + if (!fu_mei_device_write(FU_MEI_DEVICE(self), + st_req->buf->data, + st_req->buf->len, + FU_HECI_DEVICE_TIMEOUT, + error)) + return NULL; + + /* response */ + fu_byte_array_set_size(buf_res, FU_MKHI_READ_FILE_RESPONSE_SIZE + datasz_req, 0x0); + if (!fu_mei_device_read(FU_MEI_DEVICE(self), + buf_res->data, + buf_res->len, + NULL, + FU_HECI_DEVICE_TIMEOUT, + error)) + return NULL; + st_res = fu_mkhi_read_file_response_parse(buf_res->data, buf_res->len, 0x0, error); + if (st_res == NULL) + return NULL; + if (!fu_heci_device_result_to_error(fu_mkhi_read_file_response_get_result(st_res), error)) + return NULL; + + /* verify we got what we asked for */ + data_size = fu_mkhi_read_file_response_get_data_size(st_res); + if (data_size > datasz_req) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "invalid response data size, requested 0x%x and got 0x%x", + datasz_req, + data_size); + return NULL; + } + + /* success */ + g_byte_array_append(bufout, &buf_res->data[st_res->buf->len], data_size); + return g_steal_pointer(&bufout); +} + +/** + * fu_heci_device_read_file_ex: + * @self: a #FuHeciDevice + * @file_id: MFS file ID + * @section: MFS section + * @datasz_req: the maximum size of data to request + * @error: (nullable): optional return location for an error + * + * Reads a file from the MFS. + * + * Returns: (transfer container): file data + * + * Since: 2.0.9 + **/ +GByteArray * +fu_heci_device_read_file_ex(FuHeciDevice *self, + guint32 file_id, + guint32 section, + guint32 datasz_req, + GError **error) +{ + guint32 data_size; + g_autoptr(FuMkhiReadFileExRequest) st_req = fu_mkhi_read_file_ex_request_new(); + g_autoptr(FuMkhiReadFileExResponse) st_res = NULL; + g_autoptr(GByteArray) bufout = g_byte_array_new(); + g_autoptr(GByteArray) buf_res = g_byte_array_new(); + + g_return_val_if_fail(FU_IS_HECI_DEVICE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* request */ + fu_mkhi_read_file_ex_request_set_file_id(st_req, file_id); + fu_mkhi_read_file_ex_request_set_data_size(st_req, datasz_req); + fu_mkhi_read_file_ex_request_set_flags(st_req, section); + if (!fu_mei_device_write(FU_MEI_DEVICE(self), + st_req->buf->data, + st_req->buf->len, + FU_HECI_DEVICE_TIMEOUT, + error)) + return NULL; + + /* response */ + fu_byte_array_set_size(buf_res, FU_MKHI_READ_FILE_EX_REQUEST_SIZE + datasz_req, 0x0); + if (!fu_mei_device_read(FU_MEI_DEVICE(self), + buf_res->data, + buf_res->len, + NULL, + FU_HECI_DEVICE_TIMEOUT, + error)) + return NULL; + st_res = fu_mkhi_read_file_ex_response_parse(buf_res->data, buf_res->len, 0x0, error); + if (st_res == NULL) + return NULL; + if (!fu_heci_device_result_to_error(fu_mkhi_read_file_ex_response_get_result(st_res), + error)) + return NULL; + + /* verify we got what we asked for */ + data_size = fu_mkhi_read_file_ex_response_get_data_size(st_res); + if (data_size > datasz_req) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "invalid response data size, requested 0x%x and got 0x%x", + datasz_req, + data_size); + return NULL; + } + + /* success */ + g_byte_array_append(bufout, &buf_res->data[st_res->buf->len], data_size); + return g_steal_pointer(&bufout); +} + +/** + * fu_heci_device_arbh_svn_get_info: + * @self: a #FuHeciDevice + * @usage_id: usage ID, e.g. %FU_MKHI_ARBH_SVN_INFO_ENTRY_USAGE_ID_CSE_RBE + * @executing: (out) (nullable): currently executing SVN + * @min_allowed: (out) (nullable): minimal allowed SVN + * @error: (nullable): optional return location for an error + * + * Reads the ARBH SVN for a specific usage ID. + * + * Returns: %TRUE for success + * + * Since: 2.0.9 + **/ +gboolean +fu_heci_device_arbh_svn_get_info(FuHeciDevice *self, + guint8 usage_id, + guint8 *executing, + guint8 *min_allowed, + GError **error) +{ + guint32 num_entries; + gsize offset = 0; + gboolean found_usage = FALSE; + g_autoptr(FuMkhiArbhSvnGetInfoRequest) st_req = fu_mkhi_arbh_svn_get_info_request_new(); + g_autoptr(FuMkhiArbhSvnGetInfoResponse) st_res = NULL; + g_autoptr(GByteArray) buf_res = g_byte_array_new(); + + g_return_val_if_fail(FU_IS_HECI_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* request */ + if (!fu_mei_device_write(FU_MEI_DEVICE(self), + st_req->buf->data, + st_req->buf->len, + FU_HECI_DEVICE_TIMEOUT, + error)) + return FALSE; + + /* response */ + fu_byte_array_set_size(buf_res, fu_mei_device_get_max_msg_length(FU_MEI_DEVICE(self)), 0x0); + if (!fu_mei_device_read(FU_MEI_DEVICE(self), + buf_res->data, + buf_res->len, + NULL, + FU_HECI_DEVICE_TIMEOUT, + error)) + return FALSE; + st_res = fu_mkhi_arbh_svn_get_info_response_parse(buf_res->data, buf_res->len, 0x0, error); + if (st_res == NULL) + return FALSE; + if (!fu_heci_device_result_to_error(fu_mkhi_arbh_svn_get_info_response_get_result(st_res), + error)) + return FALSE; + + /* verify we got what we asked for */ + num_entries = fu_mkhi_arbh_svn_get_info_response_get_num_entries(st_res); + offset += st_res->buf->len; + for (guint32 i = 0; i < num_entries; i++) { + g_autoptr(FuMkhiArbhSvnInfoEntry) st_entry = NULL; + + /* parse each entry */ + st_entry = + fu_mkhi_arbh_svn_info_entry_parse(buf_res->data, buf_res->len, offset, error); + if (st_entry == NULL) + return FALSE; + + /* matches */ + if (fu_mkhi_arbh_svn_info_entry_get_usage_id(st_entry) == usage_id) { + found_usage = TRUE; + if (executing != NULL) + *executing = fu_mkhi_arbh_svn_info_entry_get_executing(st_entry); + if (min_allowed != NULL) + *min_allowed = + fu_mkhi_arbh_svn_info_entry_get_min_allowed(st_entry); + break; + } + + /* next */ + offset += st_entry->buf->len; + } + + /* did not find */ + if (!found_usage) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "no entry for usage ID 0x%x", + usage_id); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_heci_device_init(FuHeciDevice *self) +{ +} + +static void +fu_heci_device_class_init(FuHeciDeviceClass *klass) +{ +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-heci-device.h fwupd-2.0.20/libfwupdplugin/fu-heci-device.h --- fwupd-2.0.8/libfwupdplugin/fu-heci-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-heci-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,62 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-heci-struct.h" +#include "fu-mei-device.h" + +#define FU_TYPE_HECI_DEVICE (fu_heci_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuHeciDevice, fu_heci_device, FU, HECI_DEVICE, FuMeiDevice) + +struct _FuHeciDeviceClass { + FuMeiDeviceClass parent_class; +}; + +/** + * FU_HECI_DEVICE_UUID_MKHI: + * + * UUID for MKHI, usually a legacy interface. + */ +#define FU_HECI_DEVICE_UUID_MKHI "8e6a6715-9abc-4043-88ef-9e39c6f63e0f" + +/** + * FU_HECI_DEVICE_UUID_MCHI: + * + * UUID for MCHI, commonly called MCA. + */ +#define FU_HECI_DEVICE_UUID_MCHI "dd17041c-09ea-4b17-a271-5b989867ec65" + +/** + * FU_HECI_DEVICE_UUID_MCHI2: + * + * Another UUID for MCHI, commonly called MCA. + */ +#define FU_HECI_DEVICE_UUID_MCHI2 "fe2af7a6-ef22-4b45-872f-176b0bbc8b43" + +/** + * FU_HECI_DEVICE_UUID_FWUPDATE: + * + * UUID for firmware updates. + */ +#define FU_HECI_DEVICE_UUID_FWUPDATE "87d90ca5-3495-4559-8105-3fbfa37b8b79" + +GByteArray * +fu_heci_device_read_file(FuHeciDevice *self, + const gchar *filename, + GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); +GByteArray * +fu_heci_device_read_file_ex(FuHeciDevice *self, + guint32 file_id, + guint32 section, + guint32 datasz_req, + GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); +gboolean +fu_heci_device_arbh_svn_get_info(FuHeciDevice *self, + guint8 usage_id, + guint8 *executing, + guint8 *min_allowed, + GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-heci.rs fwupd-2.0.20/libfwupdplugin/fu-heci.rs --- fwupd-2.0.8/libfwupdplugin/fu-heci.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-heci.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,153 @@ +// Copyright 2024 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[repr(u8)] +enum FuMkhiGroupId { + Cbm, + Pm, // no longer used + Pwd, + Fwcaps, + App, // no longer used + Fwupdate, // for manufacturing downgrade + FirmwareUpdate, + Bist, + Mdes, + MeDbg, + Mca, // sometimes called "FPF" + Gen = 0xFF, +} + +#[derive(ToString)] +enum FuMkhiStatus { + Success, + InvalidState, + MessageSkipped, + SizeError = 0x05, + UnknownPerhapsNotSupported = 0x0b, // guessed + NotSet = 0x0F, // guessed + NotAvailable = 0x18, // guessed + InvalidAccess = 0x84, + InvalidParams = 0x85, + NotReady = 0x88, + NotSupported = 0x89, + InvalidAddress = 0x8C, + InvalidCommand = 0x8D, + Failure = 0x9E, + InvalidResource = 0xE4, + ResourceInUse = 0xE5, + NoResource = 0xE6, + GeneralError = 0xFF, + // GPU fw update + LowerArbSvn = 0x233, + LowerTcbSvn = 0x23B, + LowerVcn = 0x23C, + UpdateIupSvn = 0x29A, + UpdateIupVcn = 0x29B, + UpdateImageLen = 0x29C, + UpdatePvBit = 0x29D, + UpdateEngineeringMismatch = 0x2B2, + UpdateVerManFailedOrom = 0x102C, + UpdateDeviceIdNotMatch = 0x102F, + UpdateOpromSectionNotExist = 0x1032, + UpdateOpromInvalidStructure = 0x1035, + UpdateGetOpromVersionFailed 0x103C, + UpdateOromInvalidStructure = 0x1045, + UpdateVerManFailedGfxData = 0x1048, + UpdateGfxDataOemManufVer = 0x104B, +} + +#[repr(u8)] +enum FuMkhiCommand { + ReadFile = 0x02, + ReadFileEx = 0x0A, + ArbhSvnCommit = 0x1B, + ArbhSvnGetInfo = 0x1C, + // not real commands, but makes debugging easier + ReadFileResponse = 0x82, + ReadFileExResponse = 0x8A, + ArbhSvnCommitResponse = 0x9B, + ArbhSvnGetInfoResponse = 0x9C, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuMkhiReadFileRequest { + group_id: FuMkhiGroupId == Mca, + command: FuMkhiCommand == ReadFile, + _rsvd: u8, + result: u8 == 0x0, + filename: [char; 0x40], + offset: u32le == 0x0, + data_size: u32le, + flags: u8, +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuMkhiReadFileResponse { + group_id: FuMkhiGroupId == Mca, + command: FuMkhiCommand == ReadFileResponse, + _rsvd: u8, + result: u8, + data_size: u32le, + // payload here +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuMkhiReadFileExRequest { + group_id: FuMkhiGroupId == Mca, + command: FuMkhiCommand == ReadFileEx, + _rsvd: u8, + result: u8 == 0x0, + file_id: u32le, + offset: u32le == 0x0, + data_size: u32le, + flags: u8, +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuMkhiReadFileExResponse { + group_id: FuMkhiGroupId == Mca, + command: FuMkhiCommand == ReadFileExResponse, + _rsvd: u8, + result: u8, + data_size: u32le, + // payload here +} + + +#[repr(u8)] +enum FuMkhiArbhSvnInfoEntryUsageId { + CseRbe = 3, +} + +#[derive(Parse)] +#[repr(C, packed)] +struct FuMkhiArbhSvnInfoEntry { + usage_id: FuMkhiArbhSvnInfoEntryUsageId, + _flags: u8, + executing: u8, + min_allowed: u8, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuMkhiArbhSvnGetInfoRequest { + group_id: FuMkhiGroupId == Mca, + command: FuMkhiCommand == ArbhSvnGetInfo, + reserved: u8, + result: u8 == 0x0, +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuMkhiArbhSvnGetInfoResponse { + group_id: FuMkhiGroupId == Mca, + command: FuMkhiCommand == ArbhSvnGetInfoResponse, + _rsvd: u8, + result: u8, + num_entries: u32le, + // FuMkhiArbhSvnInfoEntry entries[] +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-hid-descriptor.c fwupd-2.0.20/libfwupdplugin/fu-hid-descriptor.c --- fwupd-2.0.8/libfwupdplugin/fu-hid-descriptor.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-hid-descriptor.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,7 +13,6 @@ #include "fu-hid-report-item.h" #include "fu-hid-struct.h" #include "fu-input-stream.h" -#include "fu-mem.h" /** * FuHidDescriptor: @@ -52,7 +51,7 @@ static gboolean fu_hid_descriptor_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize offset = 0; @@ -91,7 +90,7 @@ /* only for debugging */ itemstr = fu_firmware_to_string(FU_FIRMWARE(item)); - g_debug("add to table-state:\n%s", itemstr); + g_debug("add to table-state: %s", itemstr); /* if there is a sane number of duplicate tokens then add to table */ if (fu_hid_report_item_get_kind(item) == FU_HID_ITEM_KIND_GLOBAL) { @@ -132,19 +131,19 @@ /* copy the table state to the new report */ for (guint i = 0; i < table_state->len; i++) { FuHidReportItem *item_tmp = g_ptr_array_index(table_state, i); - if (!fu_firmware_add_image_full(FU_FIRMWARE(report), - FU_FIRMWARE(item_tmp), - error)) + if (!fu_firmware_add_image(FU_FIRMWARE(report), + FU_FIRMWARE(item_tmp), + error)) return FALSE; } for (guint i = 0; i < table_local->len; i++) { FuHidReportItem *item_tmp = g_ptr_array_index(table_local, i); - if (!fu_firmware_add_image_full(FU_FIRMWARE(report), - FU_FIRMWARE(item_tmp), - error)) + if (!fu_firmware_add_image(FU_FIRMWARE(report), + FU_FIRMWARE(item_tmp), + error)) return FALSE; } - if (!fu_firmware_add_image_full(firmware, FU_FIRMWARE(report), error)) + if (!fu_firmware_add_image(firmware, FU_FIRMWARE(report), error)) return FALSE; /* remove all the local items */ @@ -291,8 +290,11 @@ { fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION); fu_firmware_set_size_max(FU_FIRMWARE(self), 64 * 1024); - fu_firmware_set_images_max(FU_FIRMWARE(self), - g_getenv("FWUPD_FUZZER_RUNNING") != NULL ? 10 : 1024); +#ifdef HAVE_FUZZER + fu_firmware_set_images_max(FU_FIRMWARE(self), 10); +#else + fu_firmware_set_images_max(FU_FIRMWARE(self), 1024); +#endif g_type_ensure(FU_TYPE_HID_REPORT); g_type_ensure(FU_TYPE_HID_REPORT_ITEM); } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-hid-device.c fwupd-2.0.20/libfwupdplugin/fu-hid-device.c --- fwupd-2.0.8/libfwupdplugin/fu-hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,8 +10,6 @@ #include "fu-dump.h" #include "fu-hid-device.h" -#include "fu-string.h" -#include "fu-usb-device-private.h" #include "fu-usb-endpoint.h" #define FU_HID_REPORT_GET 0x01 @@ -114,7 +112,11 @@ g_autoptr(FuFirmware) descriptor = fu_hid_descriptor_new(); g_autofree gchar *title = g_strdup_printf("HidDescriptor:0x%x", i); fu_dump_bytes(G_LOG_DOMAIN, title, fw); - if (!fu_firmware_parse_bytes(descriptor, fw, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_bytes(descriptor, + fw, + 0x0, + FU_FIRMWARE_PARSE_FLAG_NONE, + error)) return NULL; g_ptr_array_add(descriptors, g_steal_pointer(&descriptor)); } @@ -228,7 +230,7 @@ if ((priv->flags & FU_HID_DEVICE_FLAG_NO_KERNEL_UNBIND) == 0) flags |= FU_USB_DEVICE_CLAIM_FLAG_KERNEL_DRIVER; if (!fu_usb_device_claim_interface(FU_USB_DEVICE(self), priv->interface, flags, error)) { - g_prefix_error(error, "failed to claim HID interface: "); + g_prefix_error_literal(error, "failed to claim HID interface: "); return FALSE; } @@ -306,7 +308,7 @@ fu_hid_device_get_interface(FuHidDevice *self) { FuHidDevicePrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FU_HID_DEVICE(self), 0xff); + g_return_val_if_fail(FU_HID_DEVICE(self), G_MAXUINT8); return priv->interface; } @@ -345,7 +347,7 @@ fu_hid_device_get_ep_addr_in(FuHidDevice *self) { FuHidDevicePrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FU_HID_DEVICE(self), 0xff); + g_return_val_if_fail(FU_HID_DEVICE(self), G_MAXUINT8); return priv->ep_addr_in; } @@ -384,7 +386,7 @@ fu_hid_device_get_ep_addr_out(FuHidDevice *self) { FuHidDevicePrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FU_HID_DEVICE(self), 0xff); + g_return_val_if_fail(FU_HID_DEVICE(self), G_MAXUINT8); return priv->ep_addr_out; } @@ -439,7 +441,7 @@ helper->timeout, NULL, /* cancellable */ error)) { - g_prefix_error(error, "failed to SetReport [interrupt-transfer]: "); + g_prefix_error_literal(error, "failed to SetReport [interrupt-transfer]: "); return FALSE; } } else { @@ -467,7 +469,7 @@ helper->timeout, NULL, error)) { - g_prefix_error(error, "failed to SetReport: "); + g_prefix_error_literal(error, "failed to SetReport: "); return FALSE; } } @@ -596,7 +598,7 @@ helper->timeout, NULL, error)) { - g_prefix_error(error, "failed to GetReport: "); + g_prefix_error_literal(error, "failed to GetReport: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, title, helper->buf, actual_len); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-hid-device.h fwupd-2.0.20/libfwupdplugin/fu-hid-device.h --- fwupd-2.0.8/libfwupdplugin/fu-hid-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-hid-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -40,7 +40,7 @@ FU_HID_DEVICE_FLAG_AUTODETECT_EPS = 1 << 6, /*< private >*/ FU_HID_DEVICE_FLAG_LAST -} FuHidDeviceFlags; +} G_GNUC_FLAG_ENUM FuHidDeviceFlags; void fu_hid_device_add_flag(FuHidDevice *self, FuHidDeviceFlags flag) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-hid-report-item.c fwupd-2.0.20/libfwupdplugin/fu-hid-report-item.c --- fwupd-2.0.8/libfwupdplugin/fu-hid-report-item.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-hid-report-item.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,11 +9,9 @@ #include "config.h" #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-hid-report-item.h" #include "fu-input-stream.h" -#include "fu-mem-private.h" #include "fu-partial-input-stream.h" #include "fu-string.h" @@ -57,7 +55,7 @@ static gboolean fu_hid_report_item_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuHidReportItem *self = FU_HID_REPORT_ITEM(firmware); @@ -108,7 +106,7 @@ } partial_stream = fu_partial_input_stream_new(stream, 1, data_size, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut HID payload: "); + g_prefix_error_literal(error, "failed to cut HID payload: "); return FALSE; } if (!fu_firmware_set_stream(firmware, partial_stream, error)) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-hidraw-device.c fwupd-2.0.20/libfwupdplugin/fu-hidraw-device.c --- fwupd-2.0.8/libfwupdplugin/fu-hidraw-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-hidraw-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,10 +25,106 @@ * See also: #FuUdevDevice */ -G_DEFINE_TYPE(FuHidrawDevice, fu_hidraw_device, FU_TYPE_UDEV_DEVICE) +typedef struct { + FuHidrawBusType bus_type; +} FuHidrawDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuHidrawDevice, fu_hidraw_device, FU_TYPE_UDEV_DEVICE) + +#define GET_PRIVATE(o) (fu_hidraw_device_get_instance_private(o)) #define FU_HIDRAW_DEVICE_IOCTL_TIMEOUT 2500 /* ms */ +static void +fu_hidraw_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuHidrawDevice *self = FU_HIDRAW_DEVICE(device); + FuHidrawDevicePrivate *priv = GET_PRIVATE(self); + fwupd_codec_string_append(str, + idt, + "BusType", + fu_hidraw_bus_type_to_string(priv->bus_type)); +} + +/** + * fu_hidraw_device_get_bus_type: + * @self: a #FuHidrawDevice + * + * Gets the bus type. + * + * Returns: a #FuHidrawBusType, e.g. %FU_HIDRAW_BUS_TYPE_USB + * + * Since: 2.0.14 + **/ +FuHidrawBusType +fu_hidraw_device_get_bus_type(FuHidrawDevice *self) +{ + FuHidrawDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_HIDRAW_DEVICE(self), FU_HIDRAW_BUS_TYPE_UNKNOWN); + return priv->bus_type; +} + +/** + * fu_hidraw_device_parse_descriptor: + * @self: a #FuHidrawDevice + * @error: (nullable): optional return location for an error + * + * Retrieves and parses the HID descriptor. + * + * Returns: (transfer full): a #FuHidDescriptor, or %NULL on error + * + * Since: 2.0.12 + **/ +FuHidDescriptor * +fu_hidraw_device_parse_descriptor(FuHidrawDevice *self, GError **error) +{ +#ifdef HAVE_HIDRAW_H + gint desc_size = 0; + struct hidraw_report_descriptor rpt_desc = {0x0}; + g_autoptr(FuFirmware) descriptor = fu_hid_descriptor_new(); + g_autoptr(FuIoctl) ioctl = fu_udev_device_ioctl_new(FU_UDEV_DEVICE(self)); + g_autoptr(GBytes) fw = NULL; + + /* get report descriptor size */ + if (!fu_ioctl_execute(ioctl, + HIDIOCGRDESCSIZE, + (guint8 *)&desc_size, + sizeof(desc_size), + NULL, + 5000, + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to get report descriptor size: "); + return NULL; + } + + rpt_desc.size = desc_size; + if (!fu_ioctl_execute(ioctl, + HIDIOCGRDESC, + (guint8 *)&rpt_desc, + sizeof(rpt_desc), + NULL, + 5000, + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to get report descriptor: "); + return NULL; + } + fu_dump_raw(G_LOG_DOMAIN, "HID descriptor", rpt_desc.value, rpt_desc.size); + + fw = g_bytes_new(rpt_desc.value, rpt_desc.size); + if (!fu_firmware_parse_bytes(descriptor, fw, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, error)) + return NULL; + return FU_HID_DESCRIPTOR(g_steal_pointer(&descriptor)); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + " not available"); + return NULL; +#endif /* HAVE_HIDRAW_H */ +} + static gboolean fu_hidraw_device_probe_usb(FuHidrawDevice *self, GError **error) { @@ -48,10 +144,56 @@ } static gboolean +fu_hidraw_device_setup(FuDevice *device, GError **error) +{ +#ifdef HAVE_HIDRAW_H + FuHidrawDevice *self = FU_HIDRAW_DEVICE(device); + FuHidrawDevicePrivate *priv = GET_PRIVATE(self); + struct hidraw_devinfo hid_raw_info = {0x0}; + g_autoptr(FuIoctl) ioctl = fu_udev_device_ioctl_new(FU_UDEV_DEVICE(self)); + g_autoptr(GError) error_local = NULL; + + if (!fu_ioctl_execute(ioctl, + HIDIOCGRAWINFO, + (guint8 *)&hid_raw_info, + sizeof(hid_raw_info), + NULL, + FU_HIDRAW_DEVICE_IOCTL_TIMEOUT, + FU_IOCTL_FLAG_NONE, + &error_local)) { + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED) && + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("ignoring missing emulation data: %s", error_local->message); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + priv->bus_type = hid_raw_info.bustype; + + /* fallback only */ + if (fu_device_get_vid(device) == 0x0) + fu_device_set_vid(device, hid_raw_info.vendor); + if (fu_device_get_pid(device) == 0x0) + fu_device_set_pid(device, hid_raw_info.product); + + /* success */ + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE; +#endif +} + +static gboolean fu_hidraw_device_probe(FuDevice *device, GError **error) { FuHidrawDevice *self = FU_HIDRAW_DEVICE(device); g_autofree gchar *prop_id = NULL; + g_autofree gchar *version = NULL; g_auto(GStrv) split = NULL; g_autoptr(FuDevice) hid_device = NULL; @@ -78,7 +220,7 @@ G_MAXUINT16, FU_INTEGER_BASE_16, error)) { - g_prefix_error(error, "failed to parse HID_ID: "); + g_prefix_error_literal(error, "failed to parse HID_ID: "); return FALSE; } fu_device_set_vid(device, (guint16)val); @@ -91,7 +233,7 @@ G_MAXUINT16, FU_INTEGER_BASE_16, error)) { - g_prefix_error(error, "failed to parse HID_ID: "); + g_prefix_error_literal(error, "failed to parse HID_ID: "); return FALSE; } fu_device_set_pid(device, (guint16)val); @@ -130,6 +272,23 @@ } } + version = + fu_udev_device_read_property(FU_UDEV_DEVICE(hid_device), "HID_FIRMWARE_VERSION", NULL); + if (version != NULL) { + guint64 hid_version = 0; + g_autoptr(GError) error_local = NULL; + + if (!fu_strtoull(version, + &hid_version, + 0x0, + G_MAXUINT64, + FU_INTEGER_BASE_AUTO, + &error_local)) { + g_info("failed to parse HID_FIRMWARE_VERSION: %s", error_local->message); + } else + fu_device_set_version_raw(FU_DEVICE(self), hid_version); + } + /* set the hidraw device */ if (fu_udev_device_get_device_file(FU_UDEV_DEVICE(self)) == NULL) { g_autofree gchar *device_file = NULL; @@ -143,6 +302,13 @@ } /* USB\\VID_1234 */ + fu_device_add_instance_u16(FU_DEVICE(self), "VID", fu_device_get_vid(device)); + fu_device_build_instance_id_full(device, + FU_DEVICE_INSTANCE_FLAG_QUIRKS, + NULL, + "USB", + "VID", + NULL); fu_device_add_instance_u16(FU_DEVICE(self), "VEN", fu_device_get_vid(device)); fu_device_add_instance_u16(FU_DEVICE(self), "DEV", fu_device_get_pid(device)); fu_device_build_instance_id_full(device, @@ -274,6 +440,138 @@ #endif } +/** + * fu_hidraw_device_get_input: + * @self: a #FuHidrawDevice + * @buf: (not nullable): a buffer to use, which *must* be large enough for the request + * @bufsz: the size of @buf + * @flags: some #FuIoctlFlags, e.g. %FU_IOCTL_FLAG_RETRY + * @error: (nullable): optional return location for an error + * + * Do a HID GetInput request. + * + * Returns: %TRUE for success + * + * Since: 2.0.19 + **/ +gboolean +fu_hidraw_device_get_input(FuHidrawDevice *self, + guint8 *buf, + gsize bufsz, + FuIoctlFlags flags, + GError **error) +{ +#ifdef HAVE_HIDRAW_H + g_autoptr(FuIoctl) ioctl = fu_udev_device_ioctl_new(FU_UDEV_DEVICE(self)); +#endif + + g_return_val_if_fail(FU_IS_HIDRAW_DEVICE(self), FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + +#ifdef HAVE_HIDRAW_H + fu_dump_raw(G_LOG_DOMAIN, "GetInput[req]", buf, bufsz); + if (!fu_ioctl_execute(ioctl, + HIDIOCGINPUT(bufsz), /* nocheck:blocked */ + buf, + bufsz, + NULL, + FU_HIDRAW_DEVICE_IOCTL_TIMEOUT, + flags, + error)) + return FALSE; + fu_dump_raw(G_LOG_DOMAIN, "GetInput[res]", buf, bufsz); + + /* success */ + return TRUE; +#else + /* failed */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + " not available"); + return FALSE; +#endif +} + +/** + * fu_hidraw_device_set_report: + * @self: a #FuHidrawDevice + * @buf: (not nullable): a buffer to use, which *must* be large enough for the request + * @bufsz: the size of @buf + * @flags: some #FuIOChannelFlags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: (nullable): optional return location for an error + * + * Do a HID SetOutputReport request. + * + * Returns: %TRUE for success + * + * Since: 2.0.14 + **/ +gboolean +fu_hidraw_device_set_report(FuHidrawDevice *self, + const guint8 *buf, + gsize bufsz, + FuIOChannelFlags flags, + GError **error) +{ + g_return_val_if_fail(FU_IS_HIDRAW_DEVICE(self), FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + fu_dump_raw(G_LOG_DOMAIN, "SetReport", buf, bufsz); + return fu_udev_device_write(FU_UDEV_DEVICE(self), + buf, + bufsz, + FU_HIDRAW_DEVICE_IOCTL_TIMEOUT, + flags, + error); +} + +/** + * fu_hidraw_device_get_report: + * @self: a #FuHidrawDevice + * @buf: (not nullable): a buffer to use, which *must* be large enough for the request + * @bufsz: the size of @buf + * @flags: some #FuIOChannelFlags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: (nullable): optional return location for an error + * + * Do a HID GetInputReport request. + * + * Returns: %TRUE for success + * + * Since: 2.0.14 + **/ +gboolean +fu_hidraw_device_get_report(FuHidrawDevice *self, + guint8 *buf, + gsize bufsz, + FuIOChannelFlags flags, + GError **error) +{ + gsize bytes_read = 0; + + g_return_val_if_fail(FU_IS_HIDRAW_DEVICE(self), FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + fu_dump_raw(G_LOG_DOMAIN, "GetReport", buf, bufsz); + if (!fu_udev_device_read(FU_UDEV_DEVICE(self), + buf, + bufsz, + &bytes_read, + FU_HIDRAW_DEVICE_IOCTL_TIMEOUT, + flags, + error)) + return FALSE; + + if (bytes_read != bufsz) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, "invalid response"); + return FALSE; + } + return TRUE; +} + static void fu_hidraw_device_init(FuHidrawDevice *self) { @@ -285,4 +583,6 @@ { FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); device_class->probe = fu_hidraw_device_probe; + device_class->setup = fu_hidraw_device_setup; + device_class->to_string = fu_hidraw_device_to_string; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-hidraw-device.h fwupd-2.0.20/libfwupdplugin/fu-hidraw-device.h --- fwupd-2.0.8/libfwupdplugin/fu-hidraw-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-hidraw-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -6,6 +6,8 @@ #pragma once +#include "fu-hid-descriptor.h" +#include "fu-hidraw-struct.h" #include "fu-udev-device.h" #define FU_TYPE_HIDRAW_DEVICE (fu_hidraw_device_get_type()) @@ -15,6 +17,9 @@ FuUdevDeviceClass parent_class; }; +FuHidrawBusType +fu_hidraw_device_get_bus_type(FuHidrawDevice *self) G_GNUC_NON_NULL(1); + gboolean fu_hidraw_device_set_feature(FuHidrawDevice *self, const guint8 *buf, @@ -27,3 +32,24 @@ gsize bufsz, FuIoctlFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); +gboolean +fu_hidraw_device_get_input(FuHidrawDevice *self, + guint8 *buf, + gsize bufsz, + FuIoctlFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); +gboolean +fu_hidraw_device_set_report(FuHidrawDevice *self, + const guint8 *buf, + gsize bufsz, + FuIOChannelFlags flags, + GError **error); +gboolean +fu_hidraw_device_get_report(FuHidrawDevice *self, + guint8 *buf, + gsize bufsz, + FuIOChannelFlags flags, + GError **error); +FuHidDescriptor * +fu_hidraw_device_parse_descriptor(FuHidrawDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT + G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-hidraw.rs fwupd-2.0.20/libfwupdplugin/fu-hidraw.rs --- fwupd-2.0.8/libfwupdplugin/fu-hidraw.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-hidraw.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,31 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[repr(u32)] +#[derive(ToString)] +enum FuHidrawBusType { + Unknown = 0x00, + Pci = 0x01, + Isapnp = 0x02, + Usb = 0x03, + Hil = 0x04, + Bluetooth = 0x05, + Virtual = 0x06, + Isa = 0x10, + I8042 = 0x11, + Xtkbd = 0x12, + Rs232 = 0x13, + Gameport = 0x14, + Parport = 0x15, + Amiga = 0x16, + Adb = 0x17, + I2c = 0x18, + Host = 0x19, + Gsc = 0x1A, + Atari = 0x1B, + Spi = 0x1C, + Rmi = 0x1D, + Cec = 0x1E, + IntelIshtp = 0x1F, + AmdSfh = 0x20, +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-hwids-config.c fwupd-2.0.20/libfwupdplugin/fu-hwids-config.c --- fwupd-2.0.8/libfwupdplugin/fu-hwids-config.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-hwids-config.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,6 @@ #include "fu-config.h" #include "fu-context-private.h" #include "fu-hwids-private.h" -#include "fu-path.h" gboolean fu_hwids_config_setup(FuContext *ctx, FuHwids *self, GError **error) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-hwids-darwin.c fwupd-2.0.20/libfwupdplugin/fu-hwids-darwin.c --- fwupd-2.0.8/libfwupdplugin/fu-hwids-darwin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-hwids-darwin.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,7 +8,9 @@ #include "config.h" +#ifdef HOST_MACHINE_SYSTEM_DARWIN #include "fu-context-private.h" +#endif #include "fu-hwids-private.h" gboolean diff -Nru fwupd-2.0.8/libfwupdplugin/fu-hwids-kenv.c fwupd-2.0.20/libfwupdplugin/fu-hwids-kenv.c --- fwupd-2.0.8/libfwupdplugin/fu-hwids-kenv.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-hwids-kenv.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,9 +8,7 @@ #include "config.h" -#include "fu-context-private.h" #include "fu-hwids-private.h" -#include "fu-kenv.h" gboolean fu_hwids_kenv_setup(FuContext *ctx, FuHwids *self, GError **error) @@ -27,10 +25,10 @@ {FU_HWIDS_KEY_MANUFACTURER, "smbios.system.maker"}, {FU_HWIDS_KEY_PRODUCT_NAME, "smbios.system.product"}, {FU_HWIDS_KEY_PRODUCT_SKU, "smbios.system.sku"}, - {{NULL, NULL}}}; + {NULL, NULL}}; for (guint i = 0; map[i].key != NULL; i++) { g_autoptr(GError) error_local = NULL; - g_autofree gchar *value = fu_kenv_get_string(map[i].key, error_local); + g_autofree gchar *value = fu_kenv_get_string(map[i].key, &error_local); if (value == NULL) { g_debug("ignoring: %s", error_local->message); continue; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-hwids-private.h fwupd-2.0.20/libfwupdplugin/fu-hwids-private.h --- fwupd-2.0.8/libfwupdplugin/fu-hwids-private.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-hwids-private.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,6 +8,7 @@ #include "fu-context.h" #include "fu-hwids.h" +#include "fu-kenv.h" FuHwids * fu_hwids_new(void); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-hwids.c fwupd-2.0.20/libfwupdplugin/fu-hwids.c --- fwupd-2.0.8/libfwupdplugin/fu-hwids.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-hwids.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,15 +9,11 @@ #include "config.h" #include -#include #include "fwupd-common.h" #include "fwupd-error.h" -#include "fu-common.h" #include "fu-hwids-private.h" -#include "fu-path.h" -#include "fu-string.h" /** * FuHwids: diff -Nru fwupd-2.0.8/libfwupdplugin/fu-i2c-device.c fwupd-2.0.20/libfwupdplugin/fu-i2c-device.c --- fwupd-2.0.8/libfwupdplugin/fu-i2c-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-i2c-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -100,10 +100,10 @@ break; } if (number == G_MAXUINT64) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Could not find i2c bus number in sysfs path"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Could not find i2c bus number in sysfs path"); return FALSE; } fu_udev_device_set_number(FU_UDEV_DEVICE(self), number); @@ -154,11 +154,11 @@ if (!fu_ioctl_execute(ioctl, force ? I2C_SLAVE_FORCE : I2C_SLAVE, - (guint8 *)(guintptr)address, - sizeof(guintptr), + GUINT_TO_POINTER(address), + sizeof(guint32), NULL, FU_I2C_DEVICE_IOCTL_TIMEOUT, - FU_IOCTL_FLAG_NONE, + FU_IOCTL_FLAG_PTR_AS_INTEGER, error)) { g_prefix_error(error, "failed to set address 0x%02x: ", address); return FALSE; @@ -167,10 +167,10 @@ /* success */ return TRUE; #else - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Not supported as not found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); return FALSE; #endif } @@ -214,17 +214,11 @@ } static void -fu_i2c_device_register_flags(FuDevice *device) -{ - FU_DEVICE_CLASS(fu_i2c_device_parent_class)->register_flags(device); - fu_device_register_private_flag_safe(device, FU_I2C_DEVICE_PRIVATE_FLAG_NO_HWID_GUIDS); -} - -static void fu_i2c_device_init(FuI2cDevice *self) { fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); + fu_device_register_private_flag(FU_DEVICE(self), FU_I2C_DEVICE_PRIVATE_FLAG_NO_HWID_GUIDS); } static void @@ -232,5 +226,4 @@ { FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); device_class->probe = fu_i2c_device_probe; - device_class->register_flags = fu_i2c_device_register_flags; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-ifd-bios.c fwupd-2.0.20/libfwupdplugin/fu-ifd-bios.c --- fwupd-2.0.8/libfwupdplugin/fu-ifd-bios.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-ifd-bios.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,12 +8,9 @@ #include "config.h" -#include "fu-bytes.h" -#include "fu-common.h" #include "fu-efi-volume.h" #include "fu-ifd-bios.h" #include "fu-input-stream.h" -#include "fu-mem.h" /** * FuIfdBios: @@ -30,7 +27,7 @@ static gboolean fu_ifd_bios_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize offset = 0; @@ -56,7 +53,7 @@ continue; } fu_firmware_set_offset(firmware_tmp, offset); - if (!fu_firmware_add_image_full(firmware, firmware_tmp, error)) + if (!fu_firmware_add_image(firmware, firmware_tmp, error)) return FALSE; /* next! */ diff -Nru fwupd-2.0.8/libfwupdplugin/fu-ifd-common.c fwupd-2.0.20/libfwupdplugin/fu-ifd-common.c --- fwupd-2.0.8/libfwupdplugin/fu-ifd-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-ifd-common.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,113 +0,0 @@ -/* - * Copyright 2021 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-ifd-common.h" - -/** - * fu_ifd_region_to_name: - * @region: A #FuIfdRegion, e.g. %FU_IFD_REGION_BIOS - * - * Converts a #FuIfdRegion to a name the user might recognize. - * - * Returns: identifier string - * - * Since: 1.6.2 - **/ -const gchar * -fu_ifd_region_to_name(FuIfdRegion region) -{ - if (region == FU_IFD_REGION_DESC) - return "IFD descriptor region"; - if (region == FU_IFD_REGION_BIOS) - return "BIOS"; - if (region == FU_IFD_REGION_ME) - return "Intel Management Engine"; - if (region == FU_IFD_REGION_GBE) - return "Gigabit Ethernet"; - if (region == FU_IFD_REGION_PLATFORM) - return "Platform firmware"; - if (region == FU_IFD_REGION_DEVEXP) - return "Device Firmware"; - if (region == FU_IFD_REGION_BIOS2) - return "BIOS Backup"; - if (region == FU_IFD_REGION_EC) - return "Embedded Controller"; - if (region == FU_IFD_REGION_IE) - return "Innovation Engine"; - if (region == FU_IFD_REGION_10GBE) - return "10 Gigabit Ethernet"; - return NULL; -} - -/** - * fu_ifd_access_to_string: - * @access: A #FuIfdAccess, e.g. %FU_IFD_ACCESS_READ - * - * Converts a #FuIfdAccess to a string. - * - * Returns: identifier string - * - * Since: 1.6.2 - **/ -const gchar * -fu_ifd_access_to_string(FuIfdAccess access) -{ - if (access == FU_IFD_ACCESS_NONE) - return "--"; - if (access == FU_IFD_ACCESS_READ) - return "ro"; - if (access == FU_IFD_ACCESS_WRITE) - return "wr"; - if (access == (FU_IFD_ACCESS_READ | FU_IFD_ACCESS_WRITE)) - return "rw"; - return NULL; -} - -/** - * fu_ifd_region_to_access: - * @region: A #FuIfdRegion, e.g. %FU_IFD_REGION_BIOS - * @flash_master: flash master number - * @new_layout: if Skylake or newer - * - * Converts a #FuIfdRegion to an access level. - * - * Returns: access - * - * Since: 1.6.2 - **/ -FuIfdAccess -fu_ifd_region_to_access(FuIfdRegion region, guint32 flash_master, gboolean new_layout) -{ - guint8 bit_r = 0; - guint8 bit_w = 0; - - /* new layout */ - if (new_layout) { - bit_r = (flash_master >> (region + 8)) & 0b1; - bit_w = (flash_master >> (region + 20)) & 0b1; - return (bit_r ? FU_IFD_ACCESS_READ : FU_IFD_ACCESS_NONE) | - (bit_w ? FU_IFD_ACCESS_WRITE : FU_IFD_ACCESS_NONE); - } - - /* old layout */ - if (region == FU_IFD_REGION_DESC) { - bit_r = 16; - bit_w = 24; - } else if (region == FU_IFD_REGION_BIOS) { - bit_r = 17; - bit_w = 25; - } else if (region == FU_IFD_REGION_ME) { - bit_r = 18; - bit_w = 26; - } else if (region == FU_IFD_REGION_GBE) { - bit_r = 19; - bit_w = 27; - } - return ((flash_master >> bit_r) & 0b1 ? FU_IFD_ACCESS_READ : FU_IFD_ACCESS_NONE) | - ((flash_master >> bit_w) & 0b1 ? FU_IFD_ACCESS_WRITE : FU_IFD_ACCESS_NONE); -} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-ifd-common.h fwupd-2.0.20/libfwupdplugin/fu-ifd-common.h --- fwupd-2.0.8/libfwupdplugin/fu-ifd-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-ifd-common.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,33 +0,0 @@ -/* - * Copyright 2021 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#include "fu-ifd-struct.h" - -/** - * FuIfdAccess: - * @FU_IFD_ACCESS_NONE: None - * @FU_IFD_ACCESS_READ: Readable - * @FU_IFD_ACCESS_WRITE: Writable - * - * The flags to use for IFD access permissions. - **/ -typedef enum { - FU_IFD_ACCESS_NONE = 0, - FU_IFD_ACCESS_READ = 1 << 0, - FU_IFD_ACCESS_WRITE = 1 << 1, -} FuIfdAccess; - -const gchar * -fu_ifd_region_to_name(FuIfdRegion region); -const gchar * -fu_ifd_access_to_string(FuIfdAccess access); - -FuIfdAccess -fu_ifd_region_to_access(FuIfdRegion region, guint32 flash_master, gboolean new_layout); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-ifd-firmware.c fwupd-2.0.20/libfwupdplugin/fu-ifd-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-ifd-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-ifd-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,14 +9,13 @@ #include "config.h" #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-composite-input-stream.h" #include "fu-efi-volume.h" #include "fu-ifd-bios.h" -#include "fu-ifd-common.h" #include "fu-ifd-firmware.h" #include "fu-ifd-image.h" +#include "fu-ifd-struct.h" #include "fu-input-stream.h" #include "fu-mem.h" #include "fu-partial-input-stream.h" @@ -123,17 +122,65 @@ return g_steal_pointer(&stream2); } +static FuIfdAccess +fu_ifd_firmware_region_to_access(FuIfdRegion region, guint32 flash_master, gboolean new_layout) +{ + guint8 bit_r = 0; + guint8 bit_w = 0; + + /* new layout */ + if (new_layout) { + /* + * Skylake+ master layout: + * bits 0..3 ext_read (regions 12..15) + * bits 4..7 ext_write (regions 12..15) + * bits 8..19 read (regions 0..11) + * bits 20..31 write (regions 0..11) + */ + if (region < 12) { + bit_r = (flash_master >> (region + 8)) & 0b1; + bit_w = (flash_master >> (region + 20)) & 0b1; + } else { + guint8 ext_idx = (guint8)(region - 12); + bit_r = (flash_master >> ext_idx) & 0b1; + bit_w = (flash_master >> (4 + ext_idx)) & 0b1; + } + return (bit_r ? FU_IFD_ACCESS_READ : FU_IFD_ACCESS_NONE) | + (bit_w ? FU_IFD_ACCESS_WRITE : FU_IFD_ACCESS_NONE); + } + + /* old layout */ + if (region == FU_IFD_REGION_DESC) { + bit_r = 16; + bit_w = 24; + } else if (region == FU_IFD_REGION_BIOS) { + bit_r = 17; + bit_w = 25; + } else if (region == FU_IFD_REGION_ME) { + bit_r = 18; + bit_w = 26; + } else if (region == FU_IFD_REGION_GBE) { + bit_r = 19; + bit_w = 27; + } else if (region == FU_IFD_REGION_PLATFORM) { + bit_r = 20; + bit_w = 28; + } + return ((flash_master >> bit_r) & 0b1 ? FU_IFD_ACCESS_READ : FU_IFD_ACCESS_NONE) | + ((flash_master >> bit_w) & 0b1 ? FU_IFD_ACCESS_WRITE : FU_IFD_ACCESS_NONE); +} + static gboolean fu_ifd_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIfdFirmware *self = FU_IFD_FIRMWARE(firmware); FuIfdFirmwarePrivate *priv = GET_PRIVATE(self); gsize streamsz = 0; - g_autoptr(GByteArray) st_fcba = NULL; - g_autoptr(GByteArray) st_fdbar = NULL; + g_autoptr(FuStructIfdFcba) st_fcba = NULL; + g_autoptr(FuStructIfdFdbar) st_fdbar = NULL; g_autoptr(GInputStream) stream2 = NULL; /* check size */ @@ -161,6 +208,13 @@ priv->num_regions = (priv->descriptor_map0 >> 24) & 0b111; if (priv->num_regions == 0) priv->num_regions = 10; + + /* + * Choose master layout based on number of regions parsed from the descriptor: + * - Old layout (pre-Skylake) used 5/7 regions and different bit positions. + * - New layout (Skylake+) uses 10 or more regions and the split ext/read/write fields. + */ + priv->new_layout = priv->num_regions >= 10; priv->num_components = (priv->descriptor_map0 >> 8) & 0b11; priv->flash_component_base_addr = (priv->descriptor_map0 << 4) & 0x00000FF0; priv->flash_region_base_addr = (priv->descriptor_map0 >> 12) & 0x00000FF0; @@ -224,10 +278,13 @@ g_debug("freg %s 0x%04x -> 0x%04x", freg_str, freg_base, freg_limt); partial_stream = fu_partial_input_stream_new(stream2, freg_base, freg_size, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut IFD image: "); + g_prefix_error_literal(error, "failed to cut IFD image: "); return FALSE; } - if (i == FU_IFD_REGION_BIOS) { + + /* do not parse all the EFI volumes if we only care about the structure */ + if (i == FU_IFD_REGION_BIOS && + (flags & FU_FIRMWARE_PARSE_FLAG_ONLY_PARTITION_LAYOUT) == 0) { img = fu_ifd_bios_new(); } else { img = fu_ifd_image_new(); @@ -235,20 +292,22 @@ if (!fu_firmware_parse_stream(img, partial_stream, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return FALSE; fu_firmware_set_addr(img, freg_base); fu_firmware_set_idx(img, i); if (freg_str != NULL) fu_firmware_set_id(img, freg_str); - if (!fu_firmware_add_image_full(firmware, img, error)) + if (!fu_firmware_add_image(firmware, img, error)) return FALSE; /* is writable by anything other than the region itself */ for (FuIfdRegion r = 1; r <= 3; r++) { FuIfdAccess acc; - acc = fu_ifd_region_to_access(i, priv->flash_master[r], priv->new_layout); + acc = fu_ifd_firmware_region_to_access(i, + priv->flash_master[r], + priv->new_layout); fu_ifd_image_set_access(FU_IFD_IMAGE(img), r, acc); } } @@ -288,8 +347,8 @@ FuIfdFirmwarePrivate *priv = GET_PRIVATE(self); gsize bufsz_max = 0x0; g_autoptr(GByteArray) buf = g_byte_array_new(); - g_autoptr(GByteArray) st_fcba = fu_struct_ifd_fcba_new(); - g_autoptr(GByteArray) st_fdbar = fu_struct_ifd_fdbar_new(); + g_autoptr(FuStructIfdFcba) st_fcba = fu_struct_ifd_fcba_new(); + g_autoptr(FuStructIfdFdbar) st_fdbar = fu_struct_ifd_fdbar_new(); g_autoptr(GHashTable) blobs = NULL; g_autoptr(FuFirmware) img_desc = NULL; @@ -306,7 +365,8 @@ fu_firmware_set_addr(img_desc, 0x0); fu_firmware_set_idx(img_desc, FU_IFD_REGION_DESC); fu_firmware_set_id(img_desc, "desc"); - fu_firmware_add_image(firmware, img_desc); + if (!fu_firmware_add_image(firmware, img_desc, error)) + return NULL; } /* generate ahead of time */ @@ -347,10 +407,10 @@ if (!fu_memcpy_safe(buf->data, buf->len, 0x0, - st_fdbar->data, - st_fdbar->len, + st_fdbar->buf->data, + st_fdbar->buf->len, 0x0, - st_fdbar->len, + st_fdbar->buf->len, error)) return NULL; @@ -361,10 +421,10 @@ if (!fu_memcpy_safe(buf->data, buf->len, priv->flash_component_base_addr, - st_fcba->data, - st_fcba->len, + st_fcba->buf->data, + st_fcba->buf->len, 0x0, - st_fcba->len, + st_fcba->buf->len, error)) return NULL; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-ifd-image.h fwupd-2.0.20/libfwupdplugin/fu-ifd-image.h --- fwupd-2.0.8/libfwupdplugin/fu-ifd-image.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-ifd-image.h 2026-02-26 11:36:18.000000000 +0000 @@ -7,7 +7,7 @@ #pragma once #include "fu-firmware.h" -#include "fu-ifd-common.h" +#include "fu-ifd-struct.h" #define FU_TYPE_IFD_IMAGE (fu_ifd_image_get_type()) G_DECLARE_DERIVABLE_TYPE(FuIfdImage, fu_ifd_image, FU, IFD_IMAGE, FuFirmware) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-ifd.rs fwupd-2.0.20/libfwupdplugin/fu-ifd.rs --- fwupd-2.0.8/libfwupdplugin/fu-ifd.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-ifd.rs 2026-02-26 11:36:18.000000000 +0000 @@ -19,7 +19,7 @@ #[derive(ParseStream, New, ValidateStream, Default)] #[repr(C, packed)] struct FuStructIfdFdbar { - reserved: [u8; 16] = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF, + reserved: [u8; 16] = [0xFF; 16], signature: u32le == 0x0FF0A55A, descriptor_map0: u32le, descriptor_map1: u32le, @@ -33,3 +33,11 @@ flill: u32le, flill1: u32le, } + +#[derive(ToString)] +#[repr(C, packed)] +enum FuIfdAccess { + None = 0, + Read = 1 << 0, + Write = 1 << 1, +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-ifwi-cpd-firmware.c fwupd-2.0.20/libfwupdplugin/fu-ifwi-cpd-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-ifwi-cpd-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-ifwi-cpd-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,16 +9,14 @@ #include "config.h" -#include - #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-ifwi-cpd-firmware.h" #include "fu-ifwi-struct.h" #include "fu-input-stream.h" #include "fu-partial-input-stream.h" #include "fu-string.h" +#include "fu-version-common.h" /** * FuIfwiCpdFirmware: @@ -52,18 +50,29 @@ } static gboolean -fu_ifwi_cpd_firmware_parse_manifest(FuFirmware *firmware, GInputStream *stream, GError **error) +fu_ifwi_cpd_firmware_parse_manifest(FuIfwiCpdFirmware *self, + FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) { gsize streamsz = 0; guint32 size; gsize offset = 0; - g_autoptr(GByteArray) st_mhd = NULL; + guint64 version_raw = 0; + g_autoptr(FuStructIfwiCpdManifest) st_mhd = NULL; /* raw version */ st_mhd = fu_struct_ifwi_cpd_manifest_parse_stream(stream, offset, error); if (st_mhd == NULL) return FALSE; - fu_firmware_set_version_raw(firmware, fu_struct_ifwi_cpd_manifest_get_version(st_mhd)); + + /* read as [u16le;4] and then build back into a native u64 */ + version_raw += (guint64)fu_struct_ifwi_cpd_manifest_get_version_major(st_mhd) << 48; + version_raw += (guint64)fu_struct_ifwi_cpd_manifest_get_version_minor(st_mhd) << 32; + version_raw += (guint64)fu_struct_ifwi_cpd_manifest_get_version_hotfix(st_mhd) << 16; + version_raw += ((guint64)fu_struct_ifwi_cpd_manifest_get_version_build(st_mhd)) << 0; + fu_firmware_set_version_raw(FU_FIRMWARE(self), version_raw); /* verify the size */ if (!fu_input_stream_size(stream, &streamsz, error)) @@ -85,7 +94,7 @@ guint32 extension_type = 0; guint32 extension_length = 0; g_autoptr(FuFirmware) img = fu_firmware_new(); - g_autoptr(GByteArray) st_mex = NULL; + g_autoptr(FuStructIfwiCpdManifestExt) st_mex = NULL; g_autoptr(GInputStream) partial_stream = NULL; /* set the extension type as the index */ @@ -101,7 +110,7 @@ extension_length = fu_struct_ifwi_cpd_manifest_ext_get_extension_length(st_mex); if (extension_length == 0x0) break; - if (extension_length < st_mex->len) { + if (extension_length < st_mex->buf->len) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, @@ -110,23 +119,19 @@ return FALSE; } partial_stream = fu_partial_input_stream_new(stream, - offset + st_mex->len, - extension_length - st_mex->len, + offset + st_mex->buf->len, + extension_length - st_mex->buf->len, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut CPD extension: "); + g_prefix_error_literal(error, "failed to cut CPD extension: "); return FALSE; } - if (!fu_firmware_parse_stream(img, - partial_stream, - 0x0, - FWUPD_INSTALL_FLAG_NONE, - error)) + if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error)) return FALSE; /* success */ fu_firmware_set_offset(img, offset); - if (!fu_firmware_add_image_full(firmware, img, error)) + if (!fu_firmware_add_image(firmware, img, error)) return FALSE; offset += extension_length; } @@ -147,12 +152,12 @@ static gboolean fu_ifwi_cpd_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIfwiCpdFirmware *self = FU_IFWI_CPD_FIRMWARE(firmware); FuIfwiCpdFirmwarePrivate *priv = GET_PRIVATE(self); - g_autoptr(GByteArray) st_hdr = NULL; + g_autoptr(FuStructIfwiCpd) st_hdr = NULL; gsize offset = 0; guint32 num_of_entries; @@ -180,7 +185,7 @@ guint32 img_offset = 0; g_autofree gchar *id = NULL; g_autoptr(FuFirmware) img = fu_firmware_new(); - g_autoptr(GByteArray) st_ent = NULL; + g_autoptr(FuStructIfwiCpdEntry) st_ent = NULL; g_autoptr(GInputStream) partial_stream = NULL; /* the IDX is the position in the file */ @@ -206,7 +211,7 @@ fu_struct_ifwi_cpd_entry_get_length(st_ent), error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut IFD image: "); + g_prefix_error_literal(error, "failed to cut IFD image: "); return FALSE; } if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error)) @@ -216,14 +221,18 @@ if (i == FU_IFWI_CPD_FIRMWARE_IDX_MANIFEST && fu_struct_ifwi_cpd_entry_get_length(st_ent) > FU_STRUCT_IFWI_CPD_MANIFEST_SIZE) { - if (!fu_ifwi_cpd_firmware_parse_manifest(img, partial_stream, error)) + if (!fu_ifwi_cpd_firmware_parse_manifest(self, + img, + partial_stream, + flags, + error)) return FALSE; } /* success */ - if (!fu_firmware_add_image_full(firmware, img, error)) + if (!fu_firmware_add_image(firmware, img, error)) return FALSE; - offset += st_ent->len; + offset += st_ent->buf->len; } /* success */ @@ -236,19 +245,19 @@ FuIfwiCpdFirmware *self = FU_IFWI_CPD_FIRMWARE(firmware); FuIfwiCpdFirmwarePrivate *priv = GET_PRIVATE(self); gsize offset = 0; - g_autoptr(GByteArray) buf = fu_struct_ifwi_cpd_new(); + g_autoptr(FuStructIfwiCpd) st = fu_struct_ifwi_cpd_new(); g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); /* write the header */ - fu_struct_ifwi_cpd_set_num_of_entries(buf, imgs->len); - fu_struct_ifwi_cpd_set_header_version(buf, priv->header_version); - fu_struct_ifwi_cpd_set_entry_version(buf, priv->entry_version); - fu_struct_ifwi_cpd_set_checksum(buf, 0x0); - fu_struct_ifwi_cpd_set_partition_name(buf, fu_firmware_get_idx(firmware)); - fu_struct_ifwi_cpd_set_crc32(buf, 0x0); + fu_struct_ifwi_cpd_set_num_of_entries(st, imgs->len); + fu_struct_ifwi_cpd_set_header_version(st, priv->header_version); + fu_struct_ifwi_cpd_set_entry_version(st, priv->entry_version); + fu_struct_ifwi_cpd_set_checksum(st, 0x0); + fu_struct_ifwi_cpd_set_partition_name(st, fu_firmware_get_idx(firmware)); + fu_struct_ifwi_cpd_set_crc32(st, 0x0); /* fixup the image offsets */ - offset += buf->len; + offset += st->buf->len; offset += FU_STRUCT_IFWI_CPD_ENTRY_SIZE * imgs->len; for (guint i = 0; i < imgs->len; i++) { FuFirmware *img = g_ptr_array_index(imgs, i); @@ -266,7 +275,7 @@ /* add entry headers */ for (guint i = 0; i < imgs->len; i++) { FuFirmware *img = g_ptr_array_index(imgs, i); - g_autoptr(GByteArray) st_ent = fu_struct_ifwi_cpd_entry_new(); + g_autoptr(FuStructIfwiCpdEntry) st_ent = fu_struct_ifwi_cpd_entry_new(); /* sanity check */ if (fu_firmware_get_id(img) == NULL) { @@ -281,7 +290,7 @@ return NULL; fu_struct_ifwi_cpd_entry_set_offset(st_ent, fu_firmware_get_offset(img)); fu_struct_ifwi_cpd_entry_set_length(st_ent, fu_firmware_get_size(img)); - g_byte_array_append(buf, st_ent->data, st_ent->len); + fu_byte_array_append_array(st->buf, st_ent->buf); } /* add entry data */ @@ -291,11 +300,11 @@ blob = fu_firmware_get_bytes(img, error); if (blob == NULL) return NULL; - fu_byte_array_append_bytes(buf, blob); + fu_byte_array_append_bytes(st->buf, blob); } /* success */ - return g_steal_pointer(&buf); + return g_steal_pointer(&st->buf); } static gboolean @@ -325,10 +334,17 @@ return TRUE; } +static gchar * +fu_ifwi_cpd_firmware_convert_version(FuFirmware *firmware, guint64 version_raw) +{ + return fu_version_from_uint64(version_raw, fu_firmware_get_version_format(firmware)); +} + static void fu_ifwi_cpd_firmware_init(FuIfwiCpdFirmware *self) { fu_firmware_set_images_max(FU_FIRMWARE(self), FU_IFWI_CPD_FIRMWARE_ENTRIES_MAX); + fu_firmware_set_version_format(FU_FIRMWARE(self), FWUPD_VERSION_FORMAT_QUAD); } static void @@ -340,6 +356,7 @@ firmware_class->parse = fu_ifwi_cpd_firmware_parse; firmware_class->write = fu_ifwi_cpd_firmware_write; firmware_class->build = fu_ifwi_cpd_firmware_build; + firmware_class->convert_version = fu_ifwi_cpd_firmware_convert_version; } /** diff -Nru fwupd-2.0.8/libfwupdplugin/fu-ifwi-fpt-firmware.c fwupd-2.0.20/libfwupdplugin/fu-ifwi-fpt-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-ifwi-fpt-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-ifwi-fpt-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,7 +12,6 @@ #include #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-ifwi-fpt-firmware.h" #include "fu-ifwi-struct.h" #include "fu-partial-input-stream.h" @@ -46,12 +45,12 @@ static gboolean fu_ifwi_fpt_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint32 num_of_entries; gsize offset = 0; - g_autoptr(GByteArray) st_hdr = NULL; + g_autoptr(FuStructIfwiFpt) st_hdr = NULL; /* sanity check */ st_hdr = fu_struct_ifwi_fpt_parse_stream(stream, offset, error); @@ -85,7 +84,7 @@ guint32 partition_name; g_autofree gchar *id = NULL; g_autoptr(FuFirmware) img = fu_firmware_new(); - g_autoptr(GByteArray) st_ent = NULL; + g_autoptr(FuStructIfwiFptEntry) st_ent = NULL; /* read IDX */ st_ent = fu_struct_ifwi_fpt_entry_parse_stream(stream, offset, error); @@ -107,18 +106,18 @@ partial_stream = fu_partial_input_stream_new(stream, data_offset, data_length, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut FPT image: "); + g_prefix_error_literal(error, "failed to cut FPT image: "); return FALSE; } if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error)) return FALSE; fu_firmware_set_offset(img, data_offset); } - if (!fu_firmware_add_image_full(firmware, img, error)) + if (!fu_firmware_add_image(firmware, img, error)) return FALSE; /* next */ - offset += st_ent->len; + offset += st_ent->buf->len; } /* success */ @@ -129,11 +128,11 @@ fu_ifwi_fpt_firmware_write(FuFirmware *firmware, GError **error) { gsize offset = 0; - g_autoptr(GByteArray) buf = fu_struct_ifwi_fpt_new(); + g_autoptr(FuStructIfwiFpt) st = fu_struct_ifwi_fpt_new(); g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); /* fixup the image offsets */ - offset += buf->len; + offset += st->buf->len; offset += FU_STRUCT_IFWI_FPT_ENTRY_SIZE * imgs->len; for (guint i = 0; i < imgs->len; i++) { FuFirmware *img = g_ptr_array_index(imgs, i); @@ -148,16 +147,16 @@ } /* write the header */ - fu_struct_ifwi_fpt_set_num_of_entries(buf, imgs->len); + fu_struct_ifwi_fpt_set_num_of_entries(st, imgs->len); /* add entries */ for (guint i = 0; i < imgs->len; i++) { FuFirmware *img = g_ptr_array_index(imgs, i); - g_autoptr(GByteArray) st_ent = fu_struct_ifwi_fpt_entry_new(); + g_autoptr(FuStructIfwiFptEntry) st_ent = fu_struct_ifwi_fpt_entry_new(); fu_struct_ifwi_fpt_entry_set_partition_name(st_ent, fu_firmware_get_idx(img)); fu_struct_ifwi_fpt_entry_set_offset(st_ent, fu_firmware_get_offset(img)); fu_struct_ifwi_fpt_entry_set_length(st_ent, fu_firmware_get_size(img)); - g_byte_array_append(buf, st_ent->data, st_ent->len); + fu_byte_array_append_array(st->buf, st_ent->buf); } /* add entry data */ @@ -167,11 +166,11 @@ blob = fu_firmware_get_bytes(img, error); if (blob == NULL) return NULL; - fu_byte_array_append_bytes(buf, blob); + fu_byte_array_append_bytes(st->buf, blob); } /* success */ - return g_steal_pointer(&buf); + return g_steal_pointer(&st->buf); } static void diff -Nru fwupd-2.0.8/libfwupdplugin/fu-ifwi.rs fwupd-2.0.20/libfwupdplugin/fu-ifwi.rs --- fwupd-2.0.8/libfwupdplugin/fu-ifwi.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-ifwi.rs 2026-02-26 11:36:18.000000000 +0000 @@ -35,7 +35,10 @@ size: u32le, // dwords id: u32le, rsvd: u32le, - version: u64le, + version_major: u16le, + version_minor: u16le, + version_hotfix: u16le, + version_build: u16le, svn: u32le, } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-ihex-firmware.c fwupd-2.0.20/libfwupdplugin/fu-ihex-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-ihex-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-ihex-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,8 +11,6 @@ #include #include "fu-byte-array.h" -#include "fu-bytes.h" -#include "fu-common.h" #include "fu-firmware-common.h" #include "fu-ihex-firmware.h" #include "fu-mem.h" @@ -88,7 +86,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuIhexFirmwareRecord, fu_ihex_firmware_record_free) static FuIhexFirmwareRecord * -fu_ihex_firmware_record_new(guint ln, const gchar *line, FwupdInstallFlags flags, GError **error) +fu_ihex_firmware_record_new(guint ln, const gchar *line, FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuIhexFirmwareRecord) rcd = NULL; gsize linesz = strlen(line); @@ -138,7 +136,7 @@ } /* verify checksum */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint8 checksum = 0; for (guint i = 1; i < line_end + 2; i += 2) { guint8 data_tmp = 0; @@ -168,7 +166,7 @@ typedef struct { FuIhexFirmware *self; - FwupdInstallFlags flags; + FuFirmwareParseFlags flags; } FuIhexFirmwareTokenHelper; static gboolean @@ -212,7 +210,7 @@ static gboolean fu_ihex_firmware_tokenize(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIhexFirmware *self = FU_IHEX_FIRMWARE(firmware); @@ -223,7 +221,7 @@ static gboolean fu_ihex_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIhexFirmware *self = FU_IHEX_FIRMWARE(firmware); @@ -398,7 +396,7 @@ g_autoptr(FuFirmware) img_sig = fu_firmware_new_from_bytes(data_sig); fu_firmware_set_id(img_sig, FU_FIRMWARE_ID_SIGNATURE); - if (!fu_firmware_add_image_full(firmware, img_sig, error)) + if (!fu_firmware_add_image(firmware, img_sig, error)) return FALSE; } got_sig = TRUE; @@ -475,7 +473,7 @@ /* need to offset */ if (address_offset != address_offset_last) { - guint8 buf[2]; + guint8 buf[2]; /* nocheck:zero-init */ fu_memwrite_uint16(buf, address_offset, G_BIG_ENDIAN); fu_ihex_firmware_emit_chunk(str, 0x0, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-input-stream.c fwupd-2.0.20/libfwupdplugin/fu-input-stream.c --- fwupd-2.0.8/libfwupdplugin/fu-input-stream.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-input-stream.c 2026-02-26 11:36:18.000000000 +0000 @@ -36,8 +36,10 @@ file = g_file_new_for_path(path); stream = g_file_read(file, NULL, error); - if (stream == NULL) + if (stream == NULL) { + fwupd_error_convert(error); return NULL; + } return G_INPUT_STREAM(g_steal_pointer(&stream)); } @@ -268,7 +270,7 @@ FuProgress *progress, GError **error) { - guint8 tmp[0x8000]; + guint8 tmp[0x8000]; /* nocheck:zero-init */ g_autoptr(GByteArray) buf = g_byte_array_new(); g_autoptr(GError) error_local = NULL; @@ -426,7 +428,7 @@ } if (!g_seekable_seek(G_SEEKABLE(stream), 0, G_SEEK_END, NULL, error)) { - g_prefix_error(error, "seek to end: "); + g_prefix_error_literal(error, "seek to end: "); return FALSE; } if (val != NULL) @@ -709,26 +711,28 @@ * @stream: a #GInputStream * @buf: input buffer to look for * @bufsz: size of @buf - * @offset: (nullable): found offset + * @offset: starting offset, typically 0x0 + * @offset_found: (nullable) (out): found offset * @error: (nullable): optional return location for an error * * Find a memory buffer within an input stream, without loading the entire stream into a buffer. * * Returns: %TRUE if @buf was found * - * Since: 2.0.0 + * Since: 2.0.18 **/ gboolean fu_input_stream_find(GInputStream *stream, const guint8 *buf, gsize bufsz, - gsize *offset, + gsize offset, + gsize *offset_found, GError **error) { g_autoptr(GByteArray) buf_acc = g_byte_array_new(); const gsize blocksz = 0x10000; gsize offset_add = 0; - gsize offset_cur = 0; + gsize offset_cur = offset; g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE); g_return_val_if_fail(buf != NULL, FALSE); @@ -736,7 +740,7 @@ g_return_val_if_fail(bufsz < blocksz, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - while (offset_cur < bufsz) { + while (TRUE) { g_autoptr(GByteArray) buf_tmp = NULL; g_autoptr(GError) error_local = NULL; @@ -755,9 +759,9 @@ g_byte_array_append(buf_acc, buf_tmp->data, buf_tmp->len); /* we found something */ - if (fu_memmem_safe(buf_acc->data, buf_acc->len, buf, bufsz, offset, NULL)) { - if (offset != NULL) - *offset += offset_add; + if (fu_memmem_safe(buf_acc->data, buf_acc->len, buf, bufsz, offset_found, NULL)) { + if (offset_found != NULL) + *offset_found += offset + offset_add; return TRUE; } @@ -777,20 +781,3 @@ (guint)bufsz); return FALSE; } - -/** - * fu_input_stream_locker_unref: - * @stream: a #GInputStream - * - * Closes an input stream and then unrefs it. - * - * Since: 2.0.7 - **/ -void -fu_input_stream_locker_unref(FuInputStreamLocker *stream) -{ - g_autoptr(GError) error_local = NULL; - if (!g_input_stream_close(stream, NULL, &error_local)) - g_warning("failed to close input stream: %s", error_local->message); - g_object_unref(stream); -} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-input-stream.h fwupd-2.0.20/libfwupdplugin/fu-input-stream.h --- fwupd-2.0.8/libfwupdplugin/fu-input-stream.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-input-stream.h 2026-02-26 11:36:18.000000000 +0000 @@ -104,11 +104,6 @@ fu_input_stream_find(GInputStream *stream, const guint8 *buf, gsize bufsz, - gsize *offset, + gsize offset, + gsize *offset_found, GError **error) G_GNUC_NON_NULL(1, 2); - -/* useful to close file descriptors backing the input stream */ -typedef GInputStream FuInputStreamLocker; -void -fu_input_stream_locker_unref(FuInputStreamLocker *stream) G_GNUC_NON_NULL(1); -G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuInputStreamLocker, fu_input_stream_locker_unref); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-intel-thunderbolt-firmware.c fwupd-2.0.20/libfwupdplugin/fu-intel-thunderbolt-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-intel-thunderbolt-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-intel-thunderbolt-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,6 @@ #include "fu-byte-array.h" #include "fu-input-stream.h" #include "fu-intel-thunderbolt-firmware.h" -#include "fu-mem.h" #include "fu-partial-input-stream.h" /** @@ -39,7 +38,7 @@ static gboolean fu_intel_thunderbolt_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { const guint32 farb_offsets[] = {0x0, 0x1000}; @@ -73,7 +72,7 @@ /* FuIntelThunderboltNvm->parse */ partial_stream = fu_partial_input_stream_new(stream, farb_pointer, G_MAXSIZE, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut from NVM: "); + g_prefix_error_literal(error, "failed to cut from NVM: "); return FALSE; } return FU_FIRMWARE_CLASS(fu_intel_thunderbolt_firmware_parent_class) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-intel-thunderbolt-nvm.c fwupd-2.0.20/libfwupdplugin/fu-intel-thunderbolt-nvm.c --- fwupd-2.0.8/libfwupdplugin/fu-intel-thunderbolt-nvm.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-intel-thunderbolt-nvm.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,13 +12,10 @@ #include "config.h" #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-input-stream.h" #include "fu-intel-thunderbolt-nvm.h" #include "fu-intel-thunderbolt-struct.h" -#include "fu-mem.h" -#include "fu-partial-input-stream.h" #include "fu-string.h" #include "fu-version-common.h" @@ -238,7 +235,7 @@ value, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to read ucode section len: "); + g_prefix_error_literal(error, "failed to read ucode section len: "); return FALSE; } *value *= sizeof(guint32); @@ -302,7 +299,7 @@ FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_AVAILABLE_SECTIONS, &available_sections, error)) { - g_prefix_error(error, "failed to read available sections: "); + g_prefix_error_literal(error, "failed to read available sections: "); return FALSE; } if (!fu_input_stream_read_u16( @@ -312,7 +309,7 @@ &ucode_offset, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to read ucode offset: "); + g_prefix_error_literal(error, "failed to read ucode offset: "); return FALSE; } offset = ucode_offset; @@ -356,7 +353,7 @@ static gboolean fu_intel_thunderbolt_nvm_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware); @@ -402,7 +399,7 @@ FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_IS_NATIVE, &tmp, error)) { - g_prefix_error(error, "failed to read native: "); + g_prefix_error_literal(error, "failed to read native: "); return FALSE; } priv->is_native = tmp & 0x20; @@ -419,7 +416,7 @@ FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLAGS_HOST, &tmp, error)) { - g_prefix_error(error, "failed to read is-host: "); + g_prefix_error_literal(error, "failed to read is-host: "); return FALSE; } priv->is_host = tmp & (1 << 1); @@ -431,7 +428,7 @@ &priv->device_id, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to read device-id: "); + g_prefix_error_literal(error, "failed to read device-id: "); return FALSE; } @@ -480,7 +477,7 @@ &priv->vendor_id, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to read vendor-id: "); + g_prefix_error_literal(error, "failed to read vendor-id: "); return FALSE; } if (!fu_input_stream_read_u16( @@ -490,7 +487,7 @@ &priv->model_id, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to read model-id: "); + g_prefix_error_literal(error, "failed to read model-id: "); return FALSE; } } @@ -507,7 +504,7 @@ &version_raw, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to read version: "); + g_prefix_error_literal(error, "failed to read version: "); return FALSE; } fu_firmware_set_version_raw(FU_FIRMWARE(self), version_raw); @@ -528,7 +525,7 @@ FU_INTEL_THUNDERBOLT_NVM_DIGITAL_OFFSET_FLASH_SIZE, &tmp, error)) { - g_prefix_error(error, "failed to read flash size: "); + g_prefix_error_literal(error, "failed to read flash size: "); return FALSE; } priv->flash_size = tmp & 0x07; @@ -552,7 +549,7 @@ &pd_pointer, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to read pd-pointer: "); + g_prefix_error_literal(error, "failed to read pd-pointer: "); return FALSE; } priv->has_pd = fu_intel_thunderbolt_nvm_valid_pd_pointer(pd_pointer); @@ -562,7 +559,7 @@ if (!fu_firmware_parse_stream(img_payload, stream, 0x0, flags, error)) return FALSE; fu_firmware_set_id(img_payload, FU_FIRMWARE_ID_PAYLOAD); - if (!fu_firmware_add_image_full(firmware, img_payload, error)) + if (!fu_firmware_add_image(firmware, img_payload, error)) return FALSE; /* success */ @@ -575,10 +572,11 @@ { FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware); FuIntelThunderboltNvmPrivate *priv = GET_PRIVATE(self); - g_autoptr(GByteArray) st = fu_intel_thunderbolt_nvm_digital_new(); - g_autoptr(GByteArray) st_drom = fu_intel_thunderbolt_nvm_drom_new(); - g_autoptr(GByteArray) st_arc = fu_intel_thunderbolt_nvm_arc_params_new(); - g_autoptr(GByteArray) st_dram = fu_intel_thunderbolt_nvm_dram_new(); + g_autoptr(FuIntelThunderboltNvmDigital) st = fu_intel_thunderbolt_nvm_digital_new(); + g_autoptr(FuIntelThunderboltNvmDrom) st_drom = fu_intel_thunderbolt_nvm_drom_new(); + g_autoptr(FuIntelThunderboltNvmArcParams) st_arc = + fu_intel_thunderbolt_nvm_arc_params_new(); + g_autoptr(FuIntelThunderboltNvmDram) st_dram = fu_intel_thunderbolt_nvm_dram_new(); /* digital section */ fu_intel_thunderbolt_nvm_digital_set_available_sections( @@ -591,28 +589,28 @@ fu_intel_thunderbolt_nvm_digital_set_flags_is_native(st, priv->is_native ? 0x20 : 0x0); /* drom section */ - fu_intel_thunderbolt_nvm_digital_set_drom(st, st->len); + fu_intel_thunderbolt_nvm_digital_set_drom(st, st->buf->len); fu_intel_thunderbolt_nvm_drom_set_vendor_id(st_drom, priv->vendor_id); fu_intel_thunderbolt_nvm_drom_set_model_id(st_drom, priv->model_id); - g_byte_array_append(st, st_drom->data, st_drom->len); + fu_byte_array_append_array(st->buf, st_drom->buf); /* ARC param section */ - fu_intel_thunderbolt_nvm_digital_set_arc_params(st, st->len); + fu_intel_thunderbolt_nvm_digital_set_arc_params(st, st->buf->len); fu_intel_thunderbolt_nvm_arc_params_set_pd_pointer(st_arc, priv->has_pd ? 0x1 : 0x0); - g_byte_array_append(st, st_arc->data, st_arc->len); + fu_byte_array_append_array(st->buf, st_arc->buf); /* dram section */ - fu_intel_thunderbolt_nvm_digital_set_ucode(st, st->len); - g_byte_array_append(st, st_dram->data, st_dram->len); + fu_intel_thunderbolt_nvm_digital_set_ucode(st, st->buf->len); + fu_byte_array_append_array(st->buf, st_dram->buf); /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static gboolean fu_intel_thunderbolt_nvm_check_compatible(FuFirmware *firmware, FuFirmware *firmware_other, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIntelThunderboltNvm *self = FU_INTEL_THUNDERBOLT_NVM(firmware); @@ -647,7 +645,7 @@ priv_other->device_id); return FALSE; } - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) == 0) { if (priv->model_id != priv_other->model_id) { g_set_error(error, FWUPD_ERROR, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-intel-thunderbolt.rs fwupd-2.0.20/libfwupdplugin/fu-intel-thunderbolt.rs --- fwupd-2.0.8/libfwupdplugin/fu-intel-thunderbolt.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-intel-thunderbolt.rs 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,7 @@ } #[repr(u8)] -enum FuIntelThunderboltNvmSectionFlag { +enum FuIntelThunderboltNvmSectionFlags { Dram = 1 << 6, } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-io-channel.c fwupd-2.0.20/libfwupdplugin/fu-io-channel.c --- fwupd-2.0.8/libfwupdplugin/fu-io-channel.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-io-channel.c 2026-02-26 11:36:18.000000000 +0000 @@ -23,10 +23,8 @@ #include "fwupd-error.h" -#include "fu-common.h" #include "fu-input-stream.h" #include "fu-io-channel.h" -#include "fu-string.h" /** * FuIOChannel: @@ -118,7 +116,7 @@ #endif "failed to seek to 0x%04x: %s", (guint)offset, - g_strerror(errno)); + fwupd_strerror(errno)); fwupd_error_convert(error); return FALSE; } @@ -284,7 +282,7 @@ FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "failed to write: %s", - g_strerror(errno)); + fwupd_strerror(errno)); return FALSE; } g_set_error(error, @@ -333,7 +331,7 @@ FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "failed to write: %s", - g_strerror(errno)); + fwupd_strerror(errno)); return FALSE; } g_set_error(error, @@ -342,7 +340,7 @@ "failed to write %" G_GSIZE_FORMAT " bytes to %i: %s", datasz, self->fd, - g_strerror(errno)); + fwupd_strerror(errno)); return FALSE; } if (flags & FU_IO_CHANNEL_FLAG_SINGLE_SHOT) @@ -429,7 +427,7 @@ #endif "failed to read %i: %s", self->fd, - g_strerror(errno)); + fwupd_strerror(errno)); fwupd_error_convert(error); return NULL; } @@ -447,7 +445,7 @@ /* wait for data to appear */ gint rc = g_poll(&fds, 1, (gint)timeout_ms); if (rc == 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT, "timeout"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT, "timeout"); return NULL; } if (rc < 0) { @@ -478,7 +476,7 @@ #endif "failed to read %i: %s", self->fd, - g_strerror(errno)); + fwupd_strerror(errno)); fwupd_error_convert(error); return NULL; } @@ -614,7 +612,7 @@ /** * fu_io_channel_new_file: * @filename: device file - * @open_flags: some #FuIoChannelOpenFlag typically %FU_IO_CHANNEL_OPEN_FLAG_READ + * @open_flags: some #FuIoChannelOpenFlags typically %FU_IO_CHANNEL_OPEN_FLAG_READ * @error: (nullable): optional return location for an error * * Creates a new object to write and/or read from. @@ -624,7 +622,7 @@ * Since: 2.0.0 **/ FuIOChannel * -fu_io_channel_new_file(const gchar *filename, FuIoChannelOpenFlag open_flags, GError **error) +fu_io_channel_new_file(const gchar *filename, FuIoChannelOpenFlags open_flags, GError **error) { gint fd; int flags = 0; @@ -659,7 +657,7 @@ #endif "failed to open %s: %s", filename, - g_strerror(errno)); + fwupd_strerror(errno)); fwupd_error_convert(error); return NULL; } @@ -697,7 +695,7 @@ #endif "failed to create %s: %s", name, - g_strerror(errno)); + fwupd_strerror(errno)); fwupd_error_convert(error); return NULL; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-io-channel.h fwupd-2.0.20/libfwupdplugin/fu-io-channel.h --- fwupd-2.0.8/libfwupdplugin/fu-io-channel.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-io-channel.h 2026-02-26 11:36:18.000000000 +0000 @@ -16,21 +16,45 @@ /** * FuIOChannelFlags: - * @FU_IO_CHANNEL_FLAG_NONE: No flags are set - * @FU_IO_CHANNEL_FLAG_SINGLE_SHOT: Only one read or write is expected - * @FU_IO_CHANNEL_FLAG_FLUSH_INPUT: Flush pending input before writing - * @FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO: Block waiting for the TTY * * The flags used when reading data from the TTY. **/ typedef enum { - FU_IO_CHANNEL_FLAG_NONE = 0, /* Since: 1.2.2 */ - FU_IO_CHANNEL_FLAG_SINGLE_SHOT = 1 << 0, /* Since: 1.2.2 */ - FU_IO_CHANNEL_FLAG_FLUSH_INPUT = 1 << 1, /* Since: 1.2.2 */ - FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO = 1 << 2, /* Since: 1.2.2 */ + /** + * FU_IO_CHANNEL_FLAG_NONE: + * + * No flags are set. + * + * Since: 1.2.2 + */ + FU_IO_CHANNEL_FLAG_NONE = 0, + /** + * FU_IO_CHANNEL_FLAG_SINGLE_SHOT: + * + * Only one read or write is expected. + * + * Since: 1.2.2 + */ + FU_IO_CHANNEL_FLAG_SINGLE_SHOT = 1 << 0, + /** + * FU_IO_CHANNEL_FLAG_FLUSH_INPUT: + * + * Flush pending input before writing. + * + * Since: 1.2.2 + */ + FU_IO_CHANNEL_FLAG_FLUSH_INPUT = 1 << 1, + /** + * FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO: + * + * Block waiting for the TTY. + * + * Since: 1.2.2 + */ + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO = 1 << 2, /*< private >*/ FU_IO_CHANNEL_FLAG_LAST -} FuIOChannelFlags; +} G_GNUC_FLAG_ENUM FuIOChannelFlags; FuIOChannel * fu_io_channel_unix_new(gint fd); @@ -39,7 +63,7 @@ G_GNUC_NON_NULL(1); FuIOChannel * fu_io_channel_new_file(const gchar *filename, - FuIoChannelOpenFlag open_flags, + FuIoChannelOpenFlags open_flags, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); gint diff -Nru fwupd-2.0.8/libfwupdplugin/fu-io-channel.rs fwupd-2.0.20/libfwupdplugin/fu-io-channel.rs --- fwupd-2.0.8/libfwupdplugin/fu-io-channel.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-io-channel.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,8 +1,8 @@ // Copyright 2023 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later -#[derive(ToBitString)] -enum FuIoChannelOpenFlag { +#[derive(ToString)] +enum FuIoChannelOpenFlags { None = 0, Read = 1 << 0, Write = 1 << 1, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-ioctl.c fwupd-2.0.20/libfwupdplugin/fu-ioctl.c --- fwupd-2.0.8/libfwupdplugin/fu-ioctl.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-ioctl.c 2026-02-26 11:36:18.000000000 +0000 @@ -95,6 +95,13 @@ } static void +fu_ioctl_append_key_as_u32(GString *event_id, const gchar *key, guint32 value) +{ + g_autofree gchar *value2 = g_strdup_printf("0x%08x", (guint)value); + fu_ioctl_append_key(event_id, key, value2); +} + +static void fu_ioctl_append_key_from_buf(GString *event_id, const gchar *key, const guint8 *buf, gsize bufsz) { g_autofree gchar *key_data = g_strdup_printf("%sData", key != NULL ? key : ""); @@ -249,7 +256,11 @@ event_id = g_string_new(self->event_id->str); if (g_strcmp0(event_id->str, "Ioctl:") == 0) { fu_ioctl_append_key_as_u16(event_id, "Request", request); - fu_ioctl_append_key_from_buf(event_id, NULL, buf, bufsz); + if (flags & FU_IOCTL_FLAG_PTR_AS_INTEGER) { + fu_ioctl_append_key_as_u32(event_id, NULL, GPOINTER_TO_UINT(buf)); + } else { + fu_ioctl_append_key_from_buf(event_id, NULL, buf, bufsz); + } } } @@ -259,8 +270,15 @@ if (event == NULL) return FALSE; if (self->fixups->len == 0) { - if (!fu_device_event_copy_data(event, "DataOut", buf, bufsz, NULL, error)) - return FALSE; + if ((flags & FU_IOCTL_FLAG_PTR_AS_INTEGER) == 0) { + if (!fu_device_event_copy_data(event, + "DataOut", + buf, + bufsz, + NULL, + error)) + return FALSE; + } } for (guint i = 0; i < self->fixups->len; i++) { FuIoctlFixup *fixup = g_ptr_array_index(self->fixups, i); @@ -311,8 +329,10 @@ if (event != NULL) { if (rc != NULL && *rc != 0) fu_device_event_set_i64(event, "Rc", *rc); - if (self->fixups->len == 0) - fu_device_event_set_data(event, "DataOut", buf, bufsz); + if (self->fixups->len == 0) { + if ((flags & FU_IOCTL_FLAG_PTR_AS_INTEGER) == 0) + fu_device_event_set_data(event, "DataOut", buf, bufsz); + } for (guint i = 0; i < self->fixups->len; i++) { FuIoctlFixup *fixup = g_ptr_array_index(self->fixups, i); g_autofree gchar *key = fu_ioctl_fixup_build_key(fixup, "DataOut"); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-ioctl.h fwupd-2.0.20/libfwupdplugin/fu-ioctl.h --- fwupd-2.0.8/libfwupdplugin/fu-ioctl.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-ioctl.h 2026-02-26 11:36:18.000000000 +0000 @@ -16,15 +16,17 @@ * FuIoctlFlags: * @FU_IOCTL_FLAG: No flags set * @FU_IOCTL_FLAG_RETRY: Retry the call on failure + * @FU_IOCTL_FLAG_PTR_AS_INTEGER: The @ptr passed to ioctl is an integer, not a buffer * * Flags used when calling fu_ioctl_execute() and fu_udev_device_ioctl(). **/ typedef enum { FU_IOCTL_FLAG_NONE = 0, FU_IOCTL_FLAG_RETRY = 1 << 0, + FU_IOCTL_FLAG_PTR_AS_INTEGER = 1 << 1, /*< private >*/ FU_IOCTL_FLAG_LAST -} FuIoctlFlags; +} G_GNUC_FLAG_ENUM FuIoctlFlags; typedef gboolean (*FuIoctlFixupFunc)(FuIoctl *self, gpointer ptr, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-json-firmware.c fwupd-2.0.20/libfwupdplugin/fu-json-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-json-firmware.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-json-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,43 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#define G_LOG_DOMAIN "FuFirmware" + +#include "config.h" + +#include "fu-json-firmware.h" + +/** + * FuJsonFirmware: + * + * A "dummy" loader that just checks if the file can be parsed as JSON format. + */ + +G_DEFINE_TYPE(FuJsonFirmware, fu_json_firmware, FU_TYPE_FIRMWARE) + +static gboolean +fu_json_firmware_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ + /* just load into memory, no extraction performed */ + g_autoptr(JsonParser) parser = json_parser_new(); + return json_parser_load_from_stream(parser, stream, NULL, error); +} + +static void +fu_json_firmware_init(FuJsonFirmware *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION); +} + +static void +fu_json_firmware_class_init(FuJsonFirmwareClass *klass) +{ + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + firmware_class->parse = fu_json_firmware_parse; +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-json-firmware.h fwupd-2.0.20/libfwupdplugin/fu-json-firmware.h --- fwupd-2.0.8/libfwupdplugin/fu-json-firmware.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-json-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_JSON_FIRMWARE (fu_json_firmware_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuJsonFirmware, fu_json_firmware, FU, JSON_FIRMWARE, FuFirmware) + +struct _FuJsonFirmwareClass { + FuFirmwareClass parent_class; +}; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-kernel.c fwupd-2.0.20/libfwupdplugin/fu-kernel.c --- fwupd-2.0.8/libfwupdplugin/fu-kernel.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-kernel.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,6 @@ #include #endif -#include "fu-common.h" #include "fu-input-stream.h" #include "fu-kernel.h" #include "fu-path.h" @@ -33,8 +32,7 @@ { #ifdef __linux__ gsize len = 0; - g_autofree gchar *dir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_SECURITY); - g_autofree gchar *fname = g_build_filename(dir, "lockdown", NULL); + g_autofree gchar *fname = fu_path_build(FU_PATH_KIND_SYSFSDIR_SECURITY, "lockdown", NULL); g_autofree gchar *data = NULL; g_auto(GStrv) options = NULL; @@ -68,12 +66,11 @@ fu_kernel_check_version(const gchar *minimum_kernel, GError **error) { #ifdef HAVE_UTSNAME_H - struct utsname name_tmp; + struct utsname name_tmp = {0}; g_return_val_if_fail(error == NULL || *error == NULL, FALSE); g_return_val_if_fail(minimum_kernel != NULL, FALSE); - memset(&name_tmp, 0, sizeof(struct utsname)); if (uname(&name_tmp) < 0) { g_set_error_literal(error, FWUPD_ERROR, @@ -183,7 +180,6 @@ #ifdef HAVE_UTSNAME_H struct utsname name_tmp; g_autofree gchar *config_fn = NULL; - g_autofree gchar *bootdir = fu_path_from_kind(FU_PATH_KIND_HOSTFS_BOOT); memset(&name_tmp, 0, sizeof(struct utsname)); if (uname(&name_tmp) < 0) { @@ -194,7 +190,7 @@ return NULL; } config_fn = g_strdup_printf("config-%s", name_tmp.release); - return g_build_filename(bootdir, config_fn, NULL); + return fu_path_build(FU_PATH_KIND_HOSTFS_BOOT, config_fn, NULL); #else g_set_error_literal(error, FWUPD_ERROR, @@ -222,8 +218,7 @@ gsize bufsz = 0; g_autofree gchar *buf = NULL; g_autofree gchar *fn = NULL; - g_autofree gchar *procdir = fu_path_from_kind(FU_PATH_KIND_PROCFS); - g_autofree gchar *config_fngz = g_build_filename(procdir, "config.gz", NULL); + g_autofree gchar *config_fngz = fu_path_build(FU_PATH_KIND_PROCFS, "config.gz", NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); @@ -343,9 +338,7 @@ gboolean fu_kernel_check_cmdline_mutable(GError **error) { - g_autofree gchar *bootdir = fu_path_from_kind(FU_PATH_KIND_HOSTFS_BOOT); g_autofree gchar *grubby_path = NULL; - g_autofree gchar *sysconfdir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR); g_auto(GStrv) config_files = g_new0(gchar *, 3); /* not found */ @@ -354,8 +347,8 @@ return FALSE; /* check all the config files are writable */ - config_files[0] = g_build_filename(bootdir, "grub2", "grub.cfg", NULL); - config_files[1] = g_build_filename(sysconfdir, "grub.cfg", NULL); + config_files[0] = fu_path_build(FU_PATH_KIND_HOSTFS_BOOT, "grub2", "grub.cfg", NULL); + config_files[1] = fu_path_build(FU_PATH_KIND_SYSCONFDIR, "grub.cfg", NULL); for (guint i = 0; config_files[i] != NULL; i++) { g_autoptr(GFile) file = g_file_new_for_path(config_files[i]); g_autoptr(GFileInfo) info = NULL; @@ -398,7 +391,7 @@ grubby_path = fu_path_find_program("grubby", error); if (grubby_path == NULL) { - g_prefix_error(error, "failed to find grubby: "); + g_prefix_error_literal(error, "failed to find grubby: "); return FALSE; } if (enable) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-linear-firmware.c fwupd-2.0.20/libfwupdplugin/fu-linear-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-linear-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-linear-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,8 @@ * * A firmware made up of concatenated blobs of a different firmware type. * + * Parsed firmware images can set `FU_FIRMWARE_FLAG_IS_LAST_IMAGE` to abort processing. + * * NOTE: All the child images will be of the specified `GType`. * * See also: [class@FuFirmware] @@ -86,7 +88,7 @@ static gboolean fu_linear_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuLinearFirmware *self = FU_LINEAR_FIRMWARE(firmware); @@ -102,25 +104,39 @@ stream_tmp = fu_partial_input_stream_new(stream, offset, streamsz - offset, error); if (stream_tmp == NULL) { - g_prefix_error(error, "failed to cut linear image: "); + g_prefix_error_literal(error, "failed to cut linear image: "); return FALSE; } if (!fu_firmware_parse_stream(img, stream_tmp, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) { g_prefix_error(error, "failed to parse at 0x%x: ", (guint)offset); return FALSE; } - fu_firmware_set_offset(firmware, offset); - if (!fu_firmware_add_image_full(firmware, img, error)) + fu_firmware_set_offset(img, offset); + if (!fu_firmware_add_image(firmware, img, error)) + return FALSE; + if (fu_firmware_get_size(img) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "child image had no defined size"); return FALSE; + } /* next! */ offset += fu_firmware_get_size(img); + + /* skip any padding */ + if (fu_firmware_has_flag(img, FU_FIRMWARE_FLAG_IS_LAST_IMAGE)) + break; } + /* this might be less than streamsz if padded */ + fu_firmware_set_size(firmware, offset); + /* success */ return TRUE; } @@ -135,6 +151,8 @@ for (guint i = 0; i < images->len; i++) { FuFirmware *img = g_ptr_array_index(images, i); g_autoptr(GBytes) blob = NULL; + if (i == images->len - 1) + fu_firmware_add_flag(img, FU_FIRMWARE_FLAG_IS_LAST_IMAGE); fu_firmware_set_offset(img, buf->len); blob = fu_firmware_write(img, error); if (blob == NULL) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-linux-efivars.c fwupd-2.0.20/libfwupdplugin/fu-linux-efivars.c --- fwupd-2.0.8/libfwupdplugin/fu-linux-efivars.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-linux-efivars.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,9 +17,12 @@ #include #include +#ifdef HAVE_SYS_VFS_H +#include +#endif + #include "fwupd-error.h" -#include "fu-common.h" #include "fu-linux-efivars.h" #include "fu-path.h" @@ -32,8 +35,7 @@ static gchar * fu_linux_efivars_get_path(void) { - g_autofree gchar *sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - return g_build_filename(sysfsfwdir, "efi", "efivars", NULL); + return fu_path_build(FU_PATH_KIND_SYSFSDIR_FW, "efi", "efivars", NULL); } static gchar * @@ -76,7 +78,7 @@ FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "failed to get flags: %s", - g_strerror(errno)); + fwupd_strerror(errno)); return FALSE; } } else { @@ -105,7 +107,7 @@ FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "failed to set flags: %s", - g_strerror(errno)); + fwupd_strerror(errno)); return FALSE; } return TRUE; @@ -128,7 +130,7 @@ FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "failed to open: %s", - g_strerror(errno)); + fwupd_strerror(errno)); return FALSE; } istr = g_unix_input_stream_new(fd, TRUE); @@ -227,12 +229,12 @@ const gchar *name, guint8 **data, gsize *data_sz, - guint32 *attr, + FuEfiVariableAttrs *attr, GError **error) { gssize attr_sz; gssize data_sz_tmp; - guint32 attr_tmp; + FuEfiVariableAttrs attr_tmp; guint64 sz; g_autofree gchar *fn = NULL; g_autoptr(GFile) file = NULL; @@ -252,7 +254,7 @@ NULL, error); if (info == NULL) { - g_prefix_error(error, "failed to get stream info: "); + g_prefix_error_literal(error, "failed to get stream info: "); fwupd_error_convert(error); return FALSE; } @@ -271,7 +273,7 @@ /* read out the attributes */ attr_sz = g_input_stream_read(istr, &attr_tmp, sizeof(attr_tmp), NULL, error); if (attr_sz == -1) { - g_prefix_error(error, "failed to read attr: "); + g_prefix_error_literal(error, "failed to read attr: "); fwupd_error_convert(error); return FALSE; } @@ -292,7 +294,7 @@ if (data != NULL) { g_autofree guint8 *data_tmp = g_malloc0(data_sz_tmp); if (!g_input_stream_read_all(istr, data_tmp, data_sz_tmp, NULL, NULL, error)) { - g_prefix_error(error, "failed to read data: "); + g_prefix_error_literal(error, "failed to read data: "); return FALSE; } *data = g_steal_pointer(&data_tmp); @@ -412,13 +414,61 @@ return total; } +static guint64 +fu_linux_efivars_space_free(FuEfivars *efivars, GError **error) +{ + guint64 total = 0; + g_autofree gchar *path = fu_linux_efivars_get_path(); + g_autoptr(GFile) file_fs = g_file_new_for_path(path); + g_autoptr(GFileInfo) info_fs = NULL; + + /* try GIO first */ + info_fs = g_file_query_info(file_fs, + G_FILE_ATTRIBUTE_FILESYSTEM_FREE, + G_FILE_QUERY_INFO_NONE, + NULL, + error); + if (info_fs == NULL) { + fwupd_error_convert(error); + return G_MAXUINT64; + } + total = g_file_info_get_attribute_uint64(info_fs, G_FILE_ATTRIBUTE_FILESYSTEM_FREE); + +#ifdef HAVE_SYS_VFS_H + /* fall back to standard C library */ + if (total == 0) { + struct statfs sfs = {0}; + int rc = statfs(path, &sfs); + if (rc != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to get filesystem statistics: %s", + fwupd_strerror(errno)); + return G_MAXUINT64; + } + total = sfs.f_bsize * sfs.f_bfree; + } +#endif + if (total == 0 || total == G_MAXUINT64) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "getting efivars free space is not supported"); + return G_MAXUINT64; + } + + /* success */ + return total; +} + static gboolean fu_linux_efivars_set_data(FuEfivars *efivars, const gchar *guid, const gchar *name, const guint8 *data, gsize sz, - guint32 attr, + FuEfiVariableAttrs attr, GError **error) { int fd; @@ -439,7 +489,7 @@ } /* open file for writing, optionally append */ - if (attr & FU_EFIVARS_ATTR_APPEND_WRITE) + if (attr & FU_EFI_VARIABLE_ATTR_APPEND_WRITE) open_wflags |= O_APPEND; fd = open(fn, open_wflags, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { @@ -448,14 +498,14 @@ FWUPD_ERROR_INVALID_DATA, "failed to open %s: %s", fn, - g_strerror(errno)); + fwupd_strerror(errno)); return FALSE; } ostr = g_unix_output_stream_new(fd, TRUE); memcpy(buf, &attr, sizeof(attr)); /* nocheck:blocked */ memcpy(buf + sizeof(attr), data, sz); /* nocheck:blocked */ if (g_output_stream_write(ostr, buf, sizeof(attr) + sz, NULL, error) < 0) { - g_prefix_error(error, "failed to write data to efivarsfs: "); + g_prefix_error_literal(error, "failed to write data to efivarsfs: "); fwupd_error_convert(error); return FALSE; } @@ -481,6 +531,7 @@ FuEfivarsClass *efivars_class = FU_EFIVARS_CLASS(klass); efivars_class->supported = fu_linux_efivars_supported; efivars_class->space_used = fu_linux_efivars_space_used; + efivars_class->space_free = fu_linux_efivars_space_free; efivars_class->exists = fu_linux_efivars_exists; efivars_class->get_monitor = fu_linux_efivars_get_monitor; efivars_class->get_data = fu_linux_efivars_get_data; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-lzma-common.c fwupd-2.0.20/libfwupdplugin/fu-lzma-common.c --- fwupd-2.0.8/libfwupdplugin/fu-lzma-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-lzma-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -89,7 +89,8 @@ strm.next_in = g_bytes_get_data(blob, NULL); strm.avail_in = g_bytes_get_size(blob); - rc = lzma_easy_encoder(&strm, 9, LZMA_CHECK_CRC64); + /* xz default compression level is 6, higher values increase CPU and memory usage */ + rc = lzma_easy_encoder(&strm, 6, LZMA_CHECK_CRC64); if (rc != LZMA_OK) { lzma_end(&strm); g_set_error(error, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-mei-device.c fwupd-2.0.20/libfwupdplugin/fu-mei-device.c --- fwupd-2.0.8/libfwupdplugin/fu-mei-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-mei-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -16,19 +16,7 @@ #ifdef HAVE_IOCTL_H #include #endif -#ifdef HAVE_ERRNO_H -#include -#endif -#ifdef HAVE_SELECT_H -#include -#endif -#include -#include -#include - -#include "fu-bytes.h" -#include "fu-dump.h" #include "fu-mei-device.h" #include "fu-string.h" @@ -46,7 +34,6 @@ guint32 max_msg_length; guint8 protocol_version; gchar *uuid; - gchar *parent_device_file; } FuMeiDevicePrivate; G_DEFINE_TYPE_WITH_PRIVATE(FuMeiDevice, fu_mei_device, FU_TYPE_UDEV_DEVICE) @@ -59,65 +46,33 @@ FuMeiDevice *self = FU_MEI_DEVICE(device); FuMeiDevicePrivate *priv = GET_PRIVATE(self); fwupd_codec_string_append(str, idt, "Uuid", priv->uuid); - fwupd_codec_string_append(str, idt, "ParentDeviceFile", priv->parent_device_file); fwupd_codec_string_append_hex(str, idt, "MaxMsgLength", priv->max_msg_length); fwupd_codec_string_append_hex(str, idt, "ProtocolVer", priv->protocol_version); } static gboolean -fu_mei_device_ensure_parent_device_file(FuMeiDevice *self, GError **error) +fu_mei_device_set_uuid(FuMeiDevice *self, const gchar *uuid) { FuMeiDevicePrivate *priv = GET_PRIVATE(self); - const gchar *fn; - g_autofree gchar *parent_tmp = NULL; - g_autofree gchar *parent_mei_path = NULL; - g_autoptr(FuUdevDevice) parent = NULL; - g_autoptr(GDir) dir = NULL; - - /* get direct parent */ - parent = FU_UDEV_DEVICE( - fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), "pci", error)); - if (parent == NULL) - return FALSE; - - /* look for the only child with this subsystem */ - parent_mei_path = g_build_filename(fu_udev_device_get_sysfs_path(parent), "mei", NULL); - dir = g_dir_open(parent_mei_path, 0, NULL); - if (dir == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no MEI parent dir for %s", - fu_udev_device_get_sysfs_path(parent)); - return FALSE; - } - fn = g_dir_read_name(dir); - if (fn == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no MEI parent in %s", - parent_mei_path); + if (g_strcmp0(priv->uuid, uuid) == 0) return FALSE; - } - - /* success */ - parent_tmp = g_build_filename(fu_udev_device_get_sysfs_path(parent), "mei", fn, NULL); - if (g_strcmp0(parent_tmp, priv->parent_device_file) != 0) { - g_free(priv->parent_device_file); - priv->parent_device_file = g_steal_pointer(&parent_tmp); - } + g_free(priv->uuid); + priv->uuid = g_strdup(uuid); return TRUE; } -static void -fu_mei_device_set_uuid(FuMeiDevice *self, const gchar *uuid) +static gboolean +fu_mei_device_close(FuDevice *device, GError **error) { - FuMeiDevicePrivate *priv = GET_PRIVATE(self); - if (g_strcmp0(priv->uuid, uuid) == 0) - return; - g_free(priv->uuid); - priv->uuid = g_strdup(uuid); + FuMeiDevice *self = FU_MEI_DEVICE(device); + + /* FuUdevDevice->close */ + if (!FU_DEVICE_CLASS(fu_mei_device_parent_class)->close(device, error)) + return FALSE; + + /* this is no longer valid */ + fu_mei_device_disconnect(self); + return TRUE; } static gboolean @@ -134,6 +89,7 @@ pci_donor, FU_DEVICE_INCORPORATE_FLAG_VENDOR_IDS | FU_DEVICE_INCORPORATE_FLAG_VID | FU_DEVICE_INCORPORATE_FLAG_PID | + FU_DEVICE_INCORPORATE_FLAG_INSTANCE_KEYS | FU_DEVICE_INCORPORATE_FLAG_PHYSICAL_ID); /* success */ @@ -141,68 +97,90 @@ } static gboolean +fu_mei_device_interfaces_probe(FuMeiDevice *self, GError **error) +{ + gsize prefixlen; + g_autofree gchar *prefix = NULL; + g_autoptr(FuDevice) parent = NULL; + g_autoptr(GPtrArray) attrs = NULL; + + /* all the interfaces are prefixed by the parent basename */ + parent = fu_device_get_backend_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (fu_device_get_backend_id(parent) == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "no parent backend-id"); + return FALSE; + } + prefix = g_path_get_basename(fu_device_get_backend_id(parent)); + prefixlen = strlen(prefix); + + /* add any instance IDs that match */ + attrs = fu_udev_device_list_sysfs(FU_UDEV_DEVICE(parent), error); + if (attrs == NULL) + return FALSE; + for (guint i = 0; i < attrs->len; i++) { + const gchar *attr = g_ptr_array_index(attrs, i); + if (g_str_has_prefix(attr, prefix)) { + fu_device_add_instance_id_full(FU_DEVICE(self), + attr + prefixlen + 1, + FU_DEVICE_INSTANCE_FLAG_QUIRKS); + } + } + + /* success */ + return TRUE; +} + +static gboolean fu_mei_device_probe(FuDevice *device, GError **error) { FuMeiDevice *self = FU_MEI_DEVICE(device); - FuMeiDevicePrivate *priv = GET_PRIVATE(self); - g_autofree gchar *uuid = NULL; - g_autoptr(GError) error_local = NULL; /* copy the PCI-specific vendor */ if (!fu_mei_device_pci_probe(self, error)) return FALSE; - /* this has to exist */ - uuid = fu_udev_device_read_sysfs(FU_UDEV_DEVICE(device), - "uuid", - FU_UDEV_DEVICE_ATTR_READ_TIMEOUT_DEFAULT, - &error_local); - if (uuid == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "UUID not provided: %s", - error_local->message); - return FALSE; - } - fu_mei_device_set_uuid(self, uuid); - fu_device_add_instance_id(device, uuid); - - /* get the mei[0-9] device file the parent is using */ - if (!fu_mei_device_ensure_parent_device_file(self, error)) + /* add interfaces */ + if (!fu_mei_device_interfaces_probe(self, error)) return FALSE; - /* the kernel is missing `dev` on mei_me children */ - if (fu_udev_device_get_device_file(FU_UDEV_DEVICE(device)) == NULL) { - g_autofree gchar *basename = g_path_get_basename(priv->parent_device_file); - g_autofree gchar *device_file = g_build_filename("/dev", basename, NULL); - fu_udev_device_set_device_file(FU_UDEV_DEVICE(device), device_file); - } + /* for quirk matches */ + fu_device_build_instance_id_full(device, + FU_DEVICE_INSTANCE_FLAG_QUIRKS, + NULL, + "PCI", + "VEN", + NULL); + fu_device_build_instance_id_full(device, + FU_DEVICE_INSTANCE_FLAG_QUIRKS, + NULL, + "PCI", + "VEN", + "DEV", + NULL); + fu_device_build_instance_id_full(device, + FU_DEVICE_INSTANCE_FLAG_QUIRKS, + NULL, + "PCI", + "DRIVER", + NULL); /* success */ return TRUE; } static gchar * -fu_mei_device_get_parent_attr(FuMeiDevice *self, const gchar *basename, guint idx, GError **error) +fu_mei_device_get_multiline_attr(FuMeiDevice *self, const gchar *attr, guint idx, GError **error) { - FuMeiDevicePrivate *priv = GET_PRIVATE(self); - g_autofree gchar *fn = NULL; g_auto(GStrv) lines = NULL; g_autoptr(GBytes) blob = NULL; - /* sanity check */ - if (priv->parent_device_file == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no parent device file"); - return NULL; - } - /* load lines */ - fn = g_build_filename(priv->parent_device_file, basename, NULL); - blob = fu_bytes_get_contents(fn, error); + blob = fu_udev_device_read_sysfs_bytes(FU_UDEV_DEVICE(self), attr, -1, 500, error); if (blob == NULL) return NULL; lines = fu_strsplit_bytes(blob, "\n", -1); @@ -237,7 +215,7 @@ { g_return_val_if_fail(FU_IS_MEI_DEVICE(self), NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); - return fu_mei_device_get_parent_attr(self, "fw_ver", idx, error); + return fu_mei_device_get_multiline_attr(self, "fw_ver", idx, error); } /** @@ -257,7 +235,7 @@ { g_return_val_if_fail(FU_IS_MEI_DEVICE(self), NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); - return fu_mei_device_get_parent_attr(self, "fw_status", idx, error); + return fu_mei_device_get_multiline_attr(self, "fw_status", idx, error); } /** @@ -299,6 +277,7 @@ /** * fu_mei_device_connect: * @self: a #FuMeiDevice + * @uuid: interface UUID * @req_protocol_version: required protocol version, or 0 * @error: (nullable): optional return location for an error * @@ -309,7 +288,10 @@ * Since: 1.8.2 **/ gboolean -fu_mei_device_connect(FuMeiDevice *self, guchar req_protocol_version, GError **error) +fu_mei_device_connect(FuMeiDevice *self, + const gchar *uuid, + guint8 req_protocol_version, + GError **error) { #ifdef HAVE_MEI_H FuMeiDevicePrivate *priv = GET_PRIVATE(self); @@ -319,12 +301,21 @@ g_autoptr(FuIoctl) ioctl = fu_udev_device_ioctl_new(FU_UDEV_DEVICE(self)); g_return_val_if_fail(FU_IS_MEI_DEVICE(self), FALSE); + g_return_val_if_fail(uuid != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + /* already using this UUID */ + if (!fu_mei_device_set_uuid(self, uuid)) + return TRUE; + + /* MEI is weird in that you have to close() and re-open() it to do the ioctl */ + if (!fu_udev_device_reopen(FU_UDEV_DEVICE(self), error)) + return FALSE; + if (!fwupd_guid_from_string(priv->uuid, &guid_le, FWUPD_GUID_FLAG_MIXED_ENDIAN, error)) return FALSE; - fu_dump_raw(G_LOG_DOMAIN, "guid_le", (guint8 *)&guid_le, sizeof(guid_le)); memcpy(&data.in_client_uuid, &guid_le, sizeof(guid_le)); /* nocheck:blocked */ + g_debug("connecting to %s", priv->uuid); if (!fu_ioctl_execute(ioctl, IOCTL_MEI_CONNECT_CLIENT, (guint8 *)&data, @@ -359,6 +350,21 @@ } /** + * fu_mei_device_disconnect: + * @self: a #FuMeiDevice + * + * Disconnects from the MEI device. + * + * Since: 2.0.17 + **/ +void +fu_mei_device_disconnect(FuMeiDevice *self) +{ + g_return_if_fail(FU_IS_MEI_DEVICE(self)); + fu_mei_device_set_uuid(self, NULL); +} + +/** * fu_mei_device_read: * @self: a #FuMeiDevice * @buf: (out): data @@ -381,27 +387,16 @@ guint timeout_ms, GError **error) { - gssize rc; - FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); - g_return_val_if_fail(FU_IS_MEI_DEVICE(self), FALSE); g_return_val_if_fail(buf != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - - rc = read(fu_io_channel_unix_get_fd(io_channel), buf, bufsz); - if (rc < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "read failed %u: %s", - (guint)rc, - g_strerror(errno)); - return FALSE; - } - fu_dump_raw(G_LOG_DOMAIN, "read", buf, rc); - if (bytes_read != NULL) - *bytes_read = (gsize)rc; - return TRUE; + return fu_udev_device_read(FU_UDEV_DEVICE(self), + buf, + bufsz, + bytes_read, + timeout_ms, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error); } /** @@ -425,71 +420,15 @@ guint timeout_ms, GError **error) { -#ifdef HAVE_SELECT_H - struct timeval tv; - gssize written; - gssize rc; - fd_set set; - FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); - guint fd = fu_io_channel_unix_get_fd(io_channel); - g_return_val_if_fail(FU_IS_MEI_DEVICE(self), FALSE); g_return_val_if_fail(buf != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - - tv.tv_sec = timeout_ms / 1000; - tv.tv_usec = (timeout_ms % 1000) * 1000; - - fu_dump_raw(G_LOG_DOMAIN, "write", buf, bufsz); - written = write(fd, buf, bufsz); - if (written < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "write failed with status %" G_GSSIZE_FORMAT " %s", - written, - g_strerror(errno)); - return FALSE; - } - if ((gsize)written != bufsz) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "only wrote %" G_GSSIZE_FORMAT " of %" G_GSIZE_FORMAT, - written, - bufsz); - return FALSE; - } - - FD_ZERO(&set); - FD_SET(fd, &set); - rc = select(fd + 1, &set, NULL, NULL, &tv); - if (rc > 0 && FD_ISSET(fd, &set)) - return TRUE; - - /* timed out */ - if (rc == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "write failed on timeout with status"); - return FALSE; - } - - /* rc < 0 */ - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "write failed on select with status %" G_GSSIZE_FORMAT, - rc); - return FALSE; -#else - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "linux/select.h not supported"); - return FALSE; -#endif + return fu_udev_device_write(FU_UDEV_DEVICE(self), + buf, + bufsz, + timeout_ms, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error); } static void @@ -505,10 +444,6 @@ /* copy private instance data */ priv->max_msg_length = priv_donor->max_msg_length; priv->protocol_version = priv_donor->protocol_version; - if (priv->uuid == NULL) - fu_mei_device_set_uuid(self, priv_donor->uuid); - if (priv->parent_device_file == NULL) - priv->parent_device_file = g_strdup(priv_donor->parent_device_file); } static void @@ -525,7 +460,6 @@ FuMeiDevice *self = FU_MEI_DEVICE(object); FuMeiDevicePrivate *priv = GET_PRIVATE(self); g_free(priv->uuid); - g_free(priv->parent_device_file); G_OBJECT_CLASS(fu_mei_device_parent_class)->finalize(object); } @@ -536,6 +470,7 @@ GObjectClass *object_class = G_OBJECT_CLASS(klass); object_class->finalize = fu_mei_device_finalize; device_class->probe = fu_mei_device_probe; + device_class->close = fu_mei_device_close; device_class->to_string = fu_mei_device_to_string; device_class->incorporate = fu_mei_device_incorporate; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-mei-device.h fwupd-2.0.20/libfwupdplugin/fu-mei-device.h --- fwupd-2.0.8/libfwupdplugin/fu-mei-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-mei-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -16,8 +16,12 @@ }; gboolean -fu_mei_device_connect(FuMeiDevice *self, guchar req_protocol_version, GError **error) - G_GNUC_NON_NULL(1); +fu_mei_device_connect(FuMeiDevice *self, + const gchar *uuid, + guint8 req_protocol_version, + GError **error) G_GNUC_NON_NULL(1, 2); +void +fu_mei_device_disconnect(FuMeiDevice *self) G_GNUC_NON_NULL(1); gboolean fu_mei_device_read(FuMeiDevice *self, guint8 *buf, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-mem.c fwupd-2.0.20/libfwupdplugin/fu-mem.c --- fwupd-2.0.8/libfwupdplugin/fu-mem.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-mem.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,6 +10,7 @@ #include "fwupd-error.h" +#include "fu-common.h" #include "fu-mem-private.h" #include "fu-string.h" @@ -334,6 +335,15 @@ (guint)bufsz); return FALSE; } + if (fu_size_checked_add(offset, n) == G_MAXSIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "offset 0x%02x + 0x%02x overflowed", + (guint)offset, + (guint)n); + return FALSE; + } if (offset > bufsz || n + offset > bufsz) { g_set_error(error, FWUPD_ERROR, @@ -380,6 +390,15 @@ (guint)bufsz); return FALSE; } + if (fu_size_checked_add(offset, n) == G_MAXSIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "offset 0x%02x + 0x%02x overflowed", + (guint)offset, + (guint)n); + return FALSE; + } if (offset > bufsz || n + offset > bufsz) { g_set_error(error, FWUPD_ERROR, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-msgpack-item.c fwupd-2.0.20/libfwupdplugin/fu-msgpack-item.c --- fwupd-2.0.8/libfwupdplugin/fu-msgpack-item.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-msgpack-item.c 2026-02-26 11:36:18.000000000 +0000 @@ -125,16 +125,16 @@ * * Reads the number of items in the map. * - * Returns: an integer, or %G_MAXINT64 if invalid or not found + * Returns: an integer, or %G_MAXUINT64 if invalid or not found * * Since: 2.0.0 **/ guint64 fu_msgpack_item_get_map(FuMsgpackItem *self) { - g_return_val_if_fail(FU_IS_MSGPACK_ITEM(self), G_MAXINT64); - g_return_val_if_fail(self->kind == FU_MSGPACK_ITEM_KIND_MAP, G_MAXINT64); - return self->value.i64; + g_return_val_if_fail(FU_IS_MSGPACK_ITEM(self), G_MAXUINT64); + g_return_val_if_fail(self->kind == FU_MSGPACK_ITEM_KIND_MAP, G_MAXUINT64); + return (guint64)self->value.i64; } /** @@ -143,16 +143,16 @@ * * Reads the number of items in the array. * - * Returns: an integer, or %G_MAXINT64 if invalid or not found + * Returns: an integer, or %G_MAXUINT64 if invalid or not found * * Since: 2.0.0 **/ guint64 fu_msgpack_item_get_array(FuMsgpackItem *self) { - g_return_val_if_fail(FU_IS_MSGPACK_ITEM(self), G_MAXINT64); - g_return_val_if_fail(self->kind == FU_MSGPACK_ITEM_KIND_ARRAY, G_MAXINT64); - return self->value.i64; + g_return_val_if_fail(FU_IS_MSGPACK_ITEM(self), G_MAXUINT64); + g_return_val_if_fail(self->kind == FU_MSGPACK_ITEM_KIND_ARRAY, G_MAXUINT64); + return (guint64)self->value.i64; } /** diff -Nru fwupd-2.0.8/libfwupdplugin/fu-oprom-device.c fwupd-2.0.20/libfwupdplugin/fu-oprom-device.c --- fwupd-2.0.8/libfwupdplugin/fu-oprom-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-oprom-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,7 +6,9 @@ #include "config.h" +#include "fu-device-locker.h" #include "fu-oprom-device.h" +#include "fu-output-stream.h" G_DEFINE_TYPE(FuOpromDevice, fu_oprom_device, FU_TYPE_PCI_DEVICE) @@ -30,26 +32,17 @@ fu_oprom_device_set_enabled(FuOpromDevice *self, gboolean value, GError **error) { g_autofree gchar *rom_fn = NULL; - g_autoptr(GFile) file = NULL; - g_autoptr(GFileOutputStream) output_stream = NULL; + g_autoptr(GOutputStream) output_stream = NULL; rom_fn = g_build_filename(fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(self)), "rom", NULL); if (!g_str_has_prefix(rom_fn, "/sys")) return TRUE; - file = g_file_new_for_path(rom_fn); - output_stream = g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error); - if (output_stream == NULL) { - fu_error_convert(error); + output_stream = fu_output_stream_from_path(rom_fn, error); + if (output_stream == NULL) return FALSE; - } - if (!g_output_stream_write_all(G_OUTPUT_STREAM(output_stream), - value ? "1" : "0", - 1, - NULL, - NULL, - error)) { - fu_error_convert(error); + if (!g_output_stream_write_all(output_stream, value ? "1" : "0", 1, NULL, NULL, error)) { + fwupd_error_convert(error); return FALSE; } @@ -58,14 +51,14 @@ } static gboolean -fu_oprom_device_dump_enable_cb(GObject *device, GError **error) +fu_oprom_device_dump_enable_cb(FuDevice *device, GError **error) { FuOpromDevice *self = FU_OPROM_DEVICE(device); return fu_oprom_device_set_enabled(self, TRUE, error); } static gboolean -fu_oprom_device_dump_disable_cb(GObject *device, GError **error) +fu_oprom_device_dump_disable_cb(FuDevice *device, GError **error) { FuOpromDevice *self = FU_OPROM_DEVICE(device); return fu_oprom_device_set_enabled(self, FALSE, error); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-oprom-firmware.c fwupd-2.0.20/libfwupdplugin/fu-oprom-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-oprom-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-oprom-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,11 +12,9 @@ #include #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-ifwi-cpd-firmware.h" #include "fu-oprom-firmware.h" -#include "fu-oprom-struct.h" #include "fu-string.h" /** @@ -28,9 +26,9 @@ */ typedef struct { - guint16 machine_type; - guint16 subsystem; - guint16 compression_type; + FuOpromMachineType machine_type; + FuOpromSubsystem subsystem; + FuOpromCompressionType compression_type; guint16 vendor_id; guint16 device_id; } FuOpromFirmwarePrivate; @@ -38,8 +36,7 @@ G_DEFINE_TYPE_WITH_PRIVATE(FuOpromFirmware, fu_oprom_firmware, FU_TYPE_FIRMWARE) #define GET_PRIVATE(o) (fu_oprom_firmware_get_instance_private(o)) -#define FU_OPROM_FIRMWARE_ALIGN_LEN 512u -#define FU_OPROM_FIRMWARE_LAST_IMAGE_INDICATOR_BIT (1u << 7) +#define FU_OPROM_FIRMWARE_ALIGN_LEN 512u /** * fu_oprom_firmware_get_machine_type: @@ -47,11 +44,11 @@ * * Gets the machine type. * - * Returns: an integer + * Returns: a #FuOpromMachineType * * Since: 1.8.2 **/ -guint16 +FuOpromMachineType fu_oprom_firmware_get_machine_type(FuOpromFirmware *self) { FuOpromFirmwarePrivate *priv = GET_PRIVATE(self); @@ -65,11 +62,11 @@ * * Gets the machine type. * - * Returns: an integer + * Returns: a #FuOpromSubsystem * * Since: 1.8.2 **/ -guint16 +FuOpromSubsystem fu_oprom_firmware_get_subsystem(FuOpromFirmware *self) { FuOpromFirmwarePrivate *priv = GET_PRIVATE(self); @@ -83,11 +80,11 @@ * * Gets the machine type. * - * Returns: an integer + * Returns: a #FuOpromCompressionType * * Since: 1.8.2 **/ -guint16 +FuOpromCompressionType fu_oprom_firmware_get_compression_type(FuOpromFirmware *self) { FuOpromFirmwarePrivate *priv = GET_PRIVATE(self); @@ -116,7 +113,7 @@ static gboolean fu_oprom_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuOpromFirmware *self = FU_OPROM_FIRMWARE(firmware); @@ -124,8 +121,8 @@ guint16 expansion_header_offset = 0; guint16 pci_header_offset; guint16 image_length = 0; - g_autoptr(GByteArray) st_hdr = NULL; - g_autoptr(GByteArray) st_pci = NULL; + g_autoptr(FuStructOprom) st_hdr = NULL; + g_autoptr(FuStructOpromPci) st_pci = NULL; /* parse header */ st_hdr = fu_struct_oprom_parse_stream(stream, 0x0, error); @@ -138,10 +135,10 @@ /* get PCI offset */ pci_header_offset = fu_struct_oprom_get_pci_header_offset(st_hdr); if (pci_header_offset == 0x0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "no PCI data structure offset provided"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "no PCI data structure offset provided"); return FALSE; } @@ -155,30 +152,35 @@ /* get length */ image_length = fu_struct_oprom_pci_get_image_length(st_pci); if (image_length == 0x0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid image length"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid image"); return FALSE; } fu_firmware_set_size(firmware, image_length * FU_OPROM_FIRMWARE_ALIGN_LEN); fu_firmware_set_idx(firmware, fu_struct_oprom_pci_get_code_type(st_pci)); + /* is last image */ + if (fu_struct_oprom_pci_get_indicator(st_pci) & FU_OPROM_INDICATOR_FLAG_LAST) + fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_IS_LAST_IMAGE); + /* get CPD offset */ expansion_header_offset = fu_struct_oprom_get_expansion_header_offset(st_hdr); if (expansion_header_offset != 0x0) { g_autoptr(FuFirmware) img = NULL; img = fu_firmware_new_from_gtypes(stream, expansion_header_offset, - FWUPD_INSTALL_FLAG_NONE, + flags, error, FU_TYPE_IFWI_CPD_FIRMWARE, FU_TYPE_FIRMWARE, G_TYPE_INVALID); if (img == NULL) { - g_prefix_error(error, "failed to build firmware: "); + g_prefix_error_literal(error, "failed to build firmware: "); return FALSE; } fu_firmware_set_id(img, "cpd"); fu_firmware_set_offset(img, expansion_header_offset); - fu_firmware_add_image(firmware, img); + if (!fu_firmware_add_image(firmware, img, error)) + return FALSE; } /* success */ @@ -192,12 +194,12 @@ FuOpromFirmwarePrivate *priv = GET_PRIVATE(self); gsize image_size = 0; g_autoptr(GByteArray) buf = g_byte_array_new(); - g_autoptr(GByteArray) st_hdr = fu_struct_oprom_new(); - g_autoptr(GByteArray) st_pci = fu_struct_oprom_pci_new(); + g_autoptr(FuStructOprom) st_hdr = fu_struct_oprom_new(); + g_autoptr(FuStructOpromPci) st_pci = fu_struct_oprom_pci_new(); g_autoptr(GBytes) blob_cpd = NULL; /* the smallest each image (and header) can be is 512 bytes */ - image_size += fu_common_align_up(st_hdr->len, FU_FIRMWARE_ALIGNMENT_512); + image_size += fu_common_align_up(st_hdr->buf->len, FU_FIRMWARE_ALIGNMENT_512); blob_cpd = fu_firmware_get_image_by_id_bytes(firmware, "cpd", NULL); if (blob_cpd != NULL) { image_size += @@ -214,15 +216,16 @@ image_size - FU_OPROM_FIRMWARE_ALIGN_LEN); } - g_byte_array_append(buf, st_hdr->data, st_hdr->len); + g_byte_array_append(buf, st_hdr->buf->data, st_hdr->buf->len); /* add PCI section */ fu_struct_oprom_pci_set_vendor_id(st_pci, priv->vendor_id); fu_struct_oprom_pci_set_device_id(st_pci, priv->device_id); fu_struct_oprom_pci_set_image_length(st_pci, image_size / FU_OPROM_FIRMWARE_ALIGN_LEN); fu_struct_oprom_pci_set_code_type(st_pci, fu_firmware_get_idx(firmware)); - fu_struct_oprom_pci_set_indicator(st_pci, FU_OPROM_FIRMWARE_LAST_IMAGE_INDICATOR_BIT); - g_byte_array_append(buf, st_pci->data, st_pci->len); + if (fu_firmware_has_flag(firmware, FU_FIRMWARE_FLAG_IS_LAST_IMAGE)) + fu_struct_oprom_pci_set_indicator(st_pci, FU_OPROM_INDICATOR_FLAG_LAST); + fu_byte_array_append_array(buf, st_pci->buf); fu_byte_array_align_up(buf, FU_FIRMWARE_ALIGNMENT_512, 0xFF); /* add CPD */ @@ -287,6 +290,7 @@ fu_oprom_firmware_init(FuOpromFirmware *self) { fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_STORED_SIZE); + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_ALLOW_LINEAR); } static void diff -Nru fwupd-2.0.8/libfwupdplugin/fu-oprom-firmware.h fwupd-2.0.20/libfwupdplugin/fu-oprom-firmware.h --- fwupd-2.0.8/libfwupdplugin/fu-oprom-firmware.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-oprom-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,6 +8,7 @@ #pragma once #include "fu-firmware.h" +#include "fu-oprom-struct.h" #define FU_TYPE_OPROM_FIRMWARE (fu_oprom_firmware_get_type()) G_DECLARE_DERIVABLE_TYPE(FuOpromFirmware, fu_oprom_firmware, FU, OPROM_FIRMWARE, FuFirmware) @@ -16,39 +17,12 @@ FuFirmwareClass parent_class; }; -/** - * FU_OPROM_FIRMWARE_COMPRESSION_TYPE_NONE: - * - * No compression. - * - * Since: 1.8.2 - **/ -#define FU_OPROM_FIRMWARE_COMPRESSION_TYPE_NONE 0x00 - -/** - * FU_OPROM_FIRMWARE_SUBSYSTEM_EFI_BOOT_SRV_DRV: - * - * EFI boot. - * - * Since: 1.8.2 - **/ -#define FU_OPROM_FIRMWARE_SUBSYSTEM_EFI_BOOT_SRV_DRV 0x00 - -/** - * FU_OPROM_FIRMWARE_MACHINE_TYPE_X64: - * - * AMD64 machine type. - * - * Since: 1.8.2 - **/ -#define FU_OPROM_FIRMWARE_MACHINE_TYPE_X64 0x00 - FuFirmware * fu_oprom_firmware_new(void); -guint16 +FuOpromMachineType fu_oprom_firmware_get_machine_type(FuOpromFirmware *self) G_GNUC_NON_NULL(1); -guint16 +FuOpromSubsystem fu_oprom_firmware_get_subsystem(FuOpromFirmware *self) G_GNUC_NON_NULL(1); -guint16 +FuOpromCompressionType fu_oprom_firmware_get_compression_type(FuOpromFirmware *self) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-oprom.rs fwupd-2.0.20/libfwupdplugin/fu-oprom.rs --- fwupd-2.0.8/libfwupdplugin/fu-oprom.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-oprom.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,21 +1,42 @@ // Copyright 2023 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later +#[repr(u16le)] +enum FuOpromMachineType { + X64, +} + +#[repr(u16le)] +enum FuOpromSubsystem { + EfiBootSrvDrv, +} + +#[repr(u16le)] +enum FuOpromCompressionType { + None, +} + #[derive(New, ValidateStream, ParseStream, Default)] #[repr(C, packed)] struct FuStructOprom { signature: u16le == 0xAA55, image_size: u16le, // of 512 bytes init_func_entry_point: u32le, - subsystem: u16le, - machine_type: u16le, - compression_type: u16le, + subsystem: FuOpromSubsystem, + machine_type: FuOpromMachineType, + compression_type: FuOpromCompressionType, _reserved: [u8; 8], efi_image_offset: u16le, pci_header_offset: u16le = $struct_size, expansion_header_offset: u16le, } +#[repr(u8)] +enum FuOpromIndicatorFlags { + None, + Last = 0x80, +} + #[derive(New, ParseStream, Default)] #[repr(C, packed)] struct FuStructOpromPci { @@ -29,7 +50,7 @@ image_length: u16le, // of 512 bytes image_revision: u16le, code_type: u8, - indicator: u8, + indicator: FuOpromIndicatorFlags, max_runtime_image_length: u16le, conf_util_code_header_pointer: u16le, dmtf_clp_entry_point_pointer: u16le, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-output-stream.c fwupd-2.0.20/libfwupdplugin/fu-output-stream.c --- fwupd-2.0.8/libfwupdplugin/fu-output-stream.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-output-stream.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,100 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#define G_LOG_DOMAIN "FuOutputStream" + +#include "config.h" + +#include "fu-bytes.h" +#include "fu-output-stream.h" + +/** + * fu_output_stream_from_path: + * @path: a filename + * @error: (nullable): optional return location for an error + * + * Opens the file as an output stream. + * + * Returns: (transfer full): a #GOutputStream, or %NULL on error + * + * Since: 2.0.12 + **/ +GOutputStream * +fu_output_stream_from_path(const gchar *path, GError **error) +{ + g_autoptr(GFile) file = NULL; + g_autoptr(GFileOutputStream) stream = NULL; + + g_return_val_if_fail(path != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + file = g_file_new_for_path(path); + stream = g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error); + if (stream == NULL) { + fwupd_error_convert(error); + return NULL; + } + return G_OUTPUT_STREAM(g_steal_pointer(&stream)); +} + +/** + * fu_output_stream_write_bytes: + * @stream: a #GOutputStream + * @bytes: a #GBytes + * @progress: (nullable): optional #FuProgress + * @error: (nullable): optional return location for an error + * + * Write bytes into the stream, retrying as required. Will block during the operation. + * + * Returns: %TRUE for success + * + * Since: 2.0.12 + **/ +gboolean +fu_output_stream_write_bytes(GOutputStream *stream, + GBytes *bytes, + FuProgress *progress, + GError **error) +{ + gsize bufsz; + gsize total_written = 0; + + g_return_val_if_fail(G_IS_OUTPUT_STREAM(stream), FALSE); + g_return_val_if_fail(bytes != NULL, FALSE); + g_return_val_if_fail(progress == NULL || FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + bufsz = g_bytes_get_size(bytes); + do { + gssize wrote; + g_autoptr(GBytes) fw_data = NULL; + + fw_data = fu_bytes_new_offset(bytes, total_written, bufsz - total_written, error); + if (fw_data == NULL) + return FALSE; + wrote = g_output_stream_write_bytes(stream, fw_data, NULL, error); + if (wrote < 0) { + fwupd_error_convert(error); + return FALSE; + } + total_written += wrote; + + if (progress != NULL) + fu_progress_set_percentage_full(progress, total_written, bufsz); + } while (total_written < bufsz); + if (total_written != bufsz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "only wrote 0x%x of 0x%x", + (guint)total_written, + (guint)bufsz); + return FALSE; + } + + /* success */ + return TRUE; +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-output-stream.h fwupd-2.0.20/libfwupdplugin/fu-output-stream.h --- fwupd-2.0.8/libfwupdplugin/fu-output-stream.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-output-stream.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#include "fu-progress.h" + +GOutputStream * +fu_output_stream_from_path(const gchar *path, GError **error) G_GNUC_WARN_UNUSED_RESULT + G_GNUC_NON_NULL(1); +gboolean +fu_output_stream_write_bytes(GOutputStream *stream, + GBytes *bytes, + FuProgress *progress, + GError **error) G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-partial-input-stream.c fwupd-2.0.20/libfwupdplugin/fu-partial-input-stream.c --- fwupd-2.0.8/libfwupdplugin/fu-partial-input-stream.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-partial-input-stream.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,8 +9,8 @@ #include "config.h" #include "fwupd-codec.h" -#include "fwupd-common-private.h" +#include "fu-common.h" #include "fu-input-stream.h" #include "fu-partial-input-stream-private.h" @@ -169,7 +169,7 @@ /* sanity check */ if (!fu_input_stream_size(stream, &base_sz, error)) { - g_prefix_error(error, "failed to get size: "); + g_prefix_error_literal(error, "failed to get size: "); return NULL; } if (size == G_MAXSIZE) { @@ -185,7 +185,7 @@ } self->size = base_sz - offset; } else { - if (offset + size > base_sz) { + if (fu_size_checked_add(offset, size) > base_sz) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-path.c fwupd-2.0.20/libfwupdplugin/fu-path.c --- fwupd-2.0.8/libfwupdplugin/fu-path.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-path.c 2026-02-26 11:36:18.000000000 +0000 @@ -83,8 +83,10 @@ /* try to open */ dir = g_dir_open(directory, 0, error); - if (dir == NULL) + if (dir == NULL) { + fwupd_error_convert(error); return FALSE; + } /* find each */ while ((filename = g_dir_read_name(dir))) { @@ -152,7 +154,7 @@ FWUPD_ERROR_INTERNAL, "Failed to create '%s': %s", dirname, - g_strerror(errno)); + fwupd_strerror(errno)); return FALSE; } return TRUE; @@ -280,55 +282,48 @@ tmp = g_getenv("FWUPD_SYSFSFWDIR"); if (tmp != NULL) return g_strdup(tmp); - basedir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR); - return g_build_filename(basedir, "firmware", NULL); + return fu_path_build(FU_PATH_KIND_SYSFSDIR, "firmware", NULL); /* /sys/class/tpm */ case FU_PATH_KIND_SYSFSDIR_TPM: tmp = g_getenv("FWUPD_SYSFSTPMDIR"); if (tmp != NULL) return g_strdup(tmp); - basedir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR); - return g_build_filename(basedir, "class", "tpm", NULL); + return fu_path_build(FU_PATH_KIND_SYSFSDIR, "class", "tpm", NULL); /* /sys/bus/platform/drivers */ case FU_PATH_KIND_SYSFSDIR_DRIVERS: tmp = g_getenv("FWUPD_SYSFSDRIVERDIR"); if (tmp != NULL) return g_strdup(tmp); - basedir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR); - return g_build_filename(basedir, "bus", "platform", "drivers", NULL); + return fu_path_build(FU_PATH_KIND_SYSFSDIR, "bus", "platform", "drivers", NULL); /* /sys/kernel/security */ case FU_PATH_KIND_SYSFSDIR_SECURITY: tmp = g_getenv("FWUPD_SYSFSSECURITYDIR"); if (tmp != NULL) return g_strdup(tmp); - basedir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR); - return g_build_filename(basedir, "kernel", "security", NULL); + return fu_path_build(FU_PATH_KIND_SYSFSDIR, "kernel", "security", NULL); /* /sys/class/dmi/id */ case FU_PATH_KIND_SYSFSDIR_DMI: tmp = g_getenv("FWUPD_SYSFSDMIDIR"); if (tmp != NULL) return g_strdup(tmp); - basedir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR); - return g_build_filename(basedir, "class", "dmi", "id", NULL); + return fu_path_build(FU_PATH_KIND_SYSFSDIR, "class", "dmi", "id", NULL); /* /sys/firmware/acpi/tables */ case FU_PATH_KIND_ACPI_TABLES: tmp = g_getenv("FWUPD_ACPITABLESDIR"); if (tmp != NULL) return g_strdup(tmp); - basedir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR); - return g_build_filename(basedir, "firmware", "acpi", "tables", NULL); + return fu_path_build(FU_PATH_KIND_SYSFSDIR, "firmware", "acpi", "tables", NULL); /* /sys/module/firmware_class/parameters/path */ case FU_PATH_KIND_FIRMWARE_SEARCH: tmp = g_getenv("FWUPD_FIRMWARESEARCH"); if (tmp != NULL) return g_strdup(tmp); - basedir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR); - return g_build_filename(basedir, - "module", - "firmware_class", - "parameters", - "path", - NULL); + return fu_path_build(FU_PATH_KIND_SYSFSDIR, + "module", + "firmware_class", + "parameters", + "path", + NULL); /* /etc */ case FU_PATH_KIND_SYSCONFDIR: tmp = g_getenv("FWUPD_SYSCONFDIR"); @@ -397,8 +392,7 @@ tmp = g_getenv("FWUPD_DATADIR_QUIRKS"); if (tmp != NULL) return g_strdup(tmp); - basedir = fu_path_from_kind(FU_PATH_KIND_DATADIR_PKG); - return g_build_filename(basedir, "quirks.d", NULL); + return fu_path_build(FU_PATH_KIND_DATADIR_PKG, "quirks.d", NULL); /* /usr/libexec/fwupd/efi */ case FU_PATH_KIND_EFIAPPDIR: tmp = g_getenv("FWUPD_EFIAPPDIR"); @@ -417,50 +411,49 @@ tmp = g_getenv("CONFIGURATION_DIRECTORY"); if (tmp != NULL && g_file_test(tmp, G_FILE_TEST_EXISTS)) return g_build_filename(tmp, NULL); - basedir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR); - return g_build_filename(basedir, PACKAGE_NAME, NULL); + return fu_path_build(FU_PATH_KIND_SYSCONFDIR, PACKAGE_NAME, NULL); /* /var/lib/fwupd */ case FU_PATH_KIND_LOCALSTATEDIR_PKG: tmp = g_getenv("STATE_DIRECTORY"); if (tmp != NULL && g_file_test(tmp, G_FILE_TEST_EXISTS)) return g_build_filename(tmp, NULL); - basedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR); - return g_build_filename(basedir, "lib", PACKAGE_NAME, NULL); + return fu_path_build(FU_PATH_KIND_LOCALSTATEDIR, "lib", PACKAGE_NAME, NULL); /* /var/lib/fwupd/quirks.d */ case FU_PATH_KIND_LOCALSTATEDIR_QUIRKS: tmp = g_getenv("FWUPD_LOCALSTATEDIR_QUIRKS"); if (tmp != NULL) return g_build_filename(tmp, NULL); - basedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); - return g_build_filename(basedir, "quirks.d", NULL); + return fu_path_build(FU_PATH_KIND_LOCALSTATEDIR_PKG, "quirks.d", NULL); /* /var/lib/fwupd/metadata */ case FU_PATH_KIND_LOCALSTATEDIR_METADATA: tmp = g_getenv("FWUPD_LOCALSTATEDIR_METADATA"); if (tmp != NULL) return g_build_filename(tmp, NULL); - basedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); - return g_build_filename(basedir, "metadata", NULL); + return fu_path_build(FU_PATH_KIND_LOCALSTATEDIR_PKG, "metadata", NULL); /* /var/lib/fwupd/remotes.d */ case FU_PATH_KIND_LOCALSTATEDIR_REMOTES: tmp = g_getenv("FWUPD_LOCALSTATEDIR_REMOTES"); if (tmp != NULL) return g_build_filename(tmp, NULL); - basedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); - return g_build_filename(basedir, "remotes.d", NULL); + return fu_path_build(FU_PATH_KIND_LOCALSTATEDIR_PKG, "remotes.d", NULL); /* /var/cache/fwupd */ case FU_PATH_KIND_CACHEDIR_PKG: tmp = g_getenv("CACHE_DIRECTORY"); if (tmp != NULL && g_file_test(tmp, G_FILE_TEST_EXISTS)) return g_build_filename(tmp, NULL); - basedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR); - return g_build_filename(basedir, "cache", PACKAGE_NAME, NULL); + return fu_path_build(FU_PATH_KIND_LOCALSTATEDIR, "cache", PACKAGE_NAME, NULL); /* /var/etc/fwupd */ case FU_PATH_KIND_LOCALCONFDIR_PKG: tmp = g_getenv("LOCALCONF_DIRECTORY"); if (tmp != NULL && g_file_test(tmp, G_FILE_TEST_EXISTS)) return g_build_filename(tmp, NULL); - basedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR); - return g_build_filename(basedir, "etc", PACKAGE_NAME, NULL); + return fu_path_build(FU_PATH_KIND_LOCALSTATEDIR, "etc", PACKAGE_NAME, NULL); + /* /run */ + case FU_PATH_KIND_RUNDIR: + tmp = g_getenv("FWUPD_RUNDIR"); + if (tmp != NULL) + return g_strdup(tmp); + return g_strdup("/run"); /* /run/lock */ case FU_PATH_KIND_LOCKDIR: tmp = g_getenv("FWUPD_LOCKDIR"); @@ -474,8 +467,7 @@ tmp = g_getenv("FWUPD_SYSFSFWATTRIBDIR"); if (tmp != NULL) return g_strdup(tmp); - basedir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR); - return g_build_filename(basedir, "class", "firmware-attributes", NULL); + return fu_path_build(FU_PATH_KIND_SYSFSDIR, "class", "firmware-attributes", NULL); case FU_PATH_KIND_POLKIT_ACTIONS: #ifdef POLKIT_ACTIONDIR return g_strdup(POLKIT_ACTIONDIR); @@ -505,20 +497,25 @@ return g_strdup("/dev"); /* /etc/localtime or /var/lib/timezone/localtime */ case FU_PATH_KIND_LOCALTIME: { - g_autofree gchar *sysconfdir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR); - g_autofree gchar *localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR); g_autofree gchar *localtime = NULL; tmp = g_getenv("FWUPD_LOCALTIME"); if (tmp != NULL) return g_strdup(tmp); - basedir = g_build_filename(localstatedir, "lib", "timezone", "localtime", NULL); + basedir = + fu_path_build(FU_PATH_KIND_LOCALSTATEDIR, "lib", "timezone", "localtime", NULL); if (g_file_test(basedir, G_FILE_TEST_EXISTS)) return g_steal_pointer(&basedir); - localtime = g_build_filename(sysconfdir, "localtime", NULL); + localtime = fu_path_build(FU_PATH_KIND_SYSCONFDIR, "localtime", NULL); if (g_file_test(localtime, G_FILE_TEST_EXISTS)) return g_steal_pointer(&localtime); return g_strdup("/etc/localtime"); } + /* /sys/kernel/debug */ + case FU_PATH_KIND_DEBUGFSDIR: + tmp = g_getenv("FWUPD_DEBUGFSDIR"); + if (tmp != NULL) + return g_strdup(tmp); + return g_strdup("/sys/kernel/debug"); /* this shouldn't happen */ default: g_warning("cannot build path for unknown kind %u", path_kind); @@ -527,6 +524,36 @@ return NULL; } +/** + * fu_path_build: + * @path_kind: a #FuPathKind e.g. %FU_PATH_KIND_DATADIR_PKG + * @...: pairs of string key values, ending with %NULL + * + * Gets a fwupd-specific system path. These can be overridden with various + * environment variables, for instance %FWUPD_DATADIR. + * + * Returns: a system path, or %NULL if invalid + * + * Since: 2.0.18 + **/ +gchar * +fu_path_build(FuPathKind path_kind, ...) +{ + va_list args; + gchar *path; + g_autofree gchar *path_initial = NULL; + + path_initial = fu_path_from_kind(path_kind); + if (path_initial == NULL) + return NULL; + + va_start(args, path_kind); + path = g_build_filename_valist(path_initial, &args); + va_end(args); + + return path; +} + static gint fu_path_glob_sort_cb(gconstpointer a, gconstpointer b) { @@ -601,7 +628,7 @@ FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "cannot resolve path: %s", - g_strerror(errno)); + fwupd_strerror(errno)); return NULL; } #else @@ -610,7 +637,7 @@ FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "cannot resolve path: %s", - g_strerror(errno)); + fwupd_strerror(errno)); return NULL; } #endif @@ -650,7 +677,7 @@ NULL, error); if (info == NULL) { - fu_error_convert(error); + fwupd_error_convert(error); return NULL; } target = diff -Nru fwupd-2.0.8/libfwupdplugin/fu-path.h fwupd-2.0.20/libfwupdplugin/fu-path.h --- fwupd-2.0.8/libfwupdplugin/fu-path.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-path.h 2026-02-26 11:36:18.000000000 +0000 @@ -30,6 +30,7 @@ * /usr/share/polkit-1/actions/) * @FU_PATH_KIND_SYSFSDIR_SECURITY: The sysfs security location (IE /sys/kernel/security) * @FU_PATH_KIND_ACPI_TABLES: The location of the ACPI tables + * @FU_PATH_KIND_RUNDIR: The runtime directory (IE /run) * @FU_PATH_KIND_LOCKDIR: The lock directory (IE /run/lock) * @FU_PATH_KIND_SYSFSDIR_FW_ATTRIB The firmware attributes directory (IE * /sys/class/firmware-attributes) @@ -52,6 +53,7 @@ * @FU_PATH_KIND_LIBEXECDIR: The directory to launch executables * @FU_PATH_KIND_LIBEXECDIR_PKG The directory launch executables packaged with daemon * @FU_PATH_KIND_DATADIR_VENDOR_IDS: The vendor ID store (IE /usr/share/hwdata) + * @FU_PATH_KIND_DEBUGFSDIR: The debugfs directory (IE /sys/kernel/debug) * * Path types to use when dynamically determining a path at runtime **/ @@ -72,6 +74,7 @@ FU_PATH_KIND_POLKIT_ACTIONS, FU_PATH_KIND_SYSFSDIR_SECURITY, FU_PATH_KIND_ACPI_TABLES, + FU_PATH_KIND_RUNDIR, FU_PATH_KIND_LOCKDIR, FU_PATH_KIND_SYSFSDIR_FW_ATTRIB, FU_PATH_KIND_FIRMWARE_SEARCH, @@ -89,12 +92,15 @@ FU_PATH_KIND_LIBEXECDIR, FU_PATH_KIND_LIBEXECDIR_PKG, FU_PATH_KIND_DATADIR_VENDOR_IDS, + FU_PATH_KIND_DEBUGFSDIR, /*< private >*/ FU_PATH_KIND_LAST } FuPathKind; gchar * fu_path_from_kind(FuPathKind path_kind); +gchar * +fu_path_build(FuPathKind path_kind, ...) G_GNUC_NULL_TERMINATED; GPtrArray * fu_path_glob(const gchar *directory, const gchar *pattern, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-pci-device.c fwupd-2.0.20/libfwupdplugin/fu-pci-device.c --- fwupd-2.0.8/libfwupdplugin/fu-pci-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-pci-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -31,6 +31,13 @@ #define GET_PRIVATE(o) (fu_pci_device_get_instance_private(o)) +enum { + QUARK_ADD_INSTANCE_ID_REV, + QUARK_LAST, +}; + +static guint quarks[QUARK_LAST] = {0}; + static void fu_pci_device_to_string(FuDevice *device, guint idt, GString *str) { @@ -170,14 +177,14 @@ } static void -fu_pci_device_to_incorporate(FuDevice *self, FuDevice *donor) +fu_pci_device_to_incorporate(FuDevice *device, FuDevice *donor) { - FuPciDevice *uself = FU_PCI_DEVICE(self); + FuPciDevice *uself = FU_PCI_DEVICE(device); FuPciDevice *udonor = FU_PCI_DEVICE(donor); FuPciDevicePrivate *priv = GET_PRIVATE(uself); FuPciDevicePrivate *priv_donor = GET_PRIVATE(udonor); - g_return_if_fail(FU_IS_PCI_DEVICE(self)); + g_return_if_fail(FU_IS_PCI_DEVICE(device)); g_return_if_fail(FU_IS_PCI_DEVICE(donor)); if (priv->class == 0x0) @@ -266,10 +273,9 @@ fu_device_set_version(device, version); } } - if (fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV) && + if (fu_device_has_private_flag_quark(device, quarks[QUARK_ADD_INSTANCE_ID_REV]) && priv->revision != 0xFF) { - if (fu_device_has_private_flag(device, - FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV)) { + if (fu_device_has_private_flag_quark(device, quarks[QUARK_ADD_INSTANCE_ID_REV])) { fu_device_build_instance_id_full(device, FU_DEVICE_INSTANCE_FLAG_GENERIC | FU_DEVICE_INSTANCE_FLAG_VISIBLE | @@ -325,8 +331,7 @@ "DEV", "SUBSYS", NULL); - if (fu_device_has_private_flag(device, - FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV)) { + if (fu_device_has_private_flag_quark(device, quarks[QUARK_ADD_INSTANCE_ID_REV])) { fu_device_build_instance_id_full(device, FU_DEVICE_INSTANCE_FLAG_GENERIC | FU_DEVICE_INSTANCE_FLAG_VISIBLE | @@ -349,6 +354,7 @@ fu_device_set_physical_id(device, physical_id); /* success */ + fu_pci_device_ensure_subsys_instance_id(self); return TRUE; } @@ -586,6 +592,8 @@ fu_pci_device_class_init(FuPciDeviceClass *klass) { FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + quarks[QUARK_ADD_INSTANCE_ID_REV] = + g_quark_from_static_string(FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV); device_class->to_string = fu_pci_device_to_string; device_class->probe = fu_pci_device_probe; device_class->probe_complete = fu_pci_device_probe_complete; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-pefile-firmware.c fwupd-2.0.20/libfwupdplugin/fu-pefile-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-pefile-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-pefile-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,7 @@ #include "fu-coswid-firmware.h" #include "fu-csv-firmware.h" #include "fu-input-stream.h" -#include "fu-mem.h" +#include "fu-linear-firmware.h" #include "fu-partial-input-stream.h" #include "fu-pefile-firmware.h" #include "fu-pefile-struct.h" @@ -96,23 +96,23 @@ } static gboolean -fu_pefile_firmware_parse_section(FuFirmware *firmware, +fu_pefile_firmware_parse_section(FuPefileFirmware *self, GInputStream *stream, guint idx, gsize hdr_offset, gsize strtab_offset, GPtrArray *regions, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autofree gchar *sect_id = NULL; g_autofree gchar *sect_id_tmp = NULL; g_autoptr(FuFirmware) img = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructPeCoffSection) st = NULL; st = fu_struct_pe_coff_section_parse_stream(stream, hdr_offset, error); if (st == NULL) { - g_prefix_error(error, "failed to read section: "); + g_prefix_error_literal(error, "failed to read section: "); return FALSE; } sect_id_tmp = fu_struct_pe_coff_section_get_name(st); @@ -153,7 +153,7 @@ /* create new firmware */ if (g_strcmp0(sect_id, ".sbom") == 0) { - img = fu_coswid_firmware_new(); + img = fu_linear_firmware_new(FU_TYPE_COSWID_FIRMWARE); } else if (g_strcmp0(sect_id, ".sbat") == 0 || g_strcmp0(sect_id, ".sbata") == 0 || g_strcmp0(sect_id, ".sbatl") == 0) { img = fu_csv_firmware_new(); @@ -173,18 +173,24 @@ fu_firmware_set_idx(img, idx); /* add data */ - if (fu_struct_pe_coff_section_get_size_of_raw_data(st) > 0) { + if (fu_struct_pe_coff_section_get_virtual_size(st) > 0) { guint32 sect_offset = fu_struct_pe_coff_section_get_pointer_to_raw_data(st); + guint32 sect_size = fu_struct_pe_coff_section_get_virtual_size(st); g_autoptr(GInputStream) img_stream = NULL; + /* use the raw data size if the section is compressed */ + if (fu_struct_pe_coff_section_get_virtual_size(st) > + fu_struct_pe_coff_section_get_size_of_raw_data(st)) { + g_debug("virtual size 0x%x bigger than raw data, truncating to 0x%x", + sect_size, + fu_struct_pe_coff_section_get_size_of_raw_data(st)); + sect_size = fu_struct_pe_coff_section_get_size_of_raw_data(st); + } + fu_firmware_set_offset(img, sect_offset); - img_stream = - fu_partial_input_stream_new(stream, - sect_offset, - fu_struct_pe_coff_section_get_size_of_raw_data(st), - error); + img_stream = fu_partial_input_stream_new(stream, sect_offset, sect_size, error); if (img_stream == NULL) { - g_prefix_error(error, "failed to cut raw PE data: "); + g_prefix_error_literal(error, "failed to cut raw PE data: "); return FALSE; } if (!fu_firmware_parse_stream(img, img_stream, 0x0, flags, error)) { @@ -200,13 +206,13 @@ } /* success */ - return fu_firmware_add_image_full(firmware, img, error); + return fu_firmware_add_image(FU_FIRMWARE(self), img, error); } static gboolean fu_pefile_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuPefileFirmware *self = FU_PEFILE_FIRMWARE(firmware); @@ -228,16 +234,16 @@ /* parse the DOS header to get the COFF header */ st_doshdr = fu_struct_pe_dos_header_parse_stream(stream, offset, error); if (st_doshdr == NULL) { - g_prefix_error(error, "failed to read DOS header: "); + g_prefix_error_literal(error, "failed to read DOS header: "); return FALSE; } offset += fu_struct_pe_dos_header_get_lfanew(st_doshdr); st_coff = fu_struct_pe_coff_file_header_parse_stream(stream, offset, error); if (st_coff == NULL) { - g_prefix_error(error, "failed to read COFF header: "); + g_prefix_error_literal(error, "failed to read COFF header: "); return FALSE; } - offset += st_coff->len; + offset += st_coff->buf->len; regions = g_ptr_array_new_with_free_func((GDestroyNotify)fu_pefile_firmware_region_free); @@ -263,14 +269,14 @@ "chksum->cert-table", offset + FU_STRUCT_PE_COFF_OPTIONAL_HEADER64_OFFSET_SUBSYSTEM, FU_STRUCT_PE_COFF_OPTIONAL_HEADER64_OFFSET_CERTIFICATE_TABLE - - FU_STRUCT_PE_COFF_OPTIONAL_HEADER64_OFFSET_SUBSYSTEM); // end + FU_STRUCT_PE_COFF_OPTIONAL_HEADER64_OFFSET_SUBSYSTEM); /* end */ /* verify optional extra header */ if (fu_struct_pe_coff_file_header_get_size_of_optional_header(st_coff) > 0) { g_autoptr(FuStructPeCoffOptionalHeader64) st_opt = fu_struct_pe_coff_optional_header64_parse_stream(stream, offset, error); if (st_opt == NULL) { - g_prefix_error(error, "failed to read optional header: "); + g_prefix_error_literal(error, "failed to read optional header: "); return FALSE; } @@ -306,7 +312,7 @@ /* read out each section */ for (guint idx = 0; idx < nr_sections; idx++) { - if (!fu_pefile_firmware_parse_section(firmware, + if (!fu_pefile_firmware_parse_section(self, stream, idx, offset, @@ -340,14 +346,14 @@ if (r->size == 0) continue; - g_debug("Authenticode region %s: 0x%04x -> 0x%04x [0x%04x]", + g_debug("authenticode region %s: 0x%04x -> 0x%04x [0x%04x]", r->name, (guint)r->offset, (guint)(r->offset + r->size), (guint)r->size); partial_stream = fu_partial_input_stream_new(stream, r->offset, r->size, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut Authenticode region: "); + g_prefix_error_literal(error, "failed to cut Authenticode region: "); return FALSE; } fu_composite_input_stream_add_partial_stream( @@ -379,29 +385,36 @@ g_free(section); } +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuPefileSection, fu_pefile_firmware_section_free) + static GByteArray * fu_pefile_firmware_write(FuFirmware *firmware, GError **error) { gsize offset = 0; g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); - g_autoptr(GByteArray) st = fu_struct_pe_dos_header_new(); - g_autoptr(GByteArray) st_hdr = fu_struct_pe_coff_file_header_new(); - g_autoptr(GByteArray) st_opt = fu_struct_pe_coff_optional_header64_new(); + g_autoptr(FuStructPeDosHeader) st = fu_struct_pe_dos_header_new(); + g_autoptr(FuStructPeCoffFileHeader) st_hdr = fu_struct_pe_coff_file_header_new(); + g_autoptr(FuStructPeCoffOptionalHeader64) st_opt = + fu_struct_pe_coff_optional_header64_new(); g_autoptr(GByteArray) strtab = g_byte_array_new(); g_autoptr(GPtrArray) sections = g_ptr_array_new_with_free_func((GDestroyNotify)fu_pefile_firmware_section_free); /* calculate the offset for each of the sections */ - offset += st->len + st_hdr->len + st_opt->len; + offset += st->buf->len + st_hdr->buf->len + st_opt->buf->len; offset += FU_STRUCT_PE_COFF_SECTION_SIZE * imgs->len; for (guint i = 0; i < imgs->len; i++) { - g_autofree FuPefileSection *section = g_new0(FuPefileSection, 1); + g_autoptr(FuPefileSection) section = g_new0(FuPefileSection, 1); FuFirmware *img = g_ptr_array_index(imgs, i); section->offset = offset; section->blob = fu_firmware_write(img, error); if (section->blob == NULL) return NULL; + if (g_bytes_get_size(section->blob) == 0) { + g_debug("skipping zero length section %u", i); + continue; + } section->id = g_strdup(fu_firmware_get_id(img)); section->blobsz_aligned = fu_common_align_up(g_bytes_get_size(section->blob), 4); offset += section->blobsz_aligned; @@ -412,16 +425,16 @@ fu_struct_pe_coff_optional_header64_set_number_of_rva_and_sizes(st_opt, 7); /* COFF file header */ - fu_struct_pe_coff_file_header_set_size_of_optional_header(st_hdr, st_opt->len); + fu_struct_pe_coff_file_header_set_size_of_optional_header(st_hdr, st_opt->buf->len); fu_struct_pe_coff_file_header_set_number_of_sections(st_hdr, sections->len); fu_struct_pe_coff_file_header_set_pointer_to_symbol_table(st_hdr, offset); - g_byte_array_append(st, st_hdr->data, st_hdr->len); - g_byte_array_append(st, st_opt->data, st_opt->len); + fu_byte_array_append_array(st->buf, st_hdr->buf); + fu_byte_array_append_array(st->buf, st_opt->buf); /* add sections */ for (guint i = 0; i < sections->len; i++) { FuPefileSection *section = g_ptr_array_index(sections, i); - g_autoptr(GByteArray) st_sect = fu_struct_pe_coff_section_new(); + g_autoptr(FuStructPeCoffSection) st_sect = fu_struct_pe_coff_section_new(); fu_struct_pe_coff_section_set_size_of_raw_data(st_sect, g_bytes_get_size(section->blob)); @@ -463,7 +476,7 @@ fu_byte_array_set_size(strtab_buf, FU_PEFILE_SECTION_ID_STRTAB_SIZE, 0x0); g_byte_array_append(strtab, strtab_buf->data, strtab_buf->len); } - g_byte_array_append(st, st_sect->data, st_sect->len); + fu_byte_array_append_array(st->buf, st_sect->buf); } /* add the section data itself */ @@ -471,14 +484,14 @@ FuPefileSection *section = g_ptr_array_index(sections, i); g_autoptr(GBytes) blob_aligned = fu_bytes_pad(section->blob, section->blobsz_aligned, 0xFF); - fu_byte_array_append_bytes(st, blob_aligned); + fu_byte_array_append_bytes(st->buf, blob_aligned); } /* string table comes last */ - g_byte_array_append(st, strtab->data, strtab->len); + g_byte_array_append(st->buf, strtab->data, strtab->len); /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static gchar * @@ -506,6 +519,8 @@ static void fu_pefile_firmware_init(FuPefileFirmware *self) { + g_type_ensure(FU_TYPE_CSV_FIRMWARE); + g_type_ensure(FU_TYPE_SBATLEVEL_SECTION); fu_firmware_set_images_max(FU_FIRMWARE(self), 100); } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-pkcs7.c fwupd-2.0.20/libfwupdplugin/fu-pkcs7.c --- fwupd-2.0.8/libfwupdplugin/fu-pkcs7.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-pkcs7.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,156 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#ifdef HAVE_GNUTLS +#include +#include +#include +#endif + +#include "fu-input-stream.h" +#include "fu-pkcs7.h" +#include "fu-x509-certificate.h" + +#ifdef HAVE_GNUTLS +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_pkcs7_t, gnutls_pkcs7_deinit, NULL) +#pragma clang diagnostic pop +#endif + +/** + * FuPkcs7: + * + * A PKCS#7 object, typically containing signed X.509 certificates. + * + * See also: [class@FuFirmware] + */ + +struct _FuPkcs7 { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE(FuPkcs7, fu_pkcs7, FU_TYPE_FIRMWARE) + +#ifdef HAVE_GNUTLS +static gboolean +fu_pkcs7_parse_x509_certificate(FuPkcs7 *self, gnutls_datum_t *data, GError **error) +{ + g_autoptr(FuX509Certificate) crt = fu_x509_certificate_new(); + g_autoptr(GBytes) blob = NULL; + + /* parse as a X.509 certificate */ + blob = g_bytes_new(data->data, data->size); + if (!fu_firmware_parse_bytes(FU_FIRMWARE(crt), + blob, + 0x0, + FU_FIRMWARE_PARSE_FLAG_NONE, + error)) + return FALSE; + if (!fu_firmware_add_image(FU_FIRMWARE(self), FU_FIRMWARE(crt), error)) + return FALSE; + + /* success */ + return TRUE; +} +#endif + +static gboolean +fu_pkcs7_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ +#ifdef HAVE_GNUTLS + FuPkcs7 *self = FU_PKCS7(firmware); + gnutls_datum_t datum = {0}; + int rc; + g_auto(gnutls_pkcs7_t) pkcs7 = NULL; + g_autoptr(GByteArray) buf = NULL; + + /* load PKCS#7 cert */ + buf = fu_input_stream_read_byte_array(stream, 0x0, G_MAXSIZE, NULL, error); + if (buf == NULL) + return FALSE; + rc = gnutls_pkcs7_init(&pkcs7); + if (rc != GNUTLS_E_SUCCESS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to init pkcs7: %s [%i]", + gnutls_strerror(rc), + rc); + return FALSE; + } + datum.data = buf->data; + datum.size = buf->len; + rc = gnutls_pkcs7_import(pkcs7, &datum, GNUTLS_X509_FMT_DER); + if (rc != GNUTLS_E_SUCCESS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to import the PKCS7 signature: %s [%i]", + gnutls_strerror(rc), + rc); + return FALSE; + } + + /* parse each X.509 certificate */ + for (int i = 0; i < gnutls_pkcs7_get_crt_count(pkcs7); i++) { + gnutls_datum_t out; + rc = gnutls_pkcs7_get_crt_raw2(pkcs7, i, &out); + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to get raw crt: %s [%i]", + gnutls_strerror(rc), + rc); + return FALSE; + } + if (!fu_pkcs7_parse_x509_certificate(self, &out, error)) { + gnutls_free(out.data); + return FALSE; + } + gnutls_free(out.data); + } + + /* success */ + return TRUE; +#else + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no GnuTLS support"); + return FALSE; +#endif +} + +static void +fu_pkcs7_init(FuPkcs7 *self) +{ +} + +static void +fu_pkcs7_class_init(FuPkcs7Class *klass) +{ + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + firmware_class->parse = fu_pkcs7_parse; +} + +/** + * fu_pkcs7_new: + * + * Creates a new #FuPkcs7. + * + * Returns: (transfer full): object + * + * Since: 2.0.9 + **/ +FuPkcs7 * +fu_pkcs7_new(void) +{ + return g_object_new(FU_TYPE_PKCS7, NULL); +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-pkcs7.h fwupd-2.0.20/libfwupdplugin/fu-pkcs7.h --- fwupd-2.0.8/libfwupdplugin/fu-pkcs7.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-pkcs7.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,15 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_PKCS7 (fu_pkcs7_get_type()) +G_DECLARE_FINAL_TYPE(FuPkcs7, fu_pkcs7, FU, PKCS7, FuFirmware) + +FuPkcs7 * +fu_pkcs7_new(void); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-plugin-private.h fwupd-2.0.20/libfwupdplugin/fu-plugin-private.h --- fwupd-2.0.8/libfwupdplugin/fu-plugin-private.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-plugin-private.h 2026-02-26 11:36:18.000000000 +0000 @@ -76,6 +76,14 @@ GPtrArray *devices, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); gboolean +fu_plugin_runner_composite_peek_firmware(FuPlugin *self, + FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT + G_GNUC_NON_NULL(1, 2, 3, 4); +gboolean fu_plugin_runner_attach(FuPlugin *self, FuDevice *device, FuProgress *progress, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2, 3); gboolean diff -Nru fwupd-2.0.8/libfwupdplugin/fu-plugin.c fwupd-2.0.20/libfwupdplugin/fu-plugin.c --- fwupd-2.0.8/libfwupdplugin/fu-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -728,7 +728,7 @@ locker = fu_device_locker_new(proxy, error); if (locker == NULL) { - g_prefix_error(error, "failed to open device: "); + g_prefix_error_literal(error, "failed to open device: "); return FALSE; } @@ -737,7 +737,6 @@ g_autoptr(GBytes) fw_old = NULL; g_autofree gchar *path = NULL; g_autofree gchar *fn = NULL; - g_autofree gchar *localstatedir = NULL; /* progress */ fu_progress_set_id(progress, G_STRLOC); @@ -747,13 +746,12 @@ fw_old = fu_device_dump_firmware(device, fu_progress_get_child(progress), error); if (fw_old == NULL) { - g_prefix_error(error, "failed to backup old firmware: "); + g_prefix_error_literal(error, "failed to backup old firmware: "); return FALSE; } - localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); fn = g_strdup_printf("%s.bin", fu_device_get_version(device)); - path = g_build_filename( - localstatedir, + path = fu_path_build( + FU_PATH_KIND_LOCALSTATEDIR_PKG, "backup", fu_device_get_id(device), fu_device_get_serial(device) != NULL ? fu_device_get_serial(device) : "default", @@ -808,12 +806,12 @@ return FALSE; if (!fu_device_detach_full(device, progress, error)) return FALSE; - firmware = fu_device_read_firmware(device, progress, error); + firmware = fu_device_read_firmware(device, progress, FU_FIRMWARE_PARSE_FLAG_NONE, error); if (firmware == NULL) { g_autoptr(GError) error_local = NULL; if (!fu_device_attach_full(device, progress, &error_local)) g_debug("ignoring attach failure: %s", error_local->message); - g_prefix_error(error, "failed to read firmware: "); + g_prefix_error_literal(error, "failed to read firmware: "); return FALSE; } fw = fu_firmware_write(firmware, error); @@ -821,7 +819,7 @@ g_autoptr(GError) error_local = NULL; if (!fu_device_attach_full(device, progress, &error_local)) g_debug("ignoring attach failure: %s", error_local->message); - g_prefix_error(error, "failed to write firmware: "); + g_prefix_error_literal(error, "failed to write firmware: "); return FALSE; } for (guint i = 0; checksum_types[i] != 0; i++) { @@ -872,6 +870,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + /* nocheck:error-false-return */ } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), @@ -921,6 +920,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + /* nocheck:error-false-return */ } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), @@ -991,6 +991,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + /* nocheck:error-false-return */ } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), @@ -1029,6 +1030,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + /* nocheck:error-false-return */ } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), @@ -1068,6 +1070,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + /* nocheck:error-false-return */ } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), @@ -1105,6 +1108,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + /* nocheck:error-false-return */ } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), @@ -1138,7 +1142,8 @@ g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); /* progress */ - fu_progress_set_name(progress, fu_plugin_get_name(self)); + if (fu_plugin_get_name(self) != NULL) + fu_progress_set_name(progress, fu_plugin_get_name(self)); /* not enabled */ if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) @@ -1159,6 +1164,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + /* nocheck:error-false-return */ } /* coldplug failed, but we might have already added devices to the daemon... */ if (priv->devices != NULL) { @@ -1743,7 +1749,7 @@ } /* open */ - proxy = fu_device_get_proxy(dev); + proxy = fu_device_get_proxy_internal(dev); if (proxy != NULL) { g_autoptr(FuDeviceLocker) locker_proxy = NULL; locker_proxy = fu_device_locker_new(proxy, error); @@ -1816,6 +1822,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + /* nocheck:error-false-return */ } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), @@ -1864,6 +1871,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + /* nocheck:error-false-return */ } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), @@ -2050,6 +2058,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + /* nocheck:error-false-return */ } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), @@ -2258,6 +2267,66 @@ } /** + * fu_plugin_runner_composite_peek_firmware: + * @self: a #FuPlugin + * @device: a device + * @firmware: a #FuFirmware + * @progress: a #FuProgress + * @flags: install flags + * @error: (nullable): optional return location for an error + * + * Notify when the firmware has been parsed and the update is ready to be deployed + * + * Returns: #TRUE for success, #FALSE for failure + * + * Since: 2.0.19 + **/ +gboolean +fu_plugin_runner_composite_peek_firmware(FuPlugin *self, + FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuPluginVfuncs *vfuncs = fu_plugin_get_vfuncs(self); + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_PLUGIN(self), FALSE); + g_return_val_if_fail(FU_IS_DEVICE(device), FALSE); + g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE); + g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* not enabled */ + if (fu_plugin_has_flag(self, FWUPD_PLUGIN_FLAG_DISABLED)) { + g_debug("plugin not enabled, skipping"); + return TRUE; + } + + /* optional */ + if (vfuncs->composite_peek_firmware == NULL) + return TRUE; + if (!vfuncs + ->composite_peek_firmware(self, device, firmware, progress, flags, &error_local)) { + if (error_local == NULL) { + g_critical("unset plugin error in update(%s)", fu_plugin_get_name(self)); + g_set_error_literal(&error_local, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified error"); + return FALSE; + } + fu_device_set_update_error(device, error_local->message); + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + /* success */ + return TRUE; +} + +/** * fu_plugin_runner_clear_results: * @self: a #FuPlugin * @device: a device @@ -2295,6 +2364,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + /* nocheck:error-false-return */ } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), @@ -2345,6 +2415,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified error"); + /* nocheck:error-false-return */ } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), diff -Nru fwupd-2.0.8/libfwupdplugin/fu-plugin.h fwupd-2.0.20/libfwupdplugin/fu-plugin.h --- fwupd-2.0.8/libfwupdplugin/fu-plugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,6 @@ #include "fu-context.h" #include "fu-device-locker.h" #include "fu-device.h" -#include "fu-plugin.h" #include "fu-quirks.h" #include "fu-security-attrs.h" #include "fu-version-common.h" @@ -42,6 +41,7 @@ FU_PLUGIN_VERIFY_FLAG_LAST } FuPluginVerifyFlags; +/* nocheck:lines */ struct _FuPluginClass { FwupdPluginClass parent_class; /* signals */ @@ -429,6 +429,26 @@ const gchar *key, const gchar *value, GError **error); + /** + * composite_peek_firmware: + * @self: a #FuPlugin + * @dev: a device + * @firmware: a #FuFirmware + * @progress: a #FuProgress + * @flags: install flags + * @error: (nullable): optional return location for an error + * + * Notify each plugin when the firmware has been parsed and the update is ready to be + * deployed. A plugin should avoid returning with an error here unless an emergency. + * + * Since: 2.0.19 + **/ + gboolean (*composite_peek_firmware)(FuPlugin *self, + FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error); }; /** diff -Nru fwupd-2.0.8/libfwupdplugin/fu-progress.c fwupd-2.0.20/libfwupdplugin/fu-progress.c --- fwupd-2.0.8/libfwupdplugin/fu-progress.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-progress.c 2026-02-26 11:36:18.000000000 +0000 @@ -30,7 +30,7 @@ * * To get a child object, you should use [method@FuProgress.get_child]. and then * use the result in any sub-process. You should ensure that the child - * is not re-used without calling fu_progress_step_done(). + * is not reused without calling fu_progress_step_done(). * * There are a few nice touches in this module, so that if a module only has * one progress step, the child progress is used for parent updates. @@ -61,11 +61,12 @@ GObject parent_instance; gchar *id; gchar *name; - FuProgressFlag flags; + FuProgressFlags flags; guint percentage; FwupdStatus status; GPtrArray *children; /* of FuProgress */ gboolean profile; + gboolean any_child_has_step_weighting; gdouble duration; /* seconds */ gdouble global_fraction; guint step_weighting; @@ -208,7 +209,7 @@ * Since: 1.7.0 **/ void -fu_progress_add_flag(FuProgress *self, FuProgressFlag flag) +fu_progress_add_flag(FuProgress *self, FuProgressFlags flag) { g_return_if_fail(FU_IS_PROGRESS(self)); self->flags |= flag; @@ -224,7 +225,7 @@ * Since: 1.7.0 **/ void -fu_progress_remove_flag(FuProgress *self, FuProgressFlag flag) +fu_progress_remove_flag(FuProgress *self, FuProgressFlags flag) { g_return_if_fail(FU_IS_PROGRESS(self)); self->flags &= ~flag; @@ -240,7 +241,7 @@ * Since: 1.7.0 **/ gboolean -fu_progress_has_flag(FuProgress *self, FuProgressFlag flag) +fu_progress_has_flag(FuProgress *self, FuProgressFlags flag) { g_return_val_if_fail(FU_IS_PROGRESS(self), FALSE); return (self->flags & flag) > 0; @@ -459,6 +460,7 @@ } /* no more step data */ + self->any_child_has_step_weighting = FALSE; g_ptr_array_set_size(self->children, 0); } @@ -559,19 +561,9 @@ { guint current = 0; guint total = 0; - gboolean any_step_weighting = FALSE; - - /* we set the step weighting manually */ - for (guint i = 0; i < self->children->len; i++) { - FuProgress *child = g_ptr_array_index(self->children, i); - if (child->step_weighting > 0) { - any_step_weighting = TRUE; - break; - } - } /* just use proportional */ - if (!any_step_weighting) + if (!self->any_child_has_step_weighting) return -1; /* work out percentage */ @@ -681,6 +673,10 @@ fu_progress_set_status(child, status); child->step_weighting = value; + /* compute ahead of time */ + if (child->step_weighting > 0) + self->any_child_has_step_weighting = TRUE; + /* adjust global percentage */ if (value > 0) child->global_fraction = self->global_fraction * (gdouble)value / 100.f; @@ -863,8 +859,17 @@ } /* get the active child */ - if (self->children->len > 0) + if (self->children->len > 0) { + if (self->step_now >= self->children->len) { + g_autoptr(GString) str = g_string_new(NULL); + fu_progress_build_parent_chain(self, str, 0); + g_warning("progress done when no children left! [%s]: %s", + self->id, + str->str); + return; + } child = g_ptr_array_index(self->children, self->step_now); + } /* save the duration in the array */ if (self->profile) { diff -Nru fwupd-2.0.8/libfwupdplugin/fu-progress.h fwupd-2.0.20/libfwupdplugin/fu-progress.h --- fwupd-2.0.8/libfwupdplugin/fu-progress.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-progress.h 2026-02-26 11:36:18.000000000 +0000 @@ -25,11 +25,12 @@ void fu_progress_set_name(FuProgress *self, const gchar *name) G_GNUC_NON_NULL(1, 2); void -fu_progress_add_flag(FuProgress *self, FuProgressFlag flag) G_GNUC_NON_NULL(1); +fu_progress_add_flag(FuProgress *self, FuProgressFlags flag) G_GNUC_NON_NULL(1); void -fu_progress_remove_flag(FuProgress *self, FuProgressFlag flag) G_GNUC_NON_NULL(1); +fu_progress_remove_flag(FuProgress *self, FuProgressFlags flag) G_GNUC_NON_NULL(1); gboolean -fu_progress_has_flag(FuProgress *self, FuProgressFlag flag) G_GNUC_NON_NULL(1); +fu_progress_has_flag(FuProgress *self, FuProgressFlags flag) G_GNUC_WARN_UNUSED_RESULT + G_GNUC_NON_NULL(1); FwupdStatus fu_progress_get_status(FuProgress *self) G_GNUC_NON_NULL(1); void diff -Nru fwupd-2.0.8/libfwupdplugin/fu-progress.rs fwupd-2.0.20/libfwupdplugin/fu-progress.rs --- fwupd-2.0.8/libfwupdplugin/fu-progress.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-progress.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,7 +1,7 @@ // Copyright 2023 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later -enum FuProgressFlag { +enum FuProgressFlags { None = 0, // Since: 1.7.0 Guessed = 1 << 0, // Since: 1.7.0 NoProfile = 1 << 1, // Since: 1.7.0 diff -Nru fwupd-2.0.8/libfwupdplugin/fu-quirks.c fwupd-2.0.20/libfwupdplugin/fu-quirks.c --- fwupd-2.0.8/libfwupdplugin/fu-quirks.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-quirks.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,10 +18,7 @@ #include "fwupd-common.h" #include "fwupd-enums-private.h" #include "fwupd-error.h" -#include "fwupd-remote-private.h" -#include "fu-bytes.h" -#include "fu-common.h" #include "fu-path.h" #include "fu-quirks.h" #include "fu-string.h" @@ -385,8 +382,8 @@ if (file == NULL) return FALSE; } else { - g_autofree gchar *cachedirpkg = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); - g_autofree gchar *xmlbfn = g_build_filename(cachedirpkg, "quirks.xmlb", NULL); + g_autofree gchar *xmlbfn = + fu_path_build(FU_PATH_KIND_CACHEDIR_PKG, "quirks.xmlb", NULL); file = g_file_new_for_path(xmlbfn); } if (g_getenv("FWUPD_XMLB_VERBOSE") != NULL) { @@ -422,7 +419,7 @@ XB_QUERY_FLAG_OPTIMIZE, error); if (self->query_kv == NULL) { - g_prefix_error(error, "failed to prepare query: "); + g_prefix_error_literal(error, "failed to prepare query: "); return FALSE; } self->query_vs = xb_query_new_full(self->silo, @@ -430,7 +427,7 @@ XB_QUERY_FLAG_OPTIMIZE, error); if (self->query_vs == NULL) { - g_prefix_error(error, "failed to prepare query: "); + g_prefix_error_literal(error, "failed to prepare query: "); return FALSE; } if (!xb_silo_query_build_index(self->silo, "quirk/device", "id", error)) { @@ -837,7 +834,6 @@ static gboolean fu_quirks_db_load(FuQuirks *self, FuQuirksLoadFlags load_flags, GError **error) { - g_autofree gchar *vendor_ids_dir = fu_path_from_kind(FU_PATH_KIND_DATADIR_VENDOR_IDS); g_autoptr(sqlite3_stmt) stmt_insert = NULL; g_autoptr(sqlite3_stmt) stmt_query = NULL; g_autoptr(GString) fn_mtimes = g_string_new("quirks"); @@ -869,7 +865,8 @@ for (guint i = 0; i < G_N_ELEMENTS(map); i++) { const FuQuirksDbItem *item = &map[i]; guint64 mtime; - g_autofree gchar *fn = g_build_filename(vendor_ids_dir, item->fn, NULL); + g_autofree gchar *fn = + fu_path_build(FU_PATH_KIND_DATADIR_VENDOR_IDS, item->fn, NULL); g_autoptr(GFile) file = g_file_new_for_path(fn); g_autoptr(GFileInfo) info = NULL; @@ -934,7 +931,8 @@ /* populate database */ for (guint i = 0; i < G_N_ELEMENTS(map); i++) { const FuQuirksDbItem *item = &map[i]; - g_autofree gchar *fn = g_build_filename(vendor_ids_dir, item->fn, NULL); + g_autofree gchar *fn = + fu_path_build(FU_PATH_KIND_DATADIR_VENDOR_IDS, item->fn, NULL); g_autoptr(FuQuirksDbHelper) helper = g_new0(FuQuirksDbHelper, 1); g_autoptr(GFile) file = g_file_new_for_path(fn); g_autoptr(GInputStream) stream = NULL; @@ -995,8 +993,7 @@ fu_quirks_load(FuQuirks *self, FuQuirksLoadFlags load_flags, GError **error) { #ifdef HAVE_SQLITE - g_autofree gchar *cachedirpkg = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); - g_autofree gchar *quirksdb = g_build_filename(cachedirpkg, "quirks.db", NULL); + g_autofree gchar *quirksdb = fu_path_build(FU_PATH_KIND_CACHEDIR_PKG, "quirks.db", NULL); #endif g_return_val_if_fail(FU_IS_QUIRKS(self), FALSE); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-quirks.h fwupd-2.0.20/libfwupdplugin/fu-quirks.h --- fwupd-2.0.8/libfwupdplugin/fu-quirks.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-quirks.h 2026-02-26 11:36:18.000000000 +0000 @@ -28,7 +28,7 @@ FU_QUIRKS_LOAD_FLAG_NO_VERIFY = 1 << 2, /*< private >*/ FU_QUIRKS_LOAD_FLAG_LAST -} FuQuirksLoadFlags; +} G_GNUC_FLAG_ENUM FuQuirksLoadFlags; /** * FuQuirksIter: diff -Nru fwupd-2.0.8/libfwupdplugin/fu-rustgen-enum.c.in fwupd-2.0.20/libfwupdplugin/fu-rustgen-enum.c.in --- fwupd-2.0.8/libfwupdplugin/fu-rustgen-enum.c.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-rustgen-enum.c.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,19 +1,19 @@ - {%- set export = obj.export('ToString') %} {%- if export in [Export.PUBLIC, Export.PRIVATE] %} -{{export.value}}const gchar * -{{obj.c_method('ToString')}}({{obj.c_type}} val) -{ -{%- for item in obj.items %} - if (val == {{item.c_define}}) - return "{{item.value}}"; -{%- endfor %} - return NULL; -} +{%- if obj.is_bitfield %} +/** + * {{obj.c_method('ToString')}}: + * @val: value, e.g. %{{obj.items[1].c_define}} + * + * Converts an enumerated value to a string. + * + * Returns: identifier string + * +{%- if obj.since('ToString') %} + * + * Since: {{obj.since('ToString')}} {%- endif %} - -{%- set export = obj.export('ToBitString') %} -{%- if export in [Export.PUBLIC, Export.PRIVATE] %} + **/ {{export.value}}gchar * {{obj.c_method('ToString')}}({{obj.c_type}} val) { @@ -27,17 +27,73 @@ {%- endfor %} return g_strjoinv(",", (gchar **)data); } +{%- else %} +/** + * {{obj.c_method('ToString')}}: + * @val: value{%- if obj.items|length > 1 %}, e.g. %{{obj.items[1].c_define}}{%- endif %} + * + * Converts an enumerated value to a string. + * + * Returns: identifier string +{%- if obj.since('ToString') %} + * + * Since: {{obj.since('ToString')}} +{%- endif %} + **/ +{{export.value}}const gchar * +{{obj.c_method('ToString')}}({{obj.c_type}} val) +{ +{%- for item in obj.items|rejectattr('name', 'equalto', 'Unknown') %} + if (val == {{item.c_define}}) + return "{{item.value}}"; +{%- endfor %} + return NULL; +} +{%- endif %} {%- endif %} {%- set export = obj.export('FromString') %} {%- if export in [Export.PUBLIC, Export.PRIVATE] %} +/** + * {{obj.c_method('FromString')}}: + * @val: (nullable): a string{%- if obj.items|length > 1 %}, e.g. `{{obj.items[1].value}}`{%- endif %} + * + * Converts a string to an enumerated value. + * + * Returns: enumerated value +{%- if obj.since('FromString') %} + * + * Since: {{obj.since('FromString')}} +{%- endif %} + **/ {{export.value}}{{obj.c_type}} {{obj.c_method('FromString')}}(const gchar *val) { +{%- if obj.is_bitfield %} + {{obj.c_type}} value = 0; + g_auto(GStrv) split = NULL; + g_return_val_if_fail(val != NULL, 0); + split = g_strsplit(val, ",", -1); + for (guint i = 0; split[i] != NULL; i++) { {%- for item in obj.items %} + if (g_strcmp0(split[i], "{{item.value}}") == 0) { + value |= {{item.c_define}}; + continue; + } +{%- endfor %} + } + return value; +{%- else %} +{%- for item in obj.items|rejectattr('name', 'equalto', 'Unknown') %} if (g_strcmp0(val, "{{item.value}}") == 0) return {{item.c_define}}; {%- endfor %} +{%- set item_unknown = obj.item('Unknown') %} +{%- if item_unknown %} + return {{item_unknown.c_define}}; +{%- else %} return {{obj.items[0].c_define}}; +{%- endif %} +{%- endif %} } {%- endif %} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-rustgen-enum.h.in fwupd-2.0.20/libfwupdplugin/fu-rustgen-enum.h.in --- fwupd-2.0.8/libfwupdplugin/fu-rustgen-enum.h.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-rustgen-enum.h.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,23 +1,47 @@ +/** + * {{obj.c_type}}: +{%- if obj.comments %} + * +{%- for comment in obj.comments %} + * {{comment}} +{%- endfor %} +{%- endif %} + */ typedef enum { {%- for item in obj.items %} + /** + * {{item.c_define}}: +{%- if item.comments %} + * +{%- for comment in item.comments %} + * {{comment}} +{%- endfor %} +{%- endif %} +{%- if item.since %} + * + * Since: {{item.since}} +{%- endif %} + */ {%- if item.default %} {{item.c_define}} = {{item.default}}, {%- else %} {{item.c_define}}, {%- endif %} {%- endfor %} -} {{obj.c_type}}; +} {% if obj.is_bitfield %}G_GNUC_FLAG_ENUM{%- endif %} {{obj.c_type}}; {%- if not obj.items_any_defaults and not obj.item('Last') %} #define {{obj.c_define_last}} {{obj.items|length}} {%- endif %} {%- if obj.export('ToString') == Export.PUBLIC %} +{%- if obj.is_bitfield %} +gchar *{{obj.c_method('ToString')}}({{obj.c_type}} val) G_GNUC_CONST; +{%- else %} const gchar *{{obj.c_method('ToString')}}({{obj.c_type}} val) G_GNUC_CONST; {%- endif %} -{%- if obj.export('ToBitString') == Export.PUBLIC %} -gchar *{{obj.c_method('ToString')}}({{obj.c_type}} val) G_GNUC_CONST; {%- endif %} + {%- if obj.export('FromString') == Export.PUBLIC %} -{{obj.c_type}} {{obj.c_method('FromString')}}(const gchar *val) G_GNUC_CONST; +{{obj.c_type}} {{obj.c_method('FromString')}}(const gchar *val) G_GNUC_CONST G_GNUC_NON_NULL(1); {%- endif %} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-rustgen-struct.c.in fwupd-2.0.20/libfwupdplugin/fu-rustgen-struct.c.in --- fwupd-2.0.8/libfwupdplugin/fu-rustgen-struct.c.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-rustgen-struct.c.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,45 @@ +/** + * {{obj.c_method('Ref')}}: (skip): + **/ +{{obj.name}} * +{{obj.c_method('Ref')}}({{obj.name}} *st) +{ + g_return_val_if_fail(st != NULL, NULL); + st->refcount++; + return st; +} +/** + * {{obj.c_method('Unref')}}: (skip): + **/ +void +{{obj.c_method('Unref')}}({{obj.name}} *st) +{ + g_return_if_fail(st != NULL); + if (st->refcount == 0) { + g_critical("{{obj.name}} refcount already zero"); + return; + } + if (--st->refcount > 0) + return; + if (st->buf != NULL) + g_byte_array_unref(st->buf); + g_free(st); +} + +{%- set export = obj.export('NewInternal') %} +{%- if export in [Export.PUBLIC, Export.PRIVATE] %} +/** + * {{obj.c_method('NewInternal')}}: (skip): + **/ +{{export.value}}{{obj.name}} * +{{obj.c_method('NewInternal')}}(void) +{ + {{obj.name}} *st = g_new0({{obj.name}}, 1); + st->refcount = 1; + return st; +} +{%- endif %} + /* getters */ {%- for item in obj.items | selectattr('enabled') %} {%- set export = item.export('Getters') %} @@ -10,7 +52,7 @@ {{item.c_getter}}(const {{obj.name}} *st) { g_return_val_if_fail(st != NULL, NULL); - return fu_memstrsafe(st->data, st->len, {{item.offset}}, {{item.size}}, NULL); + return fu_memstrsafe(st->buf->data, st->buf->len, {{item.offset}}, {{item.size}}, NULL); } {%- elif item.struct_obj %} @@ -18,24 +60,26 @@ {{export.value}}{{item.struct_obj.name}} * {{item.c_getter}}(const {{obj.name}} *st, guint idx) { - g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr({{item.struct_obj.name}}) st_tmp = {{item.struct_obj.c_method('NewInternal')}}(); g_return_val_if_fail(st != NULL, NULL); g_return_val_if_fail(idx < {{item.n_elements}}, NULL); - g_byte_array_append(buf, - st->data + st_tmp->buf = g_byte_array_new(); + g_byte_array_append(st_tmp->buf, + st->buf->data + {{item.c_define('OFFSET')}} + ({{item.struct_obj.c_define('SIZE')}} * idx), {{item.struct_obj.size}}); - return g_steal_pointer(&buf); + return g_steal_pointer(&st_tmp); } {%- else %} {{export.value}}{{item.struct_obj.name}} * {{item.c_getter}}(const {{obj.name}} *st) { - g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr({{item.struct_obj.name}}) st_tmp = {{item.struct_obj.c_method('NewInternal')}}(); g_return_val_if_fail(st != NULL, NULL); - g_byte_array_append(buf, st->data + {{item.c_define('OFFSET')}}, {{item.size}}); - return g_steal_pointer(&buf); + st_tmp->buf = g_byte_array_new(); + g_byte_array_append(st_tmp->buf, st->buf->data + {{item.c_define('OFFSET')}}, {{item.size}}); + return g_steal_pointer(&st_tmp); } {%- endif %} @@ -46,7 +90,7 @@ g_return_val_if_fail(st != NULL, NULL); if (bufsz != NULL) *bufsz = {{item.size}}; - return st->data + {{item.offset}}; + return st->buf->data + {{item.offset}}; } {%- elif item.type == Type.GUID %} @@ -54,7 +98,7 @@ {{item.c_getter}}(const {{obj.name}} *st) { g_return_val_if_fail(st != NULL, NULL); - return (const fwupd_guid_t *) (st->data + {{item.offset}}); + return (const fwupd_guid_t *) (st->buf->data + {{item.offset}}); } {%- elif item.type == Type.U8 %} @@ -62,7 +106,7 @@ {{item.c_getter}}(const {{obj.name}} *st) { g_return_val_if_fail(st != NULL, 0x0); - return st->data[{{item.offset}}]; + return st->buf->data[{{item.offset}}]; } {%- elif item.type in [Type.U16, Type.U24, Type.U32, Type.U64, Type.I8, Type.I16, Type.I32, Type.I64] %} @@ -71,7 +115,7 @@ {{item.c_getter}}(const {{obj.name}} *st, guint idx) { g_return_val_if_fail(st != NULL, 0x0); - return fu_memread_{{item.type_mem}}(st->data + {{item.offset}} + (sizeof({{item.type_glib}}) * idx), + return fu_memread_{{item.type_mem}}(st->buf->data + {{item.offset}} + (sizeof({{item.type_glib}}) * idx), {{item.endian_glib}}); } {%- else %} @@ -79,7 +123,7 @@ {{item.c_getter}}(const {{obj.name}} *st) { g_return_val_if_fail(st != NULL, 0x0); - return fu_memread_{{item.type_mem}}(st->data + {{item.offset}}, {{item.endian_glib}}); + return fu_memread_{{item.type_mem}}(st->buf->data + {{item.offset}}, {{item.endian_glib}}); } {%- endif %} @@ -89,8 +133,8 @@ { guint32 val; g_return_val_if_fail(st != NULL, 0x0); - g_return_val_if_fail(st->len >= sizeof({{item.type_glib}}), 0x0); - val = fu_memread_{{item.type_mem}}(st->data + {{item.offset}}, {{item.endian_glib}}); + g_return_val_if_fail(st->buf->len >= sizeof({{item.type_glib}}), 0x0); + val = fu_memread_{{item.type_mem}}(st->buf->data + {{item.offset}}, {{item.endian_glib}}); return (val >> {{item.bits_offset}}) & {{item.bits_mask}}; } {%- endif %} @@ -112,7 +156,7 @@ g_return_val_if_fail(st != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); if (value == NULL) { - memset(st->data + {{item.offset}}, 0x0, {{item.size}}); + memset(st->buf->data + {{item.offset}}, 0x0, {{item.size}}); return TRUE; } len = strlen(value); @@ -124,7 +168,7 @@ value, (guint) len, (guint) {{item.size}}); return FALSE; } - return fu_memcpy_safe(st->data, st->len, {{item.offset}}, (const guint8 *)value, len, 0x0, len, error); + return fu_memcpy_safe(st->buf->data, st->buf->len, {{item.offset}}, (const guint8 *)value, len, 0x0, len, error); } {%- elif item.struct_obj %} @@ -137,21 +181,21 @@ g_return_val_if_fail(idx < {{item.n_elements}}, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - if (st_donor->len > {{item.struct_obj.c_define('SIZE')}}) { + if (st_donor->buf->len > {{item.struct_obj.c_define('SIZE')}}) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "donor '{{item.struct_obj.name}}' (0x%x bytes) does not fit in " "{{obj.name}}.{{item.element_id}} (0x%x bytes)", - (guint) st_donor->len, + (guint) st_donor->buf->len, (guint) {{item.struct_obj.c_define('SIZE')}}); return FALSE; } - memcpy(st->data + memcpy(st->buf->data + {{item.c_define('OFFSET')}} + ({{item.struct_obj.c_define('SIZE')}} * idx), - st_donor->data, - st_donor->len); + st_donor->buf->data, + st_donor->buf->len); return TRUE; } {%- else %} @@ -162,17 +206,17 @@ g_return_val_if_fail(st_donor != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - if (st_donor->len > {{item.struct_obj.c_define('SIZE')}}) { + if (st_donor->buf->len > {{item.struct_obj.c_define('SIZE')}}) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "donor '{{item.struct_obj.name}}' (0x%x bytes) does not fit in " "{{obj.name}}.{{item.element_id}} (0x%x bytes)", - (guint) st_donor->len, + (guint) st_donor->buf->len, (guint) {{item.struct_obj.c_define('SIZE')}}); return FALSE; } - memcpy(st->data + {{item.c_define('OFFSET')}}, st_donor->data, st_donor->len); + memcpy(st->buf->data + {{item.c_define('OFFSET')}}, st_donor->buf->data, st_donor->buf->len); return TRUE; } {%- endif %} @@ -184,7 +228,7 @@ g_return_val_if_fail(st != NULL, FALSE); g_return_val_if_fail(buf != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - return fu_memcpy_safe(st->data, st->len, {{item.offset}}, buf, bufsz, 0x0, bufsz, error); + return fu_memcpy_safe(st->buf->data, st->buf->len, {{item.offset}}, buf, bufsz, 0x0, bufsz, error); } {%- elif item.type == Type.GUID %} @@ -193,7 +237,7 @@ { g_return_if_fail(st != NULL); g_return_if_fail(value != NULL); - memcpy(st->data + {{item.offset}}, value, sizeof(*value)); /* nocheck:blocked */ + memcpy(st->buf->data + {{item.offset}}, value, sizeof(*value)); /* nocheck:blocked */ } {%- elif item.type == Type.U8 %} @@ -201,7 +245,7 @@ {{item.c_setter}}({{obj.name}} *st, {{item.type_glib}} value) { g_return_if_fail(st != NULL); - st->data[{{item.offset}}] = value; + st->buf->data[{{item.offset}}] = value; } {%- elif item.type == Type.B32 %} @@ -210,11 +254,11 @@ { guint32 tmp; g_return_if_fail(st != NULL); - g_return_if_fail(st->len >= sizeof({{item.type_glib}})); - tmp = fu_memread_{{item.type_mem}}(st->data + {{item.offset}}, {{item.endian_glib}}); + g_return_if_fail(st->buf->len >= sizeof({{item.type_glib}})); + tmp = fu_memread_{{item.type_mem}}(st->buf->data + {{item.offset}}, {{item.endian_glib}}); tmp &= ~({{item.bits_mask}} << {{item.bits_offset}}); tmp |= (value & {{item.bits_mask}}) << {{item.bits_offset}}; - fu_memwrite_{{item.type_mem}}(st->data + {{item.offset}}, tmp, {{item.endian_glib}}); + fu_memwrite_{{item.type_mem}}(st->buf->data + {{item.offset}}, tmp, {{item.endian_glib}}); } {%- elif item.type in [Type.U16, Type.U24, Type.U32, Type.U64, Type.I8, Type.I16, Type.I32, Type.I64] %} @@ -224,7 +268,7 @@ { g_return_if_fail(st != NULL); g_return_if_fail(idx < {{item.n_elements}}); - fu_memwrite_{{item.type_mem}}(st->data + {{item.offset}} + (sizeof({{item.type_glib}}) * idx), + fu_memwrite_{{item.type_mem}}(st->buf->data + {{item.offset}} + (sizeof({{item.type_glib}}) * idx), value, {{item.endian_glib}}); } @@ -234,7 +278,7 @@ {{item.c_setter}}({{obj.name}} *st, {{item.type_glib}} value) { g_return_if_fail(st != NULL); - fu_memwrite_{{item.type_mem}}(st->data + {{item.offset}}, value, {{item.endian_glib}}); + fu_memwrite_{{item.type_mem}}(st->buf->data + {{item.offset}}, value, {{item.endian_glib}}); } {%- endif %} {%- endif %} @@ -249,15 +293,16 @@ {{export.value}}{{obj.name}} * {{obj.c_method('New')}}(void) { - {{obj.name}} *st = g_byte_array_sized_new({{obj.size}}); - fu_byte_array_set_size(st, {{obj.size}}, 0x0); + {{obj.name}} *st = {{obj.c_method('NewInternal')}}(); + st->buf = g_byte_array_sized_new({{obj.size}}); + fu_byte_array_set_size(st->buf, {{obj.size}}, 0x0); {%- for item in obj.items | selectattr('padding') %} - memset(st->data + {{item.offset}}, {{item.padding}}, {{item.size}}); + memset(st->buf->data + {{item.offset}}, {{item.padding}}, {{item.size}}); {%- endfor %} {%- for item in obj.items | selectattr('struct_obj') %} { - g_autoptr(GByteArray) st_donor = {{item.struct_obj.c_method('New')}}(); - memcpy(st->data + 0x{{'{:X}'.format(item.offset)}}, st_donor->data, st_donor->len); /* nocheck:blocked */ + g_autoptr({{item.struct_obj.name}}) st_donor = {{item.struct_obj.c_method('New')}}(); + memcpy(st->buf->data + 0x{{'{:X}'.format(item.offset)}}, st_donor->buf->data, st_donor->buf->len); /* nocheck:blocked */ } {%- endfor %} {%- for item in obj.items | selectattr('default') %} @@ -266,7 +311,7 @@ {%- elif item.type == Type.GUID %} {{item.c_setter}}(st, (fwupd_guid_t *) "{{item.default}}"); {%- elif item.type == Type.U8 and item.n_elements %} - memcpy(st->data + 0x{{'{:X}'.format(item.offset)}}, "{{item.default}}", {{item.size}}); /* nocheck:blocked */ + memcpy(st->buf->data + 0x{{'{:X}'.format(item.offset)}}, "{{item.default}}", {{item.size}}); /* nocheck:blocked */ {%- else %} {{item.c_setter}}(st, {{item.default}}); {%- endif %} @@ -289,7 +334,11 @@ {%- if item.enum_obj %} { +{%- if item.enum_obj.is_bitfield %} + g_autofree gchar *tmp = {{item.enum_obj.c_method('ToString')}}({{item.c_getter}}(st)); +{%- else %} const gchar *tmp = {{item.enum_obj.c_method('ToString')}}({{item.c_getter}}(st)); +{%- endif %} if (tmp != NULL) { g_string_append_printf(str, " {{item.element_id}}: 0x%x [%s]\n", (guint) {{item.c_getter}}(st), tmp); } else { @@ -339,13 +388,13 @@ {%- elif item.struct_obj %} {%- if item.n_elements %} for (guint i = 0; i < {{item.n_elements}}; i++) { - g_autoptr(GByteArray) st_tmp = {{item.c_getter}}(st, i); + g_autoptr({{item.struct_obj.name}}) st_tmp = {{item.c_getter}}(st, i); g_autofree gchar *tmp = {{item.struct_obj.c_method('ToString')}}(st_tmp); g_string_append_printf(str, " {{item.element_id}}[%u]: %s\n", i, tmp); } {%- else %} { - g_autoptr(GByteArray) st_tmp = {{item.c_getter}}(st); + g_autoptr({{item.struct_obj.name}}) st_tmp = {{item.c_getter}}(st); g_autofree gchar *tmp = {{item.struct_obj.c_method('ToString')}}(st_tmp); g_string_append_printf(str, " {{item.element_id}}: %s\n", tmp); } @@ -360,6 +409,19 @@ } {%- endif %} +{%- set export = obj.export('ToBytes') %} +{%- if export in [Export.PUBLIC, Export.PRIVATE] %} +/** + * {{obj.c_method('ToBytes')}}: (skip): + **/ +{{export.value}}GBytes * +{{obj.c_method('ToBytes')}}(const {{obj.name}} *st) +{ + g_return_val_if_fail(st != NULL, NULL); + return g_bytes_new(st->buf->data, st->buf->len); +} +{%- endif %} + {%- set export = obj.export('ValidateInternal') %} {%- if export in [Export.PUBLIC, Export.PRIVATE] %} {{export.value}}gboolean @@ -368,27 +430,56 @@ g_return_val_if_fail(st != NULL, FALSE); {%- for item in obj.items | selectattr('constant') %} {%- if item.type == Type.STRING %} - if (strncmp((const gchar *) (st->data + {{item.offset}}), "{{item.constant}}", {{item.size}}) != 0) { + if (strncmp((const gchar *) (st->buf->data + {{item.offset}}), "{{item.constant}}", {{item.size}}) != 0) { {%- elif item.type == Type.GUID %} if (memcmp({{item.c_getter}}(st), "{{item.constant}}", {{item.size}}) != 0) { {%- elif item.type == Type.U8 and item.n_elements %} - if (memcmp(st->data + {{item.offset}}, "{{item.constant}}", {{item.size}}) != 0) { + if (memcmp(st->buf->data + {{item.offset}}, "{{item.constant}}", {{item.size}}) != 0) { {%- else %} if ({{item.c_getter}}(st) != {{item.constant}}) { {%- endif %} +{%- if item.type == Type.STRING %} + g_autofree gchar *str = {{item.c_getter}}(st); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "constant {{obj.name}}.{{item.element_id}} was not valid, " + "expected '{{item.constant}}' and got '%s'", + str); + return FALSE; +{%- elif item.enum_obj %} + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "constant {{obj.name}}.{{item.element_id}} was not valid, " + "expected '{{item.enum_obj.name}}' and got '%s'", + {{item.enum_obj.c_method('ToString')}}({{item.c_getter}}(st))); + return FALSE; +{%- elif item.type in [Type.U16, Type.U24, Type.U32, Type.U64, Type.I8, Type.I16, Type.I32, Type.I64] %} + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "constant {{obj.name}}.{{item.element_id}} was not valid, " + "expected 0x%x and got 0x%x", + (guint) {{item.constant}}, + (guint) {{item.c_getter}}(st)); + return FALSE; +{%- else %} g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "constant {{obj.name}}.{{item.element_id}} was not valid"); return FALSE; +{%- endif %} } {%- endfor %} {%- for item in obj.items | selectattr('struct_obj') %} { - GByteArray st_tmp = { - .data = (guint8*) st->data + 0x{{'{:X}'.format(item.offset)}}, + GByteArray buf_tmp = { + .data = (guint8*) st->buf->data + 0x{{'{:X}'.format(item.offset)}}, .len = {{item.size}}, }; + {{item.struct_obj.name}} st_tmp = { .buf = &buf_tmp }; if (!{{item.struct_obj.c_method('ValidateInternal')}}(&st_tmp, error)) return FALSE; } @@ -406,14 +497,15 @@ {{export.value}}gboolean {{obj.c_method('Validate')}}(const guint8 *buf, gsize bufsz, gsize offset, GError **error) { - GByteArray st = {.data = (guint8 *) buf + offset, .len = bufsz - offset, }; + GByteArray st_buf = {.data = (guint8 *) buf + offset, .len = bufsz - offset, }; + {{obj.name}} st_tmp = {.buf = &st_buf }; g_return_val_if_fail(buf != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); if (!fu_memchk_read(bufsz, offset, {{obj.size}}, error)) { - g_prefix_error(error, "invalid struct {{obj.name}}: "); + g_prefix_error_literal(error, "invalid struct {{obj.name}}: "); return FALSE; } - if (!{{obj.c_method('ValidateInternal')}}(&st, error)) + if (!{{obj.c_method('ValidateInternal')}}(&st_tmp, error)) return FALSE; return TRUE; } @@ -427,21 +519,21 @@ {{export.value}}gboolean {{obj.c_method('ValidateStream')}}(GInputStream *stream, gsize offset, GError **error) { - g_autoptr(GByteArray) st = NULL; + g_autoptr({{obj.name}}) st = {{obj.c_method('NewInternal')}}(); g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - st = fu_input_stream_read_byte_array(stream, offset, {{obj.size}}, NULL, error); - if (st == NULL) { + st->buf = fu_input_stream_read_byte_array(stream, offset, {{obj.size}}, NULL, error); + if (st->buf == NULL) { g_prefix_error(error, "{{obj.name}} failed read of 0x%x: ", (guint) {{obj.size}}); return FALSE; } - if (st->len != {{obj.size}}) { + if (st->buf->len != {{obj.size}}) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "{{obj.name}} requested 0x%x and got 0x%x", (guint) {{obj.size}}, - (guint) st->len); + (guint) st->buf->len); return FALSE; } return {{obj.c_method('ValidateInternal')}}(st, error); @@ -457,7 +549,9 @@ {{obj.c_method('ValidateBytes')}}(GBytes *blob, gsize offset, GError **error) { gsize bufsz = 0; - const guint8 *buf = g_bytes_get_data(blob, &bufsz); + const guint8 *buf = fu_bytes_get_data_safe(blob, &bufsz, error); + if (buf == NULL) + return FALSE; return {{obj.c_method('Validate')}}(buf, bufsz, offset, error); } {%- endif %} @@ -467,12 +561,12 @@ {{export.value}}gboolean {{obj.c_method('ParseInternal')}}({{obj.name}} *st, GError **error) { - if (!{{obj.c_method('ValidateInternal')}}(st, error)) - return FALSE; if (g_getenv("FWUPD_VERBOSE") != NULL) { g_autofree gchar *str = {{obj.c_method('ToString')}}(st); g_debug("%s", str); } + if (!{{obj.c_method('ValidateInternal')}}(st, error)) + return FALSE; return TRUE; } {%- endif %} @@ -486,14 +580,15 @@ {{export.value}}{{obj.name}} * {{obj.c_method('Parse')}}(const guint8 *buf, gsize bufsz, gsize offset, GError **error) { - g_autoptr(GByteArray) st = g_byte_array_new(); + g_autoptr({{obj.name}}) st = {{obj.c_method('NewInternal')}}(); g_return_val_if_fail(buf != NULL, NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); if (!fu_memchk_read(bufsz, offset, {{obj.size}}, error)) { - g_prefix_error(error, "invalid struct {{obj.name}}: "); + g_prefix_error_literal(error, "invalid struct {{obj.name}}: "); return NULL; } - g_byte_array_append(st, buf + offset, {{obj.size}}); + st->buf = g_byte_array_new(); + g_byte_array_append(st->buf, buf + offset, {{obj.size}}); if (!{{obj.c_method('ParseInternal')}}(st, error)) return NULL; return g_steal_pointer(&st); @@ -509,7 +604,9 @@ {{obj.c_method('ParseBytes')}}(GBytes *blob, gsize offset, GError **error) { gsize bufsz = 0; - const guint8 *buf = g_bytes_get_data(blob, &bufsz); + const guint8 *buf = fu_bytes_get_data_safe(blob, &bufsz, error); + if (buf == NULL) + return NULL; return {{obj.c_method('Parse')}}(buf, bufsz, offset, error); } {%- endif %} @@ -522,19 +619,19 @@ {{export.value}}{{obj.name}} * {{obj.c_method('ParseStream')}}(GInputStream *stream, gsize offset, GError **error) { - g_autoptr(GByteArray) st = NULL; - st = fu_input_stream_read_byte_array(stream, offset, {{obj.size}}, NULL, error); - if (st == NULL) { + g_autoptr({{obj.name}}) st = {{obj.c_method('NewInternal')}}(); + st->buf = fu_input_stream_read_byte_array(stream, offset, {{obj.size}}, NULL, error); + if (st->buf == NULL) { g_prefix_error(error, "{{obj.name}} failed read of 0x%x: ", (guint) {{obj.size}}); return NULL; } - if (st->len != {{obj.size}}) { + if (st->buf->len != {{obj.size}}) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "{{obj.name}} requested 0x%x and got 0x%x", (guint) {{obj.size}}, - (guint) st->len); + (guint) st->buf->len); return NULL; } if (!{{obj.c_method('ParseInternal')}}(st, error)) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-rustgen-struct.h.in fwupd-2.0.20/libfwupdplugin/fu-rustgen-struct.h.in --- fwupd-2.0.8/libfwupdplugin/fu-rustgen-struct.h.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-rustgen-struct.h.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,11 +1,18 @@ -typedef GByteArray {{obj.name}}; +typedef struct { + GByteArray *buf; + guint refcount; +} {{obj.name}}; -#define {{obj.c_method('Unref')}} g_byte_array_unref +{{obj.name}} *{{obj.c_method('Ref')}}({{obj.name}} *st) G_GNUC_NON_NULL(1); +void {{obj.c_method('Unref')}}({{obj.name}} *st) G_GNUC_NON_NULL(1); G_DEFINE_AUTOPTR_CLEANUP_FUNC({{obj.name}}, {{obj.c_method('Unref')}}) {%- if obj.export('New') == Export.PUBLIC %} {{obj.name}} *{{obj.c_method('New')}}(void) G_GNUC_WARN_UNUSED_RESULT; {%- endif %} +{%- if obj.export('NewInternal') == Export.PUBLIC %} +{{obj.name}} *{{obj.c_method('NewInternal')}}(void) G_GNUC_WARN_UNUSED_RESULT; +{%- endif %} {%- if obj.export('Parse') == Export.PUBLIC %} {{obj.name}} *{{obj.c_method('Parse')}}(const guint8 *buf, gsize bufsz, gsize offset, GError **error) G_GNUC_NON_NULL(1) G_GNUC_WARN_UNUSED_RESULT; {%- endif %} @@ -24,9 +31,15 @@ {%- if obj.export('ValidateStream') == Export.PUBLIC %} gboolean {{obj.c_method('ValidateStream')}}(GInputStream *stream, gsize offset, GError **error) G_GNUC_NON_NULL(1) G_GNUC_WARN_UNUSED_RESULT; {%- endif %} +{%- if obj.export('ValidateInternal') == Export.PUBLIC %} +gboolean {{obj.c_method('ValidateInternal')}}({{obj.name}} *st, GError **error) G_GNUC_NON_NULL(1) G_GNUC_WARN_UNUSED_RESULT; +{%- endif %} {%- if obj.export('ToString') == Export.PUBLIC %} gchar *{{obj.c_method('ToString')}}(const {{obj.name}} *st) G_GNUC_NON_NULL(1) G_GNUC_WARN_UNUSED_RESULT; {%- endif %} +{%- if obj.export('ToBytes') == Export.PUBLIC %} +GBytes *{{obj.c_method('ToBytes')}}(const {{obj.name}} *st) G_GNUC_NON_NULL(1) G_GNUC_WARN_UNUSED_RESULT; +{%- endif %} {%- for item in obj.items | selectattr('enabled') %} {%- if item.export('Getters') == Export.PUBLIC %} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-rustgen.c.in fwupd-2.0.20/libfwupdplugin/fu-rustgen.c.in --- fwupd-2.0.8/libfwupdplugin/fu-rustgen.c.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-rustgen.c.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,13 +1,20 @@ /* auto-generated, do not modify */ #include "config.h" +#include + #include "{{basename}}" {%- if struct_objs %} #include "fu-byte-array.h" +#include "fu-bytes.h" #include "fu-mem-private.h" #include "fu-string.h" {%- endif %} +{%- for header_basename in import_headers %} +#include "{{header_basename}}" +{%- endfor %} + #ifdef G_LOG_DOMAIN #undef G_LOG_DOMAIN #endif diff -Nru fwupd-2.0.8/libfwupdplugin/fu-rustgen.h.in fwupd-2.0.20/libfwupdplugin/fu-rustgen.h.in --- fwupd-2.0.8/libfwupdplugin/fu-rustgen.h.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-rustgen.h.in 2026-02-26 11:36:18.000000000 +0000 @@ -4,5 +4,11 @@ {%- if struct_objs %} #include {%- else %} -#include +{%- for include in includes %} +#include <{{include}}> +{%- endfor %} {%- endif %} + +{%- for header_basename in import_headers %} +#include "{{header_basename}}" +{%- endfor %} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-sbatlevel-section.c fwupd-2.0.20/libfwupdplugin/fu-sbatlevel-section.c --- fwupd-2.0.8/libfwupdplugin/fu-sbatlevel-section.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-sbatlevel-section.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,10 +9,8 @@ #include "config.h" #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-csv-firmware.h" #include "fu-input-stream.h" -#include "fu-mem.h" #include "fu-partial-input-stream.h" #include "fu-sbatlevel-section-struct.h" #include "fu-sbatlevel-section.h" @@ -20,12 +18,12 @@ G_DEFINE_TYPE(FuSbatlevelSection, fu_sbatlevel_section, FU_TYPE_FIRMWARE); static gboolean -fu_sbatlevel_section_add_entry(FuFirmware *firmware, +fu_sbatlevel_section_add_entry(FuSbatlevelSection *self, GInputStream *stream, gsize offset, const gchar *entry_name, guint64 entry_idx, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize streamsz = 0; @@ -35,6 +33,15 @@ /* stop at the null terminator */ if (!fu_input_stream_size(stream, &streamsz, error)) return FALSE; + if (offset >= streamsz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "offset 0x%x exceeds stream size 0x%x", + (guint)offset, + (guint)streamsz); + return FALSE; + } for (guint i = offset; i < streamsz; i++) { guint8 value = 0; if (!fu_input_stream_read_u8(stream, i, &value, error)) @@ -56,14 +63,14 @@ fu_firmware_set_offset(entry_fw, offset); partial_stream = fu_partial_input_stream_new(stream, offset, streamsz - offset, error); if (partial_stream == NULL) { - g_prefix_error(error, "failed to cut CSV section: "); + g_prefix_error_literal(error, "failed to cut CSV section: "); return FALSE; } if (!fu_firmware_parse_stream(entry_fw, partial_stream, 0, flags, error)) { g_prefix_error(error, "failed to parse %s: ", entry_name); return FALSE; } - if (!fu_firmware_add_image_full(firmware, entry_fw, error)) + if (!fu_firmware_add_image(FU_FIRMWARE(self), entry_fw, error)) return FALSE; /* success */ @@ -73,16 +80,17 @@ static gboolean fu_sbatlevel_section_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { - g_autoptr(GByteArray) st = NULL; + FuSbatlevelSection *self = FU_SBATLEVEL_SECTION(firmware); + g_autoptr(FuStructSbatLevelSectionHeader) st = NULL; st = fu_struct_sbat_level_section_header_parse_stream(stream, 0x0, error); if (st == NULL) return FALSE; if (!fu_sbatlevel_section_add_entry( - firmware, + self, stream, sizeof(guint32) + fu_struct_sbat_level_section_header_get_previous(st), "previous", @@ -90,7 +98,7 @@ flags, error)) return FALSE; - if (!fu_sbatlevel_section_add_entry(firmware, + if (!fu_sbatlevel_section_add_entry(self, stream, sizeof(guint32) + fu_struct_sbat_level_section_header_get_latest(st), @@ -107,23 +115,23 @@ { g_autoptr(FuFirmware) img_ltst = NULL; g_autoptr(FuFirmware) img_prev = NULL; - g_autoptr(GByteArray) buf = fu_struct_sbat_level_section_header_new(); + g_autoptr(FuStructSbatLevelSectionHeader) st = fu_struct_sbat_level_section_header_new(); g_autoptr(GBytes) blob_ltst = NULL; g_autoptr(GBytes) blob_prev = NULL; /* previous */ - fu_struct_sbat_level_section_header_set_previous(buf, sizeof(guint32) * 2); + fu_struct_sbat_level_section_header_set_previous(st, sizeof(guint32) * 2); img_prev = fu_firmware_get_image_by_id(firmware, "previous", error); if (img_prev == NULL) return NULL; blob_prev = fu_firmware_write(img_prev, error); if (blob_prev == NULL) return NULL; - fu_byte_array_append_bytes(buf, blob_prev); - fu_byte_array_append_uint8(buf, 0x0); + fu_byte_array_append_bytes(st->buf, blob_prev); + fu_byte_array_append_uint8(st->buf, 0x0); /* latest */ - fu_struct_sbat_level_section_header_set_latest(buf, + fu_struct_sbat_level_section_header_set_latest(st, (sizeof(guint32) * 2) + g_bytes_get_size(blob_prev) + 1); img_ltst = fu_firmware_get_image_by_id(firmware, "latest", error); @@ -132,11 +140,11 @@ blob_ltst = fu_firmware_write(img_ltst, error); if (blob_ltst == NULL) return NULL; - fu_byte_array_append_bytes(buf, blob_ltst); - fu_byte_array_append_uint8(buf, 0x0); + fu_byte_array_append_bytes(st->buf, blob_ltst); + fu_byte_array_append_uint8(st->buf, 0x0); /* success */ - return g_steal_pointer(&buf); + return g_steal_pointer(&st->buf); } static void diff -Nru fwupd-2.0.8/libfwupdplugin/fu-security-attrs-private.h fwupd-2.0.20/libfwupdplugin/fu-security-attrs-private.h --- fwupd-2.0.8/libfwupdplugin/fu-security-attrs-private.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-security-attrs-private.h 2026-02-26 11:36:18.000000000 +0000 @@ -18,7 +18,7 @@ FU_SECURITY_ATTRS_FLAG_ADD_VERSION = 1 << 0, /*< private >*/ FU_SECURITY_ATTRS_FLAG_LAST -} FuSecurityAttrsFlags; +} G_GNUC_FLAG_ENUM FuSecurityAttrsFlags; #include "fu-security-attrs.h" diff -Nru fwupd-2.0.8/libfwupdplugin/fu-security-attrs.c fwupd-2.0.20/libfwupdplugin/fu-security-attrs.c --- fwupd-2.0.8/libfwupdplugin/fu-security-attrs.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-security-attrs.c 2026-02-26 11:36:18.000000000 +0000 @@ -362,7 +362,7 @@ return g_strcmp0(sort1, sort2); } -static struct { +const struct { const gchar *appstream_id; FwupdSecurityAttrLevel level; } appstream_id_level_map[] = { @@ -434,7 +434,7 @@ fwupd_security_attr_set_level(attr, FWUPD_SECURITY_ATTR_LEVEL_CRITICAL); } -static struct { +const struct { const gchar *appstream_id; const gchar *fwupd_version; } appstream_id_version_map[] = { diff -Nru fwupd-2.0.8/libfwupdplugin/fu-self-test-device.c fwupd-2.0.20/libfwupdplugin/fu-self-test-device.c --- fwupd-2.0.8/libfwupdplugin/fu-self-test-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-self-test-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright 2024 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-self-test-device.h" + +struct _FuSelfTestDevice { + FuDevice parent_instance; +}; + +G_DEFINE_TYPE(FuSelfTestDevice, fu_self_test_device, FU_TYPE_DEVICE) + +static void +fu_self_test_device_init(FuSelfTestDevice *self) +{ +} + +static void +fu_self_test_device_class_init(FuSelfTestDeviceClass *klass) +{ +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-self-test-device.h fwupd-2.0.20/libfwupdplugin/fu-self-test-device.h --- fwupd-2.0.8/libfwupdplugin/fu-self-test-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-self-test-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * Copyright 2024 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-device.h" + +#define FU_TYPE_SELF_TEST_DEVICE (fu_self_test_device_get_type()) +G_DECLARE_FINAL_TYPE(FuSelfTestDevice, fu_self_test_device, FU, SELF_TEST_DEVICE, FuDevice) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-self-test.c fwupd-2.0.20/libfwupdplugin/fu-self-test.c --- fwupd-2.0.8/libfwupdplugin/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -16,13 +16,10 @@ #include "fwupd-enums-private.h" #include "fwupd-security-attr-private.h" -#include "fu-backend-private.h" #include "fu-bios-settings-private.h" #include "fu-cab-firmware-private.h" -#include "fu-common-private.h" #include "fu-config-private.h" #include "fu-context-private.h" -#include "fu-coswid-firmware.h" #include "fu-device-event-private.h" #include "fu-device-private.h" #include "fu-device-progress.h" @@ -35,14 +32,18 @@ #include "fu-plugin-private.h" #include "fu-progress-private.h" #include "fu-security-attrs-private.h" +#include "fu-self-test-device.h" #include "fu-self-test-struct.h" #include "fu-smbios-private.h" -#include "fu-test-device.h" +#include "fu-udev-device-private.h" #include "fu-volume-private.h" +/* nocheck:static */ static GMainLoop *_test_loop = NULL; static guint _test_loop_timeout_id = 0; +/* nocheck:magic-inlines=500 */ + static gboolean fu_test_hang_check_cb(gpointer user_data) { @@ -170,7 +171,7 @@ static void fu_msgpack_parse_binary_func(void) { - // 64 bit float 100.0099 + /* 64 bit float 100.0099 */ const guchar data[] = {0xCB, 0x40, 0x59, 0x00, 0xA2, 0x33, 0x9C, 0x0E, 0xBF}; g_autoptr(GByteArray) buf = g_byte_array_new(); g_autoptr(GError) error = NULL; @@ -351,6 +352,40 @@ } static void +fu_common_checked_add_func(void) +{ + g_assert_cmpint(fu_size_checked_add(0, 0), ==, 0); + g_assert_cmpint(fu_size_checked_add(0, 42), ==, 42); + g_assert_cmpint(fu_size_checked_add(42, 0), ==, 42); + g_assert_cmpint(fu_size_checked_add(G_MAXSIZE / 2, G_MAXSIZE / 2), ==, G_MAXSIZE - 1); + g_assert_cmpint(fu_size_checked_add(G_MAXSIZE, 1), ==, G_MAXSIZE); + g_assert_cmpint(fu_size_checked_add(G_MAXSIZE, G_MAXSIZE), ==, G_MAXSIZE); +} + +static void +fu_common_error_map_func(void) +{ + const FuErrorMapEntry entries[] = { + {0, FWUPD_ERROR_LAST, NULL}, + {1, FWUPD_ERROR_NOT_SUPPORTED, "not supported"}, + }; + gboolean ret; + g_autoptr(GError) error1 = NULL; + g_autoptr(GError) error2 = NULL; + g_autoptr(GError) error3 = NULL; + + ret = fu_error_map_entry_to_gerror(0, entries, G_N_ELEMENTS(entries), &error1); + g_assert_no_error(error1); + g_assert_true(ret); + ret = fu_error_map_entry_to_gerror(1, entries, G_N_ELEMENTS(entries), &error2); + g_assert_error(error2, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_false(ret); + ret = fu_error_map_entry_to_gerror(255, entries, G_N_ELEMENTS(entries), &error3); + g_assert_error(error3, FWUPD_ERROR, FWUPD_ERROR_INTERNAL); + g_assert_false(ret); +} + +static void fu_common_bitwise_func(void) { guint64 val = 0; @@ -373,6 +408,32 @@ } static void +fu_common_byte_array_safe_func(void) +{ + gboolean ret; + g_autoptr(GError) error = NULL; + g_autoptr(GByteArray) array = g_byte_array_new(); + const guint8 buf[] = {0x01, 0x02, 0x03}; + + /* all buffer */ + ret = fu_byte_array_append_safe(array, buf, sizeof(buf), 0, sizeof(buf), &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(array->len, ==, 3); + + /* +1, -1 */ + ret = fu_byte_array_append_safe(array, buf, sizeof(buf), 1, sizeof(buf) - 1, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(array->len, ==, 3 + 2); + + /* boom */ + ret = fu_byte_array_append_safe(array, buf, sizeof(buf), 1, sizeof(buf), &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_READ); + g_assert_false(ret); +} + +static void fu_common_byte_array_func(void) { g_autofree gchar *str = NULL; @@ -425,9 +486,9 @@ g_assert_cmpint(fu_crc32(FU_CRC_KIND_B32_POSIX, buf, sizeof(buf)), ==, 0x037915C4); g_assert_cmpint(fu_crc32(FU_CRC_KIND_B32_SATA, buf, sizeof(buf)), ==, 0xBA55CCAC); g_assert_cmpint(fu_crc32(FU_CRC_KIND_B32_XFER, buf, sizeof(buf)), ==, 0x868E70FC); - g_assert_cmpint(fu_crc32(FU_CRC_KIND_B32_C, buf, sizeof(buf)), ==, 0x5A14B9F9); - g_assert_cmpint(fu_crc32(FU_CRC_KIND_B32_D, buf, sizeof(buf)), ==, 0x68AD8D3C); - g_assert_cmpint(fu_crc32(FU_CRC_KIND_B32_Q, buf, sizeof(buf)), ==, 0xE955C875); + g_assert_cmpint(fu_crc32(FU_CRC_KIND_B32C, buf, sizeof(buf)), ==, 0x5A14B9F9); + g_assert_cmpint(fu_crc32(FU_CRC_KIND_B32D, buf, sizeof(buf)), ==, 0x68AD8D3C); + g_assert_cmpint(fu_crc32(FU_CRC_KIND_B32Q, buf, sizeof(buf)), ==, 0xE955C875); } static void @@ -474,17 +535,32 @@ { g_assert_cmpint(fu_version_guess_format(NULL), ==, FWUPD_VERSION_FORMAT_UNKNOWN); g_assert_cmpint(fu_version_guess_format(""), ==, FWUPD_VERSION_FORMAT_UNKNOWN); - g_assert_cmpint(fu_version_guess_format("1234ac"), ==, FWUPD_VERSION_FORMAT_PLAIN); + g_assert_cmpint(fu_version_guess_format("1234ac"), ==, FWUPD_VERSION_FORMAT_HEX); g_assert_cmpint(fu_version_guess_format("1.2"), ==, FWUPD_VERSION_FORMAT_PAIR); g_assert_cmpint(fu_version_guess_format("1.2.3"), ==, FWUPD_VERSION_FORMAT_TRIPLET); g_assert_cmpint(fu_version_guess_format("1.2.3.4"), ==, FWUPD_VERSION_FORMAT_QUAD); g_assert_cmpint(fu_version_guess_format("1.2.3.4.5"), ==, FWUPD_VERSION_FORMAT_UNKNOWN); g_assert_cmpint(fu_version_guess_format("1a.2b.3"), ==, FWUPD_VERSION_FORMAT_PLAIN); g_assert_cmpint(fu_version_guess_format("1"), ==, FWUPD_VERSION_FORMAT_NUMBER); + g_assert_cmpint(fu_version_guess_format("1A"), ==, FWUPD_VERSION_FORMAT_HEX); g_assert_cmpint(fu_version_guess_format("0x10201"), ==, FWUPD_VERSION_FORMAT_NUMBER); } static void +fu_version_verify_format_func(void) +{ + gboolean ret; + g_autoptr(GError) error = NULL; + + ret = fu_version_verify_format("1A", FWUPD_VERSION_FORMAT_HEX, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_version_verify_format("1A", FWUPD_VERSION_FORMAT_NUMBER, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA); + g_assert_false(ret); +} + +static void fu_device_version_format_func(void) { g_autoptr(FuDevice) device = fu_device_new(NULL); @@ -533,8 +609,7 @@ g_assert_no_error(error); g_assert_true(ret); ret = fu_device_close(device, &error); - g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO); - g_assert_false(ret); + g_assert_true(ret); } static void @@ -808,6 +883,22 @@ } static void +fu_context_efivars_func(void) +{ + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(GError) error = NULL; + + ret = fu_context_efivars_check_free_space(ctx, 10240, &error); + g_assert_no_error(error); + g_assert_true(ret); + + ret = fu_context_efivars_check_free_space(ctx, 10241, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_BROKEN_SYSTEM); + g_assert_false(ret); +} + +static void fu_context_backends_func(void) { g_autoptr(FuContext) ctx = fu_context_new(); @@ -880,8 +971,8 @@ g_assert_cmpint(fu_context_get_display_state(ctx), ==, FU_DISPLAY_STATE_UNKNOWN); g_assert_cmpint(fu_context_get_battery_level(ctx), ==, FWUPD_BATTERY_LEVEL_INVALID); - fu_context_set_power_state(ctx, FU_POWER_STATE_BATTERY_DISCHARGING); - fu_context_set_power_state(ctx, FU_POWER_STATE_BATTERY_DISCHARGING); + fu_context_set_power_state(ctx, FU_POWER_STATE_BATTERY); + fu_context_set_power_state(ctx, FU_POWER_STATE_BATTERY); fu_context_set_lid_state(ctx, FU_LID_STATE_CLOSED); fu_context_set_lid_state(ctx, FU_LID_STATE_CLOSED); fu_context_set_display_state(ctx, FU_DISPLAY_STATE_CONNECTED); @@ -889,7 +980,7 @@ fu_context_set_battery_level(ctx, 50); fu_context_set_battery_level(ctx, 50); - g_assert_cmpint(fu_context_get_power_state(ctx), ==, FU_POWER_STATE_BATTERY_DISCHARGING); + g_assert_cmpint(fu_context_get_power_state(ctx), ==, FU_POWER_STATE_BATTERY); g_assert_cmpint(fu_context_get_lid_state(ctx), ==, FU_LID_STATE_CLOSED); g_assert_cmpint(fu_context_get_display_state(ctx), ==, FU_DISPLAY_STATE_CONNECTED); g_assert_cmpint(fu_context_get_battery_level(ctx), ==, 50); @@ -938,6 +1029,29 @@ } static void +fu_context_hwids_unset_func(void) +{ + g_autofree gchar *testdatadir = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; + gboolean ret; + + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSCONFDIR", testdatadir, TRUE); + + ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_LOAD_CONFIG, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* ensure that we processed the ~hwid-test-flag */ + g_assert_false(fu_context_has_hwid_flag(ctx, "hwid-test-flag")); +} + +static void fu_context_hwids_fdt_func(void) { gboolean ret; @@ -1305,7 +1419,7 @@ g_assert_no_error(error); g_assert_true(ret); - ret = fu_config_load(config, &error); + ret = fu_config_load(config, FU_CONFIG_LOAD_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -1329,7 +1443,6 @@ GStatBuf statbuf = {0}; gboolean ret; gint rc; - g_autofree gchar *conf_dir = NULL; g_autofree gchar *fn = NULL; g_autofree gchar *value = NULL; g_autofree gchar *value_missing = NULL; @@ -1347,9 +1460,8 @@ /* remove existing file */ (void)g_setenv("FWUPD_SYSCONFDIR", "/tmp/fwupd-self-test/etc/fwupd", TRUE); - conf_dir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); fu_plugin_set_name(plugin, "test"); - fn = g_build_filename(conf_dir, "fwupd.conf", NULL); + fn = fu_path_build(FU_PATH_KIND_SYSCONFDIR_PKG, "fwupd.conf", NULL); ret = fu_path_mkdir_parent(fn, &error); g_assert_no_error(error); g_assert_true(ret); @@ -1371,7 +1483,7 @@ g_assert_true(ret); /* load context */ - ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_NONE, &error); + ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_FIX_PERMISSIONS, &error); g_assert_no_error(error); g_assert_true(ret); @@ -1705,8 +1817,7 @@ g_autofree gchar *guid3 = fwupd_guid_hash_string("PNP\\VID_ICO"); g_autofree gchar *guid4 = fwupd_guid_hash_string("PCI\\VEN_8086&DEV_0007"); g_autofree gchar *guid5 = fwupd_guid_hash_string("USB\\VID_8086&PID_0001"); - g_autofree gchar *datadata = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); - g_autofree gchar *quirksdb = g_build_filename(datadata, "quirks.db", NULL); + g_autofree gchar *quirksdb = fu_path_build(FU_PATH_KIND_CACHEDIR_PKG, "quirks.db", NULL); g_autoptr(FuQuirks) quirks = fu_quirks_new(ctx); g_autoptr(GError) error = NULL; @@ -1783,12 +1894,12 @@ g_assert_cmpint(fu_plugin_get_device_gtype_default(plugin), ==, FU_TYPE_DEVICE); /* now there's no explicit default */ - fu_plugin_add_device_gtype(plugin, FU_TYPE_TEST_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_SELF_TEST_DEVICE); g_assert_cmpint(fu_plugin_get_device_gtype_default(plugin), ==, G_TYPE_INVALID); /* make it explicit */ - fu_plugin_set_device_gtype_default(plugin, FU_TYPE_TEST_DEVICE); - g_assert_cmpint(fu_plugin_get_device_gtype_default(plugin), ==, FU_TYPE_TEST_DEVICE); + fu_plugin_set_device_gtype_default(plugin, FU_TYPE_SELF_TEST_DEVICE); + g_assert_cmpint(fu_plugin_get_device_gtype_default(plugin), ==, FU_TYPE_SELF_TEST_DEVICE); } static void @@ -1844,7 +1955,7 @@ &device_new); fu_device_set_specialized_gtype(device, FU_TYPE_DEVICE); - fu_device_set_proxy_gtype(device, FU_TYPE_TEST_DEVICE); + fu_device_set_proxy_gtype(device, FU_TYPE_SELF_TEST_DEVICE); ret = fu_plugin_runner_backend_device_added(plugin, device, progress, &error); g_assert_no_error(error); g_assert_true(ret); @@ -1854,9 +1965,10 @@ g_assert_true(FU_IS_DEVICE(device_new)); /* check proxy was constructed */ - proxy = fu_device_get_proxy(device_new); + proxy = fu_device_get_proxy(device_new, &error); + g_assert_no_error(error); g_assert_nonnull(proxy); - g_assert_true(FU_IS_TEST_DEVICE(proxy)); + g_assert_true(FU_IS_SELF_TEST_DEVICE(proxy)); } static void @@ -1865,6 +1977,8 @@ FuDevice *device_tmp; GPtrArray *children; gboolean ret; + g_autoptr(FuDevice) child1 = NULL; + g_autoptr(FuDevice) child2 = NULL; g_autoptr(FuDevice) device = fu_device_new(NULL); g_autoptr(FuContext) ctx = fu_context_new(); g_autoptr(GError) error = NULL; @@ -1904,6 +2018,14 @@ device_tmp = g_ptr_array_index(children, 0); g_assert_cmpstr(fu_device_get_name(device_tmp), ==, "HDMI"); g_assert_true(fu_device_has_flag(device_tmp, FWUPD_DEVICE_FLAG_UPDATABLE)); + + /* get this one specific child */ + child1 = fu_device_get_child_by_logical_id(device, "USB\\VID_0763&PID_2806&I2C_01", &error); + g_assert_no_error(error); + g_assert_nonnull(child1); + child2 = fu_device_get_child_by_logical_id(device, "SPI", &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + g_assert_null(child2); } static void @@ -1978,18 +2100,18 @@ } static gboolean -fu_test_open_cb(GObject *device, GError **error) +fu_test_open_cb(FuDevice *device, GError **error) { - g_assert_cmpstr(g_object_get_data(device, "state"), ==, "closed"); - g_object_set_data(device, "state", (gpointer) "opened"); + g_assert_cmpstr(g_object_get_data(G_OBJECT(device), "state"), ==, "closed"); + g_object_set_data(G_OBJECT(device), "state", (gpointer) "opened"); return TRUE; } static gboolean -fu_test_close_cb(GObject *device, GError **error) +fu_test_close_cb(FuDevice *device, GError **error) { - g_assert_cmpstr(g_object_get_data(device, "state"), ==, "opened"); - g_object_set_data(device, "state", (gpointer) "closed-on-unref"); + g_assert_cmpstr(g_object_get_data(G_OBJECT(device), "state"), ==, "opened"); + g_object_set_data(G_OBJECT(device), "state", (gpointer) "closed-on-unref"); return TRUE; } @@ -1998,14 +2120,14 @@ { g_autoptr(FuDeviceLocker) locker = NULL; g_autoptr(GError) error = NULL; - g_autoptr(GObject) device = g_object_new(G_TYPE_OBJECT, NULL); + g_autoptr(FuDevice) device = fu_device_new(NULL); - g_object_set_data(device, "state", (gpointer) "closed"); + g_object_set_data(G_OBJECT(device), "state", (gpointer) "closed"); locker = fu_device_locker_new_full(device, fu_test_open_cb, fu_test_close_cb, &error); g_assert_no_error(error); g_assert_nonnull(locker); g_clear_object(&locker); - g_assert_cmpstr(g_object_get_data(device, "state"), ==, "closed-on-unref"); + g_assert_cmpstr(g_object_get_data(G_OBJECT(device), "state"), ==, "closed-on-unref"); } static gboolean @@ -2044,7 +2166,7 @@ static void fu_common_endian_func(void) { - guint8 buf[3]; + guint8 buf[3] = {0}; fu_memwrite_uint16(buf, 0x1234, G_LITTLE_ENDIAN); g_assert_cmpint(buf[0], ==, 0x34); @@ -2165,7 +2287,12 @@ static void fu_device_func(void) { - g_autoptr(FuDevice) device = fu_device_new(NULL); + g_autofree gchar *fn = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDevice) device = fu_device_new(ctx); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) possible_plugins = NULL; /* only add one plugin name of the same type */ @@ -2173,6 +2300,15 @@ fu_device_add_possible_plugin(device, "test"); possible_plugins = fu_device_get_possible_plugins(device); g_assert_cmpint(possible_plugins->len, ==, 1); + + fn = g_test_build_filename(G_TEST_DIST, "tests", "sys_vendor", NULL); + str = fu_device_get_contents(device, fn, G_MAXSIZE, NULL, &error); + g_assert_no_error(error); + g_assert_cmpstr(str, ==, "FwupdTest\n"); + + blob = fu_device_get_contents_bytes(device, fn, 5, NULL, &error); + g_assert_no_error(error); + g_assert_cmpint(g_bytes_get_size(blob), ==, 5); } static void @@ -2309,7 +2445,7 @@ g_assert_false(ret); g_clear_error(&error); - firmware = fu_device_read_firmware(device, progress, &error); + firmware = fu_device_read_firmware(device, progress, FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_null(firmware); g_clear_error(&error); @@ -2553,6 +2689,10 @@ fu_device_set_proxy(device, proxy); fu_device_add_flag(proxy, FWUPD_DEVICE_FLAG_EMULATED); g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)); + + /* unsetting flags on the proxy should unpropagate to the device that *uses* the proxy */ + fu_device_remove_flag(proxy, FWUPD_DEVICE_FLAG_EMULATED); + g_assert_false(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)); } static void @@ -2607,9 +2747,9 @@ fu_device_add_child(parent, child); /* check parents */ - g_assert_true(fu_device_get_parent(child) == parent); - g_assert_true(fu_device_get_parent(parent) == grandparent); - g_assert_true(fu_device_get_parent(grandparent) == NULL); + g_assert_true(fu_device_get_parent_internal(child) == parent); + g_assert_true(fu_device_get_parent_internal(parent) == grandparent); + g_assert_true(fu_device_get_parent_internal(grandparent) == NULL); /* check root */ child_root = fu_device_get_root(child); @@ -2625,17 +2765,17 @@ { g_autoptr(FuContext) ctx = fu_context_new(); g_autoptr(FuDevice) device = fu_device_new(ctx); - g_autoptr(FuTestDevice) test_device = g_object_new(FU_TYPE_TEST_DEVICE, NULL); + g_autoptr(FuSelfTestDevice) test_device = g_object_new(FU_TYPE_SELF_TEST_DEVICE, NULL); fu_device_set_name(device, "FuDevice"); - fu_device_set_summary(FU_DEVICE(test_device), "FuTestDevice"); + fu_device_set_summary(FU_DEVICE(test_device), "FuSelfTestDevice"); fu_device_incorporate(FU_DEVICE(test_device), device, FU_DEVICE_INCORPORATE_FLAG_ALL); g_assert_cmpstr(fu_device_get_name(FU_DEVICE(test_device)), ==, "FuDevice"); /* this won't explode as device_class->incorporate is checking types */ fu_device_incorporate(device, FU_DEVICE(test_device), FU_DEVICE_INCORPORATE_FLAG_ALL); - g_assert_cmpstr(fu_device_get_summary(device), ==, "FuTestDevice"); + g_assert_cmpstr(fu_device_get_summary(device), ==, "FuSelfTestDevice"); } static void @@ -2720,7 +2860,7 @@ fu_device_register_private_flag(donor, "self-test"); fu_device_add_private_flag(donor, "self-test"); - /* match a quirk entry, and then clear to ensure encorporate uses the quirk instance ID */ + /* match a quirk entry, and then clear to ensure incorporate uses the quirk instance ID */ ret = fu_device_build_instance_id_full(donor, FU_DEVICE_INSTANCE_FLAG_QUIRKS, &error, @@ -2915,6 +3055,10 @@ device2 = fu_device_get_backend_parent_with_subsystem(device, "usb", &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); g_assert_null(device2); + + /* check version */ + g_assert_false(fu_device_check_fwupd_version(device, "5.0.0")); + g_assert_true(fu_device_check_fwupd_version(device, "1.9.19")); } static void @@ -3134,6 +3278,58 @@ } static void +fu_chunk_array_null_func(void) +{ + g_autofree gchar *chunked1_str = NULL; + g_autofree gchar *chunked2_str = NULL; + g_autoptr(GPtrArray) chunked1 = NULL; + g_autoptr(GPtrArray) chunked2 = NULL; + + chunked1 = fu_chunk_array_new(NULL, 0x100, 0, 0x100, 0x80); + g_assert_cmpint(chunked1->len, ==, 2); + chunked1_str = fu_chunk_array_to_string(chunked1); + g_assert_cmpstr(chunked1_str, + ==, + "\n" + " \n" + " 0x80\n" + " \n" + " \n" + " 0x1\n" + " 0x80\n" + " 0x80\n" + " \n" + "\n"); + + chunked2 = fu_chunk_array_new(NULL, 0x200, 0, 0x100, 0x80); + g_assert_cmpint(chunked2->len, ==, 4); + chunked2_str = fu_chunk_array_to_string(chunked2); + g_assert_cmpstr(chunked2_str, + ==, + "\n" + " \n" + " 0x80\n" + " \n" + " \n" + " 0x1\n" + " 0x80\n" + " 0x80\n" + " \n" + " \n" + " 0x2\n" + " 0x1\n" + " 0x80\n" + " \n" + " \n" + " 0x3\n" + " 0x1\n" + " 0x80\n" + " 0x80\n" + " \n" + "\n"); +} + +static void fu_strstrip_func(void) { struct { @@ -3149,6 +3345,11 @@ g_autofree gchar *tmp = fu_strstrip(map[i].old); g_assert_cmpstr(tmp, ==, map[i].new); } + for (guint i = 0; map[i].old != NULL; i++) { + g_autoptr(GString) str = g_string_new(map[i].old); + fu_string_strip(str); + g_assert_cmpstr(str->str, ==, map[i].new); + } } static void @@ -3436,13 +3637,15 @@ g_autoptr(GBytes) blob = g_bytes_new_static("hello", 5); /* no alignment */ - ret = fu_firmware_parse_bytes(firmware1, blob, 0x0, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + ret = + fu_firmware_parse_bytes(firmware1, blob, 0x0, FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, &error); g_assert_no_error(error); g_assert_true(ret); /* invalid alignment */ fu_firmware_set_alignment(firmware2, FU_FIRMWARE_ALIGNMENT_4K); - ret = fu_firmware_parse_bytes(firmware2, blob, 0x0, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + ret = + fu_firmware_parse_bytes(firmware2, blob, 0x0, FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); g_assert_false(ret); } @@ -3554,7 +3757,7 @@ ret = fu_firmware_parse_bytes(firmware_verify, data_bin, 0x0, - FWUPD_INSTALL_FLAG_NO_SEARCH, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, &error); g_assert_no_error(error); g_assert_true(ret); @@ -3686,7 +3889,7 @@ g_assert_no_error(error); g_assert_nonnull(data_srec); stream = g_memory_input_stream_new_from_bytes(data_srec); - ret = fu_firmware_tokenize(firmware, stream, FWUPD_INSTALL_FLAG_NONE, &error); + ret = fu_firmware_tokenize(firmware, stream, FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -3771,9 +3974,9 @@ } static gsize -fu_test_firmware_dfuse_image_get_size(FuFirmware *self) +fu_test_firmware_dfuse_image_get_size(FuFirmware *firmware) { - g_autoptr(GPtrArray) chunks = fu_firmware_get_chunks(self, NULL); + g_autoptr(GPtrArray) chunks = fu_firmware_get_chunks(firmware, NULL); gsize length = 0; for (guint i = 0; i < chunks->len; i++) { FuChunk *chk = g_ptr_array_index(chunks, i); @@ -3866,6 +4069,83 @@ } static void +fu_firmware_sorted_func(void) +{ + gboolean ret; + g_autofree gchar *xml1 = NULL; + g_autofree gchar *xml2 = NULL; + g_autoptr(FuFirmware) firmware1 = fu_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_firmware_new(); + g_autoptr(FuFirmware) firmware3 = fu_firmware_new(); + g_autoptr(FuFirmware) firmware = fu_firmware_new(); + g_autoptr(GError) error = NULL; + + fu_firmware_set_id(firmware1, "zzz"); + fu_firmware_set_id(firmware2, "aaa"); + fu_firmware_set_id(firmware3, "bbb"); + + fu_firmware_set_idx(firmware1, 0x999); + fu_firmware_set_idx(firmware2, 0x200); + fu_firmware_set_idx(firmware3, 0x100); + + ret = fu_firmware_add_image(firmware, firmware1, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_add_image(firmware, firmware2, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_add_image(firmware, firmware3, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* by idx */ + fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_DEDUPE_IDX); + xml1 = fu_firmware_export_to_xml(firmware, FU_FIRMWARE_EXPORT_FLAG_SORTED, &error); + g_assert_no_error(error); + g_debug("%s", xml1); + g_assert_cmpstr(xml1, + ==, + "\n" + " dedupe-idx\n" + " \n" + " bbb\n" + " 0x100\n" + " \n" + " \n" + " aaa\n" + " 0x200\n" + " \n" + " \n" + " zzz\n" + " 0x999\n" + " \n" + "\n"); + + /* now by both, here using id as it is last */ + fu_firmware_add_flag(firmware, FU_FIRMWARE_FLAG_DEDUPE_ID); + xml2 = fu_firmware_export_to_xml(firmware, FU_FIRMWARE_EXPORT_FLAG_SORTED, &error); + g_assert_no_error(error); + g_debug("%s", xml2); + g_assert_cmpstr(xml2, + ==, + "\n" + " dedupe-id,dedupe-idx\n" + " \n" + " aaa\n" + " 0x200\n" + " \n" + " \n" + " bbb\n" + " 0x100\n" + " \n" + " \n" + " zzz\n" + " 0x999\n" + " \n" + "\n"); +} + +static void fu_firmware_new_from_gtypes_func(void) { gboolean ret; @@ -3892,7 +4172,7 @@ /* dfu -> FuDfuFirmware */ firmware1 = fu_firmware_new_from_gtypes(stream, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error, FU_TYPE_SREC_FIRMWARE, FU_TYPE_DFUSE_FIRMWARE, @@ -3905,7 +4185,7 @@ /* dfu -> FuFirmware */ firmware2 = fu_firmware_new_from_gtypes(stream, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error, FU_TYPE_SREC_FIRMWARE, FU_TYPE_FIRMWARE, @@ -3917,7 +4197,7 @@ /* dfu -> error */ firmware3 = fu_firmware_new_from_gtypes(stream, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error, FU_TYPE_SREC_FIRMWARE, G_TYPE_INVALID); @@ -3956,7 +4236,7 @@ g_assert_cmpstr(fu_csv_firmware_get_column_id(FU_CSV_FIRMWARE(firmware), 6), ==, NULL); blob = g_bytes_new(data, strlen(data)); - ret = fu_firmware_parse_bytes(firmware, blob, 0x0, FWUPD_INSTALL_FLAG_NONE, &error); + ret = fu_firmware_parse_bytes(firmware, blob, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); str = fu_firmware_to_string(firmware); @@ -3994,7 +4274,7 @@ fn = g_test_build_filename(G_TEST_BUILT, "tests", "firmware.zip", NULL); file = g_file_new_for_path(fn); - ret = fu_firmware_parse_file(firmware, file, FWUPD_INSTALL_FLAG_NONE, &error); + ret = fu_firmware_parse_file(firmware, file, FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); g_assert_cmpint(fu_archive_firmware_get_format(FU_ARCHIVE_FIRMWARE(firmware)), @@ -4036,20 +4316,28 @@ /* add images then parse */ fu_firmware_set_bytes(img1, blob1); - fu_firmware_add_image(firmware1, img1); + ret = fu_firmware_add_image(firmware1, img1, &error); + g_assert_no_error(error); + g_assert_true(ret); fu_firmware_set_bytes(img2, blob2); - fu_firmware_add_image(firmware1, img2); + ret = fu_firmware_add_image(firmware1, img2, &error); + g_assert_no_error(error); + g_assert_true(ret); blob3 = fu_firmware_write(firmware1, &error); g_assert_no_error(error); g_assert_nonnull(blob3); g_assert_cmpint(g_bytes_get_size(blob3), ==, 1024); /* parse them back */ - ret = fu_firmware_parse_bytes(firmware2, blob3, 0x0, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + ret = fu_firmware_parse_bytes(firmware2, + blob3, + 0x0, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, + &error); g_assert_no_error(error); g_assert_true(ret); str = fu_firmware_to_string(firmware2); - g_debug("\n%s", str); + g_debug("%s", str); /* verify we got both images */ imgs = fu_firmware_get_images(firmware2); @@ -4167,7 +4455,11 @@ g_assert_cmpint(g_bytes_get_size(data_bin), ==, 1024); /* re-parse to get the CPD image */ - ret = fu_firmware_parse_bytes(firmware2, data_bin, 0x0, FWUPD_INSTALL_FLAG_NONE, &error); + ret = fu_firmware_parse_bytes(firmware2, + data_bin, + 0x0, + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, + &error); g_assert_no_error(error); g_assert_true(ret); img1 = fu_firmware_get_image_by_id(firmware2, "cpd", &error); @@ -4326,11 +4618,15 @@ fu_firmware_set_idx(img1, 13); fu_firmware_set_id(img1, "primary"); fu_firmware_set_filename(img1, "BIOS.bin"); - fu_firmware_add_image(firmware, img1); + ret = fu_firmware_add_image(firmware, img1, &error); + g_assert_no_error(error); + g_assert_true(ret); fu_firmware_set_addr(img2, 0x400); fu_firmware_set_idx(img2, 23); fu_firmware_set_id(img2, "secondary"); - fu_firmware_add_image(firmware, img2); + ret = fu_firmware_add_image(firmware, img2, &error); + g_assert_no_error(error); + g_assert_true(ret); /* check depth */ g_assert_cmpint(fu_firmware_get_depth(firmware), ==, 0); @@ -4440,20 +4736,28 @@ fu_firmware_set_idx(img1_old, 13); fu_firmware_set_id(img1_old, "DAVE"); - fu_firmware_add_image(firmware, img1_old); + ret = fu_firmware_add_image(firmware, img1_old, &error); + g_assert_no_error(error); + g_assert_true(ret); g_assert_true(fu_firmware_get_parent(img1_old) == firmware); fu_firmware_set_idx(img1, 13); fu_firmware_set_id(img1, "primary"); - fu_firmware_add_image(firmware, img1); + ret = fu_firmware_add_image(firmware, img1, &error); + g_assert_no_error(error); + g_assert_true(ret); fu_firmware_set_idx(img2_old, 123456); fu_firmware_set_id(img2_old, "secondary"); - fu_firmware_add_image(firmware, img2_old); + ret = fu_firmware_add_image(firmware, img2_old, &error); + g_assert_no_error(error); + g_assert_true(ret); fu_firmware_set_idx(img2, 23); fu_firmware_set_id(img2, "secondary"); - fu_firmware_add_image(firmware, img2); + ret = fu_firmware_add_image(firmware, img2, &error); + g_assert_no_error(error); + g_assert_true(ret); img_id = fu_firmware_get_image_by_id(firmware, "primary", &error); g_assert_no_error(error); @@ -4467,7 +4771,7 @@ g_assert_cmpint(fu_firmware_get_idx(img_idx), ==, 23); g_assert_cmpstr(fu_firmware_get_id(img_idx), ==, "secondary"); - ret = fu_firmware_add_image_full(firmware, img3, &error); + ret = fu_firmware_add_image(firmware, img3, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA); g_assert_false(ret); } @@ -4489,13 +4793,19 @@ g_assert_no_error(error); g_assert_true(ret); + /* check free space */ + total = fu_efivars_space_free(efivars, &error); + g_assert_no_error(error); + g_assert_cmpint(total, ==, 10240); + /* write and read a key */ ret = fu_efivars_set_data(efivars, FU_EFIVARS_GUID_EFI_GLOBAL, "Test", (guint8 *)"1", 1, - FU_EFIVARS_ATTR_NON_VOLATILE | FU_EFIVARS_ATTR_RUNTIME_ACCESS, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS, &error); g_assert_no_error(error); g_assert_true(ret); @@ -4509,9 +4819,16 @@ g_assert_no_error(error); g_assert_true(ret); g_assert_cmpint(sz, ==, 1); - g_assert_cmpint(attr, ==, FU_EFIVARS_ATTR_NON_VOLATILE | FU_EFIVARS_ATTR_RUNTIME_ACCESS); + g_assert_cmpint(attr, + ==, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE | FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS); g_assert_cmpint(data[0], ==, '1'); + /* check free space again */ + total = fu_efivars_space_free(efivars, &error); + g_assert_no_error(error); + g_assert_cmpint(total, ==, 10203); + /* check existing keys */ g_assert_false(fu_efivars_exists(efivars, FU_EFIVARS_GUID_EFI_GLOBAL, "NotGoingToExist")); g_assert_true(fu_efivars_exists(efivars, FU_EFIVARS_GUID_EFI_GLOBAL, "Test")); @@ -4559,6 +4876,11 @@ g_assert_false(fu_efivars_exists(efivars, FU_EFIVARS_GUID_EFI_GLOBAL, "Test1")); g_assert_false(fu_efivars_exists(efivars, FU_EFIVARS_GUID_EFI_GLOBAL, "Test2")); + /* check free space again */ + total = fu_efivars_space_free(efivars, &error); + g_assert_no_error(error); + g_assert_cmpint(total, ==, 10240); + /* read a key that doesn't exist */ ret = fu_efivars_get_data(efivars, FU_EFIVARS_GUID_EFI_GLOBAL, @@ -4667,7 +4989,7 @@ } FuDeviceRetryHelper; static gboolean -fu_device_retry_success(FuDevice *device, gpointer user_data, GError **error) +fu_device_retry_success_cb(FuDevice *device, gpointer user_data, GError **error) { FuDeviceRetryHelper *helper = (FuDeviceRetryHelper *)user_data; helper->cnt_success++; @@ -4675,7 +4997,7 @@ } static gboolean -fu_device_retry_failed(FuDevice *device, gpointer user_data, GError **error) +fu_device_retry_failed_cb(FuDevice *device, gpointer user_data, GError **error) { FuDeviceRetryHelper *helper = (FuDeviceRetryHelper *)user_data; helper->cnt_failed++; @@ -4684,7 +5006,7 @@ } static gboolean -fu_device_retry_success_3rd_try(FuDevice *device, gpointer user_data, GError **error) +fu_device_retry_success_3rd_try_cb(FuDevice *device, gpointer user_data, GError **error) { FuDeviceRetryHelper *helper = (FuDeviceRetryHelper *)user_data; if (helper->cnt_failed == 2) { @@ -4709,8 +5031,8 @@ fu_device_retry_add_recovery(device, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - fu_device_retry_failed); - ret = fu_device_retry(device, fu_device_retry_success, 3, &helper, &error); + fu_device_retry_failed_cb); + ret = fu_device_retry(device, fu_device_retry_success_cb, 3, &helper, &error); g_assert_no_error(error); g_assert_true(ret); g_assert_cmpint(helper.cnt_success, ==, 1); @@ -4730,8 +5052,8 @@ fu_device_retry_add_recovery(device, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - fu_device_retry_success); - ret = fu_device_retry(device, fu_device_retry_failed, 3, &helper, &error); + fu_device_retry_success_cb); + ret = fu_device_retry(device, fu_device_retry_failed_cb, 3, &helper, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL); g_assert_true(!ret); g_assert_cmpint(helper.cnt_success, ==, 2); /* do not reset for the last failure */ @@ -4748,7 +5070,7 @@ .cnt_success = 0, .cnt_failed = 0, }; - ret = fu_device_retry(device, fu_device_retry_success_3rd_try, 3, &helper, &error); + ret = fu_device_retry(device, fu_device_retry_success_3rd_try_cb, 3, &helper, &error); g_assert_no_error(error); g_assert_true(ret); g_assert_cmpint(helper.cnt_success, ==, 1); @@ -5115,7 +5437,7 @@ typedef enum { FU_FIRMWARE_BUILDER_FLAG_NONE, FU_FIRMWARE_BUILDER_FLAG_NO_BINARY_COMPARE = 1 << 0, -} FuFirmwareBuilderFlags; +} G_GNUC_FLAG_ENUM FuFirmwareBuilderFlags; static void fu_firmware_builder_round_trip_func(void) @@ -5201,21 +5523,23 @@ { FU_TYPE_SREC_FIRMWARE, "srec.builder.xml", - "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", + "c8b405b7995d5934086c56b091a4c5df47b3b0d7", FU_FIRMWARE_BUILDER_FLAG_NO_BINARY_COMPARE, }, { FU_TYPE_IHEX_FIRMWARE, "ihex.builder.xml", - "a8d74f767f3fc992b413e5ba801cedc80a4cf013", + "e7c39355f1c87a3e9bf2195a406584c5dac828bc", FU_FIRMWARE_BUILDER_FLAG_NONE, }, +#ifdef HAVE_CBOR { FU_TYPE_FMAP_FIRMWARE, "fmap.builder.xml", - "a0b9ffc10a586d217edf9e9bae7c1fe7c564ea01", + "0db91efb987353ffb779d259b130d63d1b8bcbec", FU_FIRMWARE_BUILDER_FLAG_NONE, }, +#endif { FU_TYPE_EFI_LOAD_OPTION, "efi-load-option.builder.xml", @@ -5243,13 +5567,13 @@ { FU_TYPE_EFI_SECTION, "efi-section.builder.xml", - "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", + "a0ede7316209c536b50b6e5fb22cce8135153bc3", FU_FIRMWARE_BUILDER_FLAG_NONE, }, { FU_TYPE_EFI_SECTION, "efi-section.builder.xml", - "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", + "a0ede7316209c536b50b6e5fb22cce8135153bc3", FU_FIRMWARE_BUILDER_FLAG_NONE, }, { @@ -5277,15 +5601,45 @@ FU_FIRMWARE_BUILDER_FLAG_NONE, }, { + FU_TYPE_EFI_VARIABLE_AUTHENTICATION2, + "efi-variable-authentication2.builder.xml", + "bd08e81e9c86490dc1ffb32b1e3332606eb0fa97", + FU_FIRMWARE_BUILDER_FLAG_NONE, + }, + { + FU_TYPE_EFI_FTW_STORE, + "efi-ftw-store.builder.xml", + "9bdb363e31e00d7fb0b42eacdc95771a3795b7ec", + FU_FIRMWARE_BUILDER_FLAG_NONE, + }, + { + FU_TYPE_EFI_VSS_AUTH_VARIABLE, + "efi-vss-auth-variable.builder.xml", + "de6391f8b09653859b4ff93a7d5004c52c35d5c2", + FU_FIRMWARE_BUILDER_FLAG_NONE, + }, + { + FU_TYPE_EFI_VSS2_VARIABLE_STORE, + "efi-vss2-variable-store.builder.xml", + "25ef7bf7ea600c8a739ff4dc6876bcd2f9d8d30d", + FU_FIRMWARE_BUILDER_FLAG_NONE, + }, + { FU_TYPE_EFI_VOLUME, "efi-volume.builder.xml", - "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", + "d0f658bce79c8468458e0b64e7de24f45c063076", + FU_FIRMWARE_BUILDER_FLAG_NONE, + }, + { + FU_TYPE_EFI_VOLUME, + "efi-volume-sized.builder.xml", + "d7087ea16218d700b9175a9cd0c27bd56b07a6d4", FU_FIRMWARE_BUILDER_FLAG_NONE, }, { FU_TYPE_IFD_FIRMWARE, "ifd.builder.xml", - "06ae066ea53cefe43fed2f1ca4fc7d8cccdbcf1e", + "494e7be6a72e743e6738c0ecdbdcddbf27d1dbd7", FU_FIRMWARE_BUILDER_FLAG_NO_BINARY_COMPARE, }, { @@ -5315,7 +5669,7 @@ { FU_TYPE_OPROM_FIRMWARE, "oprom.builder.xml", - "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed", + "2e8387c1ef14ed4038e6bc637146b86b4d702fa8", FU_FIRMWARE_BUILDER_FLAG_NONE, }, { @@ -5387,7 +5741,8 @@ ret = fu_firmware_parse_bytes(firmware3, blob, 0x0, - FWUPD_INSTALL_FLAG_NO_SEARCH, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH | + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, &error); if (!ret) g_prefix_error(&error, "%s: ", map[i].xml_fn); @@ -5400,6 +5755,8 @@ g_assert_nonnull(blob2); g_assert_no_error(error); ret = fu_bytes_compare(blob2, blob, &error); + if (!ret) + g_prefix_error(&error, "%s: ", map[i].xml_fn); g_assert_no_error(error); g_assert_true(ret); } @@ -5463,7 +5820,7 @@ g_assert_cmpint(helper.updates, ==, 6); g_assert_cmpfloat_with_epsilon(fu_progress_get_duration(progress), 0.1f, 0.05); str = fu_progress_traceback(progress); - g_debug("\n%s", str); + g_debug("%s", str); } static void @@ -5769,6 +6126,7 @@ g_autoptr(GError) error = NULL; g_autoptr(GInputStream) base_stream = g_memory_input_stream_new_from_bytes(blob); g_autoptr(GInputStream) stream = NULL; + g_autoptr(GInputStream) stream2 = NULL; /* use G_MAXSIZE for "rest of the stream" */ stream = fu_partial_input_stream_new(base_stream, 4, G_MAXSIZE, &error); @@ -5785,6 +6143,11 @@ g_assert_cmpint(rc, ==, 2); g_assert_cmpint(buf[0], ==, '7'); g_assert_cmpint(buf[1], ==, '8'); + + /* overflow */ + stream2 = fu_partial_input_stream_new(base_stream, 4, G_MAXSIZE - 1, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA); + g_assert_null(stream2); } static void @@ -5793,17 +6156,17 @@ gboolean ret; gssize rc; guint8 buf[2] = {0x0}; + g_autoptr(GBytes) blob = g_bytes_new_static("12345678", 8); g_autoptr(GError) error = NULL; g_autoptr(GInputStream) stream = NULL; + g_autoptr(GInputStream) base_stream = g_memory_input_stream_new_from_bytes(blob); - { - g_autoptr(GBytes) blob = g_bytes_new_static("12345678", 8); - g_autoptr(FuInputStreamLocker) base_stream = - g_memory_input_stream_new_from_bytes(blob); - stream = fu_partial_input_stream_new(base_stream, 2, 4, &error); - g_assert_no_error(error); - g_assert_nonnull(stream); - } + stream = fu_partial_input_stream_new(base_stream, 2, 4, &error); + g_assert_no_error(error); + g_assert_nonnull(stream); + ret = g_input_stream_close(base_stream, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); ret = g_seekable_seek(G_SEEKABLE(stream), 0x0, G_SEEK_SET, NULL, &error); g_assert_no_error(error); @@ -6178,7 +6541,7 @@ static void fu_input_stream_find_func(void) { - const gchar *haystack = "I write free software. Firmware troublemaker."; + const gchar *haystack = "I write free software. Firmware troublemaker, writing Firmware."; const gchar *needle1 = "Firmware"; const gchar *needle2 = "XXX"; gboolean ret; @@ -6188,14 +6551,33 @@ stream = g_memory_input_stream_new_from_data((const guint8 *)haystack, strlen(haystack), NULL); - ret = - fu_input_stream_find(stream, (const guint8 *)needle1, strlen(needle1), &offset, &error); + ret = fu_input_stream_find(stream, + (const guint8 *)needle1, + strlen(needle1), + 0x0, + &offset, + &error); g_assert_no_error(error); g_assert_true(ret); g_assert_cmpint(offset, ==, 23); - ret = - fu_input_stream_find(stream, (const guint8 *)needle2, strlen(needle2), &offset, &error); + /* find second match */ + ret = fu_input_stream_find(stream, + (const guint8 *)needle1, + strlen(needle1), + 44, + &offset, + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(offset, ==, 54); + + ret = fu_input_stream_find(stream, + (const guint8 *)needle2, + strlen(needle2), + 0x0, + &offset, + &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); g_assert_false(ret); } @@ -6331,13 +6713,69 @@ } static void +fu_plugin_efi_variable_authentication2_func(void) +{ + FuFirmware *signer; + gboolean ret; + g_autofree gchar *fn = NULL; + g_autofree gchar *str = NULL; + g_autoptr(FuFirmware) efi_x509 = NULL; + g_autoptr(FuFirmware) firmware = g_object_new(FU_TYPE_EFI_VARIABLE_AUTHENTICATION2, NULL); + g_autoptr(GError) error = NULL; + g_autoptr(GFile) file = NULL; + g_autoptr(GPtrArray) signers = NULL; + + /* parse file */ + fn = g_test_build_filename(G_TEST_DIST, "tests", "KEKUpdate.bin", NULL); + if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { + g_test_skip("Missing KEKUpdate.bin"); + return; + } + file = g_file_new_for_path(fn); + ret = fu_firmware_parse_file(firmware, file, FU_FIRMWARE_PARSE_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + str = fu_firmware_to_string(firmware); + g_debug("%s", str); + + /* get EFI sig */ + efi_x509 = fu_firmware_get_image_by_id(firmware, + "dec64d7746d983db3774829a00bf829d9f19e9cf", + &error); + g_assert_no_error(error); + g_assert_nonnull(efi_x509); + g_assert_cmpstr("C=US,O=Microsoft Corporation,CN=Microsoft RSA Devices Root CA 2021", + ==, + fu_efi_x509_signature_get_issuer(FU_EFI_X509_SIGNATURE(efi_x509))); + g_assert_cmpstr("C=US,O=Microsoft Corporation,CN=Microsoft Corporation KEK 2K CA 2023", + ==, + fu_efi_x509_signature_get_subject(FU_EFI_X509_SIGNATURE(efi_x509))); + + /* get signer */ + signers = + fu_efi_variable_authentication2_get_signers(FU_EFI_VARIABLE_AUTHENTICATION2(firmware)); + g_assert_nonnull(signers); + g_assert_cmpint(signers->len, ==, 1); + + signer = g_ptr_array_index(signers, 0); + g_assert_cmpstr("CN=DO NOT TRUST - AMI Test PK", + ==, + fu_x509_certificate_get_issuer(FU_X509_CERTIFICATE(signer))); + g_assert_cmpstr("CN=DO NOT TRUST - AMI Test PK", + ==, + fu_x509_certificate_get_subject(FU_X509_CERTIFICATE(signer))); +} + +static void fu_plugin_efi_signature_list_func(void) { FuEfiX509Signature *sig; + gboolean ret; g_autoptr(FuEfiX509Signature) sig2022 = fu_efi_x509_signature_new(); g_autoptr(FuEfiX509Signature) sig2023 = fu_efi_x509_signature_new(); g_autoptr(FuEfiX509Signature) sig2024 = fu_efi_x509_signature_new(); g_autoptr(FuFirmware) siglist = fu_efi_signature_list_new(); + g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) sigs_newest = NULL; fu_efi_x509_signature_set_subject(sig2022, "C=UK,O=Hughski,CN=Hughski Ltd. KEK CA 2022"); @@ -6345,9 +6783,15 @@ fu_efi_x509_signature_set_subject(sig2024, "C=UK,O=Hughski,CN=Hughski Ltd. KEK CA 2024"); /* 2022 -> 2024 -> 2023 */ - fu_firmware_add_image(siglist, FU_FIRMWARE(sig2022)); - fu_firmware_add_image(siglist, FU_FIRMWARE(sig2024)); - fu_firmware_add_image(siglist, FU_FIRMWARE(sig2023)); + ret = fu_firmware_add_image(siglist, FU_FIRMWARE(sig2022), &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_add_image(siglist, FU_FIRMWARE(sig2024), &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_add_image(siglist, FU_FIRMWARE(sig2023), &error); + g_assert_no_error(error); + g_assert_true(ret); /* only one */ sigs_newest = fu_efi_signature_list_get_newest(FU_EFI_SIGNATURE_LIST(siglist)); @@ -6357,6 +6801,110 @@ } static void +fu_device_possible_plugin_func(void) +{ + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(NULL); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) possible_plugins = NULL; + + ret = fu_device_set_quirk_kv(device, "Plugin", "dfu", FU_CONTEXT_QUIRK_SOURCE_FILE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* duplicate */ + ret = fu_device_set_quirk_kv(device, "Plugin", "dfu", FU_CONTEXT_QUIRK_SOURCE_FILE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* something else */ + ret = fu_device_set_quirk_kv(device, "Plugin", "abc", FU_CONTEXT_QUIRK_SOURCE_FILE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* remove the other thing */ + ret = + fu_device_set_quirk_kv(device, "Plugin", "~dfu", FU_CONTEXT_QUIRK_SOURCE_FILE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* verify */ + possible_plugins = fu_device_get_possible_plugins(device); + g_assert_cmpint(possible_plugins->len, ==, 1); + g_assert_cmpstr(g_ptr_array_index(possible_plugins, 0), ==, "abc"); +} + +static void +fu_device_parent_name_prefix_func(void) +{ + g_autoptr(FuDevice) device = fu_device_new(NULL); + g_autoptr(FuDevice) parent = fu_device_new(NULL); + + fu_device_set_id(parent, "0000000000000000000000000000000000000000"); + fu_device_set_name(parent, "Parent1"); + + fu_device_set_id(device, "1111111111111111111111111111111111111111"); + fu_device_set_name(device, "Child1"); + fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); + fu_device_set_parent(device, parent); + + g_assert_cmpstr(fu_device_get_name(parent), ==, "Parent1"); + g_assert_cmpstr(fu_device_get_name(device), ==, "Parent1 (Child1)"); + + /* still set, change child */ + g_assert_true( + fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX)); + fu_device_set_name(device, "Child2"); + g_assert_cmpstr(fu_device_get_name(device), ==, "Parent1 (Child2)"); +} + +static void +fu_device_id_display_func(void) +{ + g_autoptr(FuDevice) device = fu_device_new(NULL); + g_autofree gchar *id1 = NULL; + g_autofree gchar *id2 = NULL; + g_autofree gchar *id3 = NULL; + g_autofree gchar *id4 = NULL; + + id1 = fu_device_get_id_display(device); + g_assert_cmpstr(id1, ==, NULL); + + fu_device_set_id(device, "362301da643102b9f38477387e2193e57abaa590"); + id2 = fu_device_get_id_display(device); + g_assert_cmpstr(id2, ==, "362301da643102b9f38477387e2193e57abaa590"); + + fu_device_set_plugin(device, "uefi_dbx"); + id3 = fu_device_get_id_display(device); + g_assert_cmpstr(id3, ==, "362301da643102b9f38477387e2193e57abaa590 {uefi_dbx}"); + + fu_device_set_name(device, "UEFI dbx"); + id4 = fu_device_get_id_display(device); + g_assert_cmpstr(id4, ==, "362301da643102b9f38477387e2193e57abaa590 [UEFI dbx]"); +} + +static void +fu_device_udev_func(void) +{ + g_autofree gchar *prop = NULL; + g_autofree gchar *sysfs_path = g_test_build_filename(G_TEST_DIST, "tests", NULL); + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuUdevDevice) udev_device = fu_udev_device_new(ctx, sysfs_path); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) attrs = NULL; + + prop = fu_udev_device_read_property(udev_device, "MODALIAS", &error); + g_assert_no_error(error); + g_assert_cmpstr(prop, ==, "hdaudio:v10EC0298r00100103a01"); + + /* list all the files in the directory */ + attrs = fu_udev_device_list_sysfs(udev_device, &error); + g_assert_no_error(error); + g_assert_nonnull(attrs); + g_assert_cmpint(attrs->len, >, 10); +} + +static void fu_cab_checksum_func(void) { guint8 buf[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80}; @@ -6385,6 +6933,52 @@ } static void +fu_cab_compressed_size_func(void) +{ + gboolean ret; + g_autoptr(FuCabFirmware) cab2 = fu_cab_firmware_new(); + g_autoptr(FuCabFirmware) cab = fu_cab_firmware_new(); + g_autoptr(FuCabImage) img = fu_cab_image_new(); + g_autoptr(GByteArray) buf = NULL; + g_autoptr(GBytes) blob2 = NULL; + g_autoptr(GBytes) blob_img = g_bytes_new_static("abc", 3); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + /* build a cab file and then tweak the payload */ + fu_cab_firmware_set_compressed(cab, TRUE); + fu_firmware_set_bytes(FU_FIRMWARE(img), blob_img); + fu_firmware_set_id(FU_FIRMWARE(img), "foo.txt"); + ret = fu_firmware_add_image(FU_FIRMWARE(cab), FU_FIRMWARE(img), &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* write to a mutable buffer */ + blob = fu_firmware_write(FU_FIRMWARE(cab), &error); + g_assert_no_error(error); + g_assert_nonnull(blob); + buf = g_bytes_unref_to_array(g_steal_pointer(&blob)); + g_assert_nonnull(buf); + g_assert_nonnull(buf->data); + + /* change FuStructCabData.uncomp to be too small */ + g_assert_cmpint(buf->len, ==, 0x53); + g_assert_cmpint(buf->data[0x4A], ==, 0x03); + buf->data[0x4A] = 0x02; + + /* parse the new blob */ + blob2 = g_bytes_new(buf->data, buf->len); + ret = fu_firmware_parse_bytes(FU_FIRMWARE(cab2), + blob2, + 0x0, + FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM, + &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA); + g_assert_true(g_str_has_prefix(error->message, "decompressed size mismatch")); + g_assert_false(ret); +} + +static void fu_efi_lz77_decompressor_func(void) { gboolean ret; @@ -6412,7 +7006,7 @@ ret = fu_firmware_parse_bytes(lz77_decompressor_tiano, blob_tiano, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -6435,7 +7029,7 @@ ret = fu_firmware_parse_bytes(lz77_decompressor_legacy, blob_tiano, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -6531,14 +7125,14 @@ g_autoptr(GError) error = NULL; /* 0b1111 + 0b1 + 0b0010 = 0b111110010 -> 0x1F2 */ - g_assert_cmpint(st->len, ==, 4); - fu_dump_raw(G_LOG_DOMAIN, "buf", st->data, st->len); - g_assert_cmpint(st->data[0], ==, 0xF2); - g_assert_cmpint(st->data[1], ==, 0x01); - g_assert_cmpint(st->data[2], ==, 0x0); - g_assert_cmpint(st->data[3], ==, 0x0); + g_assert_cmpint(st->buf->len, ==, 4); + fu_dump_raw(G_LOG_DOMAIN, "buf", st->buf->data, st->buf->len); + g_assert_cmpint(st->buf->data[0], ==, 0xF2); + g_assert_cmpint(st->buf->data[1], ==, 0x01); + g_assert_cmpint(st->buf->data[2], ==, 0x0); + g_assert_cmpint(st->buf->data[3], ==, 0x0); - st2 = fu_struct_self_test_bits_parse(st->data, st->len, 0x0, &error); + st2 = fu_struct_self_test_bits_parse(st->buf->data, st->buf->len, 0x0, &error); g_assert_no_error(error); g_assert_nonnull(st2); @@ -6592,7 +7186,7 @@ } /* size */ - str = fu_byte_array_to_string(st); + str = fu_byte_array_to_string(st->buf); g_assert_cmpstr( str, ==, @@ -6603,16 +7197,16 @@ fu_plugin_struct_func(void) { gboolean ret; - g_autoptr(GByteArray) st = fu_struct_self_test_new(); - g_autoptr(GByteArray) st2 = NULL; - g_autoptr(GByteArray) st3 = NULL; + g_autoptr(FuStructSelfTest) st = fu_struct_self_test_new(); + g_autoptr(FuStructSelfTest) st2 = NULL; + g_autoptr(FuStructSelfTest) st3 = NULL; g_autoptr(GError) error = NULL; g_autofree gchar *str1 = NULL; g_autofree gchar *str2 = NULL; g_autofree gchar *oem_table_id = NULL; /* size */ - g_assert_cmpint(st->len, ==, 51); + g_assert_cmpint(st->buf->len, ==, 59); /* getters and setters */ fu_struct_self_test_set_revision(st, 0xFF); @@ -6624,14 +7218,14 @@ g_assert_cmpint(fu_struct_self_test_get_length(st), ==, 0xDEAD); /* pack */ - str1 = fu_byte_array_to_string(st); + str1 = fu_byte_array_to_string(st->buf); g_assert_cmpstr(str1, ==, "12345678adde0000ff000000000000000000000000000000004142434445465800000000" - "00000000000000dfdfdfdf00000000"); + "00000000000000dfdfdfdf00000000ffffffffffffffff"); /* parse */ - st2 = fu_struct_self_test_parse(st->data, st->len, 0x0, &error); + st2 = fu_struct_self_test_parse(st->buf->data, st->buf->len, 0x0, &error); g_assert_no_error(error); g_assert_nonnull(st2); g_assert_cmpint(fu_struct_self_test_get_revision(st2), ==, 0xFF); @@ -6653,12 +7247,12 @@ " asl_compiler_revision: 0x0"); /* parse failing signature */ - st->data[0] = 0xFF; - st3 = fu_struct_self_test_parse(st->data, st->len, 0x0, &error); + st->buf->data[0] = 0xFF; + st3 = fu_struct_self_test_parse(st->buf->data, st->buf->len, 0x0, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA); g_assert_null(st3); g_clear_error(&error); - ret = fu_struct_self_test_validate(st->data, st->len, 0x0, &error); + ret = fu_struct_self_test_validate(st->buf->data, st->buf->len, 0x0, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA); g_assert_false(ret); } @@ -6670,39 +7264,39 @@ g_autofree gchar *str1 = NULL; g_autofree gchar *str2 = NULL; g_autofree gchar *str4 = NULL; - g_autoptr(GByteArray) st2 = NULL; - g_autoptr(GByteArray) st3 = NULL; - g_autoptr(GByteArray) st_base2 = NULL; - g_autoptr(GByteArray) st_base = fu_struct_self_test_new(); - g_autoptr(GByteArray) st = fu_struct_self_test_wrapped_new(); + g_autoptr(FuStructSelfTestWrapped) st2 = NULL; + g_autoptr(FuStructSelfTestWrapped) st3 = NULL; + g_autoptr(FuStructSelfTest) st_base2 = NULL; + g_autoptr(FuStructSelfTest) st_base = fu_struct_self_test_new(); + g_autoptr(FuStructSelfTestWrapped) st = fu_struct_self_test_wrapped_new(); g_autoptr(GError) error = NULL; /* size */ - g_assert_cmpint(st->len, ==, 53); + g_assert_cmpint(st->buf->len, ==, 61); /* getters and setters */ fu_struct_self_test_wrapped_set_less(st, 0x99); fu_struct_self_test_wrapped_set_more(st, 0x12); g_assert_cmpint(fu_struct_self_test_wrapped_get_more(st), ==, 0x12); - str1 = fu_byte_array_to_string(st); + str1 = fu_byte_array_to_string(st->buf); g_assert_cmpstr(str1, ==, - "991234567833000000000000000000000000000000000000000041424344454600000000" - "0000000000000000dfdfdfdf0000000012"); + "99123456783b000000000000000000000000000000000000000041424344454600000000" + "0000000000000000dfdfdfdf00000000ffffffffffffffff12"); /* modify the base */ fu_struct_self_test_set_revision(st_base, 0xFE); ret = fu_struct_self_test_wrapped_set_base(st, st_base, &error); g_assert_no_error(error); g_assert_true(ret); - str4 = fu_byte_array_to_string(st); + str4 = fu_byte_array_to_string(st->buf); g_assert_cmpstr(str4, ==, - "991234567833000000fe0000000000000000000000000000000041424344454600000000" - "0000000000000000dfdfdfdf0000000012"); + "99123456783b000000fe0000000000000000000000000000000041424344454600000000" + "0000000000000000dfdfdfdf00000000ffffffffffffffff12"); /* parse */ - st2 = fu_struct_self_test_wrapped_parse(st->data, st->len, 0x0, &error); + st2 = fu_struct_self_test_wrapped_parse(st->buf->data, st->buf->len, 0x0, &error); g_assert_no_error(error); g_assert_nonnull(st2); g_assert_cmpint(fu_struct_self_test_wrapped_get_more(st), ==, 0x12); @@ -6717,7 +7311,7 @@ "FuStructSelfTestWrapped:\n" " less: 0x99\n" " base: FuStructSelfTest:\n" - " length: 0x33\n" + " length: 0x3b\n" " revision: 0xfe\n" " owner: 00000000-0000-0000-0000-000000000000\n" " oem_revision: 0x0\n" @@ -6726,12 +7320,12 @@ " more: 0x12"); /* parse failing signature */ - st->data[FU_STRUCT_SELF_TEST_WRAPPED_OFFSET_BASE] = 0xFF; - st3 = fu_struct_self_test_wrapped_parse(st->data, st->len, 0x0, &error); + st->buf->data[FU_STRUCT_SELF_TEST_WRAPPED_OFFSET_BASE] = 0xFF; + st3 = fu_struct_self_test_wrapped_parse(st->buf->data, st->buf->len, 0x0, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA); g_assert_null(st3); g_clear_error(&error); - ret = fu_struct_self_test_wrapped_validate(st->data, st->len, 0x0, &error); + ret = fu_struct_self_test_wrapped_validate(st->buf->data, st->buf->len, 0x0, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA); g_assert_false(ret); } @@ -6740,6 +7334,7 @@ fu_efi_load_option_path_func(void) { const gchar *tmp; + gboolean ret; g_autofree gchar *blobstr = NULL; g_autoptr(FuEfiDevicePathList) devpathlist = fu_efi_device_path_list_new(); g_autoptr(FuEfiLoadOption) loadopt = fu_efi_load_option_new(); @@ -6755,7 +7350,9 @@ g_assert_cmpstr(tmp, ==, "/foo"); fu_firmware_set_id(FU_FIRMWARE(loadopt), "id"); - fu_firmware_add_image(FU_FIRMWARE(loadopt), FU_FIRMWARE(devpathlist)); + ret = fu_firmware_add_image(FU_FIRMWARE(loadopt), FU_FIRMWARE(devpathlist), &error); + g_assert_no_error(error); + g_assert_true(ret); blob = fu_firmware_write(FU_FIRMWARE(loadopt), &error); g_assert_no_error(error); g_assert_nonnull(blob); @@ -6766,6 +7363,7 @@ static void fu_efi_load_option_hive_func(void) { + gboolean ret; g_autofree gchar *blobstr = NULL; g_autoptr(FuEfiDevicePathList) devpathlist = fu_efi_device_path_list_new(); g_autoptr(FuEfiLoadOption) loadopt = fu_efi_load_option_new(); @@ -6778,7 +7376,9 @@ g_assert_cmpint(fu_efi_load_option_get_kind(loadopt), ==, FU_EFI_LOAD_OPTION_KIND_HIVE); fu_firmware_set_id(FU_FIRMWARE(loadopt), "id"); - fu_firmware_add_image(FU_FIRMWARE(loadopt), FU_FIRMWARE(devpathlist)); + ret = fu_firmware_add_image(FU_FIRMWARE(loadopt), FU_FIRMWARE(devpathlist), &error); + g_assert_no_error(error); + g_assert_true(ret); blob = fu_firmware_write(FU_FIRMWARE(loadopt), &error); g_assert_no_error(error); g_assert_nonnull(blob); @@ -6849,6 +7449,7 @@ (void)g_setenv("CACHE_DIRECTORY", "/tmp/fwupd-self-test/cache", TRUE); g_test_add_func("/fwupd/cab{checksum}", fu_cab_checksum_func); + g_test_add_func("/fwupd/cab{compressed-size}", fu_cab_compressed_size_func); g_test_add_func("/fwupd/efi-lz77{decompressor}", fu_efi_lz77_decompressor_func); g_test_add_func("/fwupd/input-stream", fu_input_stream_func); g_test_add_func("/fwupd/input-stream{sum-overflow}", fu_input_stream_sum_overflow_func); @@ -6903,15 +7504,20 @@ g_test_add_func("/fwupd/backend", fu_backend_func); g_test_add_func("/fwupd/backend{emulate}", fu_backend_emulate_func); g_test_add_func("/fwupd/chunk", fu_chunk_func); + g_test_add_func("/fwupd/chunk-null", fu_chunk_array_null_func); g_test_add_func("/fwupd/chunks", fu_chunk_array_func); + g_test_add_func("/fwupd/common{checked-add}", fu_common_checked_add_func); + g_test_add_func("/fwupd/common{error-map}", fu_common_error_map_func); g_test_add_func("/fwupd/common{align-up}", fu_common_align_up_func); g_test_add_func("/fwupd/volume{gpt-type}", fu_volume_gpt_type_func); g_test_add_func("/fwupd/common{bitwise}", fu_common_bitwise_func); g_test_add_func("/fwupd/common{byte-array}", fu_common_byte_array_func); + g_test_add_func("/fwupd/common{byte-array-safe}", fu_common_byte_array_safe_func); g_test_add_func("/fwupd/common{crc}", fu_common_crc_func); g_test_add_func("/fwupd/common{guid}", fu_common_guid_func); g_test_add_func("/fwupd/common{string-append-kv}", fu_string_append_func); g_test_add_func("/fwupd/common{version-guess-format}", fu_version_guess_format_func); + g_test_add_func("/fwupd/common{version-verify-format}", fu_version_verify_format_func); g_test_add_func("/fwupd/common{strtoull}", fu_strtoull_func); g_test_add_func("/fwupd/common{strtoll}", fu_strtoll_func); g_test_add_func("/fwupd/common{version}", fu_common_version_func); @@ -6933,12 +7539,16 @@ g_test_add_func("/fwupd/efi-load-option{hive}", fu_efi_load_option_hive_func); g_test_add_func("/fwupd/efi-x509-signature", fu_plugin_efi_x509_signature_func); g_test_add_func("/fwupd/efi-signature-list", fu_plugin_efi_signature_list_func); + g_test_add_func("/fwupd/efi-variable-authentication2", + fu_plugin_efi_variable_authentication2_func); g_test_add_func("/fwupd/efivar", fu_efivar_func); g_test_add_func("/fwupd/efivar{bootxxxx}", fu_efivar_boot_func); g_test_add_func("/fwupd/hwids", fu_hwids_func); g_test_add_func("/fwupd/context{flags}", fu_context_flags_func); g_test_add_func("/fwupd/context{backends}", fu_context_backends_func); + g_test_add_func("/fwupd/context{efivars}", fu_context_efivars_func); g_test_add_func("/fwupd/context{hwids-dmi}", fu_context_hwids_dmi_func); + g_test_add_func("/fwupd/context{hwids-unset}", fu_context_hwids_unset_func); g_test_add_func("/fwupd/context{hwids-fdt}", fu_context_hwids_fdt_func); g_test_add_func("/fwupd/context{firmware-gtypes}", fu_context_firmware_gtypes_func); g_test_add_func("/fwupd/context{state}", fu_context_state_func); @@ -6975,9 +7585,14 @@ g_test_add_func("/fwupd/firmware{builder-round-trip}", fu_firmware_builder_round_trip_func); g_test_add_func("/fwupd/firmware{fmap}", fu_firmware_fmap_func); g_test_add_func("/fwupd/firmware{gtypes}", fu_firmware_new_from_gtypes_func); + g_test_add_func("/fwupd/firmware{sorted}", fu_firmware_sorted_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/device", fu_device_func); + g_test_add_func("/fwupd/device{parent-name-prefix}", fu_device_parent_name_prefix_func); + g_test_add_func("/fwupd/device{id-for-display}", fu_device_id_display_func); + g_test_add_func("/fwupd/device{possible-plugin}", fu_device_possible_plugin_func); + g_test_add_func("/fwupd/device{udev}", fu_device_udev_func); g_test_add_func("/fwupd/device{event}", fu_device_event_func); g_test_add_func("/fwupd/device{event-uncompressed}", fu_device_event_uncompressed_func); g_test_add_func("/fwupd/device{event-donor}", fu_device_event_donor_func); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-self-test.rs fwupd-2.0.20/libfwupdplugin/fu-self-test.rs --- fwupd-2.0.8/libfwupdplugin/fu-self-test.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-self-test.rs 2026-02-26 11:36:18.000000000 +0000 @@ -17,8 +17,9 @@ oem_id: [char; 6] == "ABCDEF", oem_table_id: [char; 8], oem_revision: u32le, - asl_compiler_id: [u8; 4] = 0xDF, + asl_compiler_id: [u8; 4] = [0xDF; 4], asl_compiler_revision: u32le, + reserved: [u8; 8] == [0xFF; 8], } #[derive(New, Validate, Parse, ToString)] diff -Nru fwupd-2.0.8/libfwupdplugin/fu-smbios.c fwupd-2.0.20/libfwupdplugin/fu-smbios.c --- fwupd-2.0.8/libfwupdplugin/fu-smbios.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-smbios.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,7 +19,6 @@ #include "fwupd-error.h" #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-input-stream.h" #include "fu-path.h" @@ -82,7 +81,7 @@ if (st_str == NULL) return FALSE; length = fu_struct_smbios_structure_get_length(st_str); - if (length < st_str->len) { + if (length < st_str->buf->len) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, @@ -175,7 +174,7 @@ guint8 csum = 0; g_autofree gchar *version_str = NULL; g_autofree gchar *intermediate_anchor_str = NULL; - g_autoptr(GByteArray) st_ep32 = NULL; + g_autoptr(FuStructSmbiosEp32) st_ep32 = NULL; /* verify checksum */ st_ep32 = fu_struct_smbios_ep32_parse(buf, bufsz, 0x0, error); @@ -227,7 +226,7 @@ { guint8 csum = 0; g_autofree gchar *version_str = NULL; - g_autoptr(GByteArray) st_ep64 = NULL; + g_autoptr(FuStructSmbiosEp64) st_ep64 = NULL; /* verify checksum */ st_ep64 = fu_struct_smbios_ep64_parse(buf, bufsz, 0x0, error); @@ -278,7 +277,7 @@ /* get the smbios entry point */ ep_fn = g_build_filename(path, "smbios_entry_point", NULL); if (!g_file_get_contents(ep_fn, &ep_raw, &sz, error)) { - fu_error_convert(error); + fwupd_error_convert(error); return FALSE; } @@ -314,16 +313,15 @@ /* get the DMI data */ dmi_fn = g_build_filename(path, "DMI", NULL); if (!g_file_get_contents(dmi_fn, &dmi_raw, &sz, error)) { - fu_error_convert(error); + fwupd_error_convert(error); return FALSE; } if (sz > self->structure_table_len) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "invalid DMI data size, got %" G_GSIZE_FORMAT - " bytes, expected %" G_GUINT32_FORMAT, - sz, + "invalid DMI data size, got 0x%x bytes, expected 0x%x", + (guint)sz, self->structure_table_len); return FALSE; } @@ -333,7 +331,10 @@ } static gboolean -fu_smbios_parse(FuFirmware *firmware, GInputStream *stream, FwupdInstallFlags flags, GError **error) +fu_smbios_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) { FuSmbios *self = FU_SMBIOS(firmware); g_autoptr(GBytes) fw = NULL; @@ -405,15 +406,13 @@ error); #else g_autofree gchar *path = NULL; - g_autofree gchar *sysfsfwdir = NULL; g_autoptr(GError) error_local = NULL; g_return_val_if_fail(FU_IS_SMBIOS(self), FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); /* DMI */ - sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - path = g_build_filename(sysfsfwdir, "dmi", "tables", NULL); + path = fu_path_build(FU_PATH_KIND_SYSFSDIR_FW, "dmi", "tables", NULL); if (!g_file_test(path, G_FILE_TEST_EXISTS)) { g_set_error(error, FWUPD_ERROR, @@ -595,7 +594,7 @@ return NULL; } if (item->buf->data[offset] == 0x00) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no data available"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no data available"); return NULL; } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-smbios.rs fwupd-2.0.20/libfwupdplugin/fu-smbios.rs --- fwupd-2.0.8/libfwupdplugin/fu-smbios.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-smbios.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,7 @@ // Copyright 2023 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later +#[repr(u8)] enum FuSmbiosStructureType { Bios, System, @@ -90,3 +91,59 @@ length: u8, handle: u16le, } + +#[repr(u64le)] +enum FuSmbiosBiosCharacteristics { + _None = 1 << 0, + _Reserved = 1 << 1, + _Unknown = 1 << 2, + BiosCharacteristicsNotSupported = 1 << 3, + IsaSupported = 1 << 4, + McaSupported = 1 << 5, + EisaSupported = 1 << 6, + PciSupported = 1 << 7, + PccardSupported = 1 << 8, + PlugAndPlaySupported = 1 << 9, + ApmSupported = 1 << 10, + BiosIsUpgradeable = 1 << 11, + BiosShadowingAllowed = 1 << 12, + VlvesaSupported = 1 << 13, + EscdSupportAvailable = 1 << 14, + BootFromCdSupported = 1 << 15, + SelectableBootSupported = 1 << 16, +} + +#[repr(u16le)] +enum FuSmbiosBiosCharacteristicsExt { + AcpiSupported = 1 << 0, + UsbLegacySupported = 1 << 1, + AgpSupported = 1 << 2, + I2oBootSupported = 1 << 3, + Ls120SuperDiskBootSupported = 1 << 4, + AtapiZipSupported = 1 << 5, + 1394BootSupported = 1 << 6, + SmartBatterySupported = 1 << 7, + BiosBootSpecificationSupported = 1 << 8, + FunctionKeyNetworkBootSupported = 1 << 9, + EnableTargetedContentDistribution = 1 << 10, + UefiSpecificationSupported = 1 << 11, + IsVirtualMachine = 1 << 12, + ManufacturingModeSupported = 1 << 13, + ManufacturingModeEnabled = 1 << 14, + _Reserved = 1 << 15, +} + +#[derive(ParseBytes, Default)] +#[repr(C, packed)] +struct FuStructSmbiosBiosInformation { + type: FuSmbiosStructureType == Bios, + length: u8, + handle: u16le, + vendor: u8, + version: u8, + starting_addr_segment: u16le, + release_date: u8, + rom_size: u8, + characteristics: FuSmbiosBiosCharacteristics, + characteristics_ext: FuSmbiosBiosCharacteristicsExt, +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-srec-firmware.c fwupd-2.0.20/libfwupdplugin/fu-srec-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-srec-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-srec-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,7 +12,6 @@ #include "fu-byte-array.h" #include "fu-chunk-array.h" -#include "fu-common.h" #include "fu-firmware-common.h" #include "fu-srec-firmware.h" #include "fu-string.h" @@ -160,7 +159,7 @@ typedef struct { FuSrecFirmware *self; - FwupdInstallFlags flags; + FuFirmwareParseFlags flags; gboolean got_eof; } FuSrecFirmwareTokenHelper; @@ -231,7 +230,7 @@ } /* checksum check */ - if ((helper->flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((helper->flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint8 rec_csum = 0; guint8 rec_csum_expected; for (guint8 i = 0; i < rec_count; i++) { @@ -373,7 +372,7 @@ static gboolean fu_srec_firmware_tokenize(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSrecFirmware *self = FU_SREC_FIRMWARE(firmware); @@ -397,7 +396,7 @@ static gboolean fu_srec_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSrecFirmware *self = FU_SREC_FIRMWARE(firmware); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-string.c fwupd-2.0.20/libfwupdplugin/fu-string.c --- fwupd-2.0.8/libfwupdplugin/fu-string.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-string.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,6 @@ #include "fu-byte-array.h" #include "fu-chunk-array.h" -#include "fu-input-stream.h" #include "fu-mem.h" #include "fu-partial-input-stream.h" #include "fu-string.h" @@ -293,6 +292,36 @@ } /** + * fu_string_strip: + * @str: a #GString, e.g. ` test ` + * + * Removes leading and trailing whitespace from a mutable string. + * + * Since: 2.0.17 + **/ +void +fu_string_strip(GString *str) +{ + guint i; + + /* leading whitespace */ + for (i = 0; i < str->len; i++) { + if (!g_ascii_isspace(str->str[i])) + break; + } + if (i > 0) + g_string_erase(str, 0, i); + + /* trailing whitespace */ + for (i = 0; i < str->len; i++) { + if (!g_ascii_isspace(str->str[str->len - (i + 1)])) + break; + } + if (i < str->len) + g_string_truncate(str, str->len - i); +} + +/** * fu_strdup: * @str: a string, e.g. ` test ` * @bufsz: the maximum size of @str @@ -501,7 +530,7 @@ if (offset > 0) { stream_partial = fu_partial_input_stream_new(stream, offset, G_MAXSIZE, error); if (stream_partial == NULL) { - g_prefix_error(error, "failed to cut string: "); + g_prefix_error_literal(error, "failed to cut string: "); return FALSE; } } else { @@ -597,7 +626,7 @@ /** * fu_strsafe: * @str: (nullable): a string to make safe for printing - * @maxsz: maximum size of returned string + * @maxsz: maximum size of returned string, or %G_MAXSIZE for no limit * * Converts a string into something that can be safely printed. * @@ -609,16 +638,15 @@ fu_strsafe(const gchar *str, gsize maxsz) { gboolean valid = FALSE; - g_autoptr(GString) tmp = NULL; + g_autoptr(GString) tmp = g_string_new(NULL); /* sanity check */ if (str == NULL || maxsz == 0) return NULL; /* replace non-printable chars with '.' */ - tmp = g_string_sized_new(maxsz); for (gsize i = 0; i < maxsz && str[i] != '\0'; i++) { - if (!g_ascii_isprint(str[i])) { + if (!g_ascii_isgraph(str[i]) && !g_ascii_isspace(str[i])) { g_string_append_c(tmp, '.'); continue; } @@ -636,7 +664,7 @@ /** * fu_strsafe_bytes: * @blob: (not nullable): a #GBytes - * @maxsz: maximum size of returned string + * @maxsz: maximum size of returned string, or %G_MAXSIZE for no limit * * Converts a #GBytes into something that can be safely printed. * diff -Nru fwupd-2.0.8/libfwupdplugin/fu-string.h fwupd-2.0.20/libfwupdplugin/fu-string.h --- fwupd-2.0.8/libfwupdplugin/fu-string.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-string.h 2026-02-26 11:36:18.000000000 +0000 @@ -50,6 +50,8 @@ fu_strjoin(const gchar *separator, GPtrArray *array) G_GNUC_NON_NULL(1, 2); GString * fu_strdup(const gchar *str, gsize bufsz, gsize offset) G_GNUC_NON_NULL(1); +void +fu_string_strip(GString *str) G_GNUC_NON_NULL(1); /** * FuStrsplitFunc: @@ -89,7 +91,7 @@ typedef enum { FU_UTF_CONVERT_FLAG_NONE = 0, FU_UTF_CONVERT_FLAG_APPEND_NUL = 1 << 0, -} FuUtfConvertFlags; +} G_GNUC_FLAG_ENUM FuUtfConvertFlags; gchar * fu_utf16_to_utf8_byte_array(GByteArray *array, FuEndianType endian, GError **error) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-test-device.c fwupd-2.0.20/libfwupdplugin/fu-test-device.c --- fwupd-2.0.8/libfwupdplugin/fu-test-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-test-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -/* - * Copyright 2024 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-test-device.h" - -struct _FuTestDevice { - FuDevice parent_instance; -}; - -G_DEFINE_TYPE(FuTestDevice, fu_test_device, FU_TYPE_DEVICE) - -static void -fu_test_device_init(FuTestDevice *self) -{ -} - -static void -fu_test_device_class_init(FuTestDeviceClass *klass) -{ -} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-test-device.h fwupd-2.0.20/libfwupdplugin/fu-test-device.h --- fwupd-2.0.8/libfwupdplugin/fu-test-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-test-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -/* - * Copyright 2024 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "fu-device.h" - -#define FU_TYPE_TEST_DEVICE (fu_test_device_get_type()) -G_DECLARE_FINAL_TYPE(FuTestDevice, fu_test_device, FU, TEST_DEVICE, FuDevice) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-udev-device.c fwupd-2.0.20/libfwupdplugin/fu-udev-device.c --- fwupd-2.0.8/libfwupdplugin/fu-udev-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-udev-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,10 +21,11 @@ #include #include +#include "fu-byte-array.h" #include "fu-device-event-private.h" #include "fu-device-private.h" -#include "fu-i2c-device.h" #include "fu-ioctl-private.h" +#include "fu-output-stream.h" #include "fu-path.h" #include "fu-string.h" #include "fu-udev-device-private.h" @@ -45,7 +46,7 @@ gchar *devtype; guint64 number; FuIOChannel *io_channel; - FuIoChannelOpenFlag open_flags; + FuIoChannelOpenFlags open_flags; GHashTable *properties; gboolean properties_valid; } FuUdevDevicePrivate; @@ -92,7 +93,7 @@ { FuUdevDevice *self = FU_UDEV_DEVICE(device); FuUdevDevicePrivate *priv = GET_PRIVATE(self); - g_autofree gchar *open_flags = fu_io_channel_open_flag_to_string(priv->open_flags); + g_autofree gchar *open_flags = fu_io_channel_open_flags_to_string(priv->open_flags); fwupd_codec_string_append_hex(str, idt, "Number", priv->number); fwupd_codec_string_append(str, idt, "Subsystem", priv->subsystem); @@ -121,7 +122,7 @@ priv->bind_id = fu_udev_device_read_property(self, "HID_PHYS", error); return priv->bind_id != NULL; } - if (g_strcmp0(priv->subsystem, "usb") == 0) { + if (g_strcmp0(priv->subsystem, "usb") == 0 || g_strcmp0(priv->subsystem, "i2c") == 0) { priv->bind_id = g_path_get_basename(fu_udev_device_get_sysfs_path(self)); return TRUE; } @@ -349,7 +350,7 @@ g_autofree gchar *subsystem_tmp = fu_udev_device_get_symlink_target(self, "subsystem", error); if (subsystem_tmp == NULL) { - g_prefix_error(error, "failed to read subsystem: "); + g_prefix_error_literal(error, "failed to read subsystem: "); return FALSE; } fu_udev_device_set_subsystem(self, subsystem_tmp); @@ -471,14 +472,6 @@ NULL); } - /* determine if we're wired internally */ - if (g_strcmp0(priv->subsystem, "i2c") != 0) { - g_autoptr(FuDevice) parent_i2c = - fu_device_get_backend_parent_with_subsystem(device, "i2c", NULL); - if (parent_i2c != NULL) - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL); - } - /* success */ return TRUE; } @@ -529,7 +522,6 @@ FuUdevDevice *self = FU_UDEV_DEVICE(device); FuUdevDevicePrivate *priv = GET_PRIVATE(self); g_autofree gchar *fn = NULL; - g_autoptr(GFile) file = NULL; g_autoptr(GOutputStream) stream = NULL; /* emulated */ @@ -548,9 +540,7 @@ /* write bus ID to file */ if (!fu_udev_device_ensure_bind_id(self, error)) return FALSE; - file = g_file_new_for_path(fn); - stream = - G_OUTPUT_STREAM(g_file_replace(file, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error)); + stream = fu_output_stream_from_path(fn, error); if (stream == NULL) return FALSE; return g_output_stream_write_all(stream, @@ -620,7 +610,7 @@ error); } -static FuIoChannelOpenFlag +static FuIoChannelOpenFlags fu_udev_device_get_open_flags(FuUdevDevice *self) { FuUdevDevicePrivate *priv = GET_PRIVATE(self); @@ -638,13 +628,13 @@ } static void -fu_udev_device_incorporate(FuDevice *self, FuDevice *donor) +fu_udev_device_incorporate(FuDevice *device, FuDevice *donor) { - FuUdevDevice *uself = FU_UDEV_DEVICE(self); + FuUdevDevice *uself = FU_UDEV_DEVICE(device); 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(device)); g_return_if_fail(FU_IS_UDEV_DEVICE(donor)); if (priv->device_file == NULL) @@ -750,6 +740,9 @@ fu_udev_device_get_sysfs_path(FuUdevDevice *self) { g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL); + + if (fu_device_has_private_flag(FU_DEVICE(self), FU_UDEV_DEVICE_FLAG_SYSFS_USE_PHYSICAL_ID)) + return fu_device_get_physical_id(FU_DEVICE(self)); return fu_device_get_backend_id(FU_DEVICE(self)); } @@ -856,7 +849,7 @@ return NULL; } g_propagate_error(error, g_steal_pointer(&error_local)); - fu_error_convert(error); + fwupd_error_convert(error); return NULL; } fn = g_dir_read_name(dir); @@ -1046,7 +1039,7 @@ * Since: 2.0.0 **/ void -fu_udev_device_remove_open_flag(FuUdevDevice *self, FuIoChannelOpenFlag flag) +fu_udev_device_remove_open_flag(FuUdevDevice *self, FuIoChannelOpenFlags flag) { FuUdevDevicePrivate *priv = GET_PRIVATE(self); g_return_if_fail(FU_IS_UDEV_DEVICE(self)); @@ -1066,7 +1059,7 @@ * Since: 2.0.0 **/ void -fu_udev_device_add_open_flag(FuUdevDevice *self, FuIoChannelOpenFlag flag) +fu_udev_device_add_open_flag(FuUdevDevice *self, FuIoChannelOpenFlags flag) { FuUdevDevicePrivate *priv = GET_PRIVATE(self); g_return_if_fail(FU_IS_UDEV_DEVICE(self)); @@ -1091,10 +1084,10 @@ * could add more flags, or set the flags back to NONE -- detect and fixup */ if (priv->device_file != NULL && priv->open_flags == FU_IO_CHANNEL_OPEN_FLAG_NONE) { #ifndef SUPPORTED_BUILD - g_critical("%s [%s] forgot to call fu_udev_device_add_open_flag() with " + g_autofree gchar *id_display = fu_device_get_id_display(device); + g_critical("%s forgot to call fu_udev_device_add_open_flag() with " "FU_IO_CHANNEL_OPEN_FLAG_READ and/or FU_IO_CHANNEL_OPEN_FLAG_WRITE", - fu_device_get_name(device), - fu_device_get_id(device)); + id_display); #endif fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); @@ -1143,6 +1136,29 @@ } /** + * fu_udev_device_reopen: + * @self: a #FuDevice + * @error: (nullable): optional return location for an error + * + * Closes and opens the device, typically used to close() and open() the device-file which is + * required by some ioctls. + * + * Returns: %TRUE for success + * + * Since: 2.0.9 + **/ +gboolean +fu_udev_device_reopen(FuUdevDevice *self, GError **error) +{ + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!fu_udev_device_close(FU_DEVICE(self), error)) + return FALSE; + return fu_udev_device_open(FU_DEVICE(self), error); +} + +/** * fu_udev_device_ioctl_new: * @self: a #FuUdevDevice * @@ -1182,12 +1198,12 @@ /* not open! */ if (priv->io_channel == NULL) { + g_autofree gchar *id_display = fu_device_get_id_display(FU_DEVICE(self)); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "%s [%s] has not been opened", - fu_device_get_id(FU_DEVICE(self)), - fu_device_get_name(FU_DEVICE(self))); + "%s has not been opened", + id_display); return FALSE; } @@ -1222,10 +1238,13 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "ioctl error: %s [%i]", - g_strerror(errno), + fwupd_strerror(errno), errno); #else - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unspecified ioctl error"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unspecified ioctl error"); #endif return FALSE; } @@ -1233,10 +1252,10 @@ /* success */ return TRUE; #else - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Not supported as not found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported as not found"); return FALSE; #endif } @@ -1266,7 +1285,7 @@ g_return_val_if_fail(buf != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - /* emulated */ + /* need event ID */ if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)), FU_CONTEXT_FLAG_SAVE_EVENTS)) { @@ -1291,12 +1310,12 @@ /* not open! */ if (priv->io_channel == NULL) { + g_autofree gchar *id_display = fu_device_get_id_display(FU_DEVICE(self)); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "%s [%s] has not been opened", - fu_device_get_id(FU_DEVICE(self)), - fu_device_get_name(FU_DEVICE(self))); + "%s has not been opened", + id_display); return FALSE; } @@ -1311,7 +1330,7 @@ #endif "failed to read from port 0x%04x: %s", (guint)port, - g_strerror(errno)); + fwupd_strerror(errno)); fwupd_error_convert(error); return FALSE; } @@ -1355,12 +1374,12 @@ /* not open! */ if (priv->io_channel == NULL) { + g_autofree gchar *id_display = fu_device_get_id_display(FU_DEVICE(self)); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "%s [%s] has not been opened", - fu_device_get_id(FU_DEVICE(self)), - fu_device_get_name(FU_DEVICE(self))); + "%s has not been opened", + id_display); return FALSE; } return fu_io_channel_seek(priv->io_channel, offset, error); @@ -1388,22 +1407,52 @@ GError **error) { FuUdevDevicePrivate *priv = GET_PRIVATE(self); + FuDeviceEvent *event = NULL; + g_autofree gchar *event_id = NULL; g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); /* emulated */ - if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) && + !fu_device_check_fwupd_version(FU_DEVICE(self), "2.0.13")) { + return TRUE; + } + + /* need event ID */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || + fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)), + FU_CONTEXT_FLAG_SAVE_EVENTS)) { + g_autofree gchar *data_base64 = g_base64_encode(buf, bufsz); + event_id = g_strdup_printf("Pwrite:" + "Port=0x%x," + "Data=%s," + "Length=0x%x", + (guint)port, + data_base64, + (guint)bufsz); + } + + /* emulated */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) { + event = fu_device_load_event(FU_DEVICE(self), event_id, error); + if (event == NULL) + return FALSE; return TRUE; + } + + /* save */ + if (event_id != NULL) + event = fu_device_save_event(FU_DEVICE(self), event_id); /* not open! */ if (priv->io_channel == NULL) { + g_autofree gchar *id_display = fu_device_get_id_display(FU_DEVICE(self)); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "%s [%s] has not been opened", - fu_device_get_id(FU_DEVICE(self)), - fu_device_get_name(FU_DEVICE(self))); + "%s has not been opened", + id_display); return FALSE; } @@ -1419,10 +1468,16 @@ #endif "failed to write to port %04x: %s", (guint)port, - g_strerror(errno)); + fwupd_strerror(errno)); fwupd_error_convert(error); return FALSE; } + + /* save response */ + if (event != NULL) + fu_device_event_set_data(event, "Data", buf, bufsz); + + /* success */ return TRUE; #else g_set_error_literal(error, @@ -1468,7 +1523,7 @@ g_return_val_if_fail(buf != NULL, FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - /* emulated */ + /* need event ID */ if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)), FU_CONTEXT_FLAG_SAVE_EVENTS)) { @@ -1491,12 +1546,12 @@ /* not open! */ if (priv->io_channel == NULL) { + g_autofree gchar *id_display = fu_device_get_id_display(FU_DEVICE(self)); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "%s [%s] has not been opened", - fu_device_get_id(FU_DEVICE(self)), - fu_device_get_name(FU_DEVICE(self))); + "%s has not been opened", + id_display); return FALSE; } if (!fu_io_channel_read_raw(priv->io_channel, @@ -1555,6 +1610,41 @@ } /** + * fu_udev_device_read_byte_array: + * @self: a #FuUdevDevice + * @count: bytes to read + * @timeout_ms: timeout in ms + * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: (nullable): optional return location for an error + * + * Read a buffer from a file descriptor. + * + * Returns: (transfer full): A #GByteArray, or %NULL + * + * Since: 2.0.18 + **/ +GByteArray * +fu_udev_device_read_byte_array(FuUdevDevice *self, + gsize count, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) +{ + gsize bytes_read = 0; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL); + g_return_val_if_fail(count > 0, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + fu_byte_array_set_size(buf, count, 0x0); + if (!fu_udev_device_read(self, buf->data, buf->len, &bytes_read, timeout_ms, flags, error)) + return NULL; + g_byte_array_set_size(buf, bytes_read); + return g_steal_pointer(&buf); +} + +/** * fu_udev_device_write: * @self: a #FuUdevDevice * @buf: (out): data @@ -1580,6 +1670,7 @@ FuUdevDevicePrivate *priv = GET_PRIVATE(self); FuDeviceEvent *event = NULL; g_autofree gchar *event_id = NULL; + g_autoptr(GError) error_local = NULL; g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE); g_return_val_if_fail(buf != NULL, FALSE); @@ -1598,6 +1689,8 @@ event = fu_device_load_event(FU_DEVICE(self), event_id, error); if (event == NULL) return FALSE; + if (!fu_device_event_check_error(event, error)) + return FALSE; return event != NULL; } @@ -1607,16 +1700,25 @@ /* not open! */ if (priv->io_channel == NULL) { + g_autofree gchar *id_display = fu_device_get_id_display(FU_DEVICE(self)); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "%s [%s] has not been opened", - fu_device_get_id(FU_DEVICE(self)), - fu_device_get_name(FU_DEVICE(self))); + "%s has not been opened", + id_display); return FALSE; } - if (!fu_io_channel_write_raw(priv->io_channel, buf, bufsz, timeout_ms, flags, error)) + if (!fu_io_channel_write_raw(priv->io_channel, + buf, + bufsz, + timeout_ms, + flags, + &error_local)) { + if (event != NULL) + fu_device_event_set_error(event, error_local); + g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; + } /* success */ return TRUE; @@ -1655,6 +1757,110 @@ } /** + * fu_udev_device_write_byte_array: + * @self: a #FuUdevDevice + * @buf: a #GByteArray + * @timeout_ms: timeout in ms + * @flags: channel flags, e.g. %FU_IO_CHANNEL_FLAG_SINGLE_SHOT + * @error: (nullable): optional return location for an error + * + * Write a buffer to a file descriptor. + * + * Returns: %TRUE for success + * + * Since: 2.0.18 + **/ +gboolean +fu_udev_device_write_byte_array(FuUdevDevice *self, + GByteArray *buf, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) +{ + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), FALSE); + g_return_val_if_fail(buf != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + return fu_udev_device_write(self, buf->data, buf->len, timeout_ms, flags, error); +} + +/** + * fu_udev_device_list_sysfs: + * @self: a #FuUdevDevice + * @error: (nullable): optional return location for an error + * + * Lists all the sysfs attributes. + * + * Returns: (transfer container) (element-type utf8): basenames, or %NULL + * + * Since: 2.0.9 + **/ +GPtrArray * +fu_udev_device_list_sysfs(FuUdevDevice *self, GError **error) +{ + FuDeviceEvent *event = NULL; + const gchar *basename; + g_autofree gchar *event_id = NULL; + g_autoptr(GDir) dir = NULL; + g_autoptr(GPtrArray) attrs = g_ptr_array_new_with_free_func(g_free); + + g_return_val_if_fail(FU_IS_UDEV_DEVICE(self), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* need event ID */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || + fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)), + FU_CONTEXT_FLAG_SAVE_EVENTS)) { + event_id = g_strdup("ListAttr"); + } + + /* emulated */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) { + const gchar *value; + g_auto(GStrv) attrs_strv = NULL; + + event = fu_device_load_event(FU_DEVICE(self), event_id, error); + if (event == NULL) + return NULL; + value = fu_device_event_get_str(event, "Data", error); + if (value == NULL) + return NULL; + attrs_strv = g_strsplit(value, "\n", -1); + for (guint i = 0; attrs_strv[i] != NULL; i++) + g_ptr_array_add(attrs, g_strdup(attrs_strv[i])); + return g_steal_pointer(&attrs); + } + + /* save */ + if (event_id != NULL) + event = fu_device_save_event(FU_DEVICE(self), event_id); + + /* list the files and directories */ + if (fu_udev_device_get_sysfs_path(self) == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "sysfs_path undefined"); + return NULL; + } + dir = g_dir_open(fu_udev_device_get_sysfs_path(self), 0, error); + if (dir == NULL) { + fwupd_error_convert(error); + return NULL; + } + while ((basename = g_dir_read_name(dir)) != NULL) + g_ptr_array_add(attrs, g_strdup(basename)); + + /* save for emulation */ + if (event != NULL) { + g_autofree gchar *value = fu_strjoin("\n", attrs); + fu_device_event_set_str(event, "Data", value); + } + + /* success */ + return g_steal_pointer(&attrs); +} + +/** * fu_udev_device_read_sysfs: * @self: a #FuUdevDevice * @attr: sysfs attribute name @@ -1717,8 +1923,10 @@ timeout_ms, FU_IO_CHANNEL_FLAG_NONE, error); - if (buf == NULL) + if (buf == NULL) { + g_prefix_error(error, "failed read of %s: ", path); return NULL; + } if (!g_utf8_validate((const gchar *)buf->data, buf->len, NULL)) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "non UTF-8 data"); return NULL; @@ -1805,8 +2013,10 @@ return NULL; blob = fu_io_channel_read_bytes(io_channel, count, timeout_ms, FU_IO_CHANNEL_FLAG_NONE, error); - if (blob == NULL) + if (blob == NULL) { + g_prefix_error(error, "failed read of %s: ", path); return NULL; + } /* save for emulation */ if (event != NULL) @@ -1876,12 +2086,18 @@ /* save */ if (event_id != NULL) event = fu_device_save_event(FU_DEVICE(self), event_id); - return fu_io_channel_write_raw(io_channel, - (const guint8 *)val, - strlen(val), - timeout_ms, - FU_IO_CHANNEL_FLAG_NONE, - error); + if (!fu_io_channel_write_raw(io_channel, + (const guint8 *)val, + strlen(val), + timeout_ms, + FU_IO_CHANNEL_FLAG_NONE, + error)) { + g_prefix_error(error, "failed write of %s: ", path); + return FALSE; + } + + /* success */ + return TRUE; } /** @@ -1945,11 +2161,17 @@ /* save */ if (event_id != NULL) event = fu_device_save_event(FU_DEVICE(self), event_id); - return fu_io_channel_write_byte_array(io_channel, - buf, - timeout_ms, - FU_IO_CHANNEL_FLAG_NONE, - error); + if (!fu_io_channel_write_byte_array(io_channel, + buf, + timeout_ms, + FU_IO_CHANNEL_FLAG_NONE, + error)) { + g_prefix_error(error, "failed write of %s: ", path); + return FALSE; + } + + /* success */ + return TRUE; } /** @@ -2014,11 +2236,17 @@ /* save */ if (event_id != NULL) event = fu_device_save_event(FU_DEVICE(self), event_id); - return fu_io_channel_write_bytes(io_channel, - blob, - timeout_ms, - FU_IO_CHANNEL_FLAG_NONE, - error); + if (!fu_io_channel_write_bytes(io_channel, + blob, + timeout_ms, + FU_IO_CHANNEL_FLAG_NONE, + error)) { + g_prefix_error(error, "failed write of %s: ", path); + return FALSE; + } + + /* success */ + return TRUE; } /** @@ -2131,10 +2359,13 @@ return NULL; uevent_lines = g_strsplit(str, "\n", -1); for (guint i = 0; uevent_lines[i] != NULL; i++) { - g_autofree gchar **kvs = g_strsplit(uevent_lines[i], "=", 2); - g_hash_table_insert(priv->properties, - g_steal_pointer(&kvs[0]), - g_steal_pointer(&kvs[1])); + /* only split KEY=VALUE */ + if (g_strstr_len(uevent_lines[i], -1, "=") != NULL) { + g_autofree gchar **kvs = g_strsplit(uevent_lines[i], "=", 2); + g_hash_table_insert(priv->properties, + g_steal_pointer(&kvs[0]), + g_steal_pointer(&kvs[1])); + } } priv->properties_valid = TRUE; } @@ -2337,6 +2568,7 @@ priv->properties = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); fu_device_set_acquiesce_delay(FU_DEVICE(self), 2500); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); + fu_device_register_private_flag(FU_DEVICE(self), FU_UDEV_DEVICE_FLAG_SYSFS_USE_PHYSICAL_ID); g_signal_connect(FU_DEVICE(self), "notify::vid", G_CALLBACK(fu_udev_device_vid_notify_cb), diff -Nru fwupd-2.0.8/libfwupdplugin/fu-udev-device.h fwupd-2.0.20/libfwupdplugin/fu-udev-device.h --- fwupd-2.0.8/libfwupdplugin/fu-udev-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-udev-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -24,6 +24,13 @@ */ #define FU_UDEV_DEVICE_ATTR_READ_TIMEOUT_DEFAULT 50 /* ms */ +/** + * FU_UDEV_DEVICE_FLAG_SYSFS_USE_PHYSICAL_ID: + * + * Use the physical ID for the sysfs path rather than the backend ID. + */ +#define FU_UDEV_DEVICE_FLAG_SYSFS_USE_PHYSICAL_ID "sysfs-use-physical-id" + const gchar * fu_udev_device_get_device_file(FuUdevDevice *self) G_GNUC_NON_NULL(1); void @@ -49,9 +56,9 @@ const gchar *subsystems, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); void -fu_udev_device_add_open_flag(FuUdevDevice *self, FuIoChannelOpenFlag flag) G_GNUC_NON_NULL(1); +fu_udev_device_add_open_flag(FuUdevDevice *self, FuIoChannelOpenFlags flag) G_GNUC_NON_NULL(1); void -fu_udev_device_remove_open_flag(FuUdevDevice *self, FuIoChannelOpenFlag flag) G_GNUC_NON_NULL(1); +fu_udev_device_remove_open_flag(FuUdevDevice *self, FuIoChannelOpenFlags flag) G_GNUC_NON_NULL(1); FuIOChannel * fu_udev_device_get_io_channel(FuUdevDevice *self) G_GNUC_NON_NULL(1); @@ -82,6 +89,12 @@ FuIOChannelFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); gboolean +fu_udev_device_write_byte_array(FuUdevDevice *self, + GByteArray *buf, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); +gboolean fu_udev_device_read(FuUdevDevice *self, guint8 *buf, gsize bufsz, @@ -95,6 +108,12 @@ guint timeout_ms, FuIOChannelFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); +GByteArray * +fu_udev_device_read_byte_array(FuUdevDevice *self, + gsize count, + guint timeout_ms, + FuIOChannelFlags flags, + GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); gboolean fu_udev_device_seek(FuUdevDevice *self, goffset offset, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); @@ -102,6 +121,9 @@ fu_udev_device_read_property(FuUdevDevice *self, const gchar *key, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); +GPtrArray * +fu_udev_device_list_sysfs(FuUdevDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT + G_GNUC_NON_NULL(1); gchar * fu_udev_device_read_sysfs(FuUdevDevice *self, const gchar *attr, guint timeout_ms, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2); @@ -135,3 +157,6 @@ fu_udev_device_get_devtype(FuUdevDevice *self) G_GNUC_NON_NULL(1); gchar * fu_udev_device_get_subsystem_devtype(FuUdevDevice *self) G_GNUC_NON_NULL(1); +gboolean +fu_udev_device_reopen(FuUdevDevice *self, GError **error) G_GNUC_WARN_UNUSED_RESULT + G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-uefi-device.c fwupd-2.0.20/libfwupdplugin/fu-uefi-device.c --- fwupd-2.0.8/libfwupdplugin/fu-uefi-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-uefi-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -26,19 +26,12 @@ gchar *name; } FuUefiDevicePrivate; -static void -fu_uefi_device_codec_iface_init(FwupdCodecInterface *iface); - -G_DEFINE_TYPE_EXTENDED(FuUefiDevice, - fu_uefi_device, - FU_TYPE_DEVICE, - 0, - G_ADD_PRIVATE(FuUefiDevice) - G_IMPLEMENT_INTERFACE(FWUPD_TYPE_CODEC, - fu_uefi_device_codec_iface_init)); +G_DEFINE_TYPE_WITH_PRIVATE(FuUefiDevice, fu_uefi_device, FU_TYPE_DEVICE); #define GET_PRIVATE(o) (fu_uefi_device_get_instance_private(o)) +#define FU_UEFI_DEVICE_INHIBIT_ID_NO_EFIVARS_SPACE "no-efivars-space" + /* private */ void fu_uefi_device_set_guid(FuUefiDevice *self, const gchar *guid) @@ -109,7 +102,7 @@ const gchar *guid, const gchar *name, GBytes *bytes, - guint32 attr, + FuEfiVariableAttrs attr, GError **error) { FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); @@ -163,7 +156,7 @@ * @self: a #FuUefiDevice * @guid: Globally unique identifier * @name: Variable name - * @attr: (nullable): Attributes + * @attr: (nullable) (out): #FuEfiVariableAttrs, e.g. %FU_EFI_VARIABLE_ATTR_NON_VOLATILE * @error: (nullable): optional return location for an error * * Gets the data from a UEFI variable in NVRAM, emulating if required. @@ -176,7 +169,7 @@ fu_uefi_device_get_efivar_bytes(FuUefiDevice *self, const gchar *guid, const gchar *name, - guint32 *attr, + FuEfiVariableAttrs *attr, GError **error) { FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); @@ -206,7 +199,7 @@ guint64 tmp = fu_device_event_get_i64(event, "Attr", error); if (tmp == G_MAXINT64) return NULL; - *attr = (guint32)tmp; + *attr = (FuEfiVariableAttrs)tmp; } return fu_device_event_get_bytes(event, "Data", error); } @@ -220,7 +213,7 @@ if (blob == NULL) return NULL; if (attr != NULL) - *attr = attr_tmp; + *attr = (FuEfiVariableAttrs)attr_tmp; /* save response */ if (event != NULL) { @@ -285,10 +278,9 @@ } static void -fu_uefi_device_add_json(FwupdCodec *codec, JsonBuilder *builder, FwupdCodecFlags flags) +fu_uefi_device_add_json(FuDevice *device, JsonBuilder *builder, FwupdCodecFlags flags) { - FuDevice *device = FU_DEVICE(codec); - FuUefiDevice *self = FU_UEFI_DEVICE(codec); + FuUefiDevice *self = FU_UEFI_DEVICE(device); FuUefiDevicePrivate *priv = GET_PRIVATE(self); GPtrArray *events = fu_device_get_events(device); @@ -326,11 +318,9 @@ } static gboolean -fu_uefi_device_from_json(FwupdCodec *codec, JsonNode *json_node, GError **error) +fu_uefi_device_from_json(FuDevice *device, JsonObject *json_object, GError **error) { - FuDevice *device = FU_DEVICE(codec); - FuUefiDevice *self = FU_UEFI_DEVICE(codec); - JsonObject *json_object = json_node_get_object(json_node); + FuUefiDevice *self = FU_UEFI_DEVICE(device); const gchar *tmp; tmp = json_object_get_string_member_with_default(json_object, "Guid", NULL); @@ -381,11 +371,38 @@ } static void +fu_uefi_device_required_free_notify_cb(FuUefiDevice *self, GParamSpec *pspec, gpointer user_data) +{ + FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); + + if (fu_device_get_required_free(FU_DEVICE(self)) > 0) { + g_autoptr(GError) error_local = NULL; + if (!fu_context_efivars_check_free_space( + ctx, + fu_device_get_required_free(FU_DEVICE(self)), + &error_local)) { + fu_device_inhibit(FU_DEVICE(self), + FU_UEFI_DEVICE_INHIBIT_ID_NO_EFIVARS_SPACE, + error_local->message); + } else { + fu_device_uninhibit(FU_DEVICE(self), + FU_UEFI_DEVICE_INHIBIT_ID_NO_EFIVARS_SPACE); + } + } else { + fu_device_uninhibit(FU_DEVICE(self), FU_UEFI_DEVICE_INHIBIT_ID_NO_EFIVARS_SPACE); + } +} + +static void fu_uefi_device_init(FuUefiDevice *self) { fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_INHIBIT_CHILDREN); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_REQUIRED_FREE); + g_signal_connect(FU_DEVICE(self), + "notify::required-free", + G_CALLBACK(fu_uefi_device_required_free_notify_cb), + NULL); } static void @@ -398,13 +415,8 @@ device_class->probe = fu_uefi_device_probe; device_class->dump_firmware = fu_uefi_device_dump_firmware; device_class->incorporate = fu_uefi_device_incorporate; -} - -static void -fu_uefi_device_codec_iface_init(FwupdCodecInterface *iface) -{ - iface->add_json = fu_uefi_device_add_json; - iface->from_json = fu_uefi_device_from_json; + device_class->from_json = fu_uefi_device_from_json; + device_class->add_json = fu_uefi_device_add_json; } FuUefiDevice * diff -Nru fwupd-2.0.8/libfwupdplugin/fu-uefi-device.h fwupd-2.0.20/libfwupdplugin/fu-uefi-device.h --- fwupd-2.0.8/libfwupdplugin/fu-uefi-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-uefi-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -20,11 +20,11 @@ const gchar *guid, const gchar *name, GBytes *bytes, - guint32 attr, + FuEfiVariableAttrs attr, GError **error) G_GNUC_NON_NULL(1, 2, 3, 4); GBytes * fu_uefi_device_get_efivar_bytes(FuUefiDevice *self, const gchar *guid, const gchar *name, - guint32 *attr, + FuEfiVariableAttrs *attr, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2, 3); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-usb-bos-descriptor.c fwupd-2.0.20/libfwupdplugin/fu-usb-bos-descriptor.c --- fwupd-2.0.8/libfwupdplugin/fu-usb-bos-descriptor.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-usb-bos-descriptor.c 2026-02-26 11:36:18.000000000 +0000 @@ -102,10 +102,14 @@ /* create child */ stream = g_memory_input_stream_new_from_data(g_steal_pointer(&buf), bufsz, g_free); - if (!fu_firmware_set_stream(img, stream, error)) + if (!fu_firmware_parse_stream(img, + stream, + 0x0, + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, + error)) return FALSE; fu_firmware_set_id(img, FU_FIRMWARE_ID_PAYLOAD); - if (!fu_firmware_add_image_full(FU_FIRMWARE(self), img, error)) + if (!fu_firmware_add_image(FU_FIRMWARE(self), img, error)) return FALSE; } @@ -155,7 +159,7 @@ static gboolean fu_usb_bos_descriptor_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuUsbBosDescriptor *self = FU_USB_BOS_DESCRIPTOR(firmware); @@ -174,22 +178,26 @@ self->bos_cap.bDevCapabilityType = fu_usb_bos_hdr_get_dev_capability_type(st); /* data */ - if (self->bos_cap.bLength > st->len) { + if (self->bos_cap.bLength > st->buf->len) { g_autoptr(FuFirmware) img = fu_firmware_new(); g_autoptr(GInputStream) img_stream = NULL; img_stream = fu_partial_input_stream_new(stream, - st->len, - self->bos_cap.bLength - st->len, + st->buf->len, + self->bos_cap.bLength - st->buf->len, error); if (img_stream == NULL) { - g_prefix_error(error, "failed to cut BOS descriptor: "); + g_prefix_error_literal(error, "failed to cut BOS descriptor: "); return FALSE; } - if (!fu_firmware_set_stream(img, img_stream, error)) + if (!fu_firmware_parse_stream(img, + img_stream, + 0x0, + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, + error)) return FALSE; fu_firmware_set_id(img, FU_FIRMWARE_ID_PAYLOAD); - if (!fu_firmware_add_image_full(FU_FIRMWARE(self), img, error)) + if (!fu_firmware_add_image(FU_FIRMWARE(self), img, error)) return FALSE; } @@ -207,12 +215,12 @@ fu_usb_bos_hdr_set_dev_capability_type(st, self->bos_cap.bDevCapabilityType); blob = fu_firmware_get_image_by_id_bytes(firmware, FU_FIRMWARE_ID_PAYLOAD, NULL); if (blob != NULL) { - fu_byte_array_append_bytes(st, blob); - fu_usb_bos_hdr_set_length(st, st->len); + fu_byte_array_append_bytes(st->buf, blob); + fu_usb_bos_hdr_set_length(st, st->buf->len); } /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static void @@ -235,6 +243,7 @@ static void fu_usb_bos_descriptor_init(FuUsbBosDescriptor *self) { + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_ALLOW_LINEAR); } /** @@ -247,7 +256,7 @@ FuUsbBosDescriptor * fu_usb_bos_descriptor_new(const struct libusb_bos_dev_capability_descriptor *bos_cap) { - FuUsbBosDescriptor *self = g_object_new(FU_TYPE_USB_BOS_DESCRIPTOR, NULL); + g_autoptr(FuUsbBosDescriptor) self = g_object_new(FU_TYPE_USB_BOS_DESCRIPTOR, NULL); g_autoptr(FuFirmware) img = fu_firmware_new(); g_autoptr(GBytes) bytes = NULL; @@ -256,6 +265,7 @@ bytes = g_bytes_new(bos_cap->dev_capability_data, bos_cap->bLength - FU_USB_BOS_HDR_SIZE); fu_firmware_set_bytes(img, bytes); fu_firmware_set_id(img, FU_FIRMWARE_ID_PAYLOAD); - fu_firmware_add_image(FU_FIRMWARE(self), img); - return FU_USB_BOS_DESCRIPTOR(self); + if (!fu_firmware_add_image(FU_FIRMWARE(self), img, NULL)) + return NULL; + return g_steal_pointer(&self); } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-usb-config-descriptor.c fwupd-2.0.20/libfwupdplugin/fu-usb-config-descriptor.c --- fwupd-2.0.8/libfwupdplugin/fu-usb-config-descriptor.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-usb-config-descriptor.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,8 +15,6 @@ #include "config.h" -#include - #include "fu-usb-config-descriptor-private.h" struct _FuUsbConfigDescriptor { @@ -114,7 +112,7 @@ static gboolean fu_usb_config_descriptor_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuUsbConfigDescriptor *self = FU_USB_CONFIG_DESCRIPTOR(firmware); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-usb-descriptor.c fwupd-2.0.20/libfwupdplugin/fu-usb-descriptor.c --- fwupd-2.0.8/libfwupdplugin/fu-usb-descriptor.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-usb-descriptor.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,7 +6,6 @@ #include "config.h" -#include "fu-partial-input-stream.h" #include "fu-usb-descriptor.h" G_DEFINE_TYPE(FuUsbDescriptor, fu_usb_descriptor, FU_TYPE_FIRMWARE) @@ -14,26 +13,17 @@ static gboolean fu_usb_descriptor_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { - FuUsbDescriptor *self = FU_USB_DESCRIPTOR(firmware); g_autoptr(FuUsbBaseHdr) st = NULL; - g_autoptr(GInputStream) stream_partial = NULL; /* parse */ st = fu_usb_base_hdr_parse_stream(stream, 0x0, error); if (st == NULL) return FALSE; - stream_partial = - fu_partial_input_stream_new(stream, 0x0, fu_usb_base_hdr_get_length(st), error); - if (stream_partial == NULL) { - g_prefix_error(error, "failed to cut USB descriptor: "); - return FALSE; - } - if (!fu_firmware_set_stream(firmware, stream_partial, error)) - return FALSE; - fu_firmware_set_idx(FU_FIRMWARE(self), fu_usb_base_hdr_get_descriptor_type(st)); + fu_firmware_set_size(firmware, fu_usb_base_hdr_get_length(st)); + fu_firmware_set_idx(firmware, fu_usb_base_hdr_get_descriptor_type(st)); /* success */ return TRUE; diff -Nru fwupd-2.0.8/libfwupdplugin/fu-usb-device-ds20.c fwupd-2.0.20/libfwupdplugin/fu-usb-device-ds20.c --- fwupd-2.0.8/libfwupdplugin/fu-usb-device-ds20.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-usb-device-ds20.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,8 +8,6 @@ #include "config.h" -#include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-dump.h" #include "fu-input-stream.h" #include "fu-usb-device-ds20-struct.h" @@ -121,7 +119,7 @@ gsize offset, GError **error) { - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructDs20) st = NULL; g_autofree gchar *guid_str = NULL; /* matches the correct UUID */ @@ -157,7 +155,7 @@ static gboolean fu_usb_device_ds20_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuUsbDeviceDs20 *self = FU_USB_DEVICE_DS20(firmware); @@ -170,7 +168,7 @@ return FALSE; for (gsize off = 0; off < streamsz; off += FU_STRUCT_DS20_SIZE) { g_autofree FuUsbDeviceDs20Item *dsinfo = g_new0(FuUsbDeviceDs20Item, 1); - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructDs20) st = NULL; /* parse */ st = fu_struct_ds20_parse_stream(stream, off, error); @@ -247,7 +245,7 @@ fu_struct_ds20_set_platform_ver(st, fu_firmware_get_version_raw(firmware)); fu_struct_ds20_set_total_length(st, fu_firmware_get_size(firmware)); fu_struct_ds20_set_vendor_code(st, fu_firmware_get_idx(firmware)); - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static void diff -Nru fwupd-2.0.8/libfwupdplugin/fu-usb-device.c fwupd-2.0.20/libfwupdplugin/fu-usb-device.c --- fwupd-2.0.8/libfwupdplugin/fu-usb-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-usb-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,6 +13,7 @@ #include "fu-bytes.h" #include "fu-context-private.h" #include "fu-device-event-private.h" +#include "fu-device-locker.h" #include "fu-device-private.h" #include "fu-dump.h" #include "fu-input-stream.h" @@ -50,7 +51,6 @@ gint configuration; GPtrArray *device_interfaces; /* (nullable) (element-type FuUsbDeviceInterface) */ guint claim_retry_count; - GPtrArray *descriptor_streams; /* (nullable) (element-type GInputStream) */ } FuUsbDevicePrivate; typedef struct { @@ -65,6 +65,13 @@ enum { PROP_0, PROP_LIBUSB_DEVICE, PROP_LAST }; +enum { + QUARK_ADD_INSTANCE_ID_REV, + QUARK_LAST, +}; + +static guint quarks[QUARK_LAST] = {0}; + #define GET_PRIVATE(o) (fu_usb_device_get_instance_private(o)) #define FU_DEVICE_CLAIM_INTERFACE_DELAY 500 /* ms */ @@ -74,7 +81,7 @@ fu_usb_device_libusb_error_to_gerror(gint rc, GError **error) { gint error_code = FWUPD_ERROR_INTERNAL; - /* Put the rc in libusb's error enum so that gcc warns us if we're + /* put the rc in libusb's error enum so that gcc warns us if we're missing an error code */ enum libusb_error result = rc; @@ -120,53 +127,16 @@ static gboolean fu_usb_device_libusb_status_to_gerror(gint status, GError **error) { - gboolean ret = FALSE; - - switch (status) { - case LIBUSB_TRANSFER_COMPLETED: - ret = TRUE; - break; - case LIBUSB_TRANSFER_ERROR: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "transfer failed"); - break; - case LIBUSB_TRANSFER_TIMED_OUT: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_TIMED_OUT, - "transfer timed out"); - break; - case LIBUSB_TRANSFER_CANCELLED: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "transfer cancelled"); - break; - case LIBUSB_TRANSFER_STALL: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "endpoint stalled or request not supported"); - break; - case LIBUSB_TRANSFER_NO_DEVICE: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "device was disconnected"); - break; - case LIBUSB_TRANSFER_OVERFLOW: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "device sent more data than requested"); - break; - default: - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "unknown status [%i]", - status); - } - return ret; + const FuErrorMapEntry entries[] = { + {LIBUSB_TRANSFER_COMPLETED, FWUPD_ERROR_LAST, NULL}, + {LIBUSB_TRANSFER_ERROR, FWUPD_ERROR_INTERNAL, "failed"}, + {LIBUSB_TRANSFER_TIMED_OUT, FWUPD_ERROR_TIMED_OUT, NULL}, + {LIBUSB_TRANSFER_CANCELLED, FWUPD_ERROR_NOTHING_TO_DO, "cancelled"}, + {LIBUSB_TRANSFER_STALL, FWUPD_ERROR_NOT_SUPPORTED, "stalled or not supported"}, + {LIBUSB_TRANSFER_NO_DEVICE, FWUPD_ERROR_NOT_FOUND, "device was disconnected"}, + {LIBUSB_TRANSFER_OVERFLOW, FWUPD_ERROR_INTERNAL, "sent more data than requested"}, + }; + return fu_error_map_entry_to_gerror(status, entries, G_N_ELEMENTS(entries), error); } /** @@ -272,8 +242,6 @@ libusb_unref_device(priv->usb_device); if (priv->device_interfaces != NULL) g_ptr_array_unref(priv->device_interfaces); - if (priv->descriptor_streams != NULL) - g_ptr_array_unref(priv->descriptor_streams); g_ptr_array_unref(priv->interfaces); g_ptr_array_unref(priv->bos_descriptors); g_ptr_array_unref(priv->hid_descriptors); @@ -417,7 +385,7 @@ 1000, NULL, error)) { - g_prefix_error(error, "failed to get USB descriptor: "); + g_prefix_error_literal(error, "failed to get USB descriptor: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, "HUB_DT", data, sz); @@ -428,8 +396,8 @@ g_string_append_printf(hub, "%02X", data[0x0B]); g_string_append_printf(hub, "%02X", data[0x0A]); } else if (sz >= 9) { - guint8 numbytes = fu_common_align_up(data[2] + 1, 0x03) / 8; - for (guint i = 0; i < numbytes; i++) { + gsize numbytes = fu_common_align_up(data[2] + 1, 0x03) / 8; + for (gsize i = 0; i < numbytes; i++) { guint8 tmp = 0x0; if (!fu_memread_uint8_safe(data, sz, 7 + i, &tmp, error)) return FALSE; @@ -453,10 +421,12 @@ static gboolean fu_usb_device_open_internal(FuUsbDevice *self, GError **error) { - FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); FuUsbDevicePrivate *priv = GET_PRIVATE(self); +#ifdef HAVE_LIBUSB_WRAP_SYS_DEVICE + FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); libusb_context *usb_ctx = fu_context_get_data(ctx, "libusb_context"); - gint rc; +#endif + gint rc = 0; /* sanity check */ if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) @@ -475,6 +445,7 @@ if (priv->usb_device != NULL) { rc = libusb_open(priv->usb_device, &priv->handle); } else { +#if defined(HAVE_LIBUSB_WRAP_SYS_DEVICE) gint fd; FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); if (io_channel == NULL) { @@ -486,6 +457,13 @@ } fd = fu_io_channel_unix_get_fd(io_channel); rc = libusb_wrap_sys_device(usb_ctx, fd, &priv->handle); +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "libusb_wrap_sys_device not available, can't wrap fd"); + return FALSE; +#endif } if (!fu_usb_device_libusb_error_to_gerror(rc, error)) { if (priv->handle != NULL) @@ -561,14 +539,14 @@ /* open */ if (!fu_usb_device_open_internal(self, error)) { - g_prefix_error(error, "failed to open device: "); + g_prefix_error_literal(error, "failed to open device: "); return FALSE; } /* if set */ if (priv->configuration >= 0) { if (!fu_usb_device_set_configuration_internal(self, priv->configuration, error)) { - g_prefix_error(error, "failed to set configuration: "); + g_prefix_error_literal(error, "failed to set configuration: "); return FALSE; } } @@ -779,7 +757,7 @@ /* get the interface GUIDs */ intfs = fu_usb_device_get_interfaces(self, error); if (intfs == NULL) { - g_prefix_error(error, "failed to get interfaces: "); + g_prefix_error_literal(error, "failed to get interfaces: "); return FALSE; } @@ -788,21 +766,21 @@ for (guint i = 0; i < intfs->len; i++) { FuUsbInterface *intf = g_ptr_array_index(intfs, i); - /* Video: Video Control: i.e. a webcam */ + /* video: i.e. a webcam */ if (fu_usb_interface_get_class(intf) == FU_USB_CLASS_VIDEO && fu_usb_interface_get_subclass(intf) == 0x01) { fu_device_add_icon(device, "camera-web"); } - /* Audio */ + /* audio */ if (fu_usb_interface_get_class(intf) == FU_USB_CLASS_AUDIO) fu_device_add_icon(device, "audio-card"); - /* Mass Storage */ + /* mass storage */ if (fu_usb_interface_get_class(intf) == FU_USB_CLASS_MASS_STORAGE) fu_device_add_icon(device, "drive-harddisk"); - /* Printer */ + /* printer */ if (fu_usb_interface_get_class(intf) == FU_USB_CLASS_PRINTER) fu_device_add_icon(device, "printer"); } @@ -874,13 +852,13 @@ return FALSE; ds20 = fu_firmware_new_from_gtypes(stream, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, error, FU_TYPE_USB_DEVICE_FW_DS20, FU_TYPE_USB_DEVICE_MS_DS20, G_TYPE_INVALID); if (ds20 == NULL) { - g_prefix_error(error, "failed to parse: "); + g_prefix_error_literal(error, "failed to parse: "); return FALSE; } str = fu_firmware_to_string(ds20); @@ -891,14 +869,14 @@ return TRUE; /* set the quirks onto the device */ - usb_locker = fu_device_locker_new_full(self, - (FuDeviceLockerFunc)fu_usb_device_open, - (FuDeviceLockerFunc)fu_usb_device_close, + usb_locker = fu_device_locker_new_full(FU_DEVICE(self), + fu_usb_device_open, + fu_usb_device_close, error); if (usb_locker == NULL) return FALSE; if (!fu_usb_device_ds20_apply_to_device(FU_USB_DEVICE_DS20(ds20), self, error)) { - g_prefix_error(error, "failed to apply DS20 data: "); + g_prefix_error_literal(error, "failed to apply DS20 data: "); return FALSE; } @@ -906,12 +884,15 @@ return TRUE; } -static GInputStream * -fu_usb_device_load_descriptor_stream(FuUsbDevice *self, const gchar *basename, GError **error) +static GBytes * +fu_usb_device_load_descriptor(FuUsbDevice *self, const gchar *basename, GError **error) { FuDeviceEvent *event = NULL; + gsize bufsz = 0; + g_autofree gchar *buf = NULL; g_autofree gchar *event_id = NULL; g_autofree gchar *fn = NULL; + g_autoptr(GBytes) data = NULL; /* build event key either for load or save */ if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || @@ -922,8 +903,6 @@ /* emulated */ if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) { - g_autoptr(GBytes) data = NULL; - /* lots of old emulations don't have this, returning FWUPD_ERROR_NOT_FOUND */ event = fu_device_load_event(FU_DEVICE(self), event_id, error); if (event == NULL) @@ -931,7 +910,7 @@ data = fu_device_event_get_bytes(event, "Data", error); if (data == NULL) return NULL; - return g_memory_input_stream_new_from_bytes(data); + return g_steal_pointer(&data); } /* save */ @@ -950,32 +929,35 @@ fn); return NULL; } + if (!g_file_get_contents(fn, &buf, &bufsz, error)) { + fwupd_error_convert(error); + return NULL; + } + data = g_bytes_new_take(g_steal_pointer(&buf), bufsz); + if (data == NULL) + return NULL; /* save */ - if (event != NULL) { - g_autoptr(GBytes) data = fu_bytes_get_contents(fn, error); - if (data == NULL) - return NULL; + if (event != NULL) fu_device_event_set_bytes(event, "Data", data); - } /* success */ - return fu_input_stream_from_path(fn, error); + return g_steal_pointer(&data); } static gboolean -fu_usb_device_parse_bos_descriptor(FuUsbDevice *self, GInputStream *stream, GError **error) +fu_usb_device_parse_bos_descriptor(FuUsbDevice *self, GBytes *blob, GError **error) { FuUsbDevicePrivate *priv = GET_PRIVATE(self); g_autoptr(FuFirmware) firmware = fu_linear_firmware_new(FU_TYPE_USB_BOS_DESCRIPTOR); g_autoptr(GError) error_local = NULL; g_autoptr(GPtrArray) imgs = NULL; - if (!fu_firmware_parse_stream(firmware, - stream, - 0x0, - FWUPD_INSTALL_FLAG_NONE, - &error_local)) { + if (!fu_firmware_parse_bytes(firmware, + blob, + 0x0, + FU_FIRMWARE_PARSE_FLAG_NONE, + &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE) || g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA)) { g_debug("ignoring: %s", error_local->message); @@ -996,18 +978,6 @@ return TRUE; } -/* these are closed on FuDevice->probe_complete() */ -static void -fu_usb_device_add_descriptor_stream(FuUsbDevice *self, GInputStream *stream) -{ - FuUsbDevicePrivate *priv = GET_PRIVATE(self); - if (priv->descriptor_streams == NULL) { - priv->descriptor_streams = - g_ptr_array_new_with_free_func((GDestroyNotify)fu_input_stream_locker_unref); - } - g_ptr_array_add(priv->descriptor_streams, g_object_ref(stream)); -} - static gboolean fu_usb_device_ensure_bos_descriptors(FuUsbDevice *self, GError **error) { @@ -1033,7 +1003,7 @@ fu_usb_device_get_spec(self)); return FALSE; } - usb_locker = fu_device_locker_new(self, error); + usb_locker = fu_device_locker_new(FU_DEVICE(self), error); if (usb_locker == NULL) return FALSE; if (priv->handle == NULL) { @@ -1054,25 +1024,25 @@ struct libusb_bos_dev_capability_descriptor *bos_cap = bos->dev_capability[i]; bos_descriptor = fu_usb_bos_descriptor_new(bos_cap); + if (bos_descriptor == NULL) + continue; g_ptr_array_add(priv->bos_descriptors, bos_descriptor); } libusb_free_bos_descriptor(bos); } else { g_autoptr(GError) error_local = NULL; - g_autoptr(GInputStream) stream = NULL; + g_autoptr(GBytes) blob = NULL; /* this is optional */ - stream = - fu_usb_device_load_descriptor_stream(self, "bos_descriptors", &error_local); - if (stream == NULL) { + blob = fu_usb_device_load_descriptor(self, "bos_descriptors", &error_local); + if (blob == NULL) { if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) && !g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } } else { - fu_usb_device_add_descriptor_stream(self, stream); /* ->probe_complete */ - if (!fu_usb_device_parse_bos_descriptor(self, stream, error)) + if (!fu_usb_device_parse_bos_descriptor(self, blob, error)) return FALSE; } } @@ -1100,7 +1070,7 @@ return TRUE; } g_propagate_error(error, g_steal_pointer(&error_local)); - fu_error_convert(error); + fwupd_error_convert(error); return FALSE; } for (guint i = 0; i < priv->bos_descriptors->len; i++) { @@ -1235,7 +1205,7 @@ "VID", "PID", NULL); - if (fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV)) { + if (fu_device_has_private_flag_quark(device, quarks[QUARK_ADD_INSTANCE_ID_REV])) { fu_device_build_instance_id_full(device, FU_DEVICE_INSTANCE_FLAG_GENERIC | FU_DEVICE_INSTANCE_FLAG_VISIBLE | @@ -1251,7 +1221,7 @@ /* add the interface GUIDs */ intfs = fu_usb_device_get_interfaces(self, error); if (intfs == NULL) { - g_prefix_error(error, "failed to get interfaces: "); + g_prefix_error_literal(error, "failed to get interfaces: "); return FALSE; } for (guint i = 0; i < intfs->len; i++) { @@ -1313,18 +1283,6 @@ return TRUE; } -static void -fu_usb_device_probe_complete(FuDevice *device) -{ - FuUsbDevice *self = FU_USB_DEVICE(device); - FuUsbDevicePrivate *priv = GET_PRIVATE(self); - - /* FuUdevDevice->probe_complete */ - FU_DEVICE_CLASS(fu_usb_device_parent_class)->probe_complete(device); - if (priv->descriptor_streams != NULL) - g_ptr_array_set_size(priv->descriptor_streams, 0); -} - /** * fu_usb_device_get_release: * @self: a #FuUsbDevice @@ -1803,17 +1761,14 @@ } static gboolean -fu_usb_device_parse_descriptor(FuUsbDevice *self, GInputStream *stream, GError **error) +fu_usb_device_parse_descriptor(FuUsbDevice *self, GBytes *blob, GError **error) { FuUsbDevicePrivate *priv = GET_PRIVATE(self); gsize offset = 0; - gsize streamsz = 0; g_autoptr(FuUsbDeviceHdr) st = NULL; g_autoptr(FuUsbInterface) iface_last = NULL; - if (!fu_input_stream_size(stream, &streamsz, error)) - return FALSE; - st = fu_usb_device_hdr_parse_stream(stream, offset, error); + st = fu_usb_device_hdr_parse_bytes(blob, offset, error); if (st == NULL) return FALSE; priv->desc.bLength = fu_usb_device_hdr_get_length(st); @@ -1832,13 +1787,13 @@ priv->desc.bNumConfigurations = fu_usb_device_hdr_get_num_configurations(st); offset += fu_usb_device_hdr_get_length(st); - while (offset < streamsz) { + while (offset < g_bytes_get_size(blob)) { FuUsbDescriptorKind descriptor_kind; g_autoptr(FuUsbBaseHdr) st_base = NULL; g_autoptr(GError) error_local = NULL; /* this is common to all descriptor types */ - st_base = fu_usb_base_hdr_parse_stream(stream, offset, &error_local); + st_base = fu_usb_base_hdr_parse_bytes(blob, offset, &error_local); if (st_base == NULL) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) break; @@ -1848,35 +1803,64 @@ return FALSE; } + /* sanity check */ + if (fu_usb_base_hdr_get_length(st_base) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "USB descriptor had impossible zero size"); + return FALSE; + } + /* config, interface or endpoint */ descriptor_kind = fu_usb_base_hdr_get_descriptor_type(st_base); if (descriptor_kind == FU_USB_DESCRIPTOR_KIND_CONFIG) { g_autoptr(FuUsbConfigDescriptor) cfg_descriptor = fu_usb_config_descriptor_new(); - if (!fu_firmware_parse_stream(FU_FIRMWARE(cfg_descriptor), - stream, - offset, - FWUPD_INSTALL_FLAG_NONE, - error)) + if (!fu_firmware_parse_bytes(FU_FIRMWARE(cfg_descriptor), + blob, + offset, + FU_FIRMWARE_PARSE_FLAG_NONE, + error)) return FALSE; g_ptr_array_add(priv->cfg_descriptors, g_steal_pointer(&cfg_descriptor)); } else if (descriptor_kind == FU_USB_DESCRIPTOR_KIND_INTERFACE) { g_autoptr(FuUsbInterface) iface = g_object_new(FU_TYPE_USB_INTERFACE, NULL); - if (!fu_firmware_parse_stream(FU_FIRMWARE(iface), - stream, - offset, - FWUPD_INSTALL_FLAG_NONE, - error)) + if (!fu_firmware_parse_bytes(FU_FIRMWARE(iface), + blob, + offset, + FU_FIRMWARE_PARSE_FLAG_NONE, + error)) return FALSE; fu_usb_device_add_interface_internal(self, iface); + + /* the next descriptor is the custom one, so just add as a child */ + if (fu_usb_interface_get_class(iface) == + FU_USB_CLASS_APPLICATION_SPECIFIC) { + g_autoptr(FuUsbDescriptor) img = + g_object_new(FU_TYPE_USB_DESCRIPTOR, NULL); + if (!fu_firmware_parse_bytes( + FU_FIRMWARE(img), + blob, + offset + fu_usb_base_hdr_get_length(st_base), + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, + error)) + return FALSE; + if (!fu_firmware_add_image(FU_FIRMWARE(iface), + FU_FIRMWARE(img), + error)) + return FALSE; + offset += fu_firmware_get_size(FU_FIRMWARE(img)); + } + g_set_object(&iface_last, iface); } else if (descriptor_kind == FU_USB_DESCRIPTOR_KIND_ENDPOINT) { g_autoptr(FuUsbEndpoint) ep = g_object_new(FU_TYPE_USB_ENDPOINT, NULL); - if (!fu_firmware_parse_stream(FU_FIRMWARE(ep), - stream, - offset, - FWUPD_INSTALL_FLAG_NONE, - error)) + if (!fu_firmware_parse_bytes(FU_FIRMWARE(ep), + blob, + offset, + FU_FIRMWARE_PARSE_FLAG_NONE, + error)) return FALSE; if (iface_last == NULL) { g_warning("endpoint 0x%x without prior interface, ignoring", @@ -1886,11 +1870,11 @@ } } else if (descriptor_kind == FU_USB_DESCRIPTOR_KIND_HID) { g_autoptr(FuUsbHidDescriptor) hid_descriptor = fu_usb_hid_descriptor_new(); - if (!fu_firmware_parse_stream(FU_FIRMWARE(hid_descriptor), - stream, - offset, - FWUPD_INSTALL_FLAG_NONE, - error)) + if (!fu_firmware_parse_bytes(FU_FIRMWARE(hid_descriptor), + blob, + offset, + FU_FIRMWARE_PARSE_FLAG_NONE, + error)) return FALSE; if (iface_last == NULL) { g_warning("hid descriptor without prior interface, ignoring"); @@ -1942,17 +1926,17 @@ } libusb_free_config_descriptor(config); } else { - g_autoptr(FuInputStreamLocker) stream = NULL; g_autoptr(GError) error_local = NULL; + g_autoptr(GBytes) blob = NULL; - stream = fu_usb_device_load_descriptor_stream(self, "descriptors", &error_local); - if (stream == NULL) { + blob = fu_usb_device_load_descriptor(self, "descriptors", &error_local); + if (blob == NULL) { if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } } else { - if (!fu_usb_device_parse_descriptor(self, stream, error)) + if (!fu_usb_device_parse_descriptor(self, blob, error)) return FALSE; } } @@ -1996,7 +1980,7 @@ * Gets the first interface that matches the vendor class interface descriptor. * If you want to find all the interfaces that match (there may be other * 'alternate' interfaces you have to use fu_usb_device_get_interfaces() and - * check each one manally. + * check each one manually. * * Return value: (transfer full): a #FuUsbInterface or %NULL for not found * @@ -2240,7 +2224,7 @@ if (flags & FU_USB_DEVICE_CLAIM_FLAG_KERNEL_DRIVER) { rc = libusb_detach_kernel_driver(priv->handle, iface); - if (rc != LIBUSB_SUCCESS && rc != LIBUSB_ERROR_NOT_FOUND && /* No driver attached */ + if (rc != LIBUSB_SUCCESS && rc != LIBUSB_ERROR_NOT_FOUND && /* no driver attached */ rc != LIBUSB_ERROR_NOT_SUPPORTED && /* win32 */ rc != LIBUSB_ERROR_BUSY /* driver rebound already */) return fu_usb_device_libusb_error_to_gerror(rc, error); @@ -2344,7 +2328,7 @@ if (flags & FU_USB_DEVICE_CLAIM_FLAG_KERNEL_DRIVER) { rc = libusb_attach_kernel_driver(priv->handle, iface); - if (rc != LIBUSB_SUCCESS && rc != LIBUSB_ERROR_NOT_FOUND && /* No driver attached */ + if (rc != LIBUSB_SUCCESS && rc != LIBUSB_ERROR_NOT_FOUND && /* no driver attached */ rc != LIBUSB_ERROR_NOT_SUPPORTED && /* win32 */ rc != LIBUSB_ERROR_BUSY /* driver rebound already */) return fu_usb_device_libusb_error_to_gerror(rc, error); @@ -2685,7 +2669,7 @@ 5000, NULL, error)) { - g_prefix_error(error, "failed to get HID report descriptor: "); + g_prefix_error_literal(error, "failed to get HID report descriptor: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, "HidDescriptor", buf, bufsz); @@ -3090,7 +3074,6 @@ device_class->ready = fu_usb_device_ready; device_class->close = fu_usb_device_close; device_class->probe = fu_usb_device_probe; - device_class->probe_complete = fu_usb_device_probe_complete; device_class->invalidate = fu_usb_device_invalidate; device_class->to_string = fu_usb_device_to_string; device_class->incorporate = fu_usb_device_incorporate; @@ -3098,6 +3081,10 @@ device_class->from_json = fu_usb_device_from_json; device_class->add_json = fu_usb_device_add_json; + /* used as device flags */ + quarks[QUARK_ADD_INSTANCE_ID_REV] = + g_quark_from_static_string(FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV); + /** * FuUsbDevice:libusb-device: * diff -Nru fwupd-2.0.8/libfwupdplugin/fu-usb-device.h fwupd-2.0.20/libfwupdplugin/fu-usb-device.h --- fwupd-2.0.8/libfwupdplugin/fu-usb-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-usb-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -6,7 +6,6 @@ #pragma once -#include "fu-plugin.h" #include "fu-udev-device.h" #include "fu-usb-interface.h" #include "fu-usb-struct.h" @@ -27,7 +26,7 @@ typedef enum { FU_USB_DEVICE_CLAIM_FLAG_NONE = 0, FU_USB_DEVICE_CLAIM_FLAG_KERNEL_DRIVER = 1 << 0, -} FuUsbDeviceClaimFlags; +} G_GNUC_FLAG_ENUM FuUsbDeviceClaimFlags; guint8 fu_usb_device_get_bus(FuUsbDevice *self) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-usb-endpoint.c fwupd-2.0.20/libfwupdplugin/fu-usb-endpoint.c --- fwupd-2.0.8/libfwupdplugin/fu-usb-endpoint.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-usb-endpoint.c 2026-02-26 11:36:18.000000000 +0000 @@ -190,7 +190,7 @@ static gboolean fu_usb_endpoint_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuUsbEndpoint *self = FU_USB_ENDPOINT(firmware); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-usb-hid-descriptor.c fwupd-2.0.20/libfwupdplugin/fu-usb-hid-descriptor.c --- fwupd-2.0.8/libfwupdplugin/fu-usb-hid-descriptor.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-usb-hid-descriptor.c 2026-02-26 11:36:18.000000000 +0000 @@ -148,7 +148,7 @@ static gboolean fu_usb_hid_descriptor_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuUsbHidDescriptor *self = FU_USB_HID_DESCRIPTOR(firmware); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-usb-interface.c fwupd-2.0.20/libfwupdplugin/fu-usb-interface.c --- fwupd-2.0.8/libfwupdplugin/fu-usb-interface.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-usb-interface.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,10 +19,8 @@ #include #include "fu-byte-array.h" -#include "fu-bytes.h" #include "fu-common.h" #include "fu-input-stream.h" -#include "fu-mem-private.h" #include "fu-usb-endpoint-private.h" #include "fu-usb-interface-private.h" @@ -65,10 +63,10 @@ if (!fu_firmware_parse_bytes(FU_FIRMWARE(img), bytes, offset, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, error)) return FALSE; - if (!fu_firmware_add_image_full(FU_FIRMWARE(self), FU_FIRMWARE(img), error)) + if (!fu_firmware_add_image(FU_FIRMWARE(self), FU_FIRMWARE(img), error)) return FALSE; offset += fu_firmware_get_size(FU_FIRMWARE(img)); } @@ -361,7 +359,7 @@ static gboolean fu_usb_interface_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuUsbInterface *self = FU_USB_INTERFACE(firmware); @@ -388,11 +386,11 @@ fu_firmware_set_size(FU_FIRMWARE(self), self->iface.bLength); /* extra data */ - if (self->iface.bLength > st->len) { + if (self->iface.bLength > st->buf->len) { g_autoptr(GByteArray) buf = NULL; buf = fu_input_stream_read_byte_array(stream, - st->len, - self->iface.bLength - st->len, + st->buf->len, + self->iface.bLength - st->buf->len, NULL, error); if (buf == NULL) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-usb.rs fwupd-2.0.20/libfwupdplugin/fu-usb.rs --- fwupd-2.0.8/libfwupdplugin/fu-usb.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-usb.rs 2026-02-26 11:36:18.000000000 +0000 @@ -32,7 +32,7 @@ Printer = 0x07, MassStorage = 0x08, Hub = 0x09, - Cdc_data = 0x0A, + CdcData = 0x0A, SmartCard = 0x0B, ContentSecurity = 0x0D, Video = 0x0E, @@ -71,14 +71,14 @@ SsEndpointCompanion = 0x30, } -#[derive(ParseStream, Parse)] +#[derive(ParseStream, ParseBytes, Parse)] #[repr(C, packed)] struct FuUsbBaseHdr { length: u8, descriptor_type: FuUsbDescriptorKind, } -#[derive(ParseStream, Default)] +#[derive(ParseBytes, Default)] #[repr(C, packed)] struct FuUsbDeviceHdr { length: u8, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-uswid-firmware.c fwupd-2.0.20/libfwupdplugin/fu-uswid-firmware.c --- fwupd-2.0.8/libfwupdplugin/fu-uswid-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-uswid-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,9 +14,7 @@ #include "fu-coswid-firmware.h" #include "fu-input-stream.h" #include "fu-lzma-common.h" -#include "fu-mem.h" #include "fu-partial-input-stream.h" -#include "fu-string.h" #include "fu-uswid-firmware.h" #include "fu-uswid-struct.h" @@ -61,7 +59,7 @@ static gboolean fu_uswid_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuUswidFirmware *self = FU_USWID_FIRMWARE(firmware); @@ -121,7 +119,7 @@ conv = G_CONVERTER(g_zlib_decompressor_new(G_ZLIB_COMPRESSOR_FORMAT_ZLIB)); istream1 = fu_partial_input_stream_new(stream, hdrsz, payloadsz, error); if (istream1 == NULL) { - g_prefix_error(error, "failed to cut uSWID payload: "); + g_prefix_error_literal(error, "failed to cut uSWID payload: "); return FALSE; } if (!g_seekable_seek(G_SEEKABLE(istream1), 0, G_SEEK_SET, NULL, error)) @@ -165,10 +163,10 @@ if (!fu_firmware_parse_bytes(firmware_coswid, fw2, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return FALSE; - if (!fu_firmware_add_image_full(firmware, firmware_coswid, error)) + if (!fu_firmware_add_image(firmware, firmware_coswid, error)) return FALSE; if (fu_firmware_get_size(firmware_coswid) == 0) { g_set_error_literal(error, @@ -189,7 +187,7 @@ { FuUswidFirmware *self = FU_USWID_FIRMWARE(firmware); FuUswidFirmwarePrivate *priv = GET_PRIVATE(self); - g_autoptr(FuStructUswid) buf = fu_struct_uswid_new(); + g_autoptr(FuStructUswid) st = fu_struct_uswid_new(); g_autoptr(GByteArray) payload = g_byte_array_new(); g_autoptr(GBytes) payload_blob = NULL; g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); @@ -225,14 +223,14 @@ } /* pack */ - fu_struct_uswid_set_hdrver(buf, priv->hdrver); - fu_struct_uswid_set_payloadsz(buf, g_bytes_get_size(payload_blob)); + fu_struct_uswid_set_hdrver(st, priv->hdrver); + fu_struct_uswid_set_payloadsz(st, g_bytes_get_size(payload_blob)); if (priv->hdrver >= 3) { guint8 flags = 0; if (priv->compression != FU_USWID_PAYLOAD_COMPRESSION_NONE) flags |= FU_USWID_HEADER_FLAG_COMPRESSED; - fu_struct_uswid_set_flags(buf, flags); - fu_struct_uswid_set_compression(buf, priv->compression); + fu_struct_uswid_set_flags(st, flags); + fu_struct_uswid_set_compression(st, priv->compression); } else if (priv->hdrver >= 2) { guint8 flags = 0; if (priv->compression != FU_USWID_PAYLOAD_COMPRESSION_NONE) { @@ -245,17 +243,17 @@ } flags |= FU_USWID_HEADER_FLAG_COMPRESSED; } - fu_struct_uswid_set_flags(buf, flags); - g_byte_array_set_size(buf, buf->len - 1); - fu_struct_uswid_set_hdrsz(buf, buf->len); + fu_struct_uswid_set_flags(st, flags); + g_byte_array_set_size(st->buf, st->buf->len - 1); + fu_struct_uswid_set_hdrsz(st, st->buf->len); } else { - g_byte_array_set_size(buf, buf->len - 2); - fu_struct_uswid_set_hdrsz(buf, buf->len); + g_byte_array_set_size(st->buf, st->buf->len - 2); + fu_struct_uswid_set_hdrsz(st, st->buf->len); } - fu_byte_array_append_bytes(buf, payload_blob); + fu_byte_array_append_bytes(st->buf, payload_blob); /* success */ - return g_steal_pointer(&buf); + return g_steal_pointer(&st->buf); } static gboolean @@ -292,6 +290,15 @@ } static void +fu_uswid_firmware_add_magic(FuFirmware *firmware) +{ + fu_firmware_add_magic(firmware, + (const guint8 *)FU_STRUCT_USWID_DEFAULT_MAGIC, + sizeof(fwupd_guid_t), + 0x0); +} + +static void fu_uswid_firmware_init(FuUswidFirmware *self) { FuUswidFirmwarePrivate *priv = GET_PRIVATE(self); @@ -312,6 +319,7 @@ firmware_class->write = fu_uswid_firmware_write; firmware_class->build = fu_uswid_firmware_build; firmware_class->export = fu_uswid_firmware_export; + firmware_class->add_magic = fu_uswid_firmware_add_magic; } /** diff -Nru fwupd-2.0.8/libfwupdplugin/fu-uswid.rs fwupd-2.0.20/libfwupdplugin/fu-uswid.rs --- fwupd-2.0.8/libfwupdplugin/fu-uswid.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-uswid.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,7 +1,7 @@ // Copyright 2023 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later -enum FuUswidHeaderFlag { +enum FuUswidHeaderFlags { None = 0b0, Compressed = 0b1, } @@ -16,7 +16,7 @@ #[derive(New, ValidateStream, ParseStream, Default)] #[repr(C, packed)] struct FuStructUswid { - magic: Guid == 0x53424F4DD6BA2EACA3E67A52AAEE3BAF, + magic: Guid == "4d4f4253-bad6-ac2e-a3e6-7a52aaee3baf", hdrver: u8, hdrsz: u16le = $struct_size, payloadsz: u32le, diff -Nru fwupd-2.0.8/libfwupdplugin/fu-v4l-device.c fwupd-2.0.20/libfwupdplugin/fu-v4l-device.c --- fwupd-2.0.8/libfwupdplugin/fu-v4l-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-v4l-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,7 +13,6 @@ #endif #include "fu-string.h" -#include "fu-usb-device.h" #include "fu-v4l-device.h" /** @@ -145,7 +144,7 @@ G_MAXUINT8, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "failed to parse index: "); + g_prefix_error_literal(error, "failed to parse index: "); return FALSE; } } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-v4l.rs fwupd-2.0.20/libfwupdplugin/fu-v4l.rs --- fwupd-2.0.8/libfwupdplugin/fu-v4l.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-v4l.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,7 +1,7 @@ // Copyright 2024 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later -#[derive(ToBitString)] +#[derive(Bitfield,ToString)] enum FuV4lCap { None = 0x00000000, VideoCapture = 0x00000001, // video capture device diff -Nru fwupd-2.0.8/libfwupdplugin/fu-version-common.c fwupd-2.0.20/libfwupdplugin/fu-version-common.c --- fwupd-2.0.8/libfwupdplugin/fu-version-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-version-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -292,9 +292,8 @@ FU_COMMON_VERSION_DECODE_BCD(val >> 8), FU_COMMON_VERSION_DECODE_BCD(val)); } - if (kind == FWUPD_VERSION_FORMAT_PAIR) { + if (kind == FWUPD_VERSION_FORMAT_PAIR) return g_strdup_printf("%u.%u", (guint)(val >> 8) & 0xff, (guint)val & 0xff); - } if (kind == FWUPD_VERSION_FORMAT_QUAD) { return g_strdup_printf("%u.%u.%u.%u", (guint)(val >> 12) & 0xF, @@ -308,9 +307,8 @@ (guint)(val >> 8) & 0xF, (guint)val & 0xFF); } - if (kind == FWUPD_VERSION_FORMAT_NUMBER || kind == FWUPD_VERSION_FORMAT_PLAIN) { + if (kind == FWUPD_VERSION_FORMAT_NUMBER || kind == FWUPD_VERSION_FORMAT_PLAIN) return g_strdup_printf("%" G_GUINT16_FORMAT, val); - } if (kind == FWUPD_VERSION_FORMAT_HEX) { /* 0xAABB */ return g_strdup_printf("0x%04x", val); @@ -403,6 +401,17 @@ return TRUE; } +static gboolean +_g_ascii_is_xdigits(const gchar *str) +{ + g_return_val_if_fail(str != NULL, FALSE); + for (gsize i = 0; str[i] != '\0'; i++) { + if (!g_ascii_isxdigit(str[i])) + return FALSE; + } + return TRUE; +} + static guint fu_version_format_number_sections(FwupdVersionFormat fmt) { @@ -593,6 +602,8 @@ if (sz == 1) { if (g_str_has_prefix(version, "0x") || _g_ascii_is_digits(version)) return FWUPD_VERSION_FORMAT_NUMBER; + if (_g_ascii_is_xdigits(version)) + return FWUPD_VERSION_FORMAT_HEX; return FWUPD_VERSION_FORMAT_PLAIN; } @@ -621,7 +632,8 @@ if (fmt == FWUPD_VERSION_FORMAT_INTEL_ME || fmt == FWUPD_VERSION_FORMAT_INTEL_ME2 || fmt == FWUPD_VERSION_FORMAT_INTEL_CSME19) return FWUPD_VERSION_FORMAT_QUAD; - if (fmt == FWUPD_VERSION_FORMAT_DELL_BIOS || fmt == FWUPD_VERSION_FORMAT_DELL_BIOS_MSB) + if (fmt == FWUPD_VERSION_FORMAT_DELL_BIOS || fmt == FWUPD_VERSION_FORMAT_DELL_BIOS_MSB || + fmt == FWUPD_VERSION_FORMAT_SURFACE || fmt == FWUPD_VERSION_FORMAT_SURFACE_LEGACY) return FWUPD_VERSION_FORMAT_TRIPLET; if (fmt == FWUPD_VERSION_FORMAT_BCD) return FWUPD_VERSION_FORMAT_PAIR; @@ -658,6 +670,12 @@ if (fmt == FWUPD_VERSION_FORMAT_UNKNOWN) return TRUE; + /* hex */ + if (fmt == FWUPD_VERSION_FORMAT_HEX) { + if (_g_ascii_is_xdigits(version)) + return TRUE; + } + /* check the base format */ fmt_guess = fu_version_guess_format(version); if (fmt == FWUPD_VERSION_FORMAT_BCD && diff -Nru fwupd-2.0.8/libfwupdplugin/fu-volume-locker.c fwupd-2.0.20/libfwupdplugin/fu-volume-locker.c --- fwupd-2.0.8/libfwupdplugin/fu-volume-locker.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-volume-locker.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,135 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#define G_LOG_DOMAIN "FuVolumeLocker" + +#include "config.h" + +#include "fu-volume-locker.h" + +/** + * FuVolumeLocker: + * + * Easily unmount a volume when an object goes out of scope. + * + * See also: [class@FuVolume] + */ + +struct _FuVolumeLocker { + GObject parent_instance; + FuVolume *volume; + gboolean is_open; +}; + +G_DEFINE_TYPE(FuVolumeLocker, fu_volume_locker, G_TYPE_OBJECT) + +/** + * fu_volume_locker_close: + * @self: a #FuVolumeLocker + * @error: (nullable): optional return location for an error + * + * Closes the volume before it gets cleaned up. + * + * This function can be used to manually unmount a volume managed by a locker, + * and allows the caller to properly handle the error. + * + * Returns: %TRUE for success + * + * Since: 2.0.15 + **/ +gboolean +fu_volume_locker_close(FuVolumeLocker *self, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FU_IS_VOLUME_LOCKER(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + if (!self->is_open) + return TRUE; + if (!fu_volume_unmount(self->volume, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("ignoring: %s", error_local->message); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + /* success */ + self->is_open = FALSE; + return TRUE; +} + +static void +fu_volume_locker_finalize(GObject *obj) +{ + FuVolumeLocker *self = FU_VOLUME_LOCKER(obj); + + if (self->is_open) { + g_autoptr(GError) error_local = NULL; + if (!fu_volume_unmount(self->volume, &error_local)) + g_warning("failed to close volume: %s", error_local->message); + } + if (self->volume != NULL) + g_object_unref(self->volume); + G_OBJECT_CLASS(fu_volume_locker_parent_class)->finalize(obj); +} + +static void +fu_volume_locker_class_init(FuVolumeLockerClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_volume_locker_finalize; +} + +static void +fu_volume_locker_init(FuVolumeLocker *self) +{ +} + +/** + * fu_volume_locker_new: + * @volume: a #GObject + * @error: (nullable): optional return location for an error + * + * Locks the volume, mounting it and unmounting it as required. If the volume is + * already mounted then it is is _not_ unmounted when the locker is closed. + * + * Returns: (transfer full): a volume locker if mounted, or %NULL + * + * Since: 2.0.15 + **/ +FuVolumeLocker * +fu_volume_locker_new(FuVolume *volume, GError **error) +{ + g_autoptr(FuVolumeLocker) self = g_object_new(FU_TYPE_VOLUME_LOCKER, NULL); + + g_return_val_if_fail(FU_IS_VOLUME(volume), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* already open, so NOP */ + if (fu_volume_is_mounted(volume)) + return g_steal_pointer(&self); + + /* open volume */ + if (!fu_volume_mount(volume, error)) { + g_autoptr(GError) error_local = NULL; + if (!fu_volume_unmount(volume, &error_local)) { + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_debug("ignoring unmount error on aborted mount: %s", + error_local->message); + } + } + return NULL; + } + + /* create object */ + self = g_object_new(FU_TYPE_VOLUME_LOCKER, NULL); + self->is_open = TRUE; + self->volume = g_object_ref(volume); + return g_steal_pointer(&self); +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-volume-locker.h fwupd-2.0.20/libfwupdplugin/fu-volume-locker.h --- fwupd-2.0.8/libfwupdplugin/fu-volume-locker.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-volume-locker.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-volume.h" + +#define FU_TYPE_VOLUME_LOCKER (fu_volume_locker_get_type()) + +G_DECLARE_FINAL_TYPE(FuVolumeLocker, fu_volume_locker, FU, VOLUME_LOCKER, GObject) + +FuVolumeLocker * +fu_volume_locker_new(FuVolume *volume, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); +gboolean +fu_volume_locker_close(FuVolumeLocker *self, GError **error) G_GNUC_WARN_UNUSED_RESULT + G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-volume.c fwupd-2.0.20/libfwupdplugin/fu-volume.c --- fwupd-2.0.8/libfwupdplugin/fu-volume.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-volume.c 2026-02-26 11:36:18.000000000 +0000 @@ -508,7 +508,7 @@ g_set_error_literal(error, G_IO_ERROR, /* nocheck:error */ g_io_error_from_errno(errno), - g_strerror(errno)); + fwupd_strerror(errno)); fwupd_error_convert(error); return 0; } @@ -517,21 +517,23 @@ g_set_error_literal(error, G_IO_ERROR, /* nocheck:error */ g_io_error_from_errno(errno), - g_strerror(errno)); + fwupd_strerror(errno)); fwupd_error_convert(error); + /* nocheck:error-false-return */ } else if (sector_size == 0) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "failed to get non-zero logical sector size"); + /* nocheck:error-false-return */ } g_close(fd, NULL); return sector_size; #else - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Not supported as or BLKSSZGET not found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported as or BLKSSZGET not found"); return 0; #endif } @@ -881,33 +883,6 @@ return TRUE; } -/** - * fu_volume_locker: - * @self: a @FuVolume - * @error: (nullable): optional return location for an error - * - * Locks the volume, mounting it and unmounting it as required. If the volume is - * already mounted then it is is _not_ unmounted when the locker is closed. - * - * Returns: (transfer full): a device locker for success, or %NULL - * - * Since: 1.4.6 - **/ -FuDeviceLocker * -fu_volume_locker(FuVolume *self, GError **error) -{ - g_return_val_if_fail(FU_IS_VOLUME(self), NULL); - g_return_val_if_fail(error == NULL || *error == NULL, NULL); - - /* already open, so NOP */ - if (fu_volume_is_mounted(self)) - return g_object_new(FU_TYPE_DEVICE_LOCKER, NULL); - return fu_device_locker_new_full(self, - (FuDeviceLockerFunc)fu_volume_mount, - (FuDeviceLockerFunc)fu_volume_unmount, - error); -} - /* private */ FuVolume * fu_volume_new_from_mount_path(const gchar *mount_path) @@ -1037,7 +1012,7 @@ devices = fu_common_get_block_devices(error); if (devices == NULL) return NULL; - g_info("Looking for volumes of type %s", kind); + g_info("looking for volumes of type %s", kind); volumes = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); for (guint i = 0; i < devices->len; i++) { GDBusProxy *proxy_blk = g_ptr_array_index(devices, i); diff -Nru fwupd-2.0.8/libfwupdplugin/fu-volume.h fwupd-2.0.20/libfwupdplugin/fu-volume.h --- fwupd-2.0.8/libfwupdplugin/fu-volume.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-volume.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,8 +9,6 @@ #include -#include "fu-device-locker.h" - #define FU_TYPE_VOLUME (fu_volume_get_type()) G_DECLARE_FINAL_TYPE(FuVolume, fu_volume, FU, VOLUME, GObject) @@ -66,8 +64,6 @@ fu_volume_mount(FuVolume *self, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); gboolean fu_volume_unmount(FuVolume *self, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); -FuDeviceLocker * -fu_volume_locker(FuVolume *self, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); gboolean fu_volume_is_internal(FuVolume *self) G_GNUC_NON_NULL(1); gchar * diff -Nru fwupd-2.0.8/libfwupdplugin/fu-windows-efivars.c fwupd-2.0.20/libfwupdplugin/fu-windows-efivars.c --- fwupd-2.0.8/libfwupdplugin/fu-windows-efivars.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-windows-efivars.c 2026-02-26 11:36:18.000000000 +0000 @@ -61,10 +61,10 @@ static gboolean fu_windows_efivars_is_running_under_wine(void) { - HKEY hKey = NULL; - LONG lResult = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, KEY_READ, &hKey); - if (lResult == ERROR_SUCCESS) { - RegCloseKey(hKey); + HKEY key = NULL; + LONG rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\Wine", 0, KEY_READ, &key); + if (rc == ERROR_SUCCESS) { + RegCloseKey(key); return TRUE; } return FALSE; @@ -76,7 +76,7 @@ const gchar *name, guint8 **data, gsize *data_sz, - guint32 *attr, + FuEfiVariableAttrs *attr, GError **error) { g_autoptr(GByteArray) buf = g_byte_array_new(); @@ -93,19 +93,19 @@ } do { - DWORD dwAttribubutes = 0; + DWORD attrs = 0; DWORD rc = GetFirmwareEnvironmentVariableExA(name, guid_win32, buf->data, buf->len, - &dwAttribubutes); + &attrs); if (rc > 0) { if (data != NULL) *data = g_byte_array_free(g_steal_pointer(&buf), FALSE); if (data_sz != NULL) *data_sz = rc; if (attr != NULL) - *attr = dwAttribubutes; + *attr = attrs; return TRUE; } if (rc == 0 && GetLastError() != ERROR_INSUFFICIENT_BUFFER) @@ -229,7 +229,7 @@ const gchar *name, const guint8 *data, gsize sz, - guint32 attr, + FuEfiVariableAttrs attr, GError **error) { g_autofree gchar *guid_win32 = g_strdup_printf("{%s}", guid); @@ -307,5 +307,5 @@ FuEfivars * fu_efivars_new(void) { - return FU_EFIVARS(g_object_new(FU_TYPE_FREEBSD_EFIVARS, NULL)); + return FU_EFIVARS(g_object_new(FU_TYPE_WINDOWS_EFIVARS, NULL)); } diff -Nru fwupd-2.0.8/libfwupdplugin/fu-windows-efivars.h fwupd-2.0.20/libfwupdplugin/fu-windows-efivars.h --- fwupd-2.0.8/libfwupdplugin/fu-windows-efivars.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-windows-efivars.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,5 +8,5 @@ #include "fu-efivars.h" -#define FU_TYPE_FREEBSD_EFIVARS (fu_windows_efivars_get_type()) -G_DECLARE_FINAL_TYPE(FuWindowsEfivars, fu_windows_efivars, FU, FREEBSD_EFIVARS, FuEfivars) +#define FU_TYPE_WINDOWS_EFIVARS (fu_windows_efivars_get_type()) +G_DECLARE_FINAL_TYPE(FuWindowsEfivars, fu_windows_efivars, FU, WINDOWS_EFIVARS, FuEfivars) diff -Nru fwupd-2.0.8/libfwupdplugin/fu-x509-certificate.c fwupd-2.0.20/libfwupdplugin/fu-x509-certificate.c --- fwupd-2.0.8/libfwupdplugin/fu-x509-certificate.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-x509-certificate.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,282 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#ifdef HAVE_GNUTLS +#include +#include +#endif + +#include "fu-common.h" +#include "fu-input-stream.h" +#include "fu-string.h" +#include "fu-x509-certificate.h" + +#ifdef HAVE_GNUTLS +static void +fu_x509_certificate_gnutls_datum_deinit(gnutls_datum_t *d) +{ + gnutls_free(d->data); + gnutls_free(d); +} + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-function" +G_DEFINE_AUTOPTR_CLEANUP_FUNC(gnutls_datum_t, fu_x509_certificate_gnutls_datum_deinit) +G_DEFINE_AUTO_CLEANUP_FREE_FUNC(gnutls_x509_crt_t, gnutls_x509_crt_deinit, NULL) +#pragma clang diagnostic pop +#endif + +/** + * FuX509Certificate: + * + * An X.509 certificate. + * + * See also: [class@FuFirmware] + */ + +struct _FuX509Certificate { + FuFirmware parent_instance; + gchar *issuer; + gchar *subject; + GDateTime *activation_time; +}; + +G_DEFINE_TYPE(FuX509Certificate, fu_x509_certificate, FU_TYPE_FIRMWARE) + +static void +fu_x509_certificate_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuX509Certificate *self = FU_X509_CERTIFICATE(firmware); + fu_xmlb_builder_insert_kv(bn, "issuer", self->issuer); + fu_xmlb_builder_insert_kv(bn, "subject", self->subject); +} + +/** + * fu_x509_certificate_get_issuer: + * @self: A #FuX509Certificate + * + * Returns the certificate issuer. + * + * Returns: string, or %NULL for unset + * + * Since: 2.0.9 + **/ +const gchar * +fu_x509_certificate_get_issuer(FuX509Certificate *self) +{ + g_return_val_if_fail(FU_IS_X509_CERTIFICATE(self), NULL); + return self->issuer; +} + +#ifdef HAVE_GNUTLS +static void +fu_x509_certificate_set_issuer(FuX509Certificate *self, const gchar *issuer) +{ + g_return_if_fail(FU_IS_X509_CERTIFICATE(self)); + if (g_strcmp0(issuer, self->issuer) == 0) + return; + g_free(self->issuer); + self->issuer = g_strdup(issuer); +} + +static void +fu_x509_certificate_set_subject(FuX509Certificate *self, const gchar *subject) +{ + g_return_if_fail(FU_IS_X509_CERTIFICATE(self)); + if (g_strcmp0(subject, self->subject) == 0) + return; + g_free(self->subject); + self->subject = g_strdup(subject); +} + +static void +fu_x509_certificate_set_activation_time(FuX509Certificate *self, gint64 activation_time) +{ + g_return_if_fail(FU_IS_X509_CERTIFICATE(self)); + if (self->activation_time != NULL) + g_date_time_unref(self->activation_time); + self->activation_time = g_date_time_new_from_unix_utc(activation_time); +} +#endif + +/** + * fu_x509_certificate_get_subject: + * @self: A #FuX509Certificate + * + * Returns the certificate subject. + * + * Returns: string, or %NULL for unset + * + * Since: 2.0.9 + **/ +const gchar * +fu_x509_certificate_get_subject(FuX509Certificate *self) +{ + g_return_val_if_fail(FU_IS_X509_CERTIFICATE(self), NULL); + return self->subject; +} + +/** + * fu_x509_certificate_get_activation_time: + * @self: A #FuX509Certificate + * + * Returns the certificate activation time. + * + * Returns: (transfer full): a #GDateTime, or %NULL for unset + * + * Since: 2.0.11 + **/ +GDateTime * +fu_x509_certificate_get_activation_time(FuX509Certificate *self) +{ + g_return_val_if_fail(FU_IS_X509_CERTIFICATE(self), NULL); + if (self->activation_time == NULL) + return NULL; + return g_date_time_ref(self->activation_time); +} + +static gboolean +fu_x509_certificate_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ +#ifdef HAVE_GNUTLS + FuX509Certificate *self = FU_X509_CERTIFICATE(firmware); + gchar buf[1024] = {'\0'}; + guchar key_id[32] = {'\0'}; + gsize key_idsz = sizeof(key_id); + gnutls_datum_t d = {0}; + gnutls_x509_dn_t dn = {0x0}; + gint64 ts; + gsize bufsz = sizeof(buf); + int rc; + g_auto(gnutls_x509_crt_t) crt = NULL; + g_autoptr(gnutls_datum_t) subject = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(GString) key_idstr = g_string_new(NULL); + + /* parse certificate */ + blob = fu_input_stream_read_bytes(stream, 0x0, G_MAXSIZE, NULL, error); + if (blob == NULL) + return FALSE; + d.size = g_bytes_get_size(blob); + d.data = (unsigned char *)g_bytes_get_data(blob, NULL); + + rc = gnutls_x509_crt_init(&crt); + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "crt_init: %s [%i]", + gnutls_strerror(rc), + rc); + return FALSE; + } + if (flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) + gnutls_x509_crt_set_flags(crt, GNUTLS_X509_CRT_FLAG_IGNORE_SANITY); + rc = gnutls_x509_crt_import(crt, &d, GNUTLS_X509_FMT_DER); + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "crt_import: %s [%i]", + gnutls_strerror(rc), + rc); + return FALSE; + } + + /* issuer */ + if (gnutls_x509_crt_get_issuer_dn(crt, buf, &bufsz) == GNUTLS_E_SUCCESS) { + g_autofree gchar *str = fu_strsafe((const gchar *)buf, bufsz); + fu_x509_certificate_set_issuer(self, str); + } + + /* subject */ + subject = (gnutls_datum_t *)gnutls_malloc(sizeof(gnutls_datum_t)); + if (gnutls_x509_crt_get_subject(crt, &dn) == GNUTLS_E_SUCCESS) { + g_autofree gchar *str = NULL; + gnutls_x509_dn_get_str(dn, subject); + str = fu_strsafe((const gchar *)subject->data, subject->size); + fu_x509_certificate_set_subject(self, str); + } + + /* activation_time */ + ts = (gint64)gnutls_x509_crt_get_activation_time(crt); + if (ts == -1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to get activation time"); + return FALSE; + } + fu_x509_certificate_set_activation_time(self, ts); + + /* key ID */ + rc = gnutls_x509_crt_get_key_id(crt, 0, key_id, &key_idsz); + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to get key ID: %s [%i]", + gnutls_strerror(rc), + rc); + return FALSE; + } + for (guint i = 0; i < key_idsz; i++) + g_string_append_printf(key_idstr, "%02x", key_id[i]); + fu_firmware_set_id(firmware, key_idstr->str); + + /* success */ + return TRUE; +#else + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no GnuTLS support"); + return FALSE; +#endif +} + +static void +fu_x509_certificate_init(FuX509Certificate *self) +{ +} + +static void +fu_x509_certificate_finalize(GObject *obj) +{ + FuX509Certificate *self = FU_X509_CERTIFICATE(obj); + g_free(self->issuer); + g_free(self->subject); + if (self->activation_time != NULL) + g_date_time_unref(self->activation_time); + G_OBJECT_CLASS(fu_x509_certificate_parent_class)->finalize(obj); +} + +static void +fu_x509_certificate_class_init(FuX509CertificateClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_x509_certificate_finalize; + firmware_class->export = fu_x509_certificate_export; + firmware_class->parse = fu_x509_certificate_parse; +} + +/** + * fu_x509_certificate_new: + * + * Creates a new #FuX509Certificate. + * + * Returns: (transfer full): object + * + * Since: 2.0.9 + **/ +FuX509Certificate * +fu_x509_certificate_new(void) +{ + return g_object_new(FU_TYPE_X509_CERTIFICATE, NULL); +} diff -Nru fwupd-2.0.8/libfwupdplugin/fu-x509-certificate.h fwupd-2.0.20/libfwupdplugin/fu-x509-certificate.h --- fwupd-2.0.8/libfwupdplugin/fu-x509-certificate.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fu-x509-certificate.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-firmware.h" + +#define FU_TYPE_X509_CERTIFICATE (fu_x509_certificate_get_type()) +G_DECLARE_FINAL_TYPE(FuX509Certificate, fu_x509_certificate, FU, X509_CERTIFICATE, FuFirmware) + +FuX509Certificate * +fu_x509_certificate_new(void); +const gchar * +fu_x509_certificate_get_issuer(FuX509Certificate *self) G_GNUC_NON_NULL(1); +const gchar * +fu_x509_certificate_get_subject(FuX509Certificate *self) G_GNUC_NON_NULL(1); +GDateTime * +fu_x509_certificate_get_activation_time(FuX509Certificate *self) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/libfwupdplugin/fwupdplugin.h fwupd-2.0.20/libfwupdplugin/fwupdplugin.h --- fwupd-2.0.8/libfwupdplugin/fwupdplugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/fwupdplugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -32,12 +32,14 @@ #include #include #include +#include #include #include #include #include #include #include +#include #include #include #include @@ -50,12 +52,16 @@ #include #include #include +#include #include #include #include #include #include +#include #include +#include +#include #include #include #include @@ -67,6 +73,7 @@ #include #include #include +#include #include #include #include @@ -85,6 +92,8 @@ #include #include #include +#include +#include #include #include #include @@ -94,10 +103,12 @@ #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -112,6 +123,8 @@ #include #include #include +#include +#include // #include #include #include diff -Nru fwupd-2.0.8/libfwupdplugin/meson.build fwupd-2.0.20/libfwupdplugin/meson.build --- fwupd-2.0.8/libfwupdplugin/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ fwupdplugin_structs = [ + 'fu-crc.rs', # fuzzing 'fu-progress.rs', # fuzzing 'fu-common.rs', # fuzzing 'fu-acpi-table.rs', # fuzzing @@ -7,6 +8,7 @@ 'fu-cfi.rs', # fuzzing 'fu-cfu-firmware.rs', # fuzzing 'fu-coswid.rs', # fuzzing + 'fu-context.rs', # fuzzing 'fu-device.rs', # fuzzing 'fu-dfu-firmware.rs', # fuzzing 'fu-dpaux.rs', # fuzzing @@ -14,13 +16,16 @@ 'fu-efi.rs', # fuzzing 'fu-elf.rs', # fuzzing 'fu-fdt.rs', # fuzzing + 'fu-firmware.rs', # fuzzing 'fu-fmap.rs', # fuzzing 'fu-hid.rs', # fuzzing + 'fu-hidraw.rs', # fuzzing 'fu-ifd.rs', # fuzzing 'fu-ifwi.rs', # fuzzing 'fu-ihex.rs', # fuzzing 'fu-intel-thunderbolt.rs', # fuzzing 'fu-io-channel.rs', # fuzzing + 'fu-heci.rs', # fuzzing 'fu-msgpack.rs', # fuzzing 'fu-oprom.rs', # fuzzing 'fu-pefile.rs', # fuzzing @@ -35,8 +40,9 @@ # do not use structgen as these are referenced in headers too... fwupdplugin_rs_targets = [] -foreach fwupdplugin_struct: fwupdplugin_structs - fwupdplugin_rs_targets += custom_target('lib' + fwupdplugin_struct, +foreach fwupdplugin_struct : fwupdplugin_structs + fwupdplugin_rs_targets += custom_target( + 'lib' + fwupdplugin_struct, input: fwupdplugin_struct, output: [ fwupdplugin_struct.replace('.rs', '-struct.c'), @@ -45,7 +51,13 @@ command: [ python3, join_paths(meson.project_source_root(), 'libfwupdplugin', 'rustgen.py'), - '@INPUT@', '@OUTPUT0@', '@OUTPUT1@', + '@INPUT@', + '@OUTPUT0@', + '@OUTPUT1@', + '--include', + 'fwupd.h', + '--prefix', + 'Fu', ], ) endforeach @@ -101,11 +113,15 @@ 'fu-efi-lz77-decompressor.c', # fuzzing 'fu-efi-hard-drive-device-path.c', # fuzzing 'fu-efi-load-option.c', # fuzzing - 'fu-efi-signature.c', - 'fu-efi-signature-list.c', + 'fu-efi-signature.c', # fuzzing + 'fu-efi-signature-list.c', # fuzzing + 'fu-efi-variable-authentication2.c', + 'fu-efi-vss2-variable-store.c', # fuzzing + 'fu-efi-vss-auth-variable.c', # fuzzing + 'fu-efi-ftw-store.c', # fuzzing 'fu-efivars.c', # fuzzing 'fu-efi-x509-device.c', - 'fu-efi-x509-signature.c', + 'fu-efi-x509-signature.c', # fuzzing 'fu-elf-firmware.c', # fuzzing 'fu-fdt-firmware.c', # fuzzing 'fu-fdt-image.c', # fuzzing @@ -127,7 +143,6 @@ 'fu-hwids-smbios.c', # fuzzing 'fu-i2c-device.c', 'fu-ifd-bios.c', # fuzzing - 'fu-ifd-common.c', # fuzzing 'fu-ifd-firmware.c', # fuzzing 'fu-ifd-image.c', # fuzzing 'fu-ifwi-cpd-firmware.c', # fuzzing @@ -138,13 +153,15 @@ 'fu-intel-thunderbolt-nvm.c', # fuzzing 'fu-io-channel.c', # fuzzing 'fu-ioctl.c', # fuzzing + 'fu-json-firmware.c', # fuzzing 'fu-kenv.c', # fuzzing 'fu-kernel.c', # fuzzing 'fu-kernel-search-path.c', # fuzzing - 'fu-linear-firmware.c', + 'fu-linear-firmware.c', # fuzzing 'fu-lzma-common.c', # fuzzing 'fu-mei-device.c', 'fu-mem.c', # fuzzing + 'fu-heci-device.c', 'fu-msgpack.c', 'fu-msgpack-item.c', 'fu-oprom-firmware.c', # fuzzing @@ -152,7 +169,9 @@ 'fu-path.c', # fuzzing 'fu-pefile-firmware.c', # fuzzing 'fu-pci-device.c', + 'fu-pkcs7.c', 'fu-oprom-device.c', + 'fu-output-stream.c', # fuzzing 'fu-plugin.c', 'fu-progress.c', # fuzzing 'fu-quirks.c', # fuzzing @@ -180,6 +199,8 @@ 'fu-version-common.c', # fuzzing 'fu-v4l-device.c', 'fu-volume.c', # fuzzing + 'fu-volume-locker.c', # fuzzing + 'fu-x509-certificate.c', # fuzzing ] if host_machine.system() in ['linux', 'android'] @@ -193,7 +214,7 @@ fwupdplugin_src += 'fu-windows-efivars.c' elif host_machine.system() == 'darwin' fwupdplugin_src += 'fu-common-darwin.c' - fwupdplugin_src += 'fu-darwin-efivars.c' # fuzzing + fwupdplugin_src += 'fu-darwin-efivars.c' # fuzzing endif fwupdplugin_headers = [ @@ -231,6 +252,7 @@ 'fu-device-event.h', 'fu-device-locker.h', 'fu-device-metadata.h', + 'fu-device-poll-locker.h', 'fu-device-private.h', 'fu-device-progress.h', 'fu-dfu-firmware.h', @@ -250,6 +272,10 @@ 'fu-efi-load-option.h', 'fu-efi-signature.h', 'fu-efi-signature-list.h', + 'fu-efi-variable-authentication2.h', + 'fu-efi-vss2-variable-store.h', + 'fu-efi-vss-auth-variable.h', + 'fu-efi-ftw-store.h', 'fu-efivars.h', 'fu-efi-x509-device.h', 'fu-efi-x509-signature.h', @@ -261,6 +287,7 @@ 'fu-firmware.h', 'fu-fit-firmware.h', 'fu-fmap-firmware.h', + 'fu-heci-device.h', 'fu-hid-descriptor.h', 'fu-hid-device.h', 'fu-hid-report.h', @@ -268,7 +295,6 @@ 'fu-hwids.h', 'fu-i2c-device.h', 'fu-ifd-bios.h', - 'fu-ifd-common.h', 'fu-ifd-firmware.h', 'fu-ifd-image.h', 'fu-ifwi-cpd-firmware.h', @@ -279,6 +305,7 @@ 'fu-intel-thunderbolt-nvm.h', 'fu-io-channel.h', 'fu-ioctl.h', + 'fu-json-firmware.h', 'fu-kenv.h', 'fu-kernel.h', 'fu-kernel-search-path.h', @@ -289,11 +316,13 @@ 'fu-msgpack-item.h', 'fu-oprom-device.h', 'fu-oprom-firmware.h', + 'fu-output-stream.h', 'fu-partial-input-stream.h', 'fu-partial-input-stream-private.h', 'fu-path.h', 'fu-pefile-firmware.h', 'fu-pci-device.h', + 'fu-pkcs7.h', 'fu-plugin.h', 'fu-plugin-private.h', 'fu-progress.h', @@ -331,22 +360,21 @@ 'fu-v4l-device.h', 'fu-version-common.h', 'fu-volume.h', + 'fu-volume-locker.h', + 'fu-x509-certificate.h', ] -introspection_deps = [ - libxmlb, - giounix, - libusb, -] +introspection_deps = [libxmlb, giounix, libusb] -pkgg_requires = [ 'gio-2.0', - 'gmodule-2.0', - 'gobject-2.0', - 'fwupd', - 'json-glib-1.0', - 'libarchive', - 'xmlb', - 'libusb', +pkgg_requires = [ + 'gio-2.0', + 'gmodule-2.0', + 'gobject-2.0', + 'fwupd', + 'json-glib-1.0', + 'libarchive', + 'xmlb', + 'libusb', ] library_deps = [ @@ -369,33 +397,28 @@ fwupdplugin = library( 'fwupdplugin', fwupdplugin_rs_targets, - sources: [ - fwupdplugin_src, - fwupdplugin_headers, - ], - include_directories: [ - root_incdir, - fwupd_incdir, - ], - dependencies: [ - library_deps - ], - link_with: [ - fwupd, - ], + sources: [fwupdplugin_src, fwupdplugin_headers], + include_directories: [root_incdir, fwupd_incdir], + dependencies: [library_deps], + link_with: [fwupd], install_dir: libdir_pkg, - install: true + install: true, ) # see https://mesonbuild.com/FAQ.html#how-do-i-tell-meson-that-my-sources-use-generated-headers fwupdplugin_rs_headers = [] -foreach fwupdplugin_rs_target: fwupdplugin_rs_targets +foreach fwupdplugin_rs_target : fwupdplugin_rs_targets fwupdplugin_rs_headers += fwupdplugin_rs_target[1] endforeach -fwupdplugin_rs_dep = declare_dependency(link_with: fwupdplugin, sources: fwupdplugin_rs_headers) +fwupdplugin_rs_dep = declare_dependency( + link_with: fwupdplugin, + sources: fwupdplugin_rs_headers, +) if introspection.allowed() - gir_dep = declare_dependency(sources: fwupd_gir) + gir_dep = declare_dependency( + sources: fwupd_gir + ) girtargets = [] extra_args = [] if libxmlb.type_name() == 'internal' @@ -403,91 +426,60 @@ else girtargets += 'Xmlb-2.0' endif - fwupdplugin_gir = gnome.generate_gir(fwupd, - sources: [ - fwupdplugin_src, - fwupdplugin_headers, - fwupdplugin_rs_targets, - ], + fwupdplugin_gir = gnome.generate_gir( + fwupd, + sources: [fwupdplugin_src, fwupdplugin_headers, fwupdplugin_rs_targets], nsversion: '1.0', namespace: 'FwupdPlugin', symbol_prefix: 'fu', identifier_prefix: 'Fu', export_packages: 'fwupdplugin', extra_args: extra_args, - include_directories: [ - fwupd_incdir, - ], + include_directories: [fwupd_incdir], header: 'fwupdplugin.h', - dependencies: [ - gir_dep, - introspection_deps - ], - link_with: [ - fwupdplugin, - ], - includes: [ - 'Gio-2.0', - 'GObject-2.0', - girtargets, - fwupd_gir[0], - ], - install: false + dependencies: [gir_dep, introspection_deps], + link_with: [fwupdplugin], + includes: ['Gio-2.0', 'GObject-2.0', girtargets, fwupd_gir[0]], + install: false, ) endif if get_option('tests') gcab = executable( 'gcab', - sources: [ - 'fu-gcab.c', - ], - include_directories: [ - root_incdir, - fwupd_incdir, - ], - dependencies: [ - library_deps, - fwupdplugin_rs_dep, - ], - link_with: [ - fwupd, - fwupdplugin - ], + sources: ['fu-gcab.c'], + include_directories: [root_incdir, fwupd_incdir], + dependencies: [library_deps, fwupdplugin_rs_dep], + link_with: [fwupd, fwupdplugin], ) subdir('tests') env = environment() env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('LSAN_OPTIONS', 'suppressions=@0@'.format(join_paths(meson.project_source_root(), 'data', 'tests', 'lsan-suppressions.txt'))) e = executable( 'fwupdplugin-self-test', installed_firmware_zip, + colorhug_test_firmware, rustgen.process('fu-self-test.rs'), - sources: [ - 'fu-test-device.c', - 'fu-self-test.c' - ], - include_directories: [ - root_incdir, - fwupd_incdir, - ], - dependencies: [ - library_deps, - fwupdplugin_rs_dep, - ], - link_with: [ - fwupd, - fwupdplugin - ], - c_args: [ - '-DSRCDIR="' + meson.current_source_dir() + '"', - ], + sources: ['fu-self-test-device.c', 'fu-self-test.c'], + include_directories: [root_incdir, fwupd_incdir], + dependencies: [library_deps, fwupdplugin_rs_dep], + link_with: [fwupd, fwupdplugin], + c_args: ['-DSRCDIR="' + meson.current_source_dir() + '"'], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, ) - test('fwupdplugin-self-test', e, is_parallel: false, timeout: 180, env: env) + test( + 'fwupdplugin-self-test', + e, + is_parallel: false, + timeout: 180, + env: env, + ) endif fwupdplugin_incdir = include_directories('.') diff -Nru fwupd-2.0.8/libfwupdplugin/rustgen.py fwupd-2.0.20/libfwupdplugin/rustgen.py --- fwupd-2.0.8/libfwupdplugin/rustgen.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/rustgen.py 2026-02-26 11:36:18.000000000 +0000 @@ -7,6 +7,7 @@ import os import sys +import uuid import argparse from enum import Enum @@ -66,17 +67,23 @@ class EnumObj: def __init__(self, name: str) -> None: self.name: str = name + self._since: Optional[str] = None + self.comments: list[str] = [] self.repr_type: Optional[str] = None self.items: List[EnumItem] = [] + self.is_imported: bool = False self._exports: Dict[str, Export] = { "ToString": Export.NONE, - "ToBitString": Export.NONE, "FromString": Export.NONE, } + self._is_bitfield = False def c_method(self, suffix: str): return f"{_camel_to_snake(self.name)}_{_camel_to_snake(suffix)}" + def since(self, derive: str) -> Optional[str]: + return self._since + @property def c_type(self): return f"{self.name}" @@ -92,10 +99,17 @@ return True return False - def check(self): + @property + def is_bitfield(self) -> bool: + for item in self.items: + if item.is_bitfield: + return True + return self._is_bitfield + + def check(self, prefix: Optional[str] = None): # check we're prefixed with something sane - if not self.name.startswith("Fu"): - raise ValueError(f"enum {self.name} does not have 'Fu' prefix") + if prefix and not self.name.startswith(prefix): + raise ValueError(f"enum {self.name} does not have '{prefix}' prefix") # check we'd not just done ZERO=0, ONE=1, TWO=2, etc indexed = True @@ -106,6 +120,10 @@ if indexed: raise ValueError(f"enum {self.name} does not need explicit defaults") + # check each enum + for item in self.items: + item.check() + def item(self, name: str) -> Optional["EnumItem"]: for item in self.items: if item.name == name: @@ -118,6 +136,9 @@ self._exports[derive] = Export.PRIVATE def add_public_export(self, derive: str) -> None: + if derive == "Bitfield": + self._is_bitfield = True + return self.add_private_export(derive) self._exports[derive] = Export.PUBLIC @@ -133,10 +154,15 @@ self.obj: EnumObj = obj self.name: str = "" self.default: Optional[str] = None + self.comments: list[str] = [] + self.since: Optional[str] = None + self.is_bitfield = False @property def c_define(self) -> str: name_snake = _camel_to_snake(self.obj.name) + if name_snake.endswith("flags") or name_snake.endswith("attrs"): + name_snake = name_snake[:-1] return f"{name_snake.upper()}_{_camel_to_snake(self.name).replace('-', '_').upper()}" def parse_default(self, val: str) -> None: @@ -146,12 +172,35 @@ "u16::MAX": "G_MAXUINT16", "u8::MAX": "G_MAXUINT8", }.get(val, val) + + # parse bitfield shifts + try: + number, bitshift = val.split("<<", maxsplit=1) + except ValueError: + pass + else: + self.is_bitfield = True + # make sure we promote to a larger integer type + if int(bitshift) >= 31: + val = f"{int(number)}ull<<{bitshift}" if val.startswith("0x") or val.startswith("0b"): val = val.replace("_", "") if val.startswith("0b"): val = hex(int(val[2:], 2)) self.default = val + def check(self): + uppercase_cnt: int = 0 + for char in self.name: + if char.isupper(): + if uppercase_cnt > 1: + raise ValueError( + f"enum {self.name} had too many consecutive uppercase chars" + ) + uppercase_cnt += 1 + else: + uppercase_cnt = 0 + @property def value(self) -> str: return _camel_to_snake(self.name).replace("_", "-") @@ -164,6 +213,7 @@ def __init__(self, name: str) -> None: self.name: str = name self.items: List[StructItem] = [] + self.is_imported: bool = False self._exports: Dict[str, Export] = { "Validate": Export.NONE, "ValidateBytes": Export.NONE, @@ -174,7 +224,9 @@ "ParseStream": Export.NONE, "ParseInternal": Export.NONE, "New": Export.NONE, + "NewInternal": Export.NONE, "ToString": Export.NONE, + "ToBytes": Export.NONE, "Default": Export.NONE, } @@ -207,10 +259,10 @@ return True return False - def check(self): + def check(self, prefix: Optional[str] = None): # check we're prefixed with something sane - if not self.name.startswith("Fu"): - raise ValueError(f"struct {self.name} does not have 'Fu' prefix") + if prefix and not self.name.startswith(prefix): + raise ValueError(f"struct {self.name} does not have '{prefix}' prefix") def add_private_export(self, derive: str) -> None: if self._exports[derive] == Export.PUBLIC: @@ -219,17 +271,16 @@ if derive == "Validate": self.add_private_export("ValidateInternal") elif derive == "ValidateStream": + self.add_private_export("NewInternal") self.add_private_export("ValidateInternal") elif derive == "ValidateBytes": self.add_private_export("Validate") elif derive == "ValidateInternal": for item in self.items: - if ( - item.constant - and item.type != Type.STRING - and not (item.type == Type.U8 and item.n_elements) - ): + if item.constant and not (item.type == Type.U8 and item.n_elements): item.add_private_export("Getters") + if item.constant and item.enum_obj: + item.enum_obj.add_private_export("ToString") if item.struct_obj: item.struct_obj.add_private_export("ValidateInternal") elif derive == "ToString": @@ -239,8 +290,10 @@ if item.enum_obj and not item.constant and item.enabled: item.enum_obj.add_private_export("ToString") elif derive == "Parse": + self.add_private_export("NewInternal") self.add_private_export("ParseInternal") elif derive == "ParseStream": + self.add_private_export("NewInternal") self.add_private_export("ParseInternal") elif derive == "ParseBytes": self.add_private_export("Parse") @@ -257,6 +310,7 @@ if item.struct_obj: item.struct_obj.add_private_export("ValidateInternal") elif derive == "New": + self.add_private_export("NewInternal") for item in self.items: if item.constant and not (item.type == Type.U8 and item.n_elements): item.add_private_export("Setters") @@ -269,6 +323,8 @@ for item in self.items: if not item.constant: item.add_public_export(derive) + if item.struct_obj: + item.struct_obj.add_private_export("NewInternal") else: self.add_private_export(derive) self._exports[derive] = Export.PUBLIC @@ -446,12 +502,35 @@ if val.startswith('"') and val.endswith('"'): return val[1:-1] raise ValueError(f"string default {val} needs double quotes") - if self.type == Type.GUID or (self.type == Type.U8 and self.n_elements): + if self.type == Type.GUID: + if val.startswith("0x"): + guid = uuid.UUID(bytes_le=bytes.fromhex(val[2:])) + raise ValueError(f"integer {val} expected, expected: {guid}") + if not val.startswith('"'): + raise ValueError(f"string expected, got: {val}") + uuid2 = uuid.UUID(val[1:-1]) + val_hex = "" + for value in uuid2.bytes_le: + val_hex += f"\\x{value:x}" + return val_hex + if self.type == Type.U8 and self.n_elements: + val_hex = "" + if val.startswith("[") and val.endswith("]"): + value, n_elements = val[1:-1].split(";", maxsplit=1) + if not value.startswith("0x"): + raise ValueError(f"0x prefix for hex number expected, got: {val}") + if self.size != int(n_elements): + raise ValueError( + f"data has to be {self.size} bytes exactly. Is {n_elements}" + ) + for _ in range(int(n_elements)): + val_hex += f"\\x{value[2:]}" + return val_hex + if not val.startswith("0x"): raise ValueError(f"0x prefix for hex number expected, got: {val}") if len(val) != (self.size * 2) + 2: raise ValueError(f"data has to be {self.size} bytes exactly") - val_hex = "" for idx in range(2, len(val), 2): val_hex += f"\\x{val[idx:idx+2]}" return val_hex @@ -469,14 +548,6 @@ raise ValueError(f"do not know how to parse value for type: {self.type}") def parse_default(self, val: str) -> None: - if ( - self.type == Type.U8 - and self.n_elements - and val.startswith("0x") - and len(val) == 4 - ): - self.padding = val - return self.default = self._parse_default(val) def parse_constant(self, val: str) -> None: @@ -561,8 +632,18 @@ class Generator: - def __init__(self, basename) -> None: + def __init__( + self, + basename, + modules_map: Dict[str, str], + prefix: Optional[str] = None, + includes=[], + ) -> None: self.basename: str = basename + self.prefix: Optional[str] = prefix + self.import_headers: list[str] = [] + self.modules_map: Dict[str, str] = modules_map + self.includes: list[str] = includes self.struct_objs: Dict[str, StructObj] = {} self.enum_objs: Dict[str, EnumObj] = {} self._env = Environment( @@ -593,22 +674,76 @@ template_c = self._env.get_template(os.path.basename("fu-rustgen-struct.c.in")) return template_c.render(subst), template_h.render(subst) - def process_input(self, contents: str) -> Tuple[str, str]: + def _use_import(self, where: str, module: str, what: str) -> None: + + module_basename = module.replace("_", "-") + try: + fn = os.path.join(self.modules_map[where], f"fu-{module_basename}.rs") + except KeyError: + raise ValueError(f"invalid module name: {where}") + child = Generator(self.basename, self.modules_map, prefix=self.prefix) + with open(fn, "rb") as f: + child._parse_input(f.read().decode()) + + # header includes + header_basename: str = f"fu-{module_basename}-struct.h" + if header_basename not in self.import_headers: + self.import_headers.append(header_basename) + + # is enum + if what in child.enum_objs: + enum_obj = child.enum_objs[what] + enum_obj.is_imported = True + self.enum_objs[what] = enum_obj + return + + # is struct + if what in child.struct_objs: + struct_obj = child.struct_objs[what] + struct_obj.is_imported = True + self.struct_objs[what] = struct_obj + return + + # not found + raise ValueError(f"invalid struct or enum name: {what}") + + def _parse_input(self, contents: str) -> None: name = None repr_type: Optional[str] = None derives: List[str] = [] offset: int = 0 struct_seen_b32: bool = False bits_offset: int = 0 + since: Optional[str] = None struct_cur: Optional[StructObj] = None enum_cur: Optional[EnumObj] = None + comments_cur: list[str] = [] for line_num, line in enumerate(contents.split("\n")): # replace all tabs with spaces line = line.replace("\t", " ") + # import one file into another + if line.startswith("use "): + if not line.endswith(";"): + raise ValueError(f"use requires a semicolon on line {line_num}") + where, why, what = line[4:-1].split("::", maxsplit=3) + self._use_import(where, why, what) + # remove comments and indent - line = line.split("//")[0].strip() + try: + line, comment = line.split("//", maxsplit=1) + except ValueError: + pass + else: + comment = comment.strip() + if comment.startswith("Since:"): + since = comment[6:].strip() + elif comment.startswith("SPDX") or comment.startswith("Copyright"): + pass + elif comment: + comments_cur.append(comment.strip()) + line = line.strip() if not line: continue @@ -628,7 +763,10 @@ raise ValueError(f"enum {name} already defined on line {line_num}") enum_cur = EnumObj(name) enum_cur.repr_type = repr_type + enum_cur._since = since + enum_cur.comments.extend(comments_cur) self.enum_objs[name] = enum_cur + comments_cur.clear() continue # the enum type @@ -649,7 +787,7 @@ # end of structure if line.startswith("}"): if struct_cur: - struct_cur.check() + struct_cur.check(prefix=self.prefix) for derive in derives: struct_cur.add_public_export(derive) for item in struct_cur.items: @@ -658,12 +796,14 @@ if item.constant == "$struct_size": item.constant = str(offset) if enum_cur: - enum_cur.check() + enum_cur.check(prefix=self.prefix) for derive in derives: enum_cur.add_public_export(derive) struct_cur = None enum_cur = None repr_type = None + comments_cur.clear() + since = None derives.clear() offset = 0 bits_offset = 0 @@ -680,11 +820,14 @@ # split enumeration into sections if enum_cur: enum_item = EnumItem(enum_cur) + enum_item._since = since + enum_item.comments.extend(comments_cur) parts = line.replace(" ", "").split("=", maxsplit=2) enum_item.name = parts[0] if len(parts) > 1: enum_item.parse_default(parts[1]) enum_cur.items.append(enum_item) + comments_cur.clear() # split structure into sections if struct_cur: @@ -726,21 +869,32 @@ bits_offset += item.bits_size struct_cur.items.append(item) + def process_input(self, contents: str) -> Tuple[str, str]: + + # parse input + self._parse_input(contents) + # process the templates here subst = { "basename": self.basename, "enum_objs": self.enum_objs, "struct_objs": self.struct_objs, + "import_headers": self.import_headers, + "includes": self.includes, } template_h = self._env.get_template(os.path.basename("fu-rustgen.h.in")) template_c = self._env.get_template(os.path.basename("fu-rustgen.c.in")) dst_h = template_h.render(subst) dst_c = template_c.render(subst) for enum_obj in self.enum_objs.values(): + if enum_obj.is_imported: + continue str_c, str_h = self._process_enums(enum_obj) dst_c += str_c dst_h += str_h for struct_obj in self.struct_objs.values(): + if struct_obj.is_imported: + continue str_c, str_h = self._process_structs(struct_obj) dst_c += str_c dst_h += str_h @@ -754,12 +908,33 @@ parser.add_argument("src", action="store", type=str, help="source") parser.add_argument("dst_c", action="store", type=str, help="destination .c") parser.add_argument("dst_h", action="store", type=str, help="destination .h") + parser.add_argument("--prefix", action="store", type=str, default="", help="prefix") + parser.add_argument("--use", action="append", default=[], help="module:path") + parser.add_argument( + "--include", action="append", default=[], help="fwupd.h|fwupdplugin.h" + ) args = parser.parse_args() - g = Generator(basename=os.path.basename(args.dst_h)) + # parse map from module to path + modules_map: dict[str, str] = {} + for entry in args.use: + try: + split = entry.split(":", maxsplit=1) + modules_map[split[0]] = split[1] + except IndexError: + sys.exit(f"expected module:path, got {entry}") + + g = Generator( + basename=os.path.basename(args.dst_h), + modules_map=modules_map, + includes=args.include, + prefix=args.prefix, + ) with open(args.src, "rb") as f: try: - dst_c, dst_h = g.process_input(f.read().decode()) + dst_c, dst_h = g.process_input( + f.read().decode(), + ) except ValueError as e: sys.exit(f"cannot process {args.src}: {str(e)}") with open(args.dst_c, "wb") as f: # type: ignore diff -Nru fwupd-2.0.8/libfwupdplugin/tests/colorhug/meson.build fwupd-2.0.20/libfwupdplugin/tests/colorhug/meson.build --- fwupd-2.0.8/libfwupdplugin/tests/colorhug/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/colorhug/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,12 +1,8 @@ -colorhug_test_firmware = custom_target('colorhug-test-firmware', - input: [ - 'firmware.txt', - 'firmware.metainfo.xml', - ], +colorhug_test_firmware = custom_target( + 'colorhug-test-firmware', + input: ['firmware.txt', 'firmware.metainfo.xml'], output: 'colorhug-als-3.0.2.cab', - command: [ - gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@', - ], + command: [gcab, '--create', '@OUTPUT@', '@INPUT@'], install: true, install_dir: join_paths(installed_test_datadir, 'tests/colorhug'), ) diff -Nru fwupd-2.0.8/libfwupdplugin/tests/coswid.builder.xml fwupd-2.0.20/libfwupdplugin/tests/coswid.builder.xml --- fwupd-2.0.8/libfwupdplugin/tests/coswid.builder.xml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/coswid.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -5,6 +5,8 @@ fwupdx64 EFI helpers to install system firmware 1.3-3-g1d0c69f + org.fwupd.fwupd-efi + SI_BIOS license https://spdx.org/licenses/LGPL-2.0.html diff -Nru fwupd-2.0.8/libfwupdplugin/tests/efi/efivars/meson.build fwupd-2.0.20/libfwupdplugin/tests/efi/efivars/meson.build --- fwupd-2.0.8/libfwupdplugin/tests/efi/efivars/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/efi/efivars/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,9 +1,39 @@ -fs.copyfile('BootNext-8be4df61-93ca-11d2-aa0d-00e098032b8c', - install: true, - install_dir: join_paths(installed_test_datadir, 'tests/efi/efivars')) -fs.copyfile('fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416', - install: true, - install_dir: join_paths(installed_test_datadir, 'tests/efi/efivars')) -fs.copyfile('SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c', - install: true, - install_dir: join_paths(installed_test_datadir, 'tests/efi/efivars')) +if meson.version().version_compare('> 0.64.0') + fs.copyfile( + 'BootNext-8be4df61-93ca-11d2-aa0d-00e098032b8c', + install: true, + install_dir: join_paths(installed_test_datadir, 'tests/efi/efivars'), + ) + fs.copyfile( + 'fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416', + install: true, + install_dir: join_paths(installed_test_datadir, 'tests/efi/efivars'), + ) + fs.copyfile( + 'SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c', + install: true, + install_dir: join_paths(installed_test_datadir, 'tests/efi/efivars'), + ) +else + configure_file( + input: 'BootNext-8be4df61-93ca-11d2-aa0d-00e098032b8c', + output: 'BootNext-8be4df61-93ca-11d2-aa0d-00e098032b8c', + copy: true, + install: true, + install_dir: join_paths(installed_test_datadir, 'tests/efi/efivars'), + ) + configure_file( + input: 'fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416', + output: 'fwupd-ddc0ee61-e7f0-4e7d-acc5-c070a398838e-0-0abba7dc-e516-4167-bbf5-4d9d1c739416', + copy: true, + install: true, + install_dir: join_paths(installed_test_datadir, 'tests/efi/efivars'), + ) + configure_file( + input: 'SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c', + output: 'SecureBoot-8be4df61-93ca-11d2-aa0d-00e098032b8c', + copy: true, + install: true, + install_dir: join_paths(installed_test_datadir, 'tests/efi/efivars'), + ) +endif diff -Nru fwupd-2.0.8/libfwupdplugin/tests/efi-ftw-store.builder.xml fwupd-2.0.20/libfwupdplugin/tests/efi-ftw-store.builder.xml --- fwupd-2.0.8/libfwupdplugin/tests/efi-ftw-store.builder.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/efi-ftw-store.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,4 @@ + + healthy + + diff -Nru fwupd-2.0.8/libfwupdplugin/tests/efi-variable-authentication2.builder.xml fwupd-2.0.20/libfwupdplugin/tests/efi-variable-authentication2.builder.xml --- fwupd-2.0.8/libfwupdplugin/tests/efi-variable-authentication2.builder.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/efi-variable-authentication2.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ + + + sha256 + 77fa9abd-0359-4d32-bd60-28f4e78f784b + 418ad44c79e3fddd6a0574b24fcf0fb8fee4b3ff2be635d21a5c0852bdea635c + + + sha256 + 77fa9abd-0359-4d32-bd60-28f4e78f784b + 819ebd0aeb8f0b73d237a02d9344ad1fd6fae6ad763cacf1694a6d13c1986cde + + diff -Nru fwupd-2.0.8/libfwupdplugin/tests/efi-volume-sized.builder.xml fwupd-2.0.20/libfwupdplugin/tests/efi-volume-sized.builder.xml --- fwupd-2.0.8/libfwupdplugin/tests/efi-volume-sized.builder.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/efi-volume-sized.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,5 @@ + + 0x100 + fff12b8d-7696-4c8b-a985-2747075b4f50 + aGVsbG8gd29ybGQ= + diff -Nru fwupd-2.0.8/libfwupdplugin/tests/efi-volume.builder.xml fwupd-2.0.20/libfwupdplugin/tests/efi-volume.builder.xml --- fwupd-2.0.8/libfwupdplugin/tests/efi-volume.builder.xml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/efi-volume.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -1,5 +1,21 @@ - 0x3 fff12b8d-7696-4c8b-a985-2747075b4f50 - aGVsbG8gd29ybGQ= + 0xfeff + + 0xdfb8 + + CustomMode + AA== + c076ec0c-7028-4399-a072-71ee5c448b9f + variable-added + non-volatile,bootservice-access + + + + + + + + healthy + diff -Nru fwupd-2.0.8/libfwupdplugin/tests/efi-vss-auth-variable.builder.xml fwupd-2.0.20/libfwupdplugin/tests/efi-vss-auth-variable.builder.xml --- fwupd-2.0.8/libfwupdplugin/tests/efi-vss-auth-variable.builder.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/efi-vss-auth-variable.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,6 @@ + + CustomMode + aGVsbG8gd29ybGQ= + c076ec0c-7028-4399-a072-71ee5c448b9f + non-volatile,bootservice-access + diff -Nru fwupd-2.0.8/libfwupdplugin/tests/efi-vss2-variable-store.builder.xml fwupd-2.0.20/libfwupdplugin/tests/efi-vss2-variable-store.builder.xml --- fwupd-2.0.8/libfwupdplugin/tests/efi-vss2-variable-store.builder.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/efi-vss2-variable-store.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,10 @@ + + 0xdfd4 + + CustomMode + variable-added + aGVsbG8gd29ybGQ= + c076ec0c-7028-4399-a072-71ee5c448b9f + non-volatile,bootservice-access + + diff -Nru fwupd-2.0.8/libfwupdplugin/tests/fmap-offset.builder.xml fwupd-2.0.20/libfwupdplugin/tests/fmap-offset.builder.xml --- fwupd-2.0.8/libfwupdplugin/tests/fmap-offset.builder.xml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/fmap-offset.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -1,5 +1,5 @@ - 0x10 + 0x10 FMAP aGVsbG8gd29ybGQ= diff -Nru fwupd-2.0.8/libfwupdplugin/tests/fmap.builder.xml fwupd-2.0.20/libfwupdplugin/tests/fmap.builder.xml --- fwupd-2.0.8/libfwupdplugin/tests/fmap.builder.xml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/fmap.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -1,10 +1,14 @@ + 0x0 FMAP aGVsbG8gd29ybGQ= - - TEST - V29ybGQh + + SBOM + 0x1 + + 456 + diff -Nru fwupd-2.0.8/libfwupdplugin/tests/fwupd/fwupd.conf fwupd-2.0.20/libfwupdplugin/tests/fwupd/fwupd.conf --- fwupd-2.0.8/libfwupdplugin/tests/fwupd/fwupd.conf 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/fwupd/fwupd.conf 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,2 @@ +[fwupd] +Manufacturer=ASUS diff -Nru fwupd-2.0.8/libfwupdplugin/tests/meson.build fwupd-2.0.20/libfwupdplugin/tests/meson.build --- fwupd-2.0.8/libfwupdplugin/tests/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,25 +1,21 @@ subdir('colorhug') subdir('efi') -installed_firmware_zip = custom_target('installed-firmware-zip', - input: [ - 'colorhug/firmware.txt', - 'colorhug/firmware.txt.asc', - ], +installed_firmware_zip = custom_target( + 'installed-firmware-zip', + input: ['colorhug/firmware.txt', 'colorhug/firmware.txt.asc'], output: 'firmware.zip', - command: [ - python3, '-m', 'zipfile', '-c', '@OUTPUT@', '@INPUT@', - ], + command: [python3, '-m', 'zipfile', '-c', '@OUTPUT@', '@INPUT@'], install: true, install_dir: join_paths(installed_test_datadir, 'tests'), ) -install_data([ - 'America/New_York', - ], +install_data( + ['America/New_York'], install_dir: join_paths(installed_test_datadir, 'tests/America'), ) -install_data([ +install_data( + [ 'cpuinfo', 'cab.builder.xml', 'cab-compressed.builder.xml', @@ -38,7 +34,12 @@ 'efi-section.builder.xml', 'efi-signature.builder.xml', 'efi-signature-list.builder.xml', + 'efi-variable-authentication2.builder.xml', 'efi-volume.builder.xml', + 'efi-volume-sized.builder.xml', + 'efi-vss2-variable-store.builder.xml', + 'efi-vss-auth-variable.builder.xml', + 'efi-ftw-store.builder.xml', 'elf.builder.xml', 'fdt.builder.xml', 'fit.builder.xml', @@ -67,44 +68,43 @@ 'uswid.builder.xml', 'uswid-compressed.builder.xml', ], - install_dir: join_paths(installed_test_datadir, 'tests') + install_dir: join_paths(installed_test_datadir, 'tests'), + follow_symlinks: true, ) -install_data([ - 'oui.txt', - 'pci.ids', - 'pnp.ids', - 'usb.ids', - ], - install_dir: join_paths(installed_test_datadir, 'tests') +install_data( + ['oui.txt', 'pci.ids', 'pnp.ids', 'usb.ids', 'uevent'], + install_dir: join_paths(installed_test_datadir, 'tests'), ) -install_data([ - 'lockdown/locked/lockdown', - ], +install_data( + ['lockdown/locked/lockdown'], install_dir: join_paths(installed_test_datadir, 'tests/lockdown/locked'), ) -install_data([ - 'lockdown/none/lockdown', - ], +install_data( + ['lockdown/none/lockdown'], install_dir: join_paths(installed_test_datadir, 'tests/lockdown/none'), ) -install_data([ - 'quirks.d/tests.quirk', - ], +install_data( + ['quirks.d/tests.quirk'], install_dir: join_paths(installed_test_datadir, 'tests/quirks.d'), ) -install_symlink('localtime', +install_symlink( + 'localtime', install_dir: join_paths(installed_test_datadir, 'tests'), pointing_to: join_paths('America', 'New_York'), ) -install_data([ - 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/pending_reboot', - ], - install_dir: join_paths(installed_test_datadir, 'tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes'), +install_data( + ['bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/pending_reboot'], + install_dir: join_paths( + installed_test_datadir, + 'tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes', + ), + follow_symlinks: true, ) -install_data([ +install_data( + [ 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/current_value', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/default_value', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/dell_modifier', @@ -114,10 +114,15 @@ 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/possible_values', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense/type', ], - install_dir: join_paths(installed_test_datadir, 'tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense'), + install_dir: join_paths( + installed_test_datadir, + 'tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/WlanAutoSense', + ), + follow_symlinks: true, ) -install_data([ +install_data( + [ 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/current_value', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/default_value', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/dell_modifier', @@ -128,10 +133,15 @@ 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/scalar_increment', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop/type', ], - install_dir: join_paths(installed_test_datadir, 'tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop'), + install_dir: join_paths( + installed_test_datadir, + 'tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/CustomChargeStop', + ), + follow_symlinks: true, ) -install_data([ +install_data( + [ 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/current_value', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/default_value', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/dell_modifier', @@ -141,11 +151,15 @@ 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/min_length', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset/max_length', ], - install_dir: join_paths(installed_test_datadir, 'tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset'), + install_dir: join_paths( + installed_test_datadir, + 'tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/Asset', + ), + follow_symlinks: true, ) -install_data([ - +install_data( + [ 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/current_value', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/default_value', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/dell_modifier', @@ -155,10 +169,15 @@ 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/possible_values', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd/type', ], - install_dir: join_paths(installed_test_datadir, 'tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd'), + install_dir: join_paths( + installed_test_datadir, + 'tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/BiosRcvrFrmHdd', + ), + follow_symlinks: true, ) -install_data([ +install_data( + [ 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/current_value', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/default_value', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/dell_modifier', @@ -168,5 +187,9 @@ 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/possible_values', 'bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot/type', ], - install_dir: join_paths(installed_test_datadir, 'tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot'), + install_dir: join_paths( + installed_test_datadir, + 'tests/bios-attrs/dell-xps13-9310/dell-wmi-sysman/attributes/SecureBoot', + ), + follow_symlinks: true, ) diff -Nru fwupd-2.0.8/libfwupdplugin/tests/quirks.d/tests.quirk fwupd-2.0.20/libfwupdplugin/tests/quirks.d/tests.quirk --- fwupd-2.0.8/libfwupdplugin/tests/quirks.d/tests.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/quirks.d/tests.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -31,3 +31,9 @@ [MEI] Plugin = two + +# a HwID +[c4e7b7b9-2520-5397-8040-8ac279f540d8] +Flags = hwid-test-flag +[c4e7b7b9-2520-5397-8040-8ac279f540d8] +Flags = ~hwid-test-flag diff -Nru fwupd-2.0.8/libfwupdplugin/tests/uevent fwupd-2.0.20/libfwupdplugin/tests/uevent --- fwupd-2.0.8/libfwupdplugin/tests/uevent 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/libfwupdplugin/tests/uevent 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,3 @@ +DRIVER=snd_hda_codec_realtek +MODALIAS=hdaudio:v10EC0298r00100103a01 + diff -Nru fwupd-2.0.8/meson.build fwupd-2.0.20/meson.build --- fwupd-2.0.8/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,7 +1,9 @@ -project('fwupd', 'c', - version: '2.0.8', +project( + 'fwupd', + 'c', + version: '2.0.20', license: 'LGPL-2.1-or-later', - meson_version: '>=1.3.0', + meson_version: '>=0.63.0', # limited by RHEL-9 default_options: ['warning_level=2', 'c_std=c17'], ) @@ -19,14 +21,23 @@ # get source version, falling back to package version source_version = fwupd_version -git = find_program('git', required: false) +git = find_program( + 'git', + required: false, +) tag = false if git.found() - source_version = run_command([git, 'describe'], check: false).stdout().strip() + source_version = run_command( + [git, 'describe'], + check: false, + ).stdout().strip() if source_version == '' source_version = fwupd_version endif - tag = run_command([git, 'describe', '--exact-match'], check: false).returncode() == 0 + tag = run_command( + [git, 'describe', '--exact-match'], + check: false, + ).returncode() == 0 endif conf.set_quoted('SOURCE_VERSION', source_version) @@ -50,7 +61,11 @@ libfwupd_lt_current = '3' libfwupd_lt_revision = '0' libfwupd_lt_age = '0' -libfwupd_lt_version = '@0@.@1@.@2@'.format(libfwupd_lt_current, libfwupd_lt_age, libfwupd_lt_revision) +libfwupd_lt_version = '@0@.@1@.@2@'.format( + libfwupd_lt_current, + libfwupd_lt_age, + libfwupd_lt_revision, +) # get supported warning flags warning_flags = [ @@ -86,7 +101,8 @@ '-Wmissing-prototypes', '-Wnested-externs', '-Wno-cast-function-type', - '-Wno-address-of-packed-member', # incompatible with g_autoptr() + '-Wno-deprecated-declarations', + '-Wno-address-of-packed-member', # incompatible with g_autoptr() '-Wno-unknown-pragmas', '-Wno-missing-field-initializers', '-Wno-strict-aliasing', @@ -113,17 +129,23 @@ '-Wunused-but-set-variable', '-Wunused-variable', '-Wvla', - '-Wwrite-strings' + '-Wwrite-strings', ] static_analysis = get_option('static_analysis') and host_machine.system() != 'windows' if static_analysis warning_flags += ['-fanalyzer', '-Wno-analyzer-null-dereference'] endif cc = meson.get_compiler('c') -add_project_arguments(cc.get_supported_arguments(warning_flags), language: 'c') +add_project_arguments( + cc.get_supported_arguments(warning_flags), + language: 'c', +) if not meson.is_cross_build() - add_project_arguments('-fstack-protector-strong', language: 'c') + add_project_arguments( + '-fstack-protector-strong', + language: 'c', + ) endif if cc.get_id() == 'msvc' @@ -144,37 +166,49 @@ # enable full RELRO where possible # FIXME: until https://github.com/mesonbuild/meson/issues/1140 is fixed global_link_args = [] -test_link_args = [ - '-Wl,-z,relro', - '-Wl,-z,defs', - '-Wl,-z,now', - '-Wl,-z,ibt,-z,shstk', -] -foreach arg: test_link_args +test_link_args = ['-Wl,-z,relro', '-Wl,-z,defs', '-Wl,-z,now', '-Wl,-z,ibt,-z,shstk'] +foreach arg : test_link_args if cc.has_link_argument(arg) global_link_args += arg endif endforeach add_project_link_arguments( global_link_args, - language: 'c' + language: 'c', ) -add_project_arguments('-DFWUPD_COMPILATION', language: 'c') +add_project_arguments( + '-DFWUPD_COMPILATION', + language: 'c', +) # Needed for realpath(), syscall(), cfmakeraw(), etc. -add_project_arguments('-D_DEFAULT_SOURCE', language: 'c') +add_project_arguments( + '-D_DEFAULT_SOURCE', + language: 'c', +) # needed for symlink() and BYTE_ORDER -add_project_arguments('-D_BSD_SOURCE', language: 'c') -add_project_arguments('-D__BSD_VISIBLE', language: 'c') -add_project_arguments('-D_XOPEN_SOURCE=700', language: 'c') +add_project_arguments( + '-D_BSD_SOURCE', + language: 'c', +) +add_project_arguments( + '-D__BSD_VISIBLE', + language: 'c', +) # needed for memfd_create() -add_project_arguments('-D_GNU_SOURCE', language: 'c') +add_project_arguments( + '-D_GNU_SOURCE', + language: 'c', +) # needed for memmem() -add_project_arguments('-D_DARWIN_C_SOURCE=900000', language: 'c') +add_project_arguments( + '-D_DARWIN_C_SOURCE=900000', + language: 'c', +) # sanity check if get_option('build') == 'all' @@ -213,8 +247,15 @@ localedir = join_paths(prefix, get_option('localedir')) diffcmd = find_program('diff') -gio = dependency('gio-2.0', version: '>= 2.72.0') -giounix = dependency('gio-unix-2.0', version: '>= 2.72.0', required: false) +gio = dependency( + 'gio-2.0', + version: '>= 2.68.0', +) # limited by RHEL-9, which has v2.68.4 +giounix = dependency( + 'gio-unix-2.0', + version: '>= 2.68.0', + required: false, +) if giounix.found() conf.set('HAVE_GIO_UNIX', '1') endif @@ -222,80 +263,161 @@ if host_machine.system() == 'linux' conf.set('HAVE_UDEV', '1') endif -if build_standalone -bluez = get_option('bluez').disable_auto_if(host_machine.system() != 'linux') -if bluez.allowed() - conf.set('HAVE_BLUEZ', '1') -endif -host_cpu = host_machine.cpu_family() -hsi = get_option('hsi').disable_auto_if(host_machine.system() != 'linux').disable_auto_if(host_cpu != 'x86' and host_cpu != 'x86_64').allowed() -if hsi - conf.set('HAVE_HSI', '1') -endif -libxmlb = dependency('xmlb', version: '>= 0.3.19', fallback: ['libxmlb', 'libxmlb_dep']) -if libxmlb.get_variable('zstd') == 'true' - lvfs_metadata_format = 'zst' -elif libxmlb.get_variable('lzma') == 'true' - lvfs_metadata_format = 'xz' -else - lvfs_metadata_format = 'gz' +if get_option('udev_hotplug') + conf.set('HAVE_UDEV_HOTPLUG' , '1') endif -conf.set_quoted('FU_LVFS_METADATA_FORMAT', lvfs_metadata_format) +if build_standalone + bluez = get_option('bluez').disable_auto_if(host_machine.system() != 'linux') + if bluez.allowed() + conf.set('HAVE_BLUEZ', '1') + endif + host_cpu = host_machine.cpu_family() + hsi = get_option('hsi').disable_auto_if(host_machine.system() != 'linux').disable_auto_if( + host_cpu != 'x86' and host_cpu != 'x86_64' + ).allowed() + if hsi + conf.set('HAVE_HSI', '1') + endif + libxmlb = dependency( + 'xmlb', + version: '>= 0.3.19', + fallback: ['libxmlb', 'libxmlb_dep'], + ) + if libxmlb.get_variable('zstd') == 'true' + lvfs_metadata_format = 'zst' + elif libxmlb.get_variable('lzma') == 'true' + lvfs_metadata_format = 'xz' + else + lvfs_metadata_format = 'gz' + endif + conf.set_quoted('FU_LVFS_METADATA_FORMAT', lvfs_metadata_format) -# FreeBSD is missing some libusb symbols -libusb = dependency('libusb-1.0', version : '>= 0.1.12') -conf.set_quoted('LIBUSB_VERSION', libusb.version()) -if cc.has_header_symbol('libusb.h', 'libusb_set_option', dependencies: libusb) - conf.set('HAVE_LIBUSB_SET_OPTION', '1') -endif -if cc.has_header_symbol('libusb.h', 'libusb_init_context', dependencies: libusb) - conf.set('HAVE_LIBUSB_INIT_CONTEXT', '1') -endif -if cc.has_header_symbol('libusb.h', 'libusb_get_parent', dependencies: libusb) - conf.set('HAVE_LIBUSB_GET_PARENT', '1') -endif + # FreeBSD is missing some libusb symbols + libusb = dependency( + 'libusb-1.0', + version: '>= 0.1.27', + ) + conf.set_quoted('LIBUSB_VERSION', libusb.version()) + if cc.has_header_symbol( + 'libusb.h', + 'libusb_set_option', + dependencies: libusb, + ) + conf.set('HAVE_LIBUSB_SET_OPTION', '1') + endif + if cc.has_header_symbol( + 'libusb.h', + 'libusb_init_context', + dependencies: libusb, + ) + conf.set('HAVE_LIBUSB_INIT_CONTEXT', '1') + endif + if cc.has_header_symbol( + 'libusb.h', + 'libusb_get_parent', + dependencies: libusb, + ) + conf.set('HAVE_LIBUSB_GET_PARENT', '1') + endif + if cc.has_header_symbol( + 'libusb.h', + 'libusb_wrap_sys_device', + dependencies: libusb, + ) + conf.set('HAVE_LIBUSB_WRAP_SYS_DEVICE', '1') + endif -sqlite = dependency('sqlite3') -if sqlite.found() - conf.set('HAVE_SQLITE', '1') -endif -passim = dependency('passim', version: '>= 0.1.6', required: get_option('passim'), fallback: ['passim', 'passim_dep']) -if passim.found() - conf.set('HAVE_PASSIM', '1') -endif -libarchive = dependency('libarchive', required: get_option('libarchive')) -if libarchive.found() - conf.set('HAVE_LIBARCHIVE', '1') - if cc.has_header_symbol('archive.h', 'archive_write_add_filter_zstd') - conf.set('HAVE_LIBARCHIVE_WRITE_ADD_FILTER_ZSTD', '1') + readline = dependency( + 'readline', + required: get_option('readline'), + ) + if readline.found() and get_option('readline').allowed() + conf.set('HAVE_READLINE', '1') + endif + sqlite = dependency('sqlite3') + if sqlite.found() + conf.set('HAVE_SQLITE', '1') + endif + passim = dependency( + 'passim', + version: '>= 0.1.6', + required: get_option('passim'), + fallback: ['passim', 'passim_dep'], + ) + if passim.found() + conf.set('HAVE_PASSIM', '1') + endif + libarchive = dependency( + 'libarchive', + required: get_option('libarchive'), + ) + if libarchive.found() + conf.set('HAVE_LIBARCHIVE', '1') + if cc.has_header_symbol('archive.h', 'archive_write_add_filter_zstd') + conf.set('HAVE_LIBARCHIVE_WRITE_ADD_FILTER_ZSTD', '1') + endif endif endif -endif -libjcat = dependency('jcat', version: '>= 0.2.0', fallback: ['libjcat', 'libjcat_dep']) -libjsonglib = dependency('json-glib-1.0', version: '>= 1.6.0', fallback: ['libjsonglib', 'libjsonglib_dep']) -libblkid = dependency('blkid', required: get_option('blkid')) +libjcat = dependency( + 'jcat', + version: '>= 0.2.0', + fallback: ['libjcat', 'libjcat_dep'], +) +libjsonglib = dependency( + 'json-glib-1.0', + version: '>= 1.6.0', + fallback: ['libjsonglib', 'libjsonglib_dep'], +) +libblkid = dependency( + 'blkid', + required: get_option('blkid'), +) if libblkid.found() conf.set('HAVE_BLKID', '1') endif -valgrind = dependency('valgrind', required: get_option('valgrind')) -libcurl = dependency('libcurl', version: '>= 7.62.0') -libdrm = dependency('libdrm', required: get_option('libdrm')) +valgrind = dependency( + 'valgrind', + required: get_option('valgrind'), +) +libcurl = dependency( + 'libcurl', + version: '>= 7.62.0', +) +libdrm = dependency( + 'libdrm', + required: get_option('libdrm'), +) if libdrm.found() conf.set('HAVE_LIBDRM' , '1') endif -polkit = dependency('polkit-gobject-1', version: '>= 0.114', - required: get_option('polkit').disable_auto_if(host_machine.system() != 'linux')) +polkit = dependency( + 'polkit-gobject-1', + version: '>= 0.114', + required: get_option('polkit').disable_auto_if(host_machine.system() != 'linux'), +) if polkit.found() conf.set('HAVE_POLKIT', '1') - conf.set_quoted ('POLKIT_ACTIONDIR', polkit.get_variable(pkgconfig: 'actiondir')) + conf.set_quoted( + 'POLKIT_ACTIONDIR', + polkit.get_variable( + pkgconfig: 'actiondir' + ), + ) endif if build_daemon if not polkit.found() warning('Polkit is disabled, the daemon will allow ALL client actions') endif endif -libm = cc.find_library('m', required: false) +libm = cc.find_library( + 'm', + required: false, +) zlib = dependency('zlib') +libmnl = dependency( + 'libmnl', + required: get_option('libmnl'), +) fs = import('fs') @@ -311,18 +433,27 @@ vendor_ids_dir = '/usr/share/hwdata' endif if not fs.is_file(join_paths(vendor_ids_dir, 'usb.ids')) + vendor_ids_dir = '/usr/local/share/hwdata' + endif + if not fs.is_file(join_paths(vendor_ids_dir, 'usb.ids')) vendor_ids_dir = '/usr/share/misc' endif if not fs.is_file(join_paths(vendor_ids_dir, 'usb.ids')) vendor_ids_dir = '/usr/local/var/homebrew/linked/usb.ids/share/misc' endif if not fs.is_file(join_paths(vendor_ids_dir, 'usb.ids')) + vendor_ids_dir = '/opt/homebrew/share/misc' + endif + if not fs.is_file(join_paths(vendor_ids_dir, 'usb.ids')) error('could not auto-detect -Dvendor_ids_dir=') endif endif -conf.set_quoted ('FWUPD_DATADIR_VENDOR_IDS', vendor_ids_dir) +conf.set_quoted('FWUPD_DATADIR_VENDOR_IDS', vendor_ids_dir) -bashcomp = dependency('bash-completion', required: false) +bashcomp = dependency( + 'bash-completion', + required: false, +) python3path = get_option('python') if python3path == '' python3 = import('python').find_installation('python3') @@ -330,13 +461,21 @@ python3 = find_program(python3path) endif -gnutls = dependency('gnutls', version: '>= 3.6.0', required: get_option('gnutls')) +gnutls = dependency( + 'gnutls', + version: '>= 3.6.0', + required: get_option('gnutls'), +) if gnutls.found() conf.set('HAVE_GNUTLS', '1') endif lzma = dependency('liblzma') -cbor = dependency('libcbor', version: '>= 0.7.0', required: get_option('cbor')) +cbor = dependency( + 'libcbor', + version: '>= 0.7.0', + required: get_option('cbor'), +) if cbor.found() conf.set('HAVE_CBOR', '1') if cc.has_header_symbol('cbor.h', 'cbor_set_allocs') @@ -351,6 +490,10 @@ endif if host_machine.system() == 'freebsd' platform_deps += dependency('efivar') + platform_deps += dependency( + 'libinotify', + required: false, + ) endif endif @@ -358,8 +501,10 @@ conf.set('HAVE_VALGRIND', '1') endif -libsystemd = dependency('libsystemd', - required: get_option('systemd').disable_auto_if(host_machine.system() != 'linux')) +libsystemd = dependency( + 'libsystemd', + required: get_option('systemd').disable_auto_if(host_machine.system() != 'linux'), +) if cc.has_header('sys/auxv.h') conf.set('HAVE_AUXV_H', '1') @@ -388,7 +533,10 @@ if cc.has_header('sys/select.h') conf.set('HAVE_SELECT_H', '1') endif -if cc.has_header('sys/io.h') and cc.has_function('outb', prefix: '#include ') +if cc.has_header('sys/io.h') and cc.has_function( + 'outb', + prefix: '#include ', +) conf.set('HAVE_IO_H', '1') endif if cc.has_header('linux/ethtool.h') @@ -412,6 +560,9 @@ if cc.has_header('sys/mman.h') conf.set('HAVE_MMAN_H', '1') endif +if cc.has_header('sys/vfs.h') + conf.set('HAVE_SYS_VFS_H', '1') +endif if cc.has_header('poll.h') conf.set('HAVE_POLL_H', '1') endif @@ -420,11 +571,18 @@ endif if cc.has_header('malloc.h') conf.set('HAVE_MALLOC_H', '1') - if cc.has_function('malloc_trim', prefix: '#include ') - conf.set('HAVE_MALLOC_TRIM', '1') + if cc.has_function( + 'malloc_trim', + prefix: '#include ', + ) + conf.set('HAVE_MALLOC_TRIM', '1') endif endif -has_cpuid = cc.has_header_symbol('cpuid.h', '__get_cpuid_count', required: false) +has_cpuid = cc.has_header_symbol( + 'cpuid.h', + '__get_cpuid_count', + required: false, +) if has_cpuid conf.set('HAVE_CPUID_H', '1') endif @@ -443,6 +601,9 @@ if cc.has_function('memfd_create') conf.set('HAVE_MEMFD_CREATE', '1') endif +if cc.has_function('strerrordesc_np') + conf.set('HAVE_STRERRORDESC_NP', '1') +endif if cc.has_header_symbol('locale.h', 'LC_MESSAGES') conf.set('HAVE_LC_MESSAGES', '1') endif @@ -455,7 +616,10 @@ if cc.has_header_symbol('fcntl.h', 'F_OFD_SETLK') conf.set('HAVE_OFD', '1') endif -if cc.has_function('pwrite', args: '-D_XOPEN_SOURCE') +if cc.has_function( + 'pwrite', + args: '-D_XOPEN_SOURCE', +) conf.set('HAVE_PWRITE', '1') endif if cc.has_header_symbol('sys/mount.h', 'BLKSSZGET') @@ -463,20 +627,29 @@ endif if host_machine.system() == 'freebsd' - if cc.has_type('struct efi_esrt_entry_v1', prefix: '#include \n#include ') + if cc.has_type( + 'struct efi_esrt_entry_v1', + prefix: '#include \n#include ', + ) conf.set('HAVE_FREEBSD_ESRT', '1') endif endif -launchctl = find_program('launchctl', required: host_machine.system() == 'darwin') +launchctl = find_program( + 'launchctl', + required: host_machine.system() == 'darwin', +) # this is way less hassle than including TargetConditionals.h and looking for TARGET_OS_MAC=1 if host_machine.system() == 'darwin' conf.set('HOST_MACHINE_SYSTEM_DARWIN', '1') - summary({ - 'launchctl': launchctl, - 'launchd_agent_dir': get_option('launchd_agent_dir'), - }, section: 'Darwin options') + summary( + { + 'launchctl': launchctl, + 'launchd_agent_dir': get_option('launchd_agent_dir'), + }, + section: 'Darwin options', + ) endif # EFI @@ -486,37 +659,69 @@ endif flashrom = get_option('plugin_flashrom').disable_auto_if(host_machine.system() != 'linux') -allow_flashrom = flashrom.allowed() +libflashrom = disabler() if build_standalone - libflashrom = dependency('flashrom', - fallback: ['flashrom', 'flashrom_dep'], - required: flashrom) - if libflashrom.type_name() == 'pkgconfig' and cc.has_function('flashrom_set_progress_callback_v2', dependencies: libflashrom) - conf.set('HAVE_FLASHROM_SET_PROGRESS_CALLBACK_V2' , '1') + libflashrom = dependency( + 'flashrom', + fallback: ['flashrom', 'flashrom_dep'], + required: flashrom, + ) + if libflashrom.type_name() == 'pkgconfig' and cc.has_function( + 'flashrom_set_progress_callback_v2', + dependencies: libflashrom, + ) + conf.set('HAVE_FLASHROM_SET_PROGRESS_CALLBACK_V2' , '1') endif + allow_flashrom = flashrom.allowed() and libflashrom.found() +else + allow_flashrom = false endif if libsystemd.found() - systemd = dependency('systemd', version: '>= 249', required: get_option('systemd')) + systemd = dependency( + 'systemd', + version: '>= 249', + required: get_option('systemd'), + ) conf.set('HAVE_SYSTEMD' , '1') conf.set('HAVE_LOGIND' , '1') systemd_root_prefix = get_option('systemd_root_prefix') if systemd_root_prefix == '' - systemdunitdir = systemd.get_variable(pkgconfig: 'systemdsystemunitdir') - systemd_shutdown_dir = systemd.get_variable(pkgconfig: 'systemdshutdowndir') - systemd_modules_load_dir = systemd.get_variable(pkgconfig: 'modulesloaddir') - systemd_sysusers_dir = systemd.get_variable(pkgconfig: 'sysusersdir') + systemdunitdir = systemd.get_variable( + pkgconfig: 'systemdsystemunitdir' + ) + systemd_shutdown_dir = systemd.get_variable( + pkgconfig: 'systemdshutdowndir' + ) + systemd_modules_load_dir = systemd.get_variable( + pkgconfig: 'modulesloaddir' + ) + systemd_sysusers_dir = systemd.get_variable( + pkgconfig: 'sysusersdir' + ) else - systemdunitdir = systemd.get_variable(pkgconfig: 'systemdsystemunitdir', pkgconfig_define: ['rootprefix', systemd_root_prefix]) - systemd_shutdown_dir = systemd.get_variable(pkgconfig: 'systemdshutdowndir', pkgconfig_define: ['root_prefix', systemd_root_prefix]) - systemd_modules_load_dir = systemd.get_variable(pkgconfig: 'modulesloaddir', pkgconfig_define: ['root_prefix', systemd_root_prefix]) - systemd_sysusers_dir = systemd.get_variable(pkgconfig: 'sysusersdir', pkgconfig_define: ['root_prefix', systemd_root_prefix]) + systemdunitdir = systemd.get_variable( + pkgconfig: 'systemdsystemunitdir', + pkgconfig_define: ['rootprefix', systemd_root_prefix], + ) + systemd_shutdown_dir = systemd.get_variable( + pkgconfig: 'systemdshutdowndir', + pkgconfig_define: ['root_prefix', systemd_root_prefix], + ) + systemd_modules_load_dir = systemd.get_variable( + pkgconfig: 'modulesloaddir', + pkgconfig_define: ['root_prefix', systemd_root_prefix], + ) + systemd_sysusers_dir = systemd.get_variable( + pkgconfig: 'sysusersdir', + pkgconfig_define: ['root_prefix', systemd_root_prefix], + ) endif endif supported_build = get_option('supported_build').disable_auto_if(not tag).allowed() if supported_build - conf.set('SUPPORTED_BUILD', '1') + conf.set('SUPPORTED_BUILD', '1') endif gnome = import('gnome') @@ -532,12 +737,12 @@ conf.set_quoted('FWUPD_LOCALEDIR', localedir) if build_standalone -if host_machine.system() == 'windows' - libdir_pkg = bindir -else - libdir_pkg = join_paths(libdir, 'fwupd-@0@'.format(fwupd_version)) -endif -conf.set_quoted('FWUPD_LIBDIR_PKG', libdir_pkg) + if host_machine.system() == 'windows' + libdir_pkg = bindir + else + libdir_pkg = join_paths(libdir, 'fwupd-@0@'.format(fwupd_version)) + endif + conf.set_quoted('FWUPD_LIBDIR_PKG', libdir_pkg) endif conf.set_quoted('GETTEXT_PACKAGE', meson.project_name()) @@ -555,28 +760,52 @@ conf.set_quoted('FU_DEFAULT_P2P_POLICY', get_option('p2p_policy')) +if get_option('plugin_uefi_capsule_splash') + conf.set('FWUPD_UEFI_CAPSULE_SPLASH_ENABLED', '1') +endif + configure_file( output: 'config.h', - configuration: conf + configuration: conf, ) -libdrm_amdgpu = dependency('libdrm_amdgpu', version: '>= 2.4.113', required: get_option('libdrm')) -protobufc = dependency('libprotobuf-c', required: get_option('protobuf')) -protoc = find_program('protoc', 'protoc-c', required: get_option('protobuf')) +libdrm_amdgpu = dependency( + 'libdrm_amdgpu', + version: '>= 2.4.113', + required: get_option('libdrm'), +) +protobufc = dependency( + 'libprotobuf-c', + required: get_option('protobuf'), +) +protoc = find_program( + 'protoc', + 'protoc-c', + required: get_option('protobuf'), +) root_incdir = include_directories('.') fwupd_gir = [] -gir_dep = dependency('gobject-introspection-1.0', required: get_option('introspection')) -introspection = get_option('introspection').disable_auto_if(host_machine.system() != 'linux').disable_auto_if(not gir_dep.found()) +gir_dep = dependency( + 'gobject-introspection-1.0', + required: get_option('introspection'), +) +introspection = get_option('introspection').disable_auto_if(host_machine.system() != 'linux').disable_auto_if( + not gir_dep.found() +) -gidocgen_dep = dependency('gi-docgen', +gidocgen_dep = dependency( + 'gi-docgen', version: '>= 2021.1', native: true, fallback: ['gi-docgen', 'dummy_dep'], required: get_option('docs'), ) -gidocgen_app = find_program('gi-docgen', required: gidocgen_dep.found()) +gidocgen_app = find_program( + 'gi-docgen', + required: gidocgen_dep.found(), +) build_docs = gidocgen_dep.found() and gidocgen_app.found() and introspection.allowed() if build_docs and gidocgen_dep.version().version_compare('< 2022.2') @@ -586,7 +815,7 @@ ).stdout().strip() build_docs = get_option('docs').require( markdown_version.version_compare('>=3.2'), - error_message: 'docs=enabled requires at least markdown >= 3.2' + error_message: 'docs=enabled requires at least markdown >= 3.2', ).allowed() endif @@ -607,13 +836,34 @@ endif # take foo.rs and generate foo-struct.c and foo-struct.h files like protobuf_c -rustgen = generator(python3, - output : ['@BASENAME@-struct.c', '@BASENAME@-struct.h'], - arguments : [ +rustgen = generator( + python3, + output: ['@BASENAME@-struct.c', '@BASENAME@-struct.h'], + # fake custom target to ensure the generator reruns if these input files change + depends: custom_target( + 'rustgen-files-timestamp', + input: files( + 'libfwupdplugin/fu-rustgen-enum.c.in', + 'libfwupdplugin/fu-rustgen-enum.h.in', + 'libfwupdplugin/fu-rustgen-struct.c.in', + 'libfwupdplugin/fu-rustgen-struct.h.in', + 'libfwupdplugin/fu-rustgen.c.in', + 'libfwupdplugin/fu-rustgen.h.in', + 'libfwupdplugin/rustgen.py', + ), + output: 'rustgen-files-timestamp', + command: [python3, '-c', 'from pathlib import Path\nPath("@OUTPUT@").touch()'], + ), + arguments: [ join_paths(meson.project_source_root(), 'libfwupdplugin', 'rustgen.py'), + '--use', 'fwupd:@0@'.format(join_paths(meson.project_source_root(), 'libfwupdplugin')), '@INPUT@', '@OUTPUT0@', '@OUTPUT1@', + '--include', + 'fwupdplugin.h', + '--prefix', + 'Fu', ], ) @@ -622,19 +872,22 @@ check: false, ) -umockdev_integration_tests = get_option('umockdev_tests') \ - .disable_auto_if(not get_option('tests')) \ - .disable_auto_if(not introspection.allowed()) \ - .disable_auto_if(not run_sanitize_unsafe_tests) \ - .disable_auto_if(dbusmock.returncode() != 0) -umockdev = dependency('umockdev-1.0', required: get_option('umockdev_tests')) +umockdev_integration_tests = get_option('umockdev_tests') \ + .disable_auto_if(not get_option('tests')) \ + .disable_auto_if(not introspection.allowed()) \ + .disable_auto_if(not run_sanitize_unsafe_tests) \ + .disable_auto_if(dbusmock.returncode() != 0) +dependency( + 'umockdev-1.0', + required: get_option('umockdev_tests'), +) if dbusmock.returncode() != 0 and get_option('umockdev_tests').allowed() warning('python dbusmock not found, umockdev tests will be disabled') endif -allow_uefi_capsule = host_machine.system() in ['linux', 'freebsd'] and \ - host_machine.cpu_family() in ['x86', 'x86_64', 'aarch64', 'riscv64', 'loongarch64'] +allow_uefi = host_machine.system() in ['linux', 'freebsd'] and \ + host_machine.cpu_family() in ['x86_64', 'aarch64', 'riscv64', 'loongarch64'] subdir('generate-build') subdir('libfwupd') @@ -651,89 +904,95 @@ # common to all plugins plugin_builtins = [] - plugin_incdirs = [ - root_incdir, - fwupd_incdir, - fwupdplugin_incdir, - ] - plugin_libs = [ - fwupd, - fwupdplugin, - ] + plugin_incdirs = [root_incdir, fwupd_incdir, fwupdplugin_incdir] + plugin_libs = [fwupd, fwupdplugin] subdir('plugins') subdir('src') subdir('docs') subdir('data') # append all the quirks into one big file and gzip it - custom_target('builtin-quirk-gz', + custom_target( + 'builtin-quirk-gz', input: plugin_quirks, output: 'builtin.quirk.gz', - command: [ - generate_quirk_builtin, - '@OUTPUT@', - '@INPUT@', - ], + command: [generate_quirk_builtin, '@OUTPUT@', '@INPUT@'], install: true, + install_tag: 'runtime', install_dir: join_paths(datadir, 'fwupd', 'quirks.d'), ) endif if libsystemd.found() - summary({ - 'systemd_unit_user': get_option('systemd_unit_user'), - 'systemd unit dir': systemdunitdir, - 'systemd shutdown dir': systemd_shutdown_dir, - 'systemd modules dir': systemd_modules_load_dir, - 'systemd sysusers dir': systemd_sysusers_dir, - }, section: 'systemd options') -endif - -summary({ - 'fwupdtool': build_standalone, - 'fwupd (daemon)': build_daemon -}, section: 'build targets') - -summary({ - 'cbor': cbor, - 'dbus_socket_address': get_option('dbus_socket_address'), - 'vendor_ids_dir': vendor_ids_dir, - 'docs': build_docs, - 'gnutls': gnutls, - 'introspection': introspection.allowed(), - 'libblkid': libblkid, - 'libdrm': libdrm, - 'valgrind': valgrind, - 'polkit': polkit, - 'python3': python3, - 'supported_build': supported_build, - 'static_analysis': static_analysis, - 'tests': get_option('tests'), - 'umockdev_tests': umockdev_integration_tests.allowed(), -}, section: 'project features') + summary( + { + 'systemd_unit_user': get_option('systemd_unit_user'), + 'systemd unit dir': systemdunitdir, + 'systemd shutdown dir': systemd_shutdown_dir, + 'systemd modules dir': systemd_modules_load_dir, + 'systemd sysusers dir': systemd_sysusers_dir, + }, + section: 'systemd options', + ) +endif + +summary( + { + 'fwupdtool': build_standalone, + 'fwupd (daemon)': build_daemon, + }, + section: 'build targets', +) + +summary( + { + 'cbor': cbor, + 'dbus_socket_address': get_option('dbus_socket_address'), + 'vendor_ids_dir': vendor_ids_dir, + 'docs': build_docs, + 'gnutls': gnutls, + 'introspection': introspection.allowed(), + 'libblkid': libblkid, + 'libdrm': libdrm, + 'valgrind': valgrind, + 'polkit': polkit, + 'python3': python3, + 'supported_build': supported_build, + 'static_analysis': static_analysis, + 'tests': get_option('tests'), + 'umockdev_tests': umockdev_integration_tests.allowed(), + }, + section: 'project features', +) if build_daemon - summary({ - 'bluez': bluez.allowed(), - 'libusb': libusb, - 'hsi': hsi, - 'lvfs_metadata_format': lvfs_metadata_format, - 'libarchive': libarchive.found(), - 'passim': passim, - 'GPG support': supported_gpg, - 'PKCS7 support': supported_pkcs7, - }, section: 'daemon features') - en=[] - dis=[] - foreach plugin: plugins.keys() + summary( + { + 'bluez': bluez.allowed(), + 'libusb': libusb, + 'hsi': hsi, + 'lvfs_metadata_format': lvfs_metadata_format, + 'libarchive': libarchive.found(), + 'passim': passim, + 'GPG support': supported_gpg, + 'PKCS7 support': supported_pkcs7, + }, + section: 'daemon features', + ) + en = [] + dis = [] + foreach plugin : plugins.keys() if plugins[plugin] en += plugin else dis += plugin endif endforeach - summary({ - 'enabled': ', '.join(en), - 'disabled': ', '.join(dis), - }, section: 'plugins') + summary( + { + 'enabled': ', '.join(en), + 'disabled': ', '.join(dis), + }, + section: 'plugins', + ) endif diff -Nru fwupd-2.0.8/meson.format fwupd-2.0.20/meson.format --- fwupd-2.0.8/meson.format 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/meson.format 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,6 @@ +max_line_length = 100 +indent_by = ' ' +tab_width = 2 +kwargs_force_multiline = true +no_single_comma_function = true +indent_before_comments = ' ' diff -Nru fwupd-2.0.8/meson_options.txt fwupd-2.0.20/meson_options.txt --- fwupd-2.0.8/meson_options.txt 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/meson_options.txt 2026-02-26 11:36:18.000000000 +0000 @@ -1,195 +1,232 @@ -option('bash_completion', +option( + 'bash_completion', type: 'boolean', value: true, description: 'enable bash completion', ) -option('bluez', +option( + 'blkid', + type: 'feature', + description: 'libblkid support', +) +option( + 'bluez', type: 'feature', description: 'BlueZ support', ) -option('build', +option( + 'build', type: 'combo', - choices: [ - 'all', - 'standalone', - 'library', - ], + choices: ['all', 'standalone', 'library'], value: 'all', description: 'build type', ) -option('cbor', +option( + 'cbor', type: 'feature', description: 'CBOR support for coSWID and uSWID', ) -option('dbus_socket_address', +option( + 'dbus_socket_address', type: 'string', value: '', description: 'D-Bus socket address to use for p2p mode', ) -option('docs', +option( + 'docs', type: 'feature', description: 'Build developer documentation', ) -option('efi_binary', +option( + 'efi_binary', type: 'boolean', value: false, description: 'generate uefi binary if missing', ) -option('plugin_qc_firehose', - type: 'boolean', - value: false, - description: 'Build the beta-quality qc_firehose plugin', -) -option('efi_os_dir', +option( + 'efi_os_dir', type: 'string', description: 'the hardcoded name of OS directory in ESP, e.g. fedora', ) -option('firmware-packager', +option( + 'firmware-packager', type: 'boolean', value: true, description: 'enable firmware-packager installation', ) -option('fish_completion', +option( + 'fish_completion', type: 'boolean', value: true, description: 'enable fish completion', ) -option('gnutls', +option( + 'gnutls', type: 'feature', description: 'GnuTLS support', ) -option('hsi', +option( + 'hsi', type: 'feature', - description: ' Host Security Information', + description: 'Host Security Information', ) -option('introspection', +option( + 'introspection', type: 'feature', description: 'generate GObject Introspection data', ) -option('launchd_agent_dir', +option( + 'launchd_agent_dir', type: 'string', value: '/Library/LaunchAgents', description: 'Directory to put the launchd agent', ) -option('libarchive', +option( + 'libarchive', type: 'feature', description: 'libarchive support', ) -option('libdrm', +option( + 'libdrm', type: 'feature', description: 'libdrm support', ) -option('valgrind', - type: 'feature', - description: 'valgrind support', -) -option('blkid', +option( + 'libmnl', type: 'feature', - description: 'libblkid support', + description: 'libmnl support', ) -option('lvfs', +option( + 'lvfs', type: 'combo', - choices: [ - 'true', - 'false', - 'disabled', - ], + choices: ['true', 'false', 'disabled'], value: 'true', description: 'install LVFS remotes', ) -option('man', +option( + 'man', type: 'boolean', value: true, description: 'enable man pages', ) -option('metainfo', +option( + 'metainfo', type: 'boolean', value: true, description: 'install the project metainfo.xml information', ) -option('passim', - type: 'feature', - description: 'Passim support', -) -option('p2p_policy', +option( + 'p2p_policy', type: 'combo', - choices: [ - 'none', - 'metadata', - 'firmware', - 'metadata,firmware', - ], + choices: ['none', 'metadata', 'firmware', 'metadata,firmware'], value: 'metadata', description: 'Default P2P sharing policy', ) -option('plugin_flashrom', +option( + 'udev_hotplug', + type: 'boolean', + value: true, + description: 'Use the udev netlink hotplug event source', +) +option( + 'passim', type: 'feature', - description: 'flashrom support', + description: 'Passim support', ) -option('protobuf', +option( + 'plugin_flashrom', type: 'feature', - description: 'protobuf support', + description: 'flashrom support', ) -option('plugin_modem_manager', +option( + 'plugin_modem_manager', type: 'feature', description: 'ModemManager support', ) -option('plugin_uefi_capsule_splash', +option( + 'plugin_uefi_capsule_splash', type: 'boolean', value: true, description: 'enable UEFI capsule splash support', ) -option('polkit', +option( + 'polkit', type: 'feature', description: 'PolKit support in daemon', ) -option('python', +option( + 'protobuf', + type: 'feature', + description: 'protobuf support', +) +option( + 'python', type: 'string', description: 'the absolute path of the python3 binary', ) -option('qubes', +option( + 'qubes', type: 'boolean', value: false, description: 'build packages for Qubes OS', ) -option('static_analysis', +option( + 'readline', + type: 'feature', + description: 'readline support', +) +option( + 'static_analysis', type: 'boolean', value: false, description: 'enable GCC static analysis support', ) -option('supported_build', +option( + 'supported_build', type: 'feature', description: 'distribution package with upstream support', ) -option('systemd', +option( + 'systemd', type: 'feature', description: 'systemd support', ) -option('systemd_root_prefix', +option( + 'systemd_root_prefix', type: 'string', value: '', description: 'Directory to base systemd’s installation directories on', ) -option('systemd_unit_user', +option( + 'systemd_unit_user', type: 'string', value: 'fwupd-refresh', description: 'User account to use for fwupd-refresh.service (empty for DynamicUser)', ) -option('tests', +option( + 'tests', type: 'boolean', value: true, description: 'enable tests', ) -option('umockdev_tests', +option( + 'umockdev_tests', type: 'feature', description: 'umockdev tests', ) -option('vendor_ids_dir', +option( + 'valgrind', + type: 'feature', + description: 'valgrind support', +) +option( + 'vendor_ids_dir', type: 'string', value: '', description: 'Directory for usb.ids, pci.ids etc.', ) -option('vendor_metadata', +option( + 'vendor_metadata', type: 'boolean', value: false, description: 'install OS vendor provided metadata', diff -Nru fwupd-2.0.8/plugins/README.md fwupd-2.0.20/plugins/README.md --- fwupd-2.0.8/plugins/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -13,8 +13,9 @@ 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, proprietary UEFI executable, proprietary library, or DBus interface. +> [!IMPORTANT] +> We will not accept plugins that upgrade hardware using a proprietary Linux +> executable, proprietary UEFI executable, proprietary library, or DBus interface. ## Plugin interaction diff -Nru fwupd-2.0.8/plugins/acpi-dmar/fu-acpi-dmar-plugin.c fwupd-2.0.20/plugins/acpi-dmar/fu-acpi-dmar-plugin.c --- fwupd-2.0.8/plugins/acpi-dmar/fu-acpi-dmar-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-dmar/fu-acpi-dmar-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,7 +19,6 @@ fu_acpi_dmar_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) { g_autofree gchar *fn = NULL; - g_autofree gchar *path = NULL; g_autoptr(FuAcpiDmar) dmar = fu_acpi_dmar_new(); g_autoptr(FwupdSecurityAttr) attr = NULL; g_autoptr(GInputStream) stream = NULL; @@ -35,8 +34,7 @@ fu_security_attrs_append(attrs, attr); /* load DMAR table */ - path = fu_path_from_kind(FU_PATH_KIND_ACPI_TABLES); - fn = g_build_filename(path, "DMAR", NULL); + fn = fu_path_build(FU_PATH_KIND_ACPI_TABLES, "DMAR", NULL); stream = fu_input_stream_from_path(fn, &error_local); if (stream == NULL) { g_debug("failed to load %s: %s", fn, error_local->message); @@ -46,7 +44,7 @@ if (!fu_firmware_parse_stream(FU_FIRMWARE(dmar), stream, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error_local)) { g_warning("failed to parse %s: %s", fn, error_local->message); fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); diff -Nru fwupd-2.0.8/plugins/acpi-dmar/fu-acpi-dmar.c fwupd-2.0.20/plugins/acpi-dmar/fu-acpi-dmar.c --- fwupd-2.0.8/plugins/acpi-dmar/fu-acpi-dmar.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-dmar/fu-acpi-dmar.c 2026-02-26 11:36:18.000000000 +0000 @@ -20,7 +20,7 @@ static gboolean fu_acpi_dmar_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAcpiDmar *self = FU_ACPI_DMAR(firmware); @@ -28,7 +28,7 @@ /* FuAcpiTable->parse */ if (!FU_FIRMWARE_CLASS(fu_acpi_dmar_parent_class) - ->parse(FU_FIRMWARE(self), stream, FWUPD_INSTALL_FLAG_NONE, error)) + ->parse(FU_FIRMWARE(self), stream, flags, error)) return FALSE; /* check signature and read flags */ @@ -42,7 +42,7 @@ } if (!fu_input_stream_read_u8(stream, 0x25, &dma_flags, error)) return FALSE; - g_debug("Flags: 0x%02x", dma_flags); + g_debug("flags: 0x%02x", dma_flags); self->opt_in = (dma_flags & DMAR_DMA_CTRL_PLATFORM_OPT_IN_FLAG) > 0; return TRUE; } diff -Nru fwupd-2.0.8/plugins/acpi-dmar/fu-self-test.c fwupd-2.0.20/plugins/acpi-dmar/fu-self-test.c --- fwupd-2.0.8/plugins/acpi-dmar/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-dmar/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -28,7 +28,7 @@ ret = fu_firmware_parse_stream(FU_FIRMWARE(dmar), stream, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -55,7 +55,7 @@ ret = fu_firmware_parse_stream(FU_FIRMWARE(dmar), stream, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); diff -Nru fwupd-2.0.8/plugins/acpi-dmar/meson.build fwupd-2.0.20/plugins/acpi-dmar/meson.build --- fwupd-2.0.8/plugins/acpi-dmar/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-dmar/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if hsi and (host_cpu == 'x86' or host_cpu == 'x86_64') +hsi or subdir_done() +host_cpu in ['x86', 'x86_64'] or subdir_done() + plugins += {meson.current_source_dir().split('/')[-1]: true} cargs = ['-DG_LOG_DOMAIN="FuPluginAcpiDmar"'] @@ -31,6 +33,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', @@ -38,4 +41,3 @@ ) test('acpi-dmar-self-test', e, env: env) # added to installed-tests endif -endif diff -Nru fwupd-2.0.8/plugins/acpi-facp/fu-acpi-facp-plugin.c fwupd-2.0.20/plugins/acpi-facp/fu-acpi-facp-plugin.c --- fwupd-2.0.8/plugins/acpi-facp/fu-acpi-facp-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-facp/fu-acpi-facp-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,7 +19,6 @@ fu_acpi_facp_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) { g_autofree gchar *fn = NULL; - g_autofree gchar *path = NULL; g_autoptr(FuAcpiFacp) facp = NULL; g_autoptr(FwupdSecurityAttr) attr = NULL; g_autoptr(GBytes) blob = NULL; @@ -31,8 +30,7 @@ fu_security_attrs_append(attrs, attr); /* load FACP table */ - path = fu_path_from_kind(FU_PATH_KIND_ACPI_TABLES); - fn = g_build_filename(path, "FACP", NULL); + fn = fu_path_build(FU_PATH_KIND_ACPI_TABLES, "FACP", NULL); blob = fu_bytes_get_contents(fn, &error_local); if (blob == NULL) { g_debug("failed to load %s: %s", fn, error_local->message); diff -Nru fwupd-2.0.8/plugins/acpi-facp/fu-acpi-facp.c fwupd-2.0.20/plugins/acpi-facp/fu-acpi-facp.c --- fwupd-2.0.8/plugins/acpi-facp/fu-acpi-facp.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-facp/fu-acpi-facp.c 2026-02-26 11:36:18.000000000 +0000 @@ -32,7 +32,7 @@ /* parse table */ if (!fu_memread_uint32_safe(buf, bufsz, 0x70, &flags, G_LITTLE_ENDIAN, error)) return NULL; - g_debug("Flags: 0x%04x", flags); + g_debug("flags: 0x%04x", flags); self->get_s2i = (flags & LOW_POWER_S0_IDLE_CAPABLE) > 0; return self; } diff -Nru fwupd-2.0.8/plugins/acpi-facp/meson.build fwupd-2.0.20/plugins/acpi-facp/meson.build --- fwupd-2.0.8/plugins/acpi-facp/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-facp/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if hsi and (host_cpu == 'x86' or host_cpu == 'x86_64') +hsi or subdir_done() +host_cpu in ['x86', 'x86_64'] or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginAcpiFacp"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -31,6 +33,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', @@ -38,4 +41,3 @@ ) test('acpi-facp-self-test', e, env: env) # added to installed-tests endif -endif diff -Nru fwupd-2.0.8/plugins/acpi-ivrs/fu-acpi-ivrs-plugin.c fwupd-2.0.20/plugins/acpi-ivrs/fu-acpi-ivrs-plugin.c --- fwupd-2.0.8/plugins/acpi-ivrs/fu-acpi-ivrs-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-ivrs/fu-acpi-ivrs-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -20,7 +20,6 @@ fu_acpi_ivrs_plugin_add_security_attrs(FuPlugin *plugin, FuSecurityAttrs *attrs) { g_autofree gchar *fn = NULL; - g_autofree gchar *path = NULL; g_autoptr(FuAcpiIvrs) ivrs = fu_acpi_ivrs_new(); g_autoptr(FwupdSecurityAttr) attr = NULL; g_autoptr(GInputStream) stream = NULL; @@ -36,8 +35,7 @@ fu_security_attrs_append(attrs, attr); /* load IVRS table */ - path = fu_path_from_kind(FU_PATH_KIND_ACPI_TABLES); - fn = g_build_filename(path, "IVRS", NULL); + fn = fu_path_build(FU_PATH_KIND_ACPI_TABLES, "IVRS", NULL); stream = fu_input_stream_from_path(fn, &error_local); if (stream == NULL) { g_debug("failed to load %s: %s", fn, error_local->message); @@ -47,7 +45,7 @@ if (!fu_firmware_parse_stream(FU_FIRMWARE(ivrs), stream, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error_local)) { g_warning("failed to parse %s: %s", fn, error_local->message); fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); diff -Nru fwupd-2.0.8/plugins/acpi-ivrs/fu-acpi-ivrs.c fwupd-2.0.20/plugins/acpi-ivrs/fu-acpi-ivrs.c --- fwupd-2.0.8/plugins/acpi-ivrs/fu-acpi-ivrs.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-ivrs/fu-acpi-ivrs.c 2026-02-26 11:36:18.000000000 +0000 @@ -22,7 +22,7 @@ static gboolean fu_acpi_ivrs_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAcpiIvrs *self = FU_ACPI_IVRS(firmware); @@ -30,7 +30,7 @@ /* FuAcpiTable->parse */ if (!FU_FIRMWARE_CLASS(fu_acpi_ivrs_parent_class) - ->parse(FU_FIRMWARE(self), stream, FWUPD_INSTALL_FLAG_NONE, error)) + ->parse(FU_FIRMWARE(self), stream, flags, error)) return FALSE; /* check signature and read flags */ @@ -44,7 +44,7 @@ } if (!fu_input_stream_read_u8(stream, 0x24, &ivinfo, error)) return FALSE; - g_debug("Flags: 0x%02x", ivinfo); + g_debug("flags: 0x%02x", ivinfo); self->remap_support = ivinfo & IVRS_DMA_REMAP_SUPPORT_FLAG; return TRUE; } diff -Nru fwupd-2.0.8/plugins/acpi-ivrs/fu-self-test.c fwupd-2.0.20/plugins/acpi-ivrs/fu-self-test.c --- fwupd-2.0.8/plugins/acpi-ivrs/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-ivrs/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -31,7 +31,7 @@ ret = fu_firmware_parse_stream(FU_FIRMWARE(ivrs), stream, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -72,7 +72,7 @@ ret = fu_firmware_parse_stream(FU_FIRMWARE(ivrs), stream, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); diff -Nru fwupd-2.0.8/plugins/acpi-ivrs/meson.build fwupd-2.0.20/plugins/acpi-ivrs/meson.build --- fwupd-2.0.8/plugins/acpi-ivrs/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-ivrs/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if hsi and (host_cpu == 'x86' or host_cpu == 'x86_64') +hsi or subdir_done() +host_cpu in ['x86', 'x86_64'] or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginAcpiIvrs"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -31,6 +33,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', @@ -38,4 +41,3 @@ ) test('acpi-ivrs-self-test', e, env: env) # added to installed-tests endif -endif diff -Nru fwupd-2.0.8/plugins/acpi-phat/fu-acpi-phat-health-record.c fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat-health-record.c --- fwupd-2.0.8/plugins/acpi-phat/fu-acpi-phat-health-record.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat-health-record.c 2026-02-26 11:36:18.000000000 +0000 @@ -36,14 +36,14 @@ static gboolean fu_acpi_phat_health_record_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware); gsize streamsz = 0; guint16 rcdlen; guint32 dataoff; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructAcpiPhatHealthRecord) st = NULL; /* sanity check record length */ st = fu_struct_acpi_phat_health_record_parse_stream(stream, 0x0, error); @@ -103,7 +103,7 @@ fu_acpi_phat_health_record_write(FuFirmware *firmware, GError **error) { FuAcpiPhatHealthRecord *self = FU_ACPI_PHAT_HEALTH_RECORD(firmware); - g_autoptr(GByteArray) st = fu_struct_acpi_phat_health_record_new(); + g_autoptr(FuStructAcpiPhatHealthRecord) st = fu_struct_acpi_phat_health_record_new(); /* convert device path ahead of time */ if (self->device_path != NULL) { @@ -113,7 +113,7 @@ error); if (buf == NULL) return NULL; - g_byte_array_append(st, buf->data, buf->len); + g_byte_array_append(st->buf, buf->data, buf->len); } /* data record */ @@ -123,12 +123,12 @@ return NULL; fu_struct_acpi_phat_health_record_set_device_signature(st, &guid); } - fu_struct_acpi_phat_health_record_set_rcdlen(st, st->len); + fu_struct_acpi_phat_health_record_set_rcdlen(st, st->buf->len); fu_struct_acpi_phat_health_record_set_version(st, fu_firmware_get_version_raw(firmware)); fu_struct_acpi_phat_health_record_set_flags(st, self->am_healthy); /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static void diff -Nru fwupd-2.0.8/plugins/acpi-phat/fu-acpi-phat-plugin.c fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat-plugin.c --- fwupd-2.0.8/plugins/acpi-phat/fu-acpi-phat-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,15 +21,13 @@ static gboolean fu_acpi_phat_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) { - g_autofree gchar *path = NULL; g_autofree gchar *fn = NULL; g_autofree gchar *str = NULL; g_autoptr(FuFirmware) phat = fu_acpi_phat_new(); g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error_local = NULL; - path = fu_path_from_kind(FU_PATH_KIND_ACPI_TABLES); - fn = g_build_filename(path, "PHAT", NULL); + fn = fu_path_build(FU_PATH_KIND_ACPI_TABLES, "PHAT", NULL); blob = fu_bytes_get_contents(fn, &error_local); if (blob == NULL) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) { @@ -42,7 +40,7 @@ g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } - if (!fu_firmware_parse_bytes(phat, blob, 0x0, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + if (!fu_firmware_parse_bytes(phat, blob, 0x0, FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return FALSE; str = fu_acpi_phat_to_report_string(FU_ACPI_PHAT(phat)); fu_plugin_add_report_metadata(plugin, "PHAT", str); diff -Nru fwupd-2.0.8/plugins/acpi-phat/fu-acpi-phat-version-element.c fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat-version-element.c --- fwupd-2.0.8/plugins/acpi-phat/fu-acpi-phat-version-element.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat-version-element.c 2026-02-26 11:36:18.000000000 +0000 @@ -32,17 +32,17 @@ static gboolean fu_acpi_phat_version_element_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAcpiPhatVersionElement *self = FU_ACPI_PHAT_VERSION_ELEMENT(firmware); - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructAcpiPhatVersionElement) st = NULL; /* unpack */ st = fu_struct_acpi_phat_version_element_parse_stream(stream, 0x0, error); if (st == NULL) return FALSE; - fu_firmware_set_size(firmware, st->len); + fu_firmware_set_size(firmware, st->buf->len); self->guid = fwupd_guid_to_string(fu_struct_acpi_phat_version_element_get_component_id(st), FWUPD_GUID_FLAG_MIXED_ENDIAN); self->producer_id = fu_struct_acpi_phat_version_element_get_producer_id(st); @@ -55,7 +55,7 @@ fu_acpi_phat_version_element_write(FuFirmware *firmware, GError **error) { FuAcpiPhatVersionElement *self = FU_ACPI_PHAT_VERSION_ELEMENT(firmware); - g_autoptr(GByteArray) st = fu_struct_acpi_phat_version_element_new(); + g_autoptr(FuStructAcpiPhatVersionElement) st = fu_struct_acpi_phat_version_element_new(); /* pack */ if (self->guid != NULL) { @@ -71,7 +71,7 @@ return NULL; /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static void diff -Nru fwupd-2.0.8/plugins/acpi-phat/fu-acpi-phat-version-record.c fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat-version-record.c --- fwupd-2.0.8/plugins/acpi-phat/fu-acpi-phat-version-record.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat-version-record.c 2026-02-26 11:36:18.000000000 +0000 @@ -20,12 +20,12 @@ static gboolean fu_acpi_phat_version_record_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize offset = 0; guint32 record_count = 0; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructAcpiPhatVersionRecord) st = NULL; st = fu_struct_acpi_phat_version_record_parse_stream(stream, offset, error); if (st == NULL) @@ -35,19 +35,19 @@ g_autoptr(FuFirmware) firmware_tmp = fu_acpi_phat_version_element_new(); g_autoptr(GInputStream) stream_tmp = NULL; stream_tmp = fu_partial_input_stream_new(stream, - offset + st->len, + offset + st->buf->len, FU_STRUCT_ACPI_PHAT_VERSION_ELEMENT_SIZE, error); if (stream_tmp == NULL) return FALSE; - fu_firmware_set_offset(firmware_tmp, offset + st->len); + fu_firmware_set_offset(firmware_tmp, offset + st->buf->len); if (!fu_firmware_parse_stream(firmware_tmp, stream_tmp, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return FALSE; - if (!fu_firmware_add_image_full(firmware, firmware_tmp, error)) + if (!fu_firmware_add_image(firmware, firmware_tmp, error)) return FALSE; offset += fu_firmware_get_size(firmware_tmp); } @@ -58,7 +58,7 @@ fu_acpi_phat_version_record_write(FuFirmware *firmware, GError **error) { g_autoptr(GByteArray) buf2 = g_byte_array_new(); - g_autoptr(GByteArray) st = fu_struct_acpi_phat_version_record_new(); + g_autoptr(FuStructAcpiPhatVersionRecord) st = fu_struct_acpi_phat_version_record_new(); g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); /* write each element so we get the image size */ @@ -71,13 +71,13 @@ } /* data record */ - fu_struct_acpi_phat_version_record_set_rcdlen(st, st->len + buf2->len); + fu_struct_acpi_phat_version_record_set_rcdlen(st, st->buf->len + buf2->len); fu_struct_acpi_phat_version_record_set_version(st, fu_firmware_get_version_raw(firmware)); fu_struct_acpi_phat_version_record_set_record_count(st, images->len); /* element data */ - g_byte_array_append(st, buf2->data, buf2->len); - return g_steal_pointer(&st); + g_byte_array_append(st->buf, buf2->data, buf2->len); + return g_steal_pointer(&st->buf); } static void diff -Nru fwupd-2.0.8/plugins/acpi-phat/fu-acpi-phat.c fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat.c --- fwupd-2.0.8/plugins/acpi-phat/fu-acpi-phat.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-phat/fu-acpi-phat.c 2026-02-26 11:36:18.000000000 +0000 @@ -29,10 +29,10 @@ } static gboolean -fu_acpi_phat_record_parse(FuFirmware *firmware, +fu_acpi_phat_record_parse(FuAcpiPhat *self, GInputStream *stream, gsize *offset, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint16 record_length = 0; @@ -74,7 +74,7 @@ fu_firmware_set_version_raw(firmware_rcd, revision); if (!fu_firmware_parse_stream(firmware_rcd, partial_stream, 0x0, flags, error)) return FALSE; - if (!fu_firmware_add_image_full(firmware, firmware_rcd, error)) + if (!fu_firmware_add_image(FU_FIRMWARE(self), firmware_rcd, error)) return FALSE; } @@ -98,7 +98,7 @@ static gboolean fu_acpi_phat_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAcpiPhat *self = FU_ACPI_PHAT(firmware); @@ -142,7 +142,7 @@ } /* verify checksum */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint8 checksum = 0; g_autoptr(GInputStream) stream_tmp = fu_partial_input_stream_new(stream, 0, length, error); @@ -189,7 +189,7 @@ /* platform telemetry records */ for (gsize offset_tmp = 36; offset_tmp < length;) { - if (!fu_acpi_phat_record_parse(firmware, stream, &offset_tmp, flags, error)) + if (!fu_acpi_phat_record_parse(self, stream, &offset_tmp, flags, error)) return FALSE; } diff -Nru fwupd-2.0.8/plugins/acpi-phat/fu-self-test.c fwupd-2.0.20/plugins/acpi-phat/fu-self-test.c --- fwupd-2.0.8/plugins/acpi-phat/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-phat/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -29,7 +29,7 @@ ret = fu_firmware_parse_bytes(phat, blob, 0x0, - FWUPD_INSTALL_FLAG_FORCE | FWUPD_INSTALL_FLAG_NO_SEARCH, + FWUPD_INSTALL_FLAG_FORCE | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, &error); g_assert_no_error(error); g_assert_true(ret); diff -Nru fwupd-2.0.8/plugins/acpi-phat/meson.build fwupd-2.0.20/plugins/acpi-phat/meson.build --- fwupd-2.0.8/plugins/acpi-phat/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/acpi-phat/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginAcpiPhat"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -37,6 +38,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', @@ -44,4 +46,3 @@ ) test('acpi-phat-self-test', e, env: env) # added to installed-tests endif -endif diff -Nru fwupd-2.0.8/plugins/algoltek-usb/README.md fwupd-2.0.20/plugins/algoltek-usb/README.md --- fwupd-2.0.8/plugins/algoltek-usb/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/algoltek-usb/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -50,10 +50,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.9.11`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -Mason Lyu: @MasonLyu diff -Nru fwupd-2.0.8/plugins/algoltek-usb/fu-algoltek-usb-device.c fwupd-2.0.20/plugins/algoltek-usb/fu-algoltek-usb-device.c --- fwupd-2.0.8/plugins/algoltek-usb/fu-algoltek-usb-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/algoltek-usb/fu-algoltek-usb-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -47,57 +47,64 @@ static GByteArray * fu_algoltek_usb_device_rdr(FuAlgoltekUsbDevice *self, int address, GError **error) { - g_autoptr(GByteArray) st = fu_struct_algoltek_cmd_address_pkt_new(); + g_autoptr(FuStructAlgoltekCmdAddressPkt) st = fu_struct_algoltek_cmd_address_pkt_new(); fu_struct_algoltek_cmd_address_pkt_set_len(st, 5); fu_struct_algoltek_cmd_address_pkt_set_cmd(st, FU_ALGOLTEK_CMD_RDR); fu_struct_algoltek_cmd_address_pkt_set_address(st, address); - fu_struct_algoltek_cmd_address_pkt_set_checksum(st, ~fu_sum8(st->data, st->len) + 1); + fu_struct_algoltek_cmd_address_pkt_set_checksum(st, + ~fu_sum8(st->buf->data, st->buf->len) + 1); if (!fu_algoltek_usb_device_ctrl_transfer(self, FU_USB_DIRECTION_DEVICE_TO_HOST, FU_ALGOLTEK_CMD_RDR, address, 0xFFFF, - st, - st->len, + st->buf, + st->buf->len, error)) return NULL; /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static GByteArray * fu_algoltek_usb_device_rdv(FuAlgoltekUsbDevice *self, GError **error) { guint16 version_prefix; - g_autoptr(GByteArray) st = fu_struct_algoltek_cmd_transfer_pkt_new(); + g_autoptr(FuStructAlgoltekCmdTransferPkt) st = fu_struct_algoltek_cmd_transfer_pkt_new(); g_autoptr(GByteArray) version_data = g_byte_array_new(); fu_struct_algoltek_cmd_transfer_pkt_set_len(st, 3); fu_struct_algoltek_cmd_transfer_pkt_set_cmd(st, FU_ALGOLTEK_CMD_RDV); - fu_struct_algoltek_cmd_transfer_pkt_set_checksum(st, ~fu_sum8(st->data, st->len) + 1); + fu_struct_algoltek_cmd_transfer_pkt_set_checksum(st, + ~fu_sum8(st->buf->data, st->buf->len) + 1); if (!fu_algoltek_usb_device_ctrl_transfer(self, FU_USB_DIRECTION_DEVICE_TO_HOST, FU_ALGOLTEK_CMD_RDV, 0xFFFF, 0xFFFF, - st, - st->len, + st->buf, + st->buf->len, error)) return NULL; - if (!fu_memread_uint16_safe(st->data, st->len, 2, &version_prefix, G_BIG_ENDIAN, error)) + if (!fu_memread_uint16_safe(st->buf->data, + st->buf->len, + 2, + &version_prefix, + G_BIG_ENDIAN, + error)) return NULL; if (version_prefix == 0x4147) { guint8 underscore_count = 0; /* remove len, cmd bytes and "AG" prefixes */ - for (guint32 i = 4; i < st->len; i++) { - if (st->data[i] == '_') { + for (guint32 i = 4; i < st->buf->len; i++) { + if (st->buf->data[i] == '_') { underscore_count++; if (underscore_count == 1) continue; @@ -105,13 +112,13 @@ if (underscore_count > 2) break; if (underscore_count > 0) - fu_byte_array_append_uint8(version_data, st->data[i]); + fu_byte_array_append_uint8(version_data, st->buf->data[i]); } } else { /* remove len and cmd bytes */ - for (guint32 i = 2; i < st->len; i++) { - if (st->data[i] < 128) - fu_byte_array_append_uint8(version_data, st->data[i]); + for (guint32 i = 2; i < st->buf->len; i++) { + if (st->buf->data[i] < 128) + fu_byte_array_append_uint8(version_data, st->buf->data[i]); } } @@ -122,21 +129,22 @@ static gboolean fu_algoltek_usb_device_en(FuAlgoltekUsbDevice *self, GError **error) { - g_autoptr(GByteArray) st = fu_struct_algoltek_cmd_address_pkt_new(); + g_autoptr(FuStructAlgoltekCmdAddressPkt) st = fu_struct_algoltek_cmd_address_pkt_new(); fu_struct_algoltek_cmd_address_pkt_set_len(st, 3); fu_struct_algoltek_cmd_address_pkt_set_cmd(st, FU_ALGOLTEK_CMD_EN); - fu_struct_algoltek_cmd_address_pkt_set_checksum(st, ~fu_sum8(st->data, st->len) + 1); + fu_struct_algoltek_cmd_address_pkt_set_checksum(st, + ~fu_sum8(st->buf->data, st->buf->len) + 1); if (!fu_algoltek_usb_device_ctrl_transfer(self, FU_USB_DIRECTION_HOST_TO_DEVICE, FU_ALGOLTEK_CMD_EN, 0, 0, - st, - st->data[0], + st->buf, + st->buf->data[0], error)) { - g_prefix_error(error, "system activation failure: "); + g_prefix_error_literal(error, "system activation failure: "); return FALSE; } @@ -146,20 +154,21 @@ static gboolean fu_algoltek_usb_device_rst(FuAlgoltekUsbDevice *self, guint16 address, GError **error) { - g_autoptr(GByteArray) st = fu_struct_algoltek_cmd_address_pkt_new(); + g_autoptr(FuStructAlgoltekCmdAddressPkt) st = fu_struct_algoltek_cmd_address_pkt_new(); fu_struct_algoltek_cmd_address_pkt_set_len(st, 4); fu_struct_algoltek_cmd_address_pkt_set_cmd(st, FU_ALGOLTEK_CMD_RST); fu_struct_algoltek_cmd_address_pkt_set_address(st, address); - fu_struct_algoltek_cmd_address_pkt_set_checksum(st, ~fu_sum8(st->data, st->len) + 1); + fu_struct_algoltek_cmd_address_pkt_set_checksum(st, + ~fu_sum8(st->buf->data, st->buf->len) + 1); - if (st->data[0] > st->len) { + if (st->buf->data[0] > st->buf->len) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "rst length invalid, 0x%x > 0x%x", - st->data[0], - st->len); + st->buf->data[0], + st->buf->len); return FALSE; } if (!fu_algoltek_usb_device_ctrl_transfer(self, @@ -167,10 +176,10 @@ FU_ALGOLTEK_CMD_RST, 0, 0, - st, - st->data[0], + st->buf, + st->buf->data[0], error)) { - g_prefix_error(error, "system reboot failure: "); + g_prefix_error_literal(error, "system reboot failure: "); return FALSE; } @@ -180,21 +189,22 @@ static gboolean fu_algoltek_usb_device_wrr(FuAlgoltekUsbDevice *self, int address, int value, GError **error) { - g_autoptr(GByteArray) st = fu_struct_algoltek_cmd_address_pkt_new(); + g_autoptr(FuStructAlgoltekCmdAddressPkt) st = fu_struct_algoltek_cmd_address_pkt_new(); fu_struct_algoltek_cmd_address_pkt_set_len(st, 7); fu_struct_algoltek_cmd_address_pkt_set_cmd(st, FU_ALGOLTEK_CMD_WRR); fu_struct_algoltek_cmd_address_pkt_set_address(st, address); fu_struct_algoltek_cmd_address_pkt_set_value(st, value); - fu_struct_algoltek_cmd_address_pkt_set_checksum(st, ~fu_sum8(st->data, st->len) + 1); + fu_struct_algoltek_cmd_address_pkt_set_checksum(st, + ~fu_sum8(st->buf->data, st->buf->len) + 1); - if (st->data[0] > st->len) { + if (st->buf->data[0] > st->buf->len) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "wrr length invalid, 0x%x > 0x%x", - st->data[0], - st->len); + st->buf->data[0], + st->buf->len); return FALSE; } if (!fu_algoltek_usb_device_ctrl_transfer(self, @@ -202,10 +212,10 @@ FU_ALGOLTEK_CMD_WRR, 0, 0, - st, - st->data[0], + st->buf, + st->buf->data[0], error)) { - g_prefix_error(error, "data write failure: "); + g_prefix_error_literal(error, "data write failure: "); return FALSE; } @@ -234,7 +244,8 @@ for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { g_autoptr(FuChunk) chk = NULL; - g_autoptr(GByteArray) st = fu_struct_algoltek_cmd_transfer_pkt_new(); + g_autoptr(FuStructAlgoltekCmdTransferPkt) st = + fu_struct_algoltek_cmd_transfer_pkt_new(); chk = fu_chunk_array_index(chunks, i, error); if (chk == NULL) @@ -248,18 +259,19 @@ fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), error)) { - g_prefix_error(error, "assign isp data failure: "); + g_prefix_error_literal(error, "assign isp data failure: "); return FALSE; } - fu_struct_algoltek_cmd_transfer_pkt_set_checksum(st, - ~fu_sum8(st->data, st->len) + 1); - if (st->data[0] > st->len) { + fu_struct_algoltek_cmd_transfer_pkt_set_checksum( + st, + ~fu_sum8(st->buf->data, st->buf->len) + 1); + if (st->buf->data[0] > st->buf->len) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "isp length invalid, 0x%x > 0x%x", - st->data[0], - st->len); + st->buf->data[0], + st->buf->len); return FALSE; } if (!fu_algoltek_usb_device_ctrl_transfer(self, @@ -267,10 +279,10 @@ FU_ALGOLTEK_CMD_ISP, 0, 0, - st, - st->data[0], + st->buf, + st->buf->data[0], error)) { - g_prefix_error(error, "isp failure: "); + g_prefix_error_literal(error, "isp failure: "); return FALSE; } fu_progress_step_done(progress); @@ -282,20 +294,21 @@ static gboolean fu_algoltek_usb_device_bot(FuAlgoltekUsbDevice *self, int address, GError **error) { - g_autoptr(GByteArray) st = fu_struct_algoltek_cmd_address_pkt_new(); + g_autoptr(FuStructAlgoltekCmdAddressPkt) st = fu_struct_algoltek_cmd_address_pkt_new(); fu_struct_algoltek_cmd_address_pkt_set_len(st, 5); fu_struct_algoltek_cmd_address_pkt_set_cmd(st, FU_ALGOLTEK_CMD_BOT); fu_struct_algoltek_cmd_address_pkt_set_address(st, address); - fu_struct_algoltek_cmd_address_pkt_set_checksum(st, ~fu_sum8(st->data, st->len) + 1); + fu_struct_algoltek_cmd_address_pkt_set_checksum(st, + ~fu_sum8(st->buf->data, st->buf->len) + 1); - if (st->data[0] > st->len) { + if (st->buf->data[0] > st->buf->len) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "bot length invalid, 0x%x > 0x%x", - st->data[0], - st->len); + st->buf->data[0], + st->buf->len); return FALSE; } if (!fu_algoltek_usb_device_ctrl_transfer(self, @@ -303,10 +316,10 @@ FU_ALGOLTEK_CMD_BOT, 0, 0, - st, - st->data[0], + st->buf, + st->buf->data[0], error)) { - g_prefix_error(error, "system boot failure: "); + g_prefix_error_literal(error, "system boot failure: "); return FALSE; } @@ -320,11 +333,12 @@ GError **error) { guint16 value; - g_autoptr(GByteArray) st = fu_struct_algoltek_cmd_address_pkt_new(); + g_autoptr(FuStructAlgoltekCmdAddressPkt) st = fu_struct_algoltek_cmd_address_pkt_new(); fu_struct_algoltek_cmd_address_pkt_set_len(st, 3); fu_struct_algoltek_cmd_address_pkt_set_cmd(st, FU_ALGOLTEK_CMD_ERS); - fu_struct_algoltek_cmd_address_pkt_set_checksum(st, ~fu_sum8(st->data, st->len) + 1); + fu_struct_algoltek_cmd_address_pkt_set_checksum(st, + ~fu_sum8(st->buf->data, st->buf->len) + 1); value = (erase_type << 8) | sector; if (!fu_algoltek_usb_device_ctrl_transfer(self, @@ -332,23 +346,23 @@ FU_ALGOLTEK_CMD_ERS, value, 0, - st, - st->len, + st->buf, + st->buf->len, error)) { - g_prefix_error(error, "data clear failure: "); + g_prefix_error_literal(error, "data clear failure: "); return FALSE; } return TRUE; } static gboolean -fu_algoltek_usb_device_status_check_cb(FuDevice *self, gpointer user_data, GError **error) +fu_algoltek_usb_device_status_check_cb(FuDevice *device, gpointer user_data, GError **error) { guint8 update_status; g_autoptr(GByteArray) update_status_array = NULL; update_status_array = - fu_algoltek_usb_device_rdr(FU_ALGOLTEK_USB_DEVICE(self), AG_UPDATE_STATUS, error); + fu_algoltek_usb_device_rdr(FU_ALGOLTEK_USB_DEVICE(device), AG_UPDATE_STATUS, error); if (update_status_array == NULL) return FALSE; @@ -359,10 +373,10 @@ break; case AG_UPDATE_FAIL: default: - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "update procedure is failed."); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "update procedure is failed"); return FALSE; } return TRUE; @@ -418,7 +432,7 @@ buf, buf->len, error)) { - g_prefix_error(error, "data write failure: "); + g_prefix_error_literal(error, "data write failure: "); return FALSE; } @@ -576,7 +590,7 @@ } static void -fu_algoltek_usb_device_set_progress(FuDevice *self, FuProgress *progress) +fu_algoltek_usb_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/algoltek-usb/fu-algoltek-usb-firmware.c fwupd-2.0.20/plugins/algoltek-usb/fu-algoltek-usb-firmware.c --- fwupd-2.0.8/plugins/algoltek-usb/fu-algoltek-usb-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/algoltek-usb/fu-algoltek-usb-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -30,14 +30,14 @@ static gboolean fu_algoltek_usb_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autofree gchar *version = NULL; gsize offset = 0; g_autoptr(FuFirmware) img_isp = fu_firmware_new(); g_autoptr(FuFirmware) img_payload = fu_firmware_new(); - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructAlgoltekProductIdentity) st = NULL; g_autoptr(GInputStream) stream_isp = NULL; g_autoptr(GInputStream) stream_payload = NULL; @@ -55,7 +55,8 @@ if (!fu_firmware_parse_stream(img_isp, stream_isp, 0x0, flags, error)) return FALSE; fu_firmware_set_id(img_isp, "isp"); - fu_firmware_add_image(firmware, img_isp); + if (!fu_firmware_add_image(firmware, img_isp, error)) + return FALSE; offset += AG_ISP_SIZE; /* payload */ @@ -66,7 +67,8 @@ return FALSE; fu_firmware_set_version(img_payload, version); fu_firmware_set_id(img_payload, FU_FIRMWARE_ID_PAYLOAD); - fu_firmware_add_image(firmware, img_payload); + if (!fu_firmware_add_image(firmware, img_payload, error)) + return FALSE; /* success */ return TRUE; diff -Nru fwupd-2.0.8/plugins/algoltek-usb/fu-algoltek-usb-plugin.c fwupd-2.0.20/plugins/algoltek-usb/fu-algoltek-usb-plugin.c --- fwupd-2.0.8/plugins/algoltek-usb/fu-algoltek-usb-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/algoltek-usb/fu-algoltek-usb-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,6 +25,7 @@ fu_algoltek_usb_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_ALGOLTEK_USB_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_ALGOLTEK_USB_FIRMWARE); } diff -Nru fwupd-2.0.8/plugins/algoltek-usb/tests/algoltek-ag9421.json fwupd-2.0.20/plugins/algoltek-usb/tests/algoltek-ag9421.json --- fwupd-2.0.8/plugins/algoltek-usb/tests/algoltek-ag9421.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/algoltek-usb/tests/algoltek-ag9421.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/60518fa0f9a78a38afda311e8a08f21995dc21020f16cd3b959c159f1ee360ca-algoltek-ag9421-00.09.07_02.18.05.cab", - "emulation-url": "https://fwupd.org/downloads/6d8137ff38bed4c60f0ed31cba15fca023827b181be57cfd753a5751ce9c2d0d-algoltek-usb_00.09.07_02.18.05_gitMain.zip", + "url": "60518fa0f9a78a38afda311e8a08f21995dc21020f16cd3b959c159f1ee360ca-algoltek-ag9421-00.09.07_02.18.05.cab", + "emulation-url": "6d8137ff38bed4c60f0ed31cba15fca023827b181be57cfd753a5751ce9c2d0d-algoltek-usb_00.09.07_02.18.05_gitMain.zip", "components": [ { "version": "00.09.07_02.18.05", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/6984d9c2e03ddc9f12879565587c2f42ded40209d0434f2f269208213f052dfd-algoltek-ag9421-00.09.14_03.00.00.cab", - "emulation-url": "https://fwupd.org/downloads/da2c904be60988cc200f1ba0920295913981bb58053a33deba5a9260caf00f46-algoltek-usb_00.09.14_03.00.00_gitMain.zip", + "url": "6984d9c2e03ddc9f12879565587c2f42ded40209d0434f2f269208213f052dfd-algoltek-ag9421-00.09.14_03.00.00.cab", + "emulation-url": "da2c904be60988cc200f1ba0920295913981bb58053a33deba5a9260caf00f46-algoltek-usb_00.09.14_03.00.00_gitMain.zip", "components": [ { "version": "00.09.14_03.00.00", diff -Nru fwupd-2.0.8/plugins/algoltek-usbcr/fu-algoltek-usbcr-device.c fwupd-2.0.20/plugins/algoltek-usbcr/fu-algoltek-usbcr-device.c --- fwupd-2.0.8/plugins/algoltek-usbcr/fu-algoltek-usbcr-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/algoltek-usbcr/fu-algoltek-usbcr-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,6 +11,8 @@ #include "fu-algoltek-usbcr-firmware.h" #include "fu-algoltek-usbcr-struct.h" +#define FU_ALGOLTEK_USBCR_VENDOR_ID 0x58f + struct _FuAlgoltekUsbcrDevice { FuBlockDevice parent_instance; }; @@ -48,7 +50,7 @@ guint8 ram_dest, GError **error) { - g_autoptr(GByteArray) st = fu_struct_ag_usbcr_reg_cdb_new(); + g_autoptr(FuStructAgUsbcrRegCdb) st = fu_struct_ag_usbcr_reg_cdb_new(); fu_struct_ag_usbcr_reg_cdb_set_cmd(st, FU_AG_USBCR_SCSIOP_VENDOR_GENERIC_CMD); fu_struct_ag_usbcr_reg_cdb_set_subcmd(st, FU_AG_USBCR_RD_WR_RAM); @@ -56,7 +58,10 @@ fu_struct_ag_usbcr_reg_cdb_set_addr(st, addr); fu_struct_ag_usbcr_reg_cdb_set_val(st, value); - return fu_block_device_sg_io_cmd_none(FU_BLOCK_DEVICE(self), st->data, st->len, error); + return fu_block_device_sg_io_cmd_none(FU_BLOCK_DEVICE(self), + st->buf->data, + st->buf->len, + error); } static gboolean @@ -67,7 +72,7 @@ guint8 ram_dest, GError **error) { - g_autoptr(GByteArray) st = fu_struct_ag_usbcr_reg_cdb_new(); + g_autoptr(FuStructAgUsbcrRegCdb) st = fu_struct_ag_usbcr_reg_cdb_new(); fu_struct_ag_usbcr_reg_cdb_set_cmd(st, FU_AG_USBCR_SCSIOP_VENDOR_GENERIC_CMD); fu_struct_ag_usbcr_reg_cdb_set_subcmd(st, FU_AG_USBCR_RD_WR_RAM); @@ -75,8 +80,8 @@ fu_struct_ag_usbcr_reg_cdb_set_addr(st, addr); return fu_block_device_sg_io_cmd_read(FU_BLOCK_DEVICE(self), - st->data, - st->len, + st->buf->data, + st->buf->len, buf, bufsz, error); @@ -86,7 +91,7 @@ fu_algoltek_usbcr_device_send_spi_cmd(FuAlgoltekUsbcrDevice *self, guint8 cmd, GError **error) { guint8 buf[8] = {0}; - g_autoptr(GByteArray) st = fu_struct_ag_usbcr_spi_cdb_new(); + g_autoptr(FuStructAgUsbcrSpiCdb) st = fu_struct_ag_usbcr_spi_cdb_new(); fu_struct_ag_usbcr_spi_cdb_set_cmd(st, FU_AG_USBCR_SCSIOP_VENDOR_EEPROM_WR); fu_struct_ag_usbcr_spi_cdb_set_addr(st, 0xFFFF); @@ -98,8 +103,8 @@ fu_struct_ag_usbcr_spi_cdb_set_spicmd(st, cmd); return fu_block_device_sg_io_cmd_write(FU_BLOCK_DEVICE(self), - st->data, - st->len, + st->buf->data, + st->buf->len, buf, sizeof(buf), error); @@ -113,7 +118,7 @@ guint8 access_sz, GError **error) { - g_autoptr(GByteArray) st = fu_struct_ag_usbcr_spi_cdb_new(); + g_autoptr(FuStructAgUsbcrSpiCdb) st = fu_struct_ag_usbcr_spi_cdb_new(); if (!fu_algoltek_usbcr_device_send_spi_cmd(self, FU_AG_USBCR_WREN, error)) return FALSE; @@ -125,8 +130,8 @@ fu_struct_ag_usbcr_spi_cdb_set_valid(st, FU_AG_SPIFLASH_VALID); return fu_block_device_sg_io_cmd_write(FU_BLOCK_DEVICE(self), - st->data, - st->len, + st->buf->data, + st->buf->len, buf, bufsz, error); @@ -139,7 +144,7 @@ guint8 bufsz, GError **error) { - g_autoptr(GByteArray) st = fu_struct_ag_usbcr_spi_cdb_new(); + g_autoptr(FuStructAgUsbcrSpiCdb) st = fu_struct_ag_usbcr_spi_cdb_new(); fu_struct_ag_usbcr_spi_cdb_set_cmd(st, FU_AG_USBCR_SCSIOP_VENDOR_EEPROM_RD); fu_struct_ag_usbcr_spi_cdb_set_addr(st, addr); @@ -148,8 +153,8 @@ fu_struct_ag_usbcr_spi_cdb_set_valid(st, FU_AG_SPIFLASH_VALID); return fu_block_device_sg_io_cmd_read(FU_BLOCK_DEVICE(self), - st->data, - st->len, + st->buf->data, + st->buf->len, buf, bufsz, error); @@ -372,26 +377,32 @@ guint8 val, GError **error) { - g_autoptr(GByteArray) st = fu_struct_ag_usbcr_reset_cdb_new(); + g_autoptr(FuStructAgUsbcrResetCdb) st = fu_struct_ag_usbcr_reset_cdb_new(); fu_struct_ag_usbcr_reset_cdb_set_cmd(st, FU_AG_USBCR_SCSIOP_VENDOR_GENERIC_CMD); fu_struct_ag_usbcr_reset_cdb_set_subcmd(st, 0x96); fu_struct_ag_usbcr_reset_cdb_set_val(st, 0x78); fu_struct_ag_usbcr_reset_cdb_set_val2(st, val); - return fu_block_device_sg_io_cmd_none(FU_BLOCK_DEVICE(self), st->data, st->len, error); + return fu_block_device_sg_io_cmd_none(FU_BLOCK_DEVICE(self), + st->buf->data, + st->buf->len, + error); } static gboolean fu_algoltek_usbcr_device_reset_chip(FuAlgoltekUsbcrDevice *self, GError **error) { - g_autoptr(GByteArray) st = fu_struct_ag_usbcr_reset_cdb_new(); + g_autoptr(FuStructAgUsbcrResetCdb) st = fu_struct_ag_usbcr_reset_cdb_new(); fu_struct_ag_usbcr_reset_cdb_set_cmd(st, FU_AG_USBCR_SCSIOP_VENDOR_GENERIC_CMD); fu_struct_ag_usbcr_reset_cdb_set_subcmd(st, 0x95); fu_struct_ag_usbcr_reset_cdb_set_val(st, 0x23); - return fu_block_device_sg_io_cmd_none(FU_BLOCK_DEVICE(self), st->data, st->len, error); + return fu_block_device_sg_io_cmd_none(FU_BLOCK_DEVICE(self), + st->buf->data, + st->buf->len, + error); } static gboolean @@ -403,7 +414,7 @@ ver_array = fu_algoltek_usbcr_device_cmd_get_ver(self, error); if (ver_array == NULL) { - g_prefix_error(error, "failed to read version: "); + g_prefix_error_literal(error, "failed to read version: "); return FALSE; } if (!fu_memread_uint16_safe(ver_array->data, @@ -431,6 +442,14 @@ /* FuUdevDevice->probe */ if (!FU_DEVICE_CLASS(fu_algoltek_usbcr_device_parent_class)->probe(device, error)) return FALSE; + if (fu_device_get_vid(device) != FU_ALGOLTEK_USBCR_VENDOR_ID) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "vendor id 0x%x not supported", + fu_device_get_vid(device)); + return FALSE; + } /* set the physical ID */ return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "usb", error); @@ -453,7 +472,7 @@ fu_algoltek_usbcr_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) firmware = fu_algoltek_usbcr_firmware_new(); @@ -644,7 +663,7 @@ } static void -fu_algoltek_usbcr_device_set_progress(FuDevice *self, FuProgress *progress) +fu_algoltek_usbcr_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); diff -Nru fwupd-2.0.8/plugins/algoltek-usbcr/fu-algoltek-usbcr-device.h fwupd-2.0.20/plugins/algoltek-usbcr/fu-algoltek-usbcr-device.h --- fwupd-2.0.8/plugins/algoltek-usbcr/fu-algoltek-usbcr-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/algoltek-usbcr/fu-algoltek-usbcr-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,6 +8,8 @@ #include +#define ALGOLTEK_USBCR_VENDOR_ID 0x58f + #define FU_TYPE_ALGOLTEK_USBCR_DEVICE (fu_algoltek_usbcr_device_get_type()) G_DECLARE_FINAL_TYPE(FuAlgoltekUsbcrDevice, fu_algoltek_usbcr_device, diff -Nru fwupd-2.0.8/plugins/algoltek-usbcr/fu-algoltek-usbcr-firmware.c fwupd-2.0.20/plugins/algoltek-usbcr/fu-algoltek-usbcr-firmware.c --- fwupd-2.0.8/plugins/algoltek-usbcr/fu-algoltek-usbcr-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/algoltek-usbcr/fu-algoltek-usbcr-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -30,7 +30,7 @@ static gboolean fu_algoltek_usbcr_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAlgoltekUsbcrFirmware *self = FU_ALGOLTEK_USBCR_FIRMWARE(firmware); diff -Nru fwupd-2.0.8/plugins/amd-gpu/README.md fwupd-2.0.20/plugins/amd-gpu/README.md --- fwupd-2.0.8/plugins/amd-gpu/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -63,10 +63,3 @@ PSP ~~~ kernel kernel ~~~ fwupdengine ``` - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Mario Limonciello: @superm1 diff -Nru fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-atom-firmware.c fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-atom-firmware.c --- fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-atom-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-atom-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -67,21 +67,19 @@ gsize offset, GError **error) { - g_autoptr(GByteArray) atom = NULL; - - atom = fu_struct_atom_image_parse_stream(stream, offset, error); - if (atom == NULL) - return FALSE; - return fu_struct_atom_rom21_header_validate_stream( - stream, - offset + fu_struct_atom_image_get_rom_loc(atom), - error); + g_autoptr(FuStructAtomImage) st = NULL; + st = fu_struct_atom_image_parse_stream(stream, offset, error); + if (st == NULL) + return FALSE; + return fu_struct_atom_rom21_header_validate_stream(stream, + offset + + fu_struct_atom_image_get_rom_loc(st), + error); } const gchar * -fu_amd_gpu_atom_firmware_get_vbios_pn(FuFirmware *firmware) +fu_amd_gpu_atom_firmware_get_vbios_pn(FuAmdGpuAtomFirmware *self) { - FuAmdGpuAtomFirmware *self = FU_AMD_GPU_ATOM_FIRMWARE(firmware); return self->part_number; } @@ -101,7 +99,7 @@ sizeof(BIOS_VERSION_PREFIX) - 1, &offset, error)) { - g_prefix_error(error, "failed to find anchor: "); + g_prefix_error_literal(error, "failed to find anchor: "); return FALSE; } @@ -119,10 +117,10 @@ static gboolean fu_amd_gpu_atom_firmware_parse_vbios_date(FuAmdGpuAtomFirmware *self, - FuStructAtomImage *atom_image, + FuStructAtomImage *st_image, GError **error) { - g_autoptr(GByteArray) st = fu_struct_atom_image_get_vbios_date(atom_image); + g_autoptr(FuStructVbiosDate) st = fu_struct_atom_image_get_vbios_date(st_image); g_autofree gchar *year = NULL; g_autofree gchar *month = NULL; g_autofree gchar *day = NULL; @@ -130,10 +128,10 @@ g_autofree gchar *minutes = NULL; if (st == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "ATOMBIOS date is invalid"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "ATOMBIOS date is invalid"); return FALSE; } @@ -152,7 +150,7 @@ static gboolean fu_amd_gpu_atom_firmware_parse_vbios_pn(FuAmdGpuAtomFirmware *self, GBytes *blob, - FuStructAtomImage *atom_image, + FuStructAtomImage *st_image, GError **error) { gsize bufsz = 0; @@ -162,30 +160,30 @@ const guint8 *buf = g_bytes_get_data(blob, &bufsz); g_autofree gchar *model = NULL; - num_str = fu_struct_atom_image_get_num_strings(atom_image); + num_str = fu_struct_atom_image_get_num_strings(st_image); if (num_str == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "ATOMBIOS number of strings is 0"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "ATOMBIOS number of strings is 0"); return FALSE; } - idx = fu_struct_atom_image_get_str_loc(atom_image); + idx = fu_struct_atom_image_get_str_loc(st_image); if (idx == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "ATOMBIOS string location is invalid"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "ATOMBIOS string location is invalid"); return FALSE; } /* make sure there is enough space for all the strings */ atombios_size = fu_firmware_get_size(FU_FIRMWARE(self)); if ((gsize)(idx + (num_str * (STRLEN_NORMAL - 1))) > atombios_size) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "bufsz is too small for all strings"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "bufsz is too small for all strings"); return FALSE; } @@ -227,10 +225,10 @@ /* make sure there is enough space for name string */ if ((gsize)(idx + STRLEN_LONG - 1) > atombios_size) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "bufsz is too small for name string"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "bufsz is too small for name string"); return FALSE; } @@ -271,13 +269,13 @@ static gboolean fu_amd_gpu_atom_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAmdGpuAtomFirmware *self = FU_AMD_GPU_ATOM_FIRMWARE(firmware); guint32 loc; - g_autoptr(FuStructAtomImage) atom_image = NULL; - g_autoptr(FuStructAtomRom21Header) atom_rom = NULL; + g_autoptr(FuStructAtomImage) st_image = NULL; + g_autoptr(FuStructAtomRom21Header) st_rom = NULL; g_autoptr(GBytes) blob = NULL; if (!FU_FIRMWARE_CLASS(fu_amd_gpu_atom_firmware_parent_class) @@ -285,27 +283,27 @@ return FALSE; /* atom rom image */ - atom_image = fu_struct_atom_image_parse_stream(stream, 0x0, error); - if (atom_image == NULL) + st_image = fu_struct_atom_image_parse_stream(stream, 0x0, error); + if (st_image == NULL) return FALSE; /* unit is 512 bytes */ - fu_firmware_set_size(firmware, fu_struct_atom_image_get_size(atom_image) * 512); + fu_firmware_set_size(firmware, fu_struct_atom_image_get_size(st_image) * 512); /* atom rom header */ - loc = fu_struct_atom_image_get_rom_loc(atom_image); - atom_rom = fu_struct_atom_rom21_header_parse_stream(stream, loc, error); - if (atom_rom == NULL) + loc = fu_struct_atom_image_get_rom_loc(st_image); + st_rom = fu_struct_atom_rom21_header_parse_stream(stream, loc, error); + if (st_rom == NULL) return FALSE; blob = fu_input_stream_read_bytes(stream, 0x0, G_MAXSIZE, NULL, error); if (blob == NULL) return FALSE; - if (!fu_amd_gpu_atom_firmware_parse_config_filename(self, blob, atom_rom, error)) + if (!fu_amd_gpu_atom_firmware_parse_config_filename(self, blob, st_rom, error)) return FALSE; - if (!fu_amd_gpu_atom_firmware_parse_vbios_date(self, atom_image, error)) + if (!fu_amd_gpu_atom_firmware_parse_vbios_date(self, st_image, error)) return FALSE; - if (!fu_amd_gpu_atom_firmware_parse_vbios_pn(self, blob, atom_image, error)) + if (!fu_amd_gpu_atom_firmware_parse_vbios_pn(self, blob, st_image, error)) return FALSE; if (!fu_amd_gpu_atom_firmware_parse_vbios_version(self, blob, error)) return FALSE; diff -Nru fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-atom-firmware.h fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-atom-firmware.h --- fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-atom-firmware.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-atom-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -24,4 +24,4 @@ fu_amd_gpu_atom_firmware_new(void); const gchar * -fu_amd_gpu_atom_firmware_get_vbios_pn(FuFirmware *firmware); +fu_amd_gpu_atom_firmware_get_vbios_pn(FuAmdGpuAtomFirmware *self); diff -Nru fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-atom.rs fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-atom.rs --- fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-atom.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-atom.rs 2026-02-26 11:36:18.000000000 +0000 @@ -75,7 +75,7 @@ #[repr(u8)] enum FuAtomStringIndex { PartNumber = 0x00, - ASIC = 0x01, + Asic = 0x01, PciType = 0x02, MemoryType = 0x03, } diff -Nru fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-device.c fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-device.c --- fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,6 +19,7 @@ #include "fu-amd-gpu-atom-firmware.h" #include "fu-amd-gpu-device.h" #include "fu-amd-gpu-psp-firmware.h" +#include "fu-amd-gpu-uma.h" struct _FuAmdGpuDevice { FuOpromDevice parent_instance; @@ -87,18 +88,17 @@ return FALSE; while ((f = g_dir_read_name(dir))) { if (g_str_has_prefix(f, "card")) { - g_autofree gchar *devbase = fu_path_from_kind(FU_PATH_KIND_DEVFS); - device_file = g_build_filename(devbase, "dri", f, NULL); + device_file = fu_path_build(FU_PATH_KIND_DEVFS, "dri", f, NULL); break; } } /* nothing found */ if (device_file == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no DRM device file found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no DRM device file found"); return FALSE; } @@ -182,7 +182,7 @@ fu_device_set_name(FU_DEVICE(self), marketing_name); amdgpu_device_deinitialize(device_handle); } else - g_warning("unable to set marketing name: %s", g_strerror(r)); + g_warning("unable to set marketing name: %s", fwupd_strerror(r)); } static gboolean @@ -219,7 +219,7 @@ 1000, /* ms */ FU_IOCTL_FLAG_NONE, error)) { - g_prefix_error(error, "failed to DRM_IOCTL_AMDGPU_INFO: "); + g_prefix_error_literal(error, "failed to DRM_IOCTL_AMDGPU_INFO: "); return FALSE; } @@ -228,6 +228,56 @@ } static gboolean +fu_amd_gpu_device_parse_version_string(FuAmdGpuDevice *self, const gchar *str, GError **error) +{ + guint64 ver; + g_autoptr(GError) error_parse = NULL; + + if (!fu_strtoull(str, &ver, 0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, &error_parse)) { + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE)) { + g_propagate_error(error, g_steal_pointer(&error_parse)); + return FALSE; + } + g_info("unable to parse version from '%s': %s", str, error_parse->message); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_version(FU_DEVICE(self), str); /* nocheck:set-version */ + } else { + fu_device_set_version_raw(FU_DEVICE(self), ver); + } + + return TRUE; +} + +static void +fu_amd_gpu_device_setup_bios_settings(FuAmdGpuDevice *self) +{ + FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); + const gchar *base = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(FuBiosSettings) bios_settings = NULL; + g_autoptr(FwupdBiosSetting) attr_setting = NULL; + + base = fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(self)); + if (!fu_amd_gpu_uma_check_support(base, &error_local)) { + g_debug("UMA carveout support not detected on device %s: %s", + fu_device_get_name(FU_DEVICE(self)), + error_local->message); + return; + } + bios_settings = fu_context_get_bios_settings(ctx); + if (bios_settings == NULL) + return; + + attr_setting = fu_amd_gpu_uma_get_setting(base, &error_local); + if (attr_setting == NULL) { + g_debug("failed to get UMA carveout setting: %s", error_local->message); + return; + } + if (!fu_bios_settings_register_attr(bios_settings, attr_setting, &error_local)) + g_debug("failed to register UMA carveout setting: %s", error_local->message); +} + +static gboolean fu_amd_gpu_device_setup(FuDevice *device, GError **error) { FuAmdGpuDevice *self = FU_AMDGPU_DEVICE(device); @@ -250,16 +300,15 @@ tokens = fu_strsplit((const gchar *)vbios_info.vbios_pn, sizeof(vbios_info.vbios_pn), "-", -1); if (g_strv_length(tokens) >= 3) { - guint64 ver; - - if (!fu_strtoull(tokens[2], &ver, 0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, error)) + if (!fu_amd_gpu_device_parse_version_string(self, tokens[2], error)) return FALSE; - fu_device_set_version_raw(device, ver); } model = fu_strsafe((const gchar *)vbios_info.name, sizeof(vbios_info.name)); fu_device_set_summary(device, model); + fu_amd_gpu_device_setup_bios_settings(self); + return TRUE; } @@ -273,7 +322,7 @@ fu_amd_gpu_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAmdGpuDevice *self = FU_AMDGPU_DEVICE(device); @@ -297,9 +346,10 @@ if (csm == NULL) return NULL; - fw_pn = fu_strsafe(fu_amd_gpu_atom_firmware_get_vbios_pn(csm), PART_NUM_STR_SIZE); + fw_pn = fu_strsafe(fu_amd_gpu_atom_firmware_get_vbios_pn(FU_AMD_GPU_ATOM_FIRMWARE(csm)), + PART_NUM_STR_SIZE); if (g_strcmp0(fw_pn, self->vbios_pn) != 0) { - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) == 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -399,7 +449,7 @@ } static void -fu_amd_gpu_device_set_progress(FuDevice *self, FuProgress *progress) +fu_amd_gpu_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-plugin.c fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-plugin.c --- fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -33,11 +33,10 @@ FuPlugin *plugin = FU_PLUGIN(obj); fu_plugin_add_udev_subsystem(plugin, "pci"); fu_plugin_add_device_gtype(plugin, FU_TYPE_AMDGPU_DEVICE); - /* Navi3x and later use PSP firmware container */ + /* navi3x and later use PSP firmware container */ fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_AMD_GPU_PSP_FIRMWARE); - /* Navi 2x and older have the ATOM firmware at start of image */ + /* navi 2x and older have the ATOM firmware at start of image */ fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_AMD_GPU_ATOM_FIRMWARE); - fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_BETTER_THAN, "optionrom"); } static void diff -Nru fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-psp-firmware.c fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-psp-firmware.c --- fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-psp-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-psp-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -52,33 +52,32 @@ gsize offset, GError **error) { - g_autoptr(GByteArray) efs = NULL; - - efs = fu_struct_efs_parse_stream(stream, 0, error); - if (efs == NULL) + g_autoptr(FuStructEfs) st = NULL; + st = fu_struct_efs_parse_stream(stream, 0, error); + if (st == NULL) return FALSE; - return fu_struct_psp_dir_validate_stream(stream, fu_struct_efs_get_psp_dir_loc(efs), error); + return fu_struct_psp_dir_validate_stream(stream, fu_struct_efs_get_psp_dir_loc(st), error); } static gboolean -fu_amd_gpu_psp_firmware_parse_l2(FuFirmware *firmware, +fu_amd_gpu_psp_firmware_parse_l2(FuAmdGpuPspFirmware *self, GInputStream *stream, gsize offset, GError **error) { - g_autoptr(FuStructPspDirTable) l2 = NULL; + g_autoptr(FuStructPspDir) st_dir = NULL; /* parse the L2 entries */ - l2 = fu_struct_psp_dir_table_parse_stream(stream, offset, error); - if (l2 == NULL) + st_dir = fu_struct_psp_dir_parse_stream(stream, offset, error); + if (st_dir == NULL) return FALSE; - offset += l2->len; - for (guint i = 0; i < fu_struct_psp_dir_get_total_entries(l2); i++) { - g_autoptr(FuStructPspDirTable) l2_entry = NULL; - l2_entry = fu_struct_psp_dir_table_parse_stream(stream, offset, error); - if (l2_entry == NULL) + offset += st_dir->buf->len; + for (guint i = 0; i < fu_struct_psp_dir_get_total_entries(st_dir); i++) { + g_autoptr(FuStructPspDirTable) st_entry = NULL; + st_entry = fu_struct_psp_dir_table_parse_stream(stream, offset, error); + if (st_entry == NULL) return FALSE; - offset += l2_entry->len; + offset += st_entry->buf->len; } /* success */ @@ -86,33 +85,34 @@ } static gboolean -fu_amd_gpu_psp_firmware_parse_l1(FuFirmware *firmware, +fu_amd_gpu_psp_firmware_parse_l1(FuAmdGpuPspFirmware *self, GInputStream *stream, gsize offset, + FuFirmwareParseFlags flags, GError **error) { - g_autoptr(FuStructPspDir) l1 = NULL; + g_autoptr(FuStructPspDir) st_dir = NULL; /* parse the L1 entries */ - l1 = fu_struct_psp_dir_parse_stream(stream, offset, error); - if (l1 == NULL) + st_dir = fu_struct_psp_dir_parse_stream(stream, offset, error); + if (st_dir == NULL) return FALSE; - offset += l1->len; - for (guint i = 0; i < fu_struct_psp_dir_get_total_entries(l1); i++) { + offset += st_dir->buf->len; + for (guint i = 0; i < fu_struct_psp_dir_get_total_entries(st_dir); i++) { guint loc; guint sz; - g_autoptr(FuStructPspDirTable) l1_entry = NULL; - g_autoptr(FuStructImageSlotHeader) ish = NULL; + g_autoptr(FuStructPspDirTable) st_entry = NULL; + g_autoptr(FuStructImageSlotHeader) st_hdr = NULL; g_autoptr(FuFirmware) ish_img = fu_firmware_new(); g_autoptr(FuFirmware) csm_img = fu_amd_gpu_atom_firmware_new(); g_autoptr(FuFirmware) l2_img = fu_firmware_new(); g_autoptr(GInputStream) l2_stream = NULL; - l1_entry = fu_struct_psp_dir_table_parse_stream(stream, offset, error); - if (l1_entry == NULL) + st_entry = fu_struct_psp_dir_table_parse_stream(stream, offset, error); + if (st_entry == NULL) return FALSE; - switch (fu_struct_psp_dir_table_get_fw_id(l1_entry)) { + switch (fu_struct_psp_dir_table_get_fw_id(st_entry)) { case FU_FWID_ISH_A: fu_firmware_set_id(ish_img, "ISH_A"); break; @@ -124,29 +124,30 @@ FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "Unknown ISH FWID: %x", - fu_struct_psp_dir_table_get_fw_id(l1_entry)); + fu_struct_psp_dir_table_get_fw_id(st_entry)); return FALSE; } /* parse the image slot header */ - loc = fu_struct_psp_dir_table_get_loc(l1_entry); - ish = fu_struct_image_slot_header_parse_stream(stream, loc, error); - if (ish == NULL) + loc = fu_struct_psp_dir_table_get_loc(st_entry); + st_hdr = fu_struct_image_slot_header_parse_stream(stream, loc, error); + if (st_hdr == NULL) return FALSE; - if (!fu_firmware_parse_stream(ish_img, stream, loc, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_stream(ish_img, stream, loc, flags, error)) return FALSE; fu_firmware_set_addr(ish_img, loc); - fu_firmware_add_image(firmware, ish_img); + if (!fu_firmware_add_image(FU_FIRMWARE(self), ish_img, error)) + return FALSE; /* parse the csm image */ - loc = fu_struct_image_slot_header_get_loc_csm(ish); + loc = fu_struct_image_slot_header_get_loc_csm(st_hdr); fu_firmware_set_addr(csm_img, loc); - if (!fu_firmware_parse_stream(csm_img, stream, loc, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_stream(csm_img, stream, loc, flags, error)) return FALSE; - loc = fu_struct_image_slot_header_get_loc(ish); - sz = fu_struct_image_slot_header_get_slot_max_size(ish); - switch (fu_struct_image_slot_header_get_fw_id(ish)) { + loc = fu_struct_image_slot_header_get_loc(st_hdr); + sz = fu_struct_image_slot_header_get_slot_max_size(st_hdr); + switch (fu_struct_image_slot_header_get_fw_id(st_hdr)) { case FU_FWID_PARTITION_A_L2: fu_firmware_set_id(l2_img, "PARTITION_A"); fu_firmware_set_id(csm_img, "ATOM_CSM_A"); @@ -160,29 +161,27 @@ FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "unknown Partition FWID: %x", - fu_struct_image_slot_header_get_fw_id(ish)); + fu_struct_image_slot_header_get_fw_id(st_hdr)); return FALSE; } - fu_firmware_add_image(l2_img, csm_img); + if (!fu_firmware_add_image(l2_img, csm_img, error)) + return FALSE; l2_stream = fu_partial_input_stream_new(stream, loc, sz, error); if (l2_stream == NULL) return FALSE; fu_firmware_set_addr(l2_img, loc); - if (!fu_firmware_parse_stream(l2_img, - l2_stream, - 0x0, - FWUPD_INSTALL_FLAG_NONE, - error)) + if (!fu_firmware_parse_stream(l2_img, l2_stream, 0x0, flags, error)) + return FALSE; + if (!fu_firmware_add_image(ish_img, l2_img, error)) return FALSE; - fu_firmware_add_image(ish_img, l2_img); /* parse the partition */ - if (!fu_amd_gpu_psp_firmware_parse_l2(l2_img, stream, loc, error)) + if (!fu_amd_gpu_psp_firmware_parse_l2(self, stream, loc, error)) return FALSE; /* next entry */ - offset += l1_entry->len; + offset += st_entry->buf->len; } return TRUE; @@ -191,18 +190,18 @@ static gboolean fu_amd_gpu_psp_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAmdGpuPspFirmware *self = FU_AMD_GPU_PSP_FIRMWARE(firmware); - g_autoptr(GByteArray) efs = NULL; + g_autoptr(FuStructEfs) st = NULL; - efs = fu_struct_efs_parse_stream(stream, 0, error); - if (efs == NULL) + st = fu_struct_efs_parse_stream(stream, 0, error); + if (st == NULL) return FALSE; - self->dir_location = fu_struct_efs_get_psp_dir_loc(efs); + self->dir_location = fu_struct_efs_get_psp_dir_loc(st); - return fu_amd_gpu_psp_firmware_parse_l1(firmware, stream, self->dir_location, error); + return fu_amd_gpu_psp_firmware_parse_l1(self, stream, self->dir_location, flags, error); } static void diff -Nru fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-uma.c fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-uma.c --- fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-uma.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-uma.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,250 @@ +/* + * Copyright 2026 Advanced Micro Devices Inc. + * All rights reserved. + * + * This file is provided under a dual MIT/LGPLv2 license. When using or + * redistributing this file, you may do so under either license. + * AMD Chooses the MIT license part of Dual MIT/LGPLv2 license agreement. + * + * SPDX-License-Identifier: LGPL-2.1-or-later OR MIT + */ + +#include "config.h" + +#include + +#include +#include + +#include "fu-amd-gpu-uma.h" + +typedef struct _FuAmdGpuUmaSetting { + FwupdBiosSetting parent_instance; + GHashTable *value_map; /* maps display value to index for writing */ + GHashTable *reverse_value_map; /* maps index to display value for reading */ + gchar *uma_path; +} FuAmdGpuUmaSetting; + +#define UMA_CARVEOUT_OPTIONS_FILE "carveout_options" +#define UMA_CARVEOUT_FILE "carveout" +#define UMA_DIR "uma" + +G_DEFINE_TYPE(FuAmdGpuUmaSetting, fu_amd_gpu_uma_setting, FWUPD_TYPE_BIOS_SETTING) + +static gboolean +fu_amd_gpu_uma_setting_write_value(FwupdBiosSetting *self, const gchar *value, GError **error) +{ + FuAmdGpuUmaSetting *setting = FU_AMD_GPU_UMA_SETTING(self); + g_autofree gchar *carveout_file = NULL; + g_autoptr(FuIOChannel) io = NULL; + const gchar *index_to_write = NULL; + + if (setting->uma_path == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "UMA path not set"); + return FALSE; + } + + index_to_write = g_hash_table_lookup(setting->value_map, value); + if (index_to_write == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Invalid value '%s'", + value); + return FALSE; + } + + carveout_file = g_build_filename(setting->uma_path, UMA_CARVEOUT_FILE, NULL); + io = fu_io_channel_new_file(carveout_file, FU_IO_CHANNEL_OPEN_FLAG_WRITE, error); + if (io == NULL) + return FALSE; + + if (!fu_io_channel_write_raw(io, + (const guint8 *)index_to_write, + strlen(index_to_write), + 1000, + FU_IO_CHANNEL_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write to %s: ", carveout_file); + return FALSE; + } + + fwupd_bios_setting_set_current_value(self, value); + g_debug("set %s to %s (index: %s)", fwupd_bios_setting_get_id(self), value, index_to_write); + return TRUE; +} + +static void +fu_amd_gpu_uma_setting_finalize(GObject *obj) +{ + FuAmdGpuUmaSetting *setting = FU_AMD_GPU_UMA_SETTING(obj); + g_hash_table_unref(setting->value_map); + g_hash_table_unref(setting->reverse_value_map); + g_free(setting->uma_path); + G_OBJECT_CLASS(fu_amd_gpu_uma_setting_parent_class)->finalize(obj); +} + +static void +fu_amd_gpu_uma_setting_init(FuAmdGpuUmaSetting *self) +{ + FuAmdGpuUmaSetting *setting = FU_AMD_GPU_UMA_SETTING(self); + setting->value_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); + setting->reverse_value_map = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, g_free); +} + +static void +fu_amd_gpu_uma_setting_class_init(FuAmdGpuUmaSettingClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FwupdBiosSettingClass *bios_setting_class = FWUPD_BIOS_SETTING_CLASS(klass); + object_class->finalize = fu_amd_gpu_uma_setting_finalize; + bios_setting_class->write_value = fu_amd_gpu_uma_setting_write_value; +} + +/** + * fu_amd_gpu_uma_read_file: + * @path: full path to file to read + * @error: a #GError or NULL + * + * Reads a file and returns its contents as a string (stripped of whitespace). + * + * Returns: (transfer full): file contents or NULL on error + **/ +static gchar * +fu_amd_gpu_uma_read_file(const gchar *path, GError **error) +{ + gchar *content = NULL; + + if (!g_file_get_contents(path, &content, NULL, error)) { + g_prefix_error(error, "failed to read %s: ", path); + return NULL; + } + + g_strchomp(content); + return content; +} + +/** + * fu_amd_gpu_uma_check_support: + * @device_sysfs_path: the device sysfs path + * @error: a #GError or NULL + * + * Checks if UMA carveout support is available on this device. + * + * Returns: TRUE if UMA carveout is supported, FALSE otherwise + **/ +gboolean +fu_amd_gpu_uma_check_support(const gchar *device_sysfs_path, GError **error) +{ + g_autofree gchar *uma_dir = NULL; + g_autofree gchar *carveout_file = NULL; + g_autofree gchar *options_file = NULL; + + g_return_val_if_fail(device_sysfs_path != NULL, FALSE); + + uma_dir = g_build_filename(device_sysfs_path, UMA_DIR, NULL); + + carveout_file = g_build_filename(uma_dir, UMA_CARVEOUT_FILE, NULL); + options_file = g_build_filename(uma_dir, UMA_CARVEOUT_OPTIONS_FILE, NULL); + + if (!g_file_test(carveout_file, G_FILE_TEST_EXISTS) || + !g_file_test(options_file, G_FILE_TEST_EXISTS)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "UMA carveout not supported on this device"); + return FALSE; + } + + return TRUE; +} + +/** + * fu_amd_gpu_uma_get_setting: + * @device_sysfs_path: the device sysfs path + * @error: a #GError or NULL + * + * Creates a FwupdBiosSetting object for the UMA carveout configuration. + * Reads the available options from carveout_options and current value from carveout. + * + * Returns: (transfer full): FwupdBiosSetting object or NULL on error + **/ +FwupdBiosSetting * +fu_amd_gpu_uma_get_setting(const gchar *device_sysfs_path, GError **error) +{ + FuAmdGpuUmaSetting *setting = NULL; + const gchar *display_current = NULL; + g_autofree gchar *uma_dir = NULL; + g_autofree gchar *options_file = NULL; + g_autofree gchar *carveout_file = NULL; + g_autofree gchar *options_content = NULL; + g_autofree gchar *current_value = NULL; + g_autoptr(FuAmdGpuUmaSetting) attr = NULL; + g_auto(GStrv) lines = NULL; + + g_return_val_if_fail(device_sysfs_path != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + if (!fu_amd_gpu_uma_check_support(device_sysfs_path, error)) + return NULL; + + uma_dir = g_build_filename(device_sysfs_path, UMA_DIR, NULL); + + attr = g_object_new(FU_TYPE_AMD_GPU_UMA_SETTING, NULL); + setting = FU_AMD_GPU_UMA_SETTING(attr); + setting->uma_path = g_strdup(uma_dir); + + fwupd_bios_setting_set_name(FWUPD_BIOS_SETTING(attr), "Dedicated Video Memory"); + fwupd_bios_setting_set_id(FWUPD_BIOS_SETTING(attr), "com.amd-gpu.uma_carveout"); + fwupd_bios_setting_set_description( + FWUPD_BIOS_SETTING(attr), + "GPU unified memory architecture carveout size for system memory"); + fwupd_bios_setting_set_kind(FWUPD_BIOS_SETTING(attr), FWUPD_BIOS_SETTING_KIND_ENUMERATION); + fwupd_bios_setting_set_path(FWUPD_BIOS_SETTING(attr), uma_dir); + + options_file = g_build_filename(uma_dir, UMA_CARVEOUT_OPTIONS_FILE, NULL); + options_content = fu_amd_gpu_uma_read_file(options_file, error); + if (options_content == NULL) + return NULL; + + lines = g_strsplit(options_content, "\n", -1); + for (guint i = 0; lines[i] != NULL; i++) { + gchar *line = lines[i]; + gchar *description = NULL; + g_autofree gchar *index_str = NULL; + g_autofree gchar *display_value = NULL; + g_auto(GStrv) parts = g_strsplit(line, ":", 2); + + if (parts[0] == NULL || parts[1] == NULL) + continue; + + index_str = g_strdup(parts[0]); + description = g_strstrip(parts[1]); + + display_value = g_strdup(description); + + fwupd_bios_setting_add_possible_value(FWUPD_BIOS_SETTING(attr), display_value); + + g_hash_table_insert(setting->value_map, + g_strdup(display_value), + g_strdup(index_str)); + g_hash_table_insert(setting->reverse_value_map, + g_strdup(index_str), + g_strdup(display_value)); + } + + carveout_file = g_build_filename(uma_dir, UMA_CARVEOUT_FILE, NULL); + current_value = fu_amd_gpu_uma_read_file(carveout_file, error); + if (current_value == NULL) + return NULL; + + display_current = g_hash_table_lookup(setting->reverse_value_map, current_value); + if (display_current != NULL) + fwupd_bios_setting_set_current_value(FWUPD_BIOS_SETTING(attr), display_current); + + fwupd_bios_setting_set_filename(FWUPD_BIOS_SETTING(attr), UMA_CARVEOUT_FILE); + return FWUPD_BIOS_SETTING(g_steal_pointer(&attr)); +} diff -Nru fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-uma.h fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-uma.h --- fwupd-2.0.8/plugins/amd-gpu/fu-amd-gpu-uma.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/fu-amd-gpu-uma.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright 2026 Advanced Micro Devices Inc. + * All rights reserved. + * + * This file is provided under a dual MIT/LGPLv2 license. When using or + * redistributing this file, you may do so under either license. + * AMD Chooses the MIT license part of Dual MIT/LGPLv2 license agreement. + * + * SPDX-License-Identifier: LGPL-2.1-or-later OR MIT + */ + +#pragma once + +#include + +#define FU_TYPE_AMD_GPU_UMA_SETTING (fu_amd_gpu_uma_setting_get_type()) +G_DECLARE_FINAL_TYPE(FuAmdGpuUmaSetting, + fu_amd_gpu_uma_setting, + FU, + AMD_GPU_UMA_SETTING, + FwupdBiosSetting) + +gboolean +fu_amd_gpu_uma_check_support(const gchar *device_sysfs_path, GError **error); + +FwupdBiosSetting * +fu_amd_gpu_uma_get_setting(const gchar *device_sysfs_path, GError **error); diff -Nru fwupd-2.0.8/plugins/amd-gpu/meson.build fwupd-2.0.20/plugins/amd-gpu/meson.build --- fwupd-2.0.8/plugins/amd-gpu/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if host_machine.system() == 'linux' and libdrm_amdgpu.found() +host_machine.system() == 'linux' or subdir_done() +libdrm_amdgpu.found() or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginAmdGpu"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -13,15 +15,19 @@ 'fu-amd-gpu-device.c', 'fu-amd-gpu-psp-firmware.c', 'fu-amd-gpu-atom-firmware.c', + 'fu-amd-gpu-uma.c', ], include_directories: plugin_incdirs, link_with: plugin_libs, c_args: cargs, dependencies: plugin_deps, ) -enumeration_data += files('tests/amd-apu-setup.json', 'tests/amd-dgpu-setup.json') -device_tests += files('tests/amd-apu.json', +enumeration_data += files('tests/amd-apu-rembrandt-setup.json', + 'tests/amd-apu-strix-setup.json', + 'tests/amd-dgpu-setup.json') +device_tests += files('tests/amd-apu-rembrandt.json', + 'tests/amd-apu-strix.json', 'tests/amd-dgpu.json', 'tests/amd-dgpu-navi3x.json', ) -endif + diff -Nru fwupd-2.0.8/plugins/amd-gpu/tests/amd-apu-rembrandt-setup.json fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-rembrandt-setup.json --- fwupd-2.0.8/plugins/amd-gpu/tests/amd-apu-rembrandt-setup.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-rembrandt-setup.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,125 @@ +{ + "FwupdVersion": "2.0.12", + "UsbDevices": [ + { + "Created": "2025-06-07T03:59:14.963714Z", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:e2:00.0", + "DeviceFile": "/dev/dri/card0", + "Subsystem": "pci", + "Driver": "amdgpu", + "BindId": "0000:e2:00.0", + "Vendor": 4098, + "Model": 5761, + "Events": [ + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "pci" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "amdgpu" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=amdgpu\nPCI_CLASS=30000\nPCI_ID=1002:1681\nPCI_SUBSYS_ID=17AA:3827\nPCI_SLOT_NAME=0000:e2:00.0\nMODALIAS=pci:v00001002d00001681sv000017AAsd00003827bc03sc00i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x1002" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0x1681" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x030000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=amdgpu\nPCI_CLASS=30000\nPCI_ID=1002:1681\nPCI_SUBSYS_ID=17AA:3827\nPCI_SLOT_NAME=0000:e2:00.0\nMODALIAS=pci:v00001002d00001681sv000017AAsd00003827bc03sc00i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x1002" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0x1681" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x030000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=i2c", + "Error": 8, + "ErrorMsg": "no parent with subsystem i2c" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x030000" + }, + { + "Id": "ReadAttr:Attr=vbios_version", + "Data": "113-REMBRANDT-X37" + }, + { + "Id": "ReadAttr:Attr=revision", + "Data": "0xc3" + }, + { + "Id": "ReadAttr:Attr=subsystem_vendor", + "Data": "0x17aa" + }, + { + "Id": "ReadAttr:Attr=subsystem_device", + "Data": "0x3827" + }, + { + "Id": "ReadProp:Key=PCI_SLOT_NAME", + "Data": "0000:e2:00.0" + }, + { + "Id": "DrmAmdgpuSetDeviceFile:Base=/sys/devices/pci0000:00/0000:00:08.1/0000:e2:00.0", + "Filename": "/dev/dri/card0" + }, + { + "Id": "FileExists:Filename=/sys/devices/pci0000:00/0000:00:08.1/0000:e2:00.0/rom", + "Exists": 0 + }, + { + "Id": "FileExists:Filename=/sys/devices/pci0000:00/0000:00:08.1/0000:e2:00.0/psp_vbflash", + "Exists": 0 + }, + { + "Id": "FileExists:Filename=/sys/devices/pci0000:00/0000:00:08.1/0000:e2:00.0/psp_vbflash_status", + "Exists": 0 + }, + { + "Id": "Ioctl:Request=0x40206445,Query=0x1b,Data=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0xc8", + "DataOut": "UmVtYnJhbmR0IEdlbmVyaWMgVkJJT1MAICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgADExMy1SRU1CUkFORFQtWDM3AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA3ABEgAAAAADAzMi4wMTcuMDAwLjA1NS4wMDAwMDAAUm1iR2VuZXJpMjAyMi8wNC8xMiAwNjoyMQAAAAAAAAAAAAAAAAAAAAA=" + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/amd-gpu/tests/amd-apu-rembrandt.json fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-rembrandt.json --- fwupd-2.0.8/plugins/amd-gpu/tests/amd-apu-rembrandt.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-rembrandt.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +{ + "name": "AMD Rembrandt", + "interactive": false, + "steps": [ + { + "emulation-file": "@enumeration_datadir@/amd-apu-rembrandt-setup.json", + "components": [ + { + "version": "X37", + "guids": [ + "ef25c32d-937d-5dc5-be95-71a0a7cc5722" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/amd-gpu/tests/amd-apu-setup.json fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-setup.json --- fwupd-2.0.8/plugins/amd-gpu/tests/amd-apu-setup.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-setup.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,119 +0,0 @@ -{ - "UsbDevices": [ - { - "GType": "FuUdevDevice", - "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c4:00.0", - "DeviceFile": "/dev/dri/card0", - "Subsystem": "pci", - "Driver": "amdgpu", - "BindId": "0000:c4:00.0", - "Vendor": 4098, - "Model": 5390, - "Created": "2024-11-01T17:27:45.279095Z", - "Events": [ - { - "Id": "#d5a801ad", - "Data": "pci" - }, - { - "Id": "#ad8c58d3", - "Data": "amdgpu" - }, - { - "Id": "#1075ed5c" - }, - { - "Id": "#bddbca22", - "Data": "DRIVER=amdgpu\nPCI_CLASS=38000\nPCI_ID=1002:150E\nPCI_SUBSYS_ID=1043:1DF3\nPCI_SLOT_NAME=0000:c4:00.0\nMODALIAS=pci:v00001002d0000150Esv00001043sd00001DF3bc03sc80i00" - }, - { - "Id": "#d432c663" - }, - { - "Id": "#9b895db2", - "Data": "0x1002" - }, - { - "Id": "#66f3e150", - "Data": "0x150e" - }, - { - "Id": "#d410b6c7", - "Data": "0x038000" - }, - { - "Id": "#1075ed5c" - }, - { - "Id": "#1075ed5c" - }, - { - "Id": "#bddbca22", - "Data": "DRIVER=amdgpu\nPCI_CLASS=38000\nPCI_ID=1002:150E\nPCI_SUBSYS_ID=1043:1DF3\nPCI_SLOT_NAME=0000:c4:00.0\nMODALIAS=pci:v00001002d0000150Esv00001043sd00001DF3bc03sc80i00" - }, - { - "Id": "#d432c663" - }, - { - "Id": "#9b895db2", - "Data": "0x1002" - }, - { - "Id": "#66f3e150", - "Data": "0x150e" - }, - { - "Id": "#d410b6c7", - "Data": "0x038000" - }, - { - "Id": "#1075ed5c" - }, - { - "Id": "#d410b6c7", - "Data": "0x038000" - }, - { - "Id": "#41bc32d8", - "Data": "113-STRIXEMU-001" - }, - { - "Id": "#bf29d2f6", - "Data": "0xc1" - }, - { - "Id": "#269abd81", - "Data": "0x1043" - }, - { - "Id": "#360cec38", - "Data": "0x1df3" - }, - { - "Id": "#d2629d83", - "Data": "0000:c4:00.0" - }, - { - "Id": "#7305decd", - "Filename": "/dev/dri/card0" - }, - { - "Id": "#c0ff063f", - "Exists": 0 - }, - { - "Id": "#1b923827", - "Exists": 0 - }, - { - "Id": "#cdb78a8a", - "Exists": 0 - }, - { - "Id": "#9747b377", - "DataOut": "QU1EIFNUUklYX0VNVQAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgADExMy1TVFJJWEVNVS0wMDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAQoXAAAAADAyMy4wMTAuMDAxLjAxNy4wMDAwMDEAU1RSSVguYmluMjAyNC8wNi8xNCAxNzo0MQAAAAAAAAAAAAAAAAAAAAA=" - } - ] - } - ] -} diff -Nru fwupd-2.0.8/plugins/amd-gpu/tests/amd-apu-strix-setup.json fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-strix-setup.json --- fwupd-2.0.8/plugins/amd-gpu/tests/amd-apu-strix-setup.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-strix-setup.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,119 @@ +{ + "UsbDevices": [ + { + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c4:00.0", + "DeviceFile": "/dev/dri/card0", + "Subsystem": "pci", + "Driver": "amdgpu", + "BindId": "0000:c4:00.0", + "Vendor": 4098, + "Model": 5390, + "Created": "2024-11-01T17:27:45.279095Z", + "Events": [ + { + "Id": "#d5a801ad", + "Data": "pci" + }, + { + "Id": "#ad8c58d3", + "Data": "amdgpu" + }, + { + "Id": "#1075ed5c" + }, + { + "Id": "#bddbca22", + "Data": "DRIVER=amdgpu\nPCI_CLASS=38000\nPCI_ID=1002:150E\nPCI_SUBSYS_ID=1043:1DF3\nPCI_SLOT_NAME=0000:c4:00.0\nMODALIAS=pci:v00001002d0000150Esv00001043sd00001DF3bc03sc80i00" + }, + { + "Id": "#d432c663" + }, + { + "Id": "#9b895db2", + "Data": "0x1002" + }, + { + "Id": "#66f3e150", + "Data": "0x150e" + }, + { + "Id": "#d410b6c7", + "Data": "0x038000" + }, + { + "Id": "#1075ed5c" + }, + { + "Id": "#1075ed5c" + }, + { + "Id": "#bddbca22", + "Data": "DRIVER=amdgpu\nPCI_CLASS=38000\nPCI_ID=1002:150E\nPCI_SUBSYS_ID=1043:1DF3\nPCI_SLOT_NAME=0000:c4:00.0\nMODALIAS=pci:v00001002d0000150Esv00001043sd00001DF3bc03sc80i00" + }, + { + "Id": "#d432c663" + }, + { + "Id": "#9b895db2", + "Data": "0x1002" + }, + { + "Id": "#66f3e150", + "Data": "0x150e" + }, + { + "Id": "#d410b6c7", + "Data": "0x038000" + }, + { + "Id": "#1075ed5c" + }, + { + "Id": "#d410b6c7", + "Data": "0x038000" + }, + { + "Id": "#41bc32d8", + "Data": "113-STRIXEMU-001" + }, + { + "Id": "#bf29d2f6", + "Data": "0xc1" + }, + { + "Id": "#269abd81", + "Data": "0x1043" + }, + { + "Id": "#360cec38", + "Data": "0x1df3" + }, + { + "Id": "#d2629d83", + "Data": "0000:c4:00.0" + }, + { + "Id": "#7305decd", + "Filename": "/dev/dri/card0" + }, + { + "Id": "#c0ff063f", + "Exists": 0 + }, + { + "Id": "#1b923827", + "Exists": 0 + }, + { + "Id": "#cdb78a8a", + "Exists": 0 + }, + { + "Id": "#9747b377", + "DataOut": "QU1EIFNUUklYX0VNVQAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgADExMy1TVFJJWEVNVS0wMDEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARAQoXAAAAADAyMy4wMTAuMDAxLjAxNy4wMDAwMDEAU1RSSVguYmluMjAyNC8wNi8xNCAxNzo0MQAAAAAAAAAAAAAAAAAAAAA=" + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/amd-gpu/tests/amd-apu-strix.json fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-strix.json --- fwupd-2.0.8/plugins/amd-gpu/tests/amd-apu-strix.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu-strix.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +{ + "name": "AMD Strix", + "interactive": false, + "steps": [ + { + "emulation-file": "@enumeration_datadir@/amd-apu-strix-setup.json", + "components": [ + { + "version": "1", + "guids": [ + "cc72cddc-68ef-5809-afce-bb1e5bcf571f" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/amd-gpu/tests/amd-apu.json fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu.json --- fwupd-2.0.8/plugins/amd-gpu/tests/amd-apu.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/tests/amd-apu.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -{ - "name": "AMD Strix", - "interactive": false, - "steps": [ - { - "emulation-file": "@enumeration_datadir@/amd-apu-setup.json", - "components": [ - { - "version": "1", - "guids": [ - "cc72cddc-68ef-5809-afce-bb1e5bcf571f" - ] - } - ] - } - ] -} diff -Nru fwupd-2.0.8/plugins/amd-gpu/tests/amd-dgpu-navi3x.json fwupd-2.0.20/plugins/amd-gpu/tests/amd-dgpu-navi3x.json --- fwupd-2.0.8/plugins/amd-gpu/tests/amd-dgpu-navi3x.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-gpu/tests/amd-dgpu-navi3x.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/5885a99e908d37ae8cad445cb2b06f1c93d468d36fa53a2355604ba0116fbba5-D704_XT_A0_20GB_MBA_NAVI31_Baseline_PRD004B_69818.cab", - "emulation-url": "https://fwupd.org/downloads/808f6a085f8a59a8db62d08921e786e15d252eb3cf5069ee4c0b991e4a250cf1-navi31.zip", + "url": "5885a99e908d37ae8cad445cb2b06f1c93d468d36fa53a2355604ba0116fbba5-D704_XT_A0_20GB_MBA_NAVI31_Baseline_PRD004B_69818.cab", + "emulation-url": "808f6a085f8a59a8db62d08921e786e15d252eb3cf5069ee4c0b991e4a250cf1-navi31.zip", "components": [ { "version": "114", diff -Nru fwupd-2.0.8/plugins/amd-kria/README.md fwupd-2.0.20/plugins/amd-kria/README.md --- fwupd-2.0.8/plugins/amd-kria/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-kria/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,8 @@ -# AMD Kria +--- +title: Plugin: AMD Kria +--- + +## Introduction The AMD Kria plugin is used to represent the system firmware stored on QSPI for the AMD Kria system on module device specifically when not booted using @@ -29,11 +33,3 @@ ## Quirks * `AmdKriaEepromAddr` represents the I2C address for the SoM EEPROM - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Mario Limonciello: @superm1 -* Michal Simek @michalsimek diff -Nru fwupd-2.0.8/plugins/amd-kria/fu-amd-kria-device.c fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-device.c --- fwupd-2.0.8/plugins/amd-kria/fu-amd-kria-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,7 +18,7 @@ typedef struct { FuVolume *esp; - FuDeviceLocker *esp_locker; + FuVolumeLocker *esp_locker; gchar *eeprom_address; } FuAmdKriaDevicePrivate; @@ -43,7 +43,13 @@ FuAmdKriaDevice *self = FU_AMD_KRIA_DEVICE(device); FuAmdKriaDevicePrivate *priv = GET_PRIVATE(self); - priv->esp_locker = fu_volume_locker(priv->esp, error); + /* sanity check */ + if (priv->esp == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no ESP"); + return FALSE; + } + + priv->esp_locker = fu_volume_locker_new(priv->esp, error); if (priv->esp_locker == NULL) return FALSE; @@ -59,7 +65,13 @@ FuAmdKriaDevice *self = FU_AMD_KRIA_DEVICE(device); FuAmdKriaDevicePrivate *priv = GET_PRIVATE(self); - if (!fu_device_locker_close(priv->esp_locker, error)) + /* sanity check */ + if (priv->esp_locker == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no ESP locker"); + return FALSE; + } + + if (!fu_volume_locker_close(priv->esp_locker, error)) return FALSE; g_clear_object(&priv->esp_locker); @@ -166,7 +178,6 @@ g_autofree gchar *buf = NULL; g_autofree gchar *path = g_build_path("/", devpath, "eeprom", NULL); g_autoptr(FuFirmware) firmware = NULL; - g_autoptr(GError) error_esp = NULL; g_autoptr(GBytes) bytes = NULL; if (!g_file_get_contents(path, &buf, &bufsz, error)) @@ -175,7 +186,7 @@ /* parse the eeprom */ bytes = g_bytes_new(buf, bufsz); firmware = fu_amd_kria_som_eeprom_new(); - if (!fu_firmware_parse_bytes(firmware, bytes, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_bytes(firmware, bytes, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, error)) return FALSE; /* build instance IDs from EEPROM data */ @@ -223,7 +234,7 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); - fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_set_summary(FU_DEVICE(self), "AMD Kria device (Updated via capsule-on-disk)"); fu_device_add_protocol(FU_DEVICE(self), "org.uefi.capsule"); diff -Nru fwupd-2.0.8/plugins/amd-kria/fu-amd-kria-image-firmware.c fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-image-firmware.c --- fwupd-2.0.8/plugins/amd-kria/fu-amd-kria-image-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-image-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,7 @@ #include "fu-amd-kria-image-firmware.h" struct _FuAmdKriaImageFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; }; #define VERSION_OFFSET 0x70 @@ -25,7 +25,7 @@ static gboolean fu_amd_kria_image_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { const gchar *buf; diff -Nru fwupd-2.0.8/plugins/amd-kria/fu-amd-kria-persistent-firmware.c fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-persistent-firmware.c --- fwupd-2.0.8/plugins/amd-kria/fu-amd-kria-persistent-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-persistent-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,7 @@ #include "fu-amd-kria-persistent-struct.h" struct _FuAmdKriaPersistentFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; FuAmdKriaBootImageId last_booted; }; @@ -24,16 +24,16 @@ static gboolean fu_amd_kria_persistent_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAmdKriaPersistentFirmware *self = FU_AMD_KRIA_PERSISTENT_FIRMWARE(firmware); - g_autoptr(FuStructAmdKriaPersistReg) content = NULL; + g_autoptr(FuStructAmdKriaPersistReg) st = NULL; - content = fu_struct_amd_kria_persist_reg_parse_stream(stream, 0x0, error); - if (content == NULL) + st = fu_struct_amd_kria_persist_reg_parse_stream(stream, 0x0, error); + if (st == NULL) return FALSE; - self->last_booted = fu_struct_amd_kria_persist_reg_get_last_booted_img(content); + self->last_booted = fu_struct_amd_kria_persist_reg_get_last_booted_img(st); return TRUE; } diff -Nru fwupd-2.0.8/plugins/amd-kria/fu-amd-kria-plugin.c fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-plugin.c --- fwupd-2.0.8/plugins/amd-kria/fu-amd-kria-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -43,7 +43,7 @@ return FALSE; firmware = fu_amd_kria_image_firmware_new(); - if (!fu_firmware_parse_bytes(firmware, bytes, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_bytes(firmware, bytes, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, error)) return FALSE; fu_device_set_version(dev, fu_firmware_get_version(firmware)); @@ -64,7 +64,7 @@ return FALSE; firmware = fu_amd_kria_persistent_firmware_new(); - if (!fu_firmware_parse_bytes(firmware, bytes, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_bytes(firmware, bytes, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, error)) return FALSE; if (fu_amd_kria_persistent_firmware_booted_image_a( @@ -80,6 +80,7 @@ fu_amd_kria_plugin_device_registered(FuPlugin *plugin, FuDevice *dev) { FuAmdKriaPlugin *self; + FuDevice *parent; const gchar *name; g_autoptr(GError) error_local = NULL; @@ -102,12 +103,13 @@ } /* mark the active partition version on the created KRIA device */ - if (fu_device_get_parent(dev) != NULL && fu_device_get_version(dev) != NULL) { + parent = fu_device_get_parent(dev, NULL); + if (parent != NULL && fu_device_get_version(dev) != NULL) { if (g_strcmp0(self->active, "A") == 0 && self->version_a != NULL) - fu_device_set_version(fu_device_get_parent(dev), self->version_a); + fu_device_set_version(parent, self->version_a); else if (g_strcmp0(self->active, "B") == 0 && self->version_b != NULL) - fu_device_set_version(fu_device_get_parent(dev), self->version_b); - fu_device_add_flag(fu_device_get_parent(dev), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_version(parent, self->version_b); + fu_device_add_flag(parent, FWUPD_DEVICE_FLAG_UPDATABLE); } fu_device_remove_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE); @@ -127,17 +129,16 @@ fu_amd_kria_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) { #ifdef __aarch64__ - g_autofree gchar *sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - g_autofree gchar *esrt_path = g_build_filename(sysfsfwdir, "efi", "esrt", NULL); + g_autofree gchar *esrt_path = fu_path_build(FU_PATH_KIND_SYSFSDIR_FW, "efi", "esrt", NULL); /* if there is an ESRT use that instead and disable the plugin */ if (g_file_test(esrt_path, G_FILE_TEST_IS_DIR)) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "system uses UEFI ESRT"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "system uses ESRT"); return FALSE; } return TRUE; #else - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "only for aarch64"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "only for aarch64"); return FALSE; #endif } diff -Nru fwupd-2.0.8/plugins/amd-kria/fu-amd-kria-som-eeprom.c fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-som-eeprom.c --- fwupd-2.0.8/plugins/amd-kria/fu-amd-kria-som-eeprom.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-kria/fu-amd-kria-som-eeprom.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,7 @@ #include "fu-amd-kria-som-eeprom.h" struct _FuAmdKriaSomEeprom { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; gchar *manufacturer; gchar *product_name; gchar *serial_number; @@ -29,7 +29,7 @@ static gboolean fu_amd_kria_som_eeprom_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAmdKriaSomEeprom *self = FU_AMD_KRIA_SOM_EEPROM(firmware); @@ -38,24 +38,24 @@ guint8 board_offset; const guint8 *buf; gsize bufsz = 0; - g_autoptr(FuStructIpmiCommon) common = NULL; - g_autoptr(FuStructBoardInfo) board = NULL; + g_autoptr(FuStructIpmiCommon) st_common = NULL; + g_autoptr(FuStructBoardInfo) st_board = NULL; g_autoptr(GBytes) fw = NULL; /* parse IPMI common header */ - common = fu_struct_ipmi_common_parse_stream(stream, 0x0, error); - if (common == NULL) + st_common = fu_struct_ipmi_common_parse_stream(stream, 0x0, error); + if (st_common == NULL) return FALSE; - board_offset = fu_struct_ipmi_common_get_board_offset(common) * 8; + board_offset = fu_struct_ipmi_common_get_board_offset(st_common) * 8; /* parse board info area */ - board = fu_struct_board_info_parse_stream(stream, board_offset, error); - if (board == NULL) + st_board = fu_struct_board_info_parse_stream(stream, board_offset, error); + if (st_board == NULL) return FALSE; fw = fu_input_stream_read_bytes(stream, board_offset, - fu_struct_board_info_get_length(board) * 8, + fu_struct_board_info_get_length(st_board) * 8, NULL, error); if (fw == NULL) @@ -66,19 +66,46 @@ /* manufacturer string in board area */ str_offset = str_offset + str_len; + if (str_offset >= bufsz) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "board info manufacturer offset out of bounds"); + return FALSE; + } str_len = LENGTH(buf[str_offset]); str_offset++; - self->manufacturer = fu_strsafe((gchar *)buf + str_offset, str_len); + self->manufacturer = fu_memstrsafe(buf, bufsz, str_offset, str_len, error); + if (self->manufacturer == NULL) + return FALSE; str_offset = str_offset + str_len; + if (str_offset >= bufsz) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "board info product name offset out of bounds"); + return FALSE; + } str_len = LENGTH(buf[str_offset]); str_offset++; - self->product_name = fu_strsafe((gchar *)buf + str_offset, str_len); + self->product_name = fu_memstrsafe(buf, bufsz, str_offset, str_len, error); + if (self->product_name == NULL) + return FALSE; str_offset = str_offset + str_len; + if (str_offset >= bufsz) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "board info serial number offset out of bounds"); + return FALSE; + } str_len = LENGTH(buf[str_offset]); str_offset++; - self->serial_number = fu_strsafe((gchar *)buf + str_offset, str_len); + self->serial_number = fu_memstrsafe(buf, bufsz, str_offset, str_len, error); + if (self->serial_number == NULL) + return FALSE; return TRUE; } diff -Nru fwupd-2.0.8/plugins/amd-kria/meson.build fwupd-2.0.20/plugins/amd-kria/meson.build --- fwupd-2.0.8/plugins/amd-kria/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-kria/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginAmdKria"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -21,5 +22,3 @@ dependencies: plugin_deps, ) plugin_builtins += plugin_builtin_kria - -endif diff -Nru fwupd-2.0.8/plugins/amd-pmc/README.md fwupd-2.0.20/plugins/amd-pmc/README.md --- fwupd-2.0.8/plugins/amd-pmc/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-pmc/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -14,10 +14,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.8.5`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Mario Limonciello: @superm1 diff -Nru fwupd-2.0.8/plugins/amd-pmc/fu-amd-pmc-device.c fwupd-2.0.20/plugins/amd-pmc/fu-amd-pmc-device.c --- fwupd-2.0.8/plugins/amd-pmc/fu-amd-pmc-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-pmc/fu-amd-pmc-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -66,9 +66,9 @@ { fu_device_set_name(FU_DEVICE(self), "System Management Unit (SMU)"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_CPU_CHILD); - fu_device_set_vendor(FU_DEVICE(self), "Advanced Micro Devices, Inc."); + fu_device_set_vendor(FU_DEVICE(self), "AMD"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_physical_id(FU_DEVICE(self), "amd-pmc"); } diff -Nru fwupd-2.0.8/plugins/amd-pmc/meson.build fwupd-2.0.20/plugins/amd-pmc/meson.build --- fwupd-2.0.8/plugins/amd-pmc/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/amd-pmc/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if host_machine.system() == 'linux' and (host_cpu == 'x86' or host_cpu == 'x86_64') +host_machine.system() == 'linux' or subdir_done() +host_cpu in ['x86', 'x86_64'] or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginAmdPmc"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -15,4 +17,3 @@ ) enumeration_data += files('tests/amd-pmc-setup.json') device_tests += files('tests/amd-pmc.json') -endif diff -Nru fwupd-2.0.8/plugins/analogix/README.md fwupd-2.0.20/plugins/analogix/README.md --- fwupd-2.0.8/plugins/analogix/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/analogix/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -51,10 +51,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.6.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Analogix: @xtcui diff -Nru fwupd-2.0.8/plugins/analogix/fu-analogix-common.h fwupd-2.0.20/plugins/analogix/fu-analogix-common.h --- fwupd-2.0.8/plugins/analogix/fu-analogix-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/analogix/fu-analogix-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -23,22 +23,3 @@ #define FLASH_RXFW_ADDR 0x34000 #define FLASH_CUSTOM_ADDR 0x38000 #define OCM_FW_VERSION_ADDR 0x14FF0 - -/* bRequest for Phoenix-Lite Billboard */ -typedef enum { - ANX_BB_RQT_SEND_UPDATE_DATA = 0x01, - ANX_BB_RQT_READ_UPDATE_DATA = 0x02, - ANX_BB_RQT_GET_UPDATE_STATUS = 0x10, - ANX_BB_RQT_READ_FW_VER = 0x12, - ANX_BB_RQT_READ_CUS_VER = 0x13, - ANX_BB_RQT_READ_FW_RVER = 0x19, - ANX_BB_RQT_READ_CUS_RVER = 0x1c, -} AnxBbRqtCode; - -/* wValue low byte */ -typedef enum { - ANX_BB_WVAL_UPDATE_OCM = 0x06, - ANX_BB_WVAL_UPDATE_CUSTOM_DEF = 0x07, - ANX_BB_WVAL_UPDATE_SECURE_TX = 0x08, - ANX_BB_WVAL_UPDATE_SECURE_RX = 0x09, -} AnxwValCode; diff -Nru fwupd-2.0.8/plugins/analogix/fu-analogix-device.c fwupd-2.0.20/plugins/analogix/fu-analogix-device.c --- fwupd-2.0.8/plugins/analogix/fu-analogix-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/analogix/fu-analogix-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -28,7 +28,7 @@ static gboolean fu_analogix_device_send(FuAnalogixDevice *self, - AnxBbRqtCode reqcode, + FuAnalogixBbRqt reqcode, guint16 val0code, guint16 index, const guint8 *buf, @@ -60,14 +60,14 @@ (guint)ANX_BB_TRANSACTION_TIMEOUT, NULL, error)) { - g_prefix_error(error, "send data error: "); + g_prefix_error_literal(error, "send data error: "); return FALSE; } if (actual_len != bufsz) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "send data length is incorrect"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "send data length is incorrect"); return FALSE; } @@ -77,7 +77,7 @@ static gboolean fu_analogix_device_receive(FuAnalogixDevice *self, - AnxBbRqtCode reqcode, + FuAnalogixBbRqt reqcode, guint16 val0code, guint16 index, guint8 *buf, @@ -103,14 +103,14 @@ (guint)ANX_BB_TRANSACTION_TIMEOUT, NULL, error)) { - g_prefix_error(error, "receive data error: "); + g_prefix_error_literal(error, "receive data error: "); return FALSE; } if (actual_len != bufsz) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "receive data length is incorrect"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "receive data length is incorrect"); return FALSE; } @@ -126,7 +126,7 @@ for (guint i = 0; i < 3000; i++) { guint8 status_tmp = FU_ANALOGIX_UPDATE_STATUS_INVALID; if (!fu_analogix_device_receive(self, - ANX_BB_RQT_GET_UPDATE_STATUS, + FU_ANALOGIX_BB_RQT_GET_UPDATE_STATUS, 0, 0, &status_tmp, @@ -164,15 +164,27 @@ return FALSE; /* get OCM version */ - if (!fu_analogix_device_receive(self, ANX_BB_RQT_READ_FW_VER, 0, 0, &buf_fw[1], 1, error)) + if (!fu_analogix_device_receive(self, + FU_ANALOGIX_BB_RQT_READ_FW_VER, + 0, + 0, + &buf_fw[1], + 1, + error)) return FALSE; - if (!fu_analogix_device_receive(self, ANX_BB_RQT_READ_FW_RVER, 0, 0, &buf_fw[0], 1, error)) + if (!fu_analogix_device_receive(self, + FU_ANALOGIX_BB_RQT_READ_FW_RVER, + 0, + 0, + &buf_fw[0], + 1, + error)) return FALSE; self->ocm_version = fu_memread_uint16(buf_fw, G_LITTLE_ENDIAN); /* get custom version */ if (!fu_analogix_device_receive(self, - ANX_BB_RQT_READ_CUS_VER, + FU_ANALOGIX_BB_RQT_READ_CUS_VER, 0, 0, &buf_custom[1], @@ -180,7 +192,7 @@ error)) return FALSE; if (!fu_analogix_device_receive(self, - ANX_BB_RQT_READ_CUS_RVER, + FU_ANALOGIX_BB_RQT_READ_CUS_RVER, 0, 0, &buf_custom[0], @@ -222,7 +234,7 @@ fu_analogix_device_probe(FuDevice *device, GError **error) { if (!fu_analogix_device_find_interface(FU_USB_DEVICE(device), error)) { - g_prefix_error(error, "failed to find update interface: "); + g_prefix_error_literal(error, "failed to find update interface: "); return FALSE; } @@ -249,7 +261,7 @@ if (chk == NULL) return FALSE; if (!fu_analogix_device_send(self, - ANX_BB_RQT_SEND_UPDATE_DATA, + FU_ANALOGIX_BB_RQT_SEND_UPDATE_DATA, req_val, i + 1, fu_chunk_get_data(chk), @@ -297,13 +309,13 @@ return FALSE; fu_memwrite_uint32(buf_init, streamsz, G_LITTLE_ENDIAN); if (!fu_analogix_device_send(self, - ANX_BB_RQT_SEND_UPDATE_DATA, + FU_ANALOGIX_BB_RQT_SEND_UPDATE_DATA, req_val, 0, buf_init, 3, error)) { - g_prefix_error(error, "program initialized failed: "); + g_prefix_error_literal(error, "program initialized failed: "); return FALSE; } if (!fu_analogix_device_get_update_status(self, &status, error)) @@ -396,10 +408,10 @@ if (fw_cus != NULL) { if (!fu_analogix_device_write_image(self, fw_cus, - ANX_BB_WVAL_UPDATE_CUSTOM_DEF, + FU_ANALOGIX_BB_WVAL_UPDATE_CUSTOM_DEF, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "program custom define failed: "); + g_prefix_error_literal(error, "program custom define failed: "); return FALSE; } fu_progress_step_done(progress); @@ -409,10 +421,10 @@ if (fw_stx != NULL) { if (!fu_analogix_device_write_image(self, fw_stx, - ANX_BB_WVAL_UPDATE_SECURE_TX, + FU_ANALOGIX_BB_WVAL_UPDATE_SECURE_TX, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "program secure TX failed: "); + g_prefix_error_literal(error, "program secure TX failed: "); return FALSE; } fu_progress_step_done(progress); @@ -422,10 +434,10 @@ if (fw_srx != NULL) { if (!fu_analogix_device_write_image(self, fw_srx, - ANX_BB_WVAL_UPDATE_SECURE_RX, + FU_ANALOGIX_BB_WVAL_UPDATE_SECURE_RX, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "program secure RX failed: "); + g_prefix_error_literal(error, "program secure RX failed: "); return FALSE; } fu_progress_step_done(progress); @@ -435,10 +447,10 @@ if (fw_ocm != NULL) { if (!fu_analogix_device_write_image(self, fw_ocm, - ANX_BB_WVAL_UPDATE_OCM, + FU_ANALOGIX_BB_WVAL_UPDATE_OCM, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "program OCM failed: "); + g_prefix_error_literal(error, "program OCM failed: "); return FALSE; } fu_progress_step_done(progress); @@ -466,7 +478,7 @@ } static void -fu_analogix_device_set_progress(FuDevice *self, FuProgress *progress) +fu_analogix_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/analogix/fu-analogix-firmware.c fwupd-2.0.20/plugins/analogix/fu-analogix-firmware.c --- fwupd-2.0.8/plugins/analogix/fu-analogix-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/analogix/fu-analogix-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,7 @@ #include "fu-analogix-firmware.h" struct _FuAnalogixFirmware { - FuIhexFirmwareClass parent_instance; + FuIhexFirmware parent_instance; }; G_DEFINE_TYPE(FuAnalogixFirmware, fu_analogix_firmware, FU_TYPE_IHEX_FIRMWARE) @@ -18,7 +18,7 @@ static gboolean fu_analogix_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuFirmwareClass *klass = FU_FIRMWARE_CLASS(fu_analogix_firmware_parent_class); @@ -56,7 +56,8 @@ fw_ocm = fu_firmware_new_from_bytes(blob_ocm); fu_firmware_set_id(fw_ocm, "ocm"); fu_firmware_set_addr(fw_ocm, FLASH_OCM_ADDR); - fu_firmware_add_image(firmware, fw_ocm); + if (!fu_firmware_add_image(firmware, fw_ocm, error)) + return FALSE; /* get OCM version */ buf = g_bytes_get_data(blob_ocm, &bufsz); @@ -83,7 +84,8 @@ g_autoptr(FuFirmware) fw2 = fu_firmware_new_from_bytes(blob_stx); fu_firmware_set_id(fw2, "stx"); fu_firmware_set_addr(fw2, FLASH_TXFW_ADDR); - fu_firmware_add_image(firmware, fw2); + if (!fu_firmware_add_image(firmware, fw2, error)) + return FALSE; } /* RXFW is optional */ @@ -93,13 +95,15 @@ g_autoptr(FuFirmware) fw2 = fu_firmware_new_from_bytes(blob_srx); fu_firmware_set_id(fw2, "srx"); fu_firmware_set_addr(fw2, FLASH_RXFW_ADDR); - fu_firmware_add_image(firmware, fw2); + if (!fu_firmware_add_image(firmware, fw2, error)) + return FALSE; } if (blob_cus != NULL && !fu_bytes_is_empty(blob_cus)) { g_autoptr(FuFirmware) fw2 = fu_firmware_new_from_bytes(blob_cus); fu_firmware_set_id(fw2, "custom"); fu_firmware_set_addr(fw2, FLASH_CUSTOM_ADDR); - fu_firmware_add_image(firmware, fw2); + if (!fu_firmware_add_image(firmware, fw2, error)) + return FALSE; } /* success */ diff -Nru fwupd-2.0.8/plugins/analogix/fu-analogix-plugin.c fwupd-2.0.20/plugins/analogix/fu-analogix-plugin.c --- fwupd-2.0.8/plugins/analogix/fu-analogix-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/analogix/fu-analogix-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,6 +25,7 @@ fu_analogix_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_ANALOGIX_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_ANALOGIX_FIRMWARE); } diff -Nru fwupd-2.0.8/plugins/analogix/fu-analogix.rs fwupd-2.0.20/plugins/analogix/fu-analogix.rs --- fwupd-2.0.8/plugins/analogix/fu-analogix.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/analogix/fu-analogix.rs 2026-02-26 11:36:18.000000000 +0000 @@ -8,3 +8,22 @@ Finish, Error = 0xFF, } + +// bRequest for Phoenix-Lite Billboard +enum FuAnalogixBbRqt { + SendUpdateData = 0x01, + ReadUpdateData = 0x02, + GetUpdateStatus = 0x10, + ReadFwVer = 0x12, + ReadCusVer = 0x13, + ReadFwRver = 0x19, + ReadCusRver = 0x1C, +} + +// wValue low byte +enum FuAnalogixBbWval { + UpdateOcm = 0x06, + UpdateCustomDef = 0x07, + UpdateSecureTx = 0x08, + UpdateSecureRx = 0x09, +} diff -Nru fwupd-2.0.8/plugins/analogix/tests/analogix-anx7518.json fwupd-2.0.20/plugins/analogix/tests/analogix-anx7518.json --- fwupd-2.0.8/plugins/analogix/tests/analogix-anx7518.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/analogix/tests/analogix-anx7518.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": true, "steps": [ { - "url": "https://fwupd.org/downloads/5d860747c1378ef8921f95e41bbb7055dc9b470043655a65b00157ab74dd12e8-Analogix-ANX7518-fw-1.5.01-rx-1206.cab", - "emulation-url": "https://fwupd.org/downloads/63698f1d36491d795f36335a28e301e7484b1735c63aa763c007c149cc74569c-Analogix-ANX7518-fw-1.5.01-rx-1206.zip", + "url": "5d860747c1378ef8921f95e41bbb7055dc9b470043655a65b00157ab74dd12e8-Analogix-ANX7518-fw-1.5.01-rx-1206.cab", + "emulation-url": "63698f1d36491d795f36335a28e301e7484b1735c63aa763c007c149cc74569c-Analogix-ANX7518-fw-1.5.01-rx-1206.zip", "components": [ { "version": "0001.1501", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/2e4b5747ea2659ddae893a7b8a238d6d9c3166b426c0d0e5feb308a443662c38-Analogix-ANX7518-fw-1.5.08.cab", - "emulation-url": "https://fwupd.org/downloads/ff75670536d3de6def5cb4e6e1377dc1ef132882288dd6ceee37c0aecc3fbda3-Analogix-ANX7518-fw-1.5.08.zip", + "url": "2e4b5747ea2659ddae893a7b8a238d6d9c3166b426c0d0e5feb308a443662c38-Analogix-ANX7518-fw-1.5.08.cab", + "emulation-url": "ff75670536d3de6def5cb4e6e1377dc1ef132882288dd6ceee37c0aecc3fbda3-Analogix-ANX7518-fw-1.5.08.zip", "components": [ { "version": "0001.1508", diff -Nru fwupd-2.0.8/plugins/android-boot/README.md fwupd-2.0.20/plugins/android-boot/README.md --- fwupd-2.0.8/plugins/android-boot/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/android-boot/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -57,10 +57,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.8.5`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Dylan Van Assche: @DylanVanAssche diff -Nru fwupd-2.0.8/plugins/android-boot/android-boot.quirk fwupd-2.0.20/plugins/android-boot/android-boot.quirk --- fwupd-2.0.8/plugins/android-boot/android-boot.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/android-boot/android-boot.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -1,27 +1,27 @@ # SHIFT6mq ABL A [DRIVE\UUID_c49183ed-aaec-9bf5-760a-66330fbcffc1&LABEL_abl-a] Flags = updatable,signed-payload -Vendor = SHIFT GmbH +Vendor = SHIFT VendorId = DRIVE:SHIFT AndroidBootVersionProperty = androidboot.abl.revision # SHIFT6mq ABL B [DRIVE\UUID_3d7b21e8-048b-db0b-0c18-d07a9bb32f2d&LABEL_abl-b] Flags = updatable,signed-payload -Vendor = SHIFT GmbH +Vendor = SHIFT VendorId = DRIVE:SHIFT AndroidBootVersionProperty = androidboot.abl.revision # SHIFTphone 8 ABL A [DRIVE\UUID_0072779b-8343-75c6-525f-44ff0e1d04db&LABEL_abl-a] Flags = updatable,signed-payload -Vendor = SHIFT GmbH +Vendor = SHIFT VendorId = DRIVE:SHIFT AndroidBootVersionProperty = androidboot.abl.revision # SHIFTphone 8 ABL B [DRIVE\UUID_584e4f5f-6023-f6d6-eb3f-48bb7da59feb&LABEL_abl-b] Flags = updatable,signed-payload -Vendor = SHIFT GmbH +Vendor = SHIFT VendorId = DRIVE:SHIFT AndroidBootVersionProperty = androidboot.abl.revision diff -Nru fwupd-2.0.8/plugins/android-boot/fu-android-boot-device.c fwupd-2.0.20/plugins/android-boot/fu-android-boot-device.c --- fwupd-2.0.8/plugins/android-boot/fu-android-boot-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/android-boot/fu-android-boot-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -97,8 +97,7 @@ if (fs_label != NULL) { fu_device_set_name(device, fs_label); - /* If the device has A/B partitioning, compare boot slot to only expose partitions - * in-use */ + /* if device has A/B partitioning, compare slot to only expose partitions in-use */ if (self->boot_slot != NULL && !g_str_has_suffix(fs_label, self->boot_slot)) { g_set_error_literal(error, FWUPD_ERROR, @@ -177,7 +176,7 @@ /* rewind */ if (!fu_udev_device_seek(FU_UDEV_DEVICE(self), 0x0, error)) { - g_prefix_error(error, "failed to rewind: "); + g_prefix_error_literal(error, "failed to rewind: "); return FALSE; } @@ -371,7 +370,7 @@ fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_SYNC); - fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); /* * Fallback for ABL without version reporting, fwupd will always provide an upgrade in this diff -Nru fwupd-2.0.8/plugins/android-boot/meson.build fwupd-2.0.20/plugins/android-boot/meson.build --- fwupd-2.0.8/plugins/android-boot/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/android-boot/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginAndroidBoot"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -13,4 +14,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/asus-hid/README.md fwupd-2.0.20/plugins/asus-hid/README.md --- fwupd-2.0.8/plugins/asus-hid/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/asus-hid/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -47,10 +47,3 @@ The number of MCUs connected to the USB endpoint. Since: 2.0.0 - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -@superm1 diff -Nru fwupd-2.0.8/plugins/asus-hid/asus-hid.quirk fwupd-2.0.20/plugins/asus-hid/asus-hid.quirk --- fwupd-2.0.8/plugins/asus-hid/asus-hid.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/asus-hid/asus-hid.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -1,28 +1,28 @@ # N-Key for Rog Ally -[USB\VID_0B05&PID_1ABE] +[HIDRAW\VEN_0B05&DEV_1ABE] Plugin = asus_hid AsusHidNumMcu = 2 -CounterpartGuid = USB\VID_048D&PID_89DB +CounterpartGuid = HIDRAW\VEN_048D&DEV_89DB FirmwareSizeMax = 0x40000 Flags = use-runtime-version # Rog Ally-X -[USB\VID_0B05&PID_1B4C] +[HIDRAW\VEN_0B05&DEV_1B4C] Plugin = asus_hid AsusHidNumMcu = 1 -CounterpartGuid = USB\VID_048D&PID_89DC +CounterpartGuid = HIDRAW\VEN_048D&DEV_89DC FirmwareSizeMax = 0x40000 Flags = use-runtime-version # Ally Bootloader -[USB\VID_048D&PID_89DB] +[HIDRAW\VEN_048D&DEV_89DB] AsusHidNumMcu = 2 Plugin = asus_hid Flags = is-bootloader,can-verify-image FirmwareSizeMax = 0x40000 # Ally X Bootloader -[USB\VID_048D&PID_89DC] +[HIDRAW\VEN_048D&DEV_89DC] AsusHidNumMcu = 1 Plugin = asus_hid Flags = is-bootloader,can-verify-image diff -Nru fwupd-2.0.8/plugins/asus-hid/fu-asus-hid-child-device.c fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-child-device.c --- fwupd-2.0.8/plugins/asus-hid/fu-asus-hid-child-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-child-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -34,29 +34,28 @@ guint8 report, GError **error) { - FuHidDevice *hid_dev = FU_HID_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuHidrawDevice *proxy; + proxy = FU_HIDRAW_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; if (req != NULL) { - if (!fu_hid_device_set_report(hid_dev, - report, - req->data, - req->len, - FU_ASUS_HID_CHILD_DEVICE_TIMEOUT, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error)) { - g_prefix_error(error, "failed to send packet: "); + if (!fu_hidraw_device_set_feature(proxy, + req->data, + req->len, + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to send packet: "); return FALSE; } } if (res != NULL) { - if (!fu_hid_device_get_report(hid_dev, - report, - res->data, - res->len, - FU_ASUS_HID_CHILD_DEVICE_TIMEOUT, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error)) { - g_prefix_error(error, "failed to receive packet: "); + if (!fu_hidraw_device_get_feature(proxy, + res->data, + res->len, + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to receive packet: "); return FALSE; } } @@ -68,17 +67,17 @@ fu_asus_hid_child_device_ensure_manufacturer(FuAsusHidChildDevice *self, GError **error) { g_autofree gchar *man = NULL; - g_autoptr(FuStructAsusManCommand) cmd = fu_struct_asus_man_command_new(); - g_autoptr(FuStructAsusManResult) result = fu_struct_asus_man_result_new(); + g_autoptr(FuStructAsusManCommand) st = fu_struct_asus_man_command_new(); + g_autoptr(FuStructAsusManResult) st_result = fu_struct_asus_man_result_new(); if (!fu_asus_hid_child_device_transfer_feature(self, - cmd, - result, + st->buf, + st_result->buf, FU_ASUS_HID_REPORT_ID_INFO, error)) return FALSE; - man = fu_struct_asus_man_result_get_data(result); + man = fu_struct_asus_man_result_get_data(st_result); if (g_strcmp0(man, "ASUSTech.Inc.") != 0) { g_set_error(error, FWUPD_ERROR, @@ -94,15 +93,15 @@ static gboolean fu_asus_hid_child_device_ensure_version(FuAsusHidChildDevice *self, GError **error) { - g_autoptr(FuStructAsusHidCommand) cmd = fu_struct_asus_hid_command_new(); - g_autoptr(FuStructAsusHidFwInfo) result = fu_struct_asus_hid_fw_info_new(); - g_autoptr(FuStructAsusHidFwInfo) fw_info = NULL; + g_autoptr(FuStructAsusHidCommand) st = fu_struct_asus_hid_command_new(); + g_autoptr(FuStructAsusHidFwInfo) st_result = fu_struct_asus_hid_fw_info_new(); + g_autoptr(FuStructAsusHidDesc) st_fwinfo = NULL; g_autofree gchar *version = NULL; if (self->idx == FU_ASUS_HID_CONTROLLER_PRIMARY) - fu_struct_asus_hid_command_set_cmd(cmd, FU_ASUS_HID_COMMAND_FW_VERSION); + fu_struct_asus_hid_command_set_cmd(st, FU_ASUS_HID_COMMAND_FW_VERSION); else if (self->idx == FU_ASUS_HID_CONTROLLER_MAIN) - fu_struct_asus_hid_command_set_cmd(cmd, FU_ASUS_HID_COMMAND_MAIN_FW_VERSION); + fu_struct_asus_hid_command_set_cmd(st, FU_ASUS_HID_COMMAND_MAIN_FW_VERSION); else { g_set_error_literal(error, FWUPD_ERROR, @@ -111,27 +110,27 @@ return FALSE; } - fu_struct_asus_hid_command_set_length(cmd, FU_STRUCT_ASUS_HID_RESULT_SIZE); + fu_struct_asus_hid_command_set_length(st, FU_STRUCT_ASUS_HID_RESULT_SIZE); if (!fu_asus_hid_child_device_transfer_feature(self, - cmd, - result, + st->buf, + st_result->buf, FU_ASUS_HID_REPORT_ID_INFO, error)) return FALSE; - fw_info = fu_struct_asus_hid_fw_info_get_description(result); - version = fu_struct_asus_hid_desc_get_version(fw_info); + st_fwinfo = fu_struct_asus_hid_fw_info_get_description(st_result); + version = fu_struct_asus_hid_desc_get_version(st_fwinfo); fu_device_set_version(FU_DEVICE(self), version); if (fu_device_get_logical_id(FU_DEVICE(self)) == NULL) { - g_autofree gchar *product = fu_struct_asus_hid_desc_get_product(fw_info); + g_autofree gchar *product = fu_struct_asus_hid_desc_get_product(st_fwinfo); fu_device_add_instance_strsafe(FU_DEVICE(self), "PART", product); fu_device_build_instance_id(FU_DEVICE(self), NULL, - "USB", - "VID", - "PID", + "HIDRAW", + "VEN", + "DEV", "PART", NULL); @@ -145,21 +144,19 @@ fu_asus_hid_child_device_setup(FuDevice *device, GError **error) { FuAsusHidChildDevice *self = FU_ASUS_HID_CHILD_DEVICE(device); + FuDevice *proxy; g_autofree gchar *name = NULL; - if (fu_device_get_proxy(FU_DEVICE(self)) == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) return FALSE; - } name = g_strdup_printf("Microcontroller %u", self->idx); fu_device_set_name(FU_DEVICE(self), name); - if (fu_device_has_flag(fu_device_get_proxy(FU_DEVICE(self)), - FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (fu_device_has_flag(proxy, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { g_autofree gchar *recovery_str = g_strdup_printf("%d", self->idx); - // RC71LS = 0 - // RC71LM = 1 + /* RC71LS = 0, RC71LM = 1 */ fu_device_add_instance_strsafe(FU_DEVICE(self), "RECOVERY", recovery_str); fu_device_build_instance_id(FU_DEVICE(self), NULL, @@ -174,11 +171,11 @@ } if (!fu_asus_hid_child_device_ensure_manufacturer(self, error)) { - g_prefix_error(error, "failed to ensure manufacturer: "); + g_prefix_error_literal(error, "failed to ensure manufacturer: "); return FALSE; } if (!fu_asus_hid_child_device_ensure_version(self, error)) { - g_prefix_error(error, "failed to ensure version: "); + g_prefix_error_literal(error, "failed to ensure version: "); return FALSE; } @@ -196,26 +193,18 @@ static gboolean fu_asus_hid_child_device_attach(FuDevice *device, FuProgress *progress, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); - - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) return FALSE; - } - return fu_device_attach(proxy, error); } static gboolean fu_asus_hid_child_device_detach(FuDevice *device, FuProgress *progress, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); - - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) return FALSE; - } - return fu_device_detach(proxy, error); } @@ -226,7 +215,9 @@ fu_device_add_protocol(FU_DEVICE(self), "com.asus.hid"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_ASUS_HID_DEVICE); } static void diff -Nru fwupd-2.0.8/plugins/asus-hid/fu-asus-hid-device.c fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-device.c --- fwupd-2.0.8/plugins/asus-hid/fu-asus-hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,12 +11,12 @@ #include "fu-asus-hid-struct.h" struct _FuAsusHidDevice { - FuHidDevice parent_instance; + FuHidrawDevice parent_instance; guint8 num_mcu; gulong child_added_id; }; -G_DEFINE_TYPE(FuAsusHidDevice, fu_asus_hid_device, FU_TYPE_HID_DEVICE) +G_DEFINE_TYPE(FuAsusHidDevice, fu_asus_hid_device, FU_TYPE_HIDRAW_DEVICE) #define FU_ASUS_HID_DEVICE_TIMEOUT 200 /* ms */ @@ -27,29 +27,23 @@ guint8 report, GError **error) { - FuHidDevice *hid_dev = FU_HID_DEVICE(self); - if (req != NULL) { - if (!fu_hid_device_set_report(hid_dev, - report, - req->data, - req->len, - FU_ASUS_HID_DEVICE_TIMEOUT, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error)) { - g_prefix_error(error, "failed to send packet: "); + if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), + req->data, + req->len, + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to send packet: "); return FALSE; } } if (res != NULL) { - if (!fu_hid_device_get_report(hid_dev, - report, - res->data, - res->len, - FU_ASUS_HID_DEVICE_TIMEOUT, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error)) { - g_prefix_error(error, "failed to receive packet: "); + if (!fu_hidraw_device_get_feature(FU_HIDRAW_DEVICE(self), + res->data, + res->len, + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to receive packet: "); return FALSE; } } @@ -60,16 +54,16 @@ static gboolean fu_asus_hid_device_init_seq(FuAsusHidDevice *self, GError **error) { - g_autoptr(FuStructAsusHidCommand) cmd = fu_struct_asus_hid_command_new(); + g_autoptr(FuStructAsusHidCommand) st = fu_struct_asus_hid_command_new(); - fu_struct_asus_hid_command_set_cmd(cmd, FU_ASUS_HID_COMMAND_INIT_SEQUENCE); + fu_struct_asus_hid_command_set_cmd(st, FU_ASUS_HID_COMMAND_INIT_SEQUENCE); if (!fu_asus_hid_device_transfer_feature(self, - cmd, + st->buf, NULL, FU_ASUS_HID_REPORT_ID_INFO, error)) { - g_prefix_error(error, "failed to initialize device: "); + g_prefix_error_literal(error, "failed to initialize device: "); return FALSE; } @@ -86,19 +80,41 @@ } static gboolean +fu_asus_hid_device_validate_descriptor(FuAsusHidDevice *self, GError **error) +{ + g_autoptr(FuHidDescriptor) descriptor = NULL; + g_autoptr(FuHidReport) report = NULL; + + descriptor = fu_hidraw_device_parse_descriptor(FU_HIDRAW_DEVICE(self), error); + if (descriptor == NULL) + return FALSE; + report = fu_hid_descriptor_find_report(descriptor, + error, + "usage-page", + 0xFF31, + "usage", + 0x76, + "collection", + 0x01, + NULL); + if (report == NULL) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean fu_asus_hid_device_probe(FuDevice *device, GError **error) { FuAsusHidDevice *self = FU_ASUS_HID_DEVICE(device); - fu_hid_device_set_interface(FU_HID_DEVICE(device), 0); - for (guint i = 0; i < self->num_mcu; i++) { g_autoptr(FuDevice) dev_tmp = fu_asus_hid_child_device_new(device, i); fu_device_add_child(device, dev_tmp); } - /* FuHidDevice->probe */ - return FU_DEVICE_CLASS(fu_asus_hid_device_parent_class)->probe(device, error); + return TRUE; } static gboolean @@ -106,14 +122,13 @@ { FuAsusHidDevice *self = FU_ASUS_HID_DEVICE(device); - /* HidDevice->setup */ - if (!FU_DEVICE_CLASS(fu_asus_hid_device_parent_class)->setup(device, error)) - return FALSE; - /* bootloader mode won't know about children */ if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) return TRUE; + if (!fu_asus_hid_device_validate_descriptor(self, error)) + return FALSE; + if (!fu_asus_hid_device_init_seq(self, error)) return FALSE; @@ -124,17 +139,17 @@ static gboolean fu_asus_hid_device_attach(FuDevice *device, FuProgress *progress, GError **error) { - g_autoptr(FuStructAsusFlashReset) cmd = fu_struct_asus_flash_reset_new(); + g_autoptr(FuStructAsusFlashReset) st = fu_struct_asus_flash_reset_new(); if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) return TRUE; if (!fu_asus_hid_device_transfer_feature(FU_ASUS_HID_DEVICE(device), - cmd, + st->buf, NULL, FU_ASUS_HID_REPORT_ID_FLASHING, error)) { - g_prefix_error(error, "failed to reset device: "); + g_prefix_error_literal(error, "failed to reset device: "); return FALSE; } @@ -147,91 +162,91 @@ fu_asus_hid_device_detach(FuDevice *device, FuProgress *progress, GError **error) { FuAsusHidDevice *self = FU_ASUS_HID_DEVICE(device); - g_autoptr(FuStructAsusPreUpdateCommand) cmd = fu_struct_asus_pre_update_command_new(); - g_autoptr(FuStructAsusHidResult) result = fu_struct_asus_hid_result_new(); + g_autoptr(FuStructAsusHidPreUpdateCommand) st = fu_struct_asus_hid_pre_update_command_new(); + g_autoptr(FuStructAsusHidResult) st_result = fu_struct_asus_hid_result_new(); guint32 previous_result; if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) return TRUE; - fu_struct_asus_hid_command_set_cmd(cmd, FU_ASUS_HID_COMMAND_PRE_UPDATE); - fu_struct_asus_hid_command_set_length(cmd, FU_STRUCT_ASUS_HID_RESULT_SIZE); + fu_struct_asus_hid_pre_update_command_set_cmd(st, FU_ASUS_HID_COMMAND_PRE_UPDATE); + fu_struct_asus_hid_pre_update_command_set_length(st, FU_STRUCT_ASUS_HID_RESULT_SIZE); if (!fu_asus_hid_device_transfer_feature(self, - cmd, - result, + st->buf, + st_result->buf, FU_ASUS_HID_REPORT_ID_INFO, error)) return FALSE; - // TODO save some bits from result here for data for next command - fu_struct_asus_hid_command_set_cmd(cmd, FU_ASUS_HID_COMMAND_PRE_UPDATE2); - fu_struct_asus_hid_command_set_length(cmd, 1); + /* TODO save some bits from result here for data for next command */ + fu_struct_asus_hid_pre_update_command_set_cmd(st, FU_ASUS_HID_COMMAND_PRE_UPDATE2); + fu_struct_asus_hid_pre_update_command_set_length(st, 1); if (!fu_asus_hid_device_transfer_feature(self, - cmd, - result, + st->buf, + st_result->buf, FU_ASUS_HID_REPORT_ID_INFO, error)) return FALSE; - // TODO save some bits from result here for data for next command + /* TODO save some bits from result here for data for next command */ previous_result = 0x1; - fu_struct_asus_hid_command_set_cmd(cmd, FU_ASUS_HID_COMMAND_PRE_UPDATE3); - fu_struct_asus_hid_command_set_length(cmd, 1); - if (!fu_struct_asus_pre_update_command_set_data(cmd, - (guint8 *)&previous_result, - sizeof(previous_result), - error)) + fu_struct_asus_hid_pre_update_command_set_cmd(st, FU_ASUS_HID_COMMAND_PRE_UPDATE3); + fu_struct_asus_hid_pre_update_command_set_length(st, 1); + if (!fu_struct_asus_hid_pre_update_command_set_data(st, + (guint8 *)&previous_result, + sizeof(previous_result), + error)) return FALSE; if (!fu_asus_hid_device_transfer_feature(self, - cmd, + st->buf, NULL, FU_ASUS_HID_REPORT_ID_INFO, error)) return FALSE; previous_result = 0x0; - fu_struct_asus_hid_command_set_cmd(cmd, FU_ASUS_HID_COMMAND_PRE_UPDATE4); - fu_struct_asus_hid_command_set_length(cmd, FU_STRUCT_ASUS_HID_RESULT_SIZE); - if (!fu_struct_asus_pre_update_command_set_data(cmd, - (guint8 *)&previous_result, - sizeof(previous_result), - error)) + fu_struct_asus_hid_pre_update_command_set_cmd(st, FU_ASUS_HID_COMMAND_PRE_UPDATE4); + fu_struct_asus_hid_pre_update_command_set_length(st, FU_STRUCT_ASUS_HID_RESULT_SIZE); + if (!fu_struct_asus_hid_pre_update_command_set_data(st, + (guint8 *)&previous_result, + sizeof(previous_result), + error)) return FALSE; if (!fu_asus_hid_device_transfer_feature(self, - cmd, - result, + st->buf, + st_result->buf, FU_ASUS_HID_REPORT_ID_INFO, error)) return FALSE; - // TODO save some bits from result here for data for next command + /* TODO save some bits from result here for data for next command */ previous_result = 0x2; - fu_struct_asus_hid_command_set_cmd(cmd, FU_ASUS_HID_COMMAND_PRE_UPDATE5); - fu_struct_asus_hid_command_set_length(cmd, 0x01); - if (!fu_struct_asus_pre_update_command_set_data(cmd, - (guint8 *)&previous_result, - sizeof(previous_result), - error)) + fu_struct_asus_hid_pre_update_command_set_cmd(st, FU_ASUS_HID_COMMAND_PRE_UPDATE5); + fu_struct_asus_hid_pre_update_command_set_length(st, 0x01); + if (!fu_struct_asus_hid_pre_update_command_set_data(st, + (guint8 *)&previous_result, + sizeof(previous_result), + error)) return FALSE; if (!fu_asus_hid_device_transfer_feature(self, - cmd, + st->buf, NULL, FU_ASUS_HID_REPORT_ID_INFO, error)) return FALSE; - /* Maybe this command unlocks for flashing mode? */ + /* maybe this command unlocks for flashing mode? */ previous_result = 0x0; - fu_struct_asus_hid_command_set_cmd(cmd, FU_ASUS_HID_COMMAND_PRE_UPDATE6); - fu_struct_asus_hid_command_set_length(cmd, 0x0); - if (!fu_struct_asus_pre_update_command_set_data(cmd, - (guint8 *)&previous_result, - sizeof(previous_result), - error)) + fu_struct_asus_hid_pre_update_command_set_cmd(st, FU_ASUS_HID_COMMAND_PRE_UPDATE6); + fu_struct_asus_hid_pre_update_command_set_length(st, 0x0); + if (!fu_struct_asus_hid_pre_update_command_set_data(st, + (guint8 *)&previous_result, + sizeof(previous_result), + error)) return FALSE; if (!fu_asus_hid_device_transfer_feature(self, - cmd, + st->buf, NULL, FU_ASUS_HID_REPORT_ID_INFO, error)) @@ -250,10 +265,10 @@ g_autoptr(GPtrArray) blocks = NULL; if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "device is not in bootloader mode"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "device is not in bootloader mode"); return NULL; } @@ -270,28 +285,28 @@ FuChunk *chk = g_ptr_array_index(blocks, i); const guint8 *buf; gsize bufsz = 0; - g_autoptr(FuStructAsusReadFlashCommand) cmd = + g_autoptr(FuStructAsusReadFlashCommand) st = fu_struct_asus_read_flash_command_new(); - g_autoptr(FuStructAsusReadFlashCommand) result = + g_autoptr(FuStructAsusReadFlashCommand) st_result = fu_struct_asus_read_flash_command_new(); - fu_struct_asus_read_flash_command_set_offset(cmd, offset); - fu_struct_asus_read_flash_command_set_datasz(cmd, fu_chunk_get_data_sz(chk)); + fu_struct_asus_read_flash_command_set_offset(st, offset); + fu_struct_asus_read_flash_command_set_datasz(st, fu_chunk_get_data_sz(chk)); if (!fu_asus_hid_device_transfer_feature(self, - cmd, - result, + st->buf, + st_result->buf, FU_ASUS_HID_REPORT_ID_FLASHING, error)) return NULL; - buf = fu_struct_asus_read_flash_command_get_data(result, &bufsz); + buf = fu_struct_asus_read_flash_command_get_data(st_result, &bufsz); if (!fu_memcpy_safe(fu_chunk_get_data_out(chk), fu_chunk_get_data_sz(chk), 0x0, buf, bufsz, 0x0, - fu_struct_asus_read_flash_command_get_datasz(result), + fu_struct_asus_read_flash_command_get_datasz(st_result), error)) return NULL; offset += fu_chunk_get_data_sz(chk); @@ -342,7 +357,7 @@ fu_asus_hid_device_init(FuAsusHidDevice *self) { /* TODO: automatic backup */ - // fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL); + /* fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL); */ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_set_remove_delay(FU_DEVICE(self), 10000); self->child_added_id = g_signal_connect(FU_DEVICE(self), diff -Nru fwupd-2.0.8/plugins/asus-hid/fu-asus-hid-device.h fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-device.h --- fwupd-2.0.8/plugins/asus-hid/fu-asus-hid-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,7 @@ #include #define FU_TYPE_ASUS_HID_DEVICE (fu_asus_hid_device_get_type()) -G_DECLARE_FINAL_TYPE(FuAsusHidDevice, fu_asus_hid_device, FU, ASUS_HID_DEVICE, FuHidDevice) +G_DECLARE_FINAL_TYPE(FuAsusHidDevice, fu_asus_hid_device, FU, ASUS_HID_DEVICE, FuHidrawDevice) gboolean fu_asus_hid_device_write_firmware(FuDevice *device, diff -Nru fwupd-2.0.8/plugins/asus-hid/fu-asus-hid-firmware.c fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-firmware.c --- fwupd-2.0.8/plugins/asus-hid/fu-asus-hid-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -33,20 +33,20 @@ static gboolean fu_asus_hid_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAsusHidFirmware *self = FU_ASUS_HID_FIRMWARE(firmware); - g_autoptr(GByteArray) desc = NULL; + g_autoptr(FuStructAsusHidDesc) st = NULL; g_autoptr(FuFirmware) img_payload = fu_firmware_new(); g_autoptr(GInputStream) stream_payload = NULL; - desc = fu_struct_asus_hid_desc_parse_stream(stream, FGA_OFFSET, error); - if (desc == NULL) + st = fu_struct_asus_hid_desc_parse_stream(stream, FGA_OFFSET, error); + if (st == NULL) return FALSE; - self->fga = fu_struct_asus_hid_desc_get_fga(desc); - self->product = fu_struct_asus_hid_desc_get_product(desc); - self->version = fu_struct_asus_hid_desc_get_version(desc); + self->fga = fu_struct_asus_hid_desc_get_fga(st); + self->product = fu_struct_asus_hid_desc_get_product(st); + self->version = fu_struct_asus_hid_desc_get_version(st); stream_payload = fu_partial_input_stream_new(stream, 0x2000, G_MAXSIZE, error); if (stream_payload == NULL) @@ -54,14 +54,13 @@ if (!fu_firmware_parse_stream(img_payload, stream_payload, 0x0, flags, error)) return FALSE; fu_firmware_set_id(img_payload, FU_FIRMWARE_ID_PAYLOAD); - fu_firmware_add_image(firmware, img_payload); - - return TRUE; + return fu_firmware_add_image(firmware, img_payload, error); } static void fu_asus_hid_firmware_init(FuAsusHidFirmware *self) { + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION); } static void diff -Nru fwupd-2.0.8/plugins/asus-hid/fu-asus-hid-plugin.c fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-plugin.c --- fwupd-2.0.8/plugins/asus-hid/fu-asus-hid-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/asus-hid/fu-asus-hid-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -30,6 +30,7 @@ fu_plugin_set_device_gtype_default(plugin, FU_TYPE_ASUS_HID_DEVICE); fu_context_add_quirk_key(ctx, "AsusHidNumMcu"); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_ASUS_HID_FIRMWARE); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); } static void diff -Nru fwupd-2.0.8/plugins/asus-hid/fu-asus-hid.rs fwupd-2.0.20/plugins/asus-hid/fu-asus-hid.rs --- fwupd-2.0.8/plugins/asus-hid/fu-asus-hid.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/asus-hid/fu-asus-hid.rs 2026-02-26 11:36:18.000000000 +0000 @@ -85,7 +85,7 @@ #[derive(Default, New)] #[repr(C, packed)] -struct FuStructAsusPreUpdateCommand { +struct FuStructAsusHidPreUpdateCommand { report_id: FuAsusHidReportId == Info, cmd: u32le, length: u8, diff -Nru fwupd-2.0.8/plugins/asus-hid/tests/asus-hid-setup.json fwupd-2.0.20/plugins/asus-hid/tests/asus-hid-setup.json --- fwupd-2.0.8/plugins/asus-hid/tests/asus-hid-setup.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/asus-hid/tests/asus-hid-setup.json 2026-02-26 11:36:18.000000000 +0000 @@ -1,163 +1,200 @@ { + "FwupdVersion": "2.0.12", "UsbDevices": [ { - "GType": "FuUsbDevice", - "PlatformId": "1-3", - "Created": "2024-10-07T02:18:32.385431Z", - "IdVendor": 2821, - "IdProduct": 6846, - "Device": 2, - "USB": 512, - "Manufacturer": 1, - "Product": 2, - "UsbConfigDescriptors": [ + "Created": "2025-06-08T02:44:08.862175Z", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:09:00.3/usb1/1-3/1-3:1.2/0003:0B05:1ABE.0003/hidraw/hidraw2", + "DeviceFile": "/dev/hidraw2", + "Subsystem": "hidraw", + "Vendor": 2821, + "Model": 6846, + "Events": [ { - "ConfigurationValue": 1 - } - ], - "UsbInterfaces": [ + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "hidraw" + }, { - "Length": 9, - "DescriptorType": 4, - "InterfaceClass": 3, - "InterfaceSubClass": 1, - "InterfaceProtocol": 1, - "Interface": 3, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 129, - "Interval": 1, - "MaxPacketSize": 64 - } - ] - }, - { - "Length": 9, - "DescriptorType": 4, - "InterfaceNumber": 1, - "InterfaceClass": 3, - "InterfaceSubClass": 1, - "InterfaceProtocol": 1, - "Interface": 1, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 130, - "Interval": 4, - "MaxPacketSize": 64 - } - ] - }, - { - "Length": 9, - "DescriptorType": 4, - "InterfaceNumber": 2, - "InterfaceClass": 3, - "InterfaceSubClass": 1, - "InterfaceProtocol": 1, - "Interface": 1, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 131, - "Interval": 1, - "MaxPacketSize": 64 - }, - { - "DescriptorType": 5, - "EndpointAddress": 4, - "Interval": 1, - "MaxPacketSize": 64 - } - ] - } - ], - "UsbEvents": [ + "Id": "GetSymlinkTarget:Attr=driver" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=244\nMINOR=2\nDEVNAME=hidraw2" + }, + { + "Id": "ReadProp:Key=DEVNAME", + "Data": "hidraw2" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=hid", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:09:00.3/usb1/1-3/1-3:1.2/0003:0B05:1ABE.0003" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "hid" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "asus_rog_ally" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=asus_rog_ally\nHID_ID=0003:00000B05:00001ABE\nHID_NAME=ASUSTeK Computer Inc. N-KEY Device\nHID_PHYS=usb-0000:09:00.3-3/input2\nHID_UNIQ=\nMODALIAS=hid:b0003g0001v00000B05p00001ABE" + }, { - "Id": "#d5a801ad", + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=HID_ID", + "Data": "0003:00000B05:00001ABE" + }, + { + "Id": "ReadProp:Key=HID_NAME", + "Data": "ASUSTeK Computer Inc. N-KEY Device" + }, + { + "Id": "ReadProp:Key=HID_UNIQ", + "Data": "" + }, + { + "Id": "ReadProp:Key=HID_PHYS", + "Data": "usb-0000:09:00.3-3/input2" + }, + { + "Id": "GetBackendParent:Subsystem=usb:usb_device", + "GType": "FuUsbDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:09:00.3/usb1/1-3", + "PhysicalId": "1-3" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", "Data": "usb" }, { - "Id": "#ad8c58d3", + "Id": "GetSymlinkTarget:Attr=driver", "Data": "usb" }, { - "Id": "#1075ed5c", + "Id": "ReadProp:Key=DEVTYPE", "Data": "usb_device" }, { - "Id": "#bddbca22", + "Id": "ReadAttr:Attr=uevent", "Data": "MAJOR=189\nMINOR=2\nDEVNAME=bus/usb/001/003\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=b05/1abe/2\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=003" }, { - "Id": "#d432c663", + "Id": "ReadProp:Key=DEVNAME", "Data": "bus/usb/001/003" }, { - "Id": "#9b895db2" + "Id": "ReadAttr:Attr=vendor" }, { - "Id": "#66f3e150" + "Id": "ReadAttr:Attr=device" }, { - "Id": "#d410b6c7" + "Id": "ReadAttr:Attr=class" }, { - "Id": "#1075ed5c", + "Id": "ReadProp:Key=DEVTYPE", "Data": "usb_device" }, { - "Id": "#4693935e", + "Id": "ReadProp:Key=BUSNUM", "Data": "001" }, { - "Id": "#bddbca22", + "Id": "ReadAttr:Attr=uevent", "Data": "MAJOR=189\nMINOR=2\nDEVNAME=bus/usb/001/003\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=b05/1abe/2\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=003" }, { - "Id": "#1ab3ae0a", + "Id": "ReadProp:Key=DEVNUM", "Data": "003" }, { - "Id": "#1fcf122d", - "Data": "Ti1LRVkgRGV2aWNlAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + "Id": "LoadDescriptor:basename=descriptors", + "Data": "EgEAAgAAAEAFC74aAgABAgABCQJbAAMBAOAyCQQAAAEDAQEDCSEQAQABIlMABwWBA0AAAQkEAQABAwEBAQkhEAEAASIwAAcFggNAAAQJBAIAAgMBAQEJIRABAAEipwAHBYMDQAABBwUEA0AAAQ==" + }, + { + "Id": "LoadDescriptor:basename=bos_descriptors" + }, + { + "Id": "ReadProp:Key=HID_FIRMWARE_VERSION" + }, + { + "Id": "Ioctl:Request=0x80044801,Data=AAAAAA==,Length=0x4", + "DataOut": "pwAAAA==" + }, + { + "Id": "Ioctl:Request=0x90044802,Data=pwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0x1004", + "DataOut": "pwAAAAYx/wl2oQGFWhkAKv8AFQAm/wB1CJUFgQAZACr/ABUAJv8AdQiVP7EAwAUMCQGhAYUCGQAqPAIVACY8AnUQlQKBAMAGMf8JeaEBhV0ZACr/ABUAJv8AdQiVH4EAGQAq/wAVACb/AHUIlT+RABkAKv8AFQAm/wB1CJU/sQDABjH/CYChAYVeGQAq/wAVACb/AHUIlQWBABkAKv8AFQAm/wB1CJU/sQDAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" }, { - "Id": "#16f4e7da", - "Data": "WtEBAQEA" + "Id": "Ioctl:Request=0xc0064806,Data=WtEBAQEA,Length=0x6", + "DataOut": "WtEBAQEA" }, { - "Id": "#a3bf98cf", - "Data": "WkFTVVNUZWNoLkluYy4AAA==" + "Id": "Ioctl:Request=0xc0104806,Data=WkFTVVNUZWNoLkluYy4AAA==,Length=0x10", + "DataOut": "WkFTVVNUZWNoLkluYy4AAA==" }, { - "Id": "#ff7d0ded", - "Data": "WkFTVVNUZWNoLkluYy4AAGUAdgBpAGMAZQAAAAAAAAA=" + "Id": "Ioctl:Request=0xc0204807,Data=WgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0x20", + "DataOut": "WkFTVVNUZWNoLkluYy4AAFJDNzFMTS4zMTkAAgAYAAA=" }, { - "Id": "#66f65422", - "Data": "WgUDMQAg" + "Id": "Ioctl:Request=0xc0064806,Data=WgUDMQAg,Length=0x6", + "DataOut": "WgUDMQAg" }, { - "Id": "#ff7d0ded", - "Data": "WgUDMQAaE0ZHQTgwMTAwLlJDNzFMUy4zMTEAAAAAAAA=" + "Id": "Ioctl:Request=0xc0204807,Data=WgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0x20", + "DataOut": "WgUDMQAaE0ZHQTgwMTAwLlJDNzFMUy4zMTkAAgAYAAA=" }, { - "Id": "#a3bf98cf", - "Data": "WkFTVVNUZWNoLkluYy4AAA==" + "Id": "Ioctl:Request=0xc0104806,Data=WkFTVVNUZWNoLkluYy4AAA==,Length=0x10", + "DataOut": "WkFTVVNUZWNoLkluYy4AAA==" }, { - "Id": "#ff7d0ded", - "Data": "WkFTVVNUZWNoLkluYy4AAFJDNzFMUy4zMTEAAAAAAAA=" + "Id": "Ioctl:Request=0xc0204807,Data=WgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0x20", + "DataOut": "WkFTVVNUZWNoLkluYy4AAFJDNzFMUy4zMTkAAgAYAAA=" }, { - "Id": "#2fa92714", - "Data": "WgUEMQAg" + "Id": "Ioctl:Request=0xc0064806,Data=WgUEMQAg,Length=0x6", + "DataOut": "WgUEMQAg" }, { - "Id": "#ff7d0ded", - "Data": "WgUEMQAaE0ZHQTgwMTAwLlJDNzFMTS4zMTEAAAAAAAA=" + "Id": "Ioctl:Request=0xc0204807,Data=WgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0x20", + "DataOut": "WgUEMQAaE0ZHQTgwMTAwLlJDNzFMTS4zMTkAAgAYAAA=" } ] } diff -Nru fwupd-2.0.8/plugins/asus-hid/tests/asus-hid.json fwupd-2.0.20/plugins/asus-hid/tests/asus-hid.json --- fwupd-2.0.8/plugins/asus-hid/tests/asus-hid.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/asus-hid/tests/asus-hid.json 2026-02-26 11:36:18.000000000 +0000 @@ -6,21 +6,20 @@ "emulation-file": "@enumeration_datadir@/asus-hid-setup.json", "components": [ { - "version": "0.2", "guids": [ - "40e5a299-fe27-5e3b-a4a3-6649aeef4875" + "45b53fed-74ce-542f-aaca-9ba6783c89be" ] }, { - "version": "311", + "version": "319", "guids": [ - "f7765461-3ff4-52a0-a4d8-d782b1f885ab" + "ae89f87e-0e1a-563c-afd2-f300c04676aa" ] }, { - "version": "311", + "version": "319", "guids": [ - "545ddbd6-f041-5d86-bfd7-4764193e0a17" + "6b00f0ca-abb2-5cc4-bb85-b7392998d51e" ] } ] diff -Nru fwupd-2.0.8/plugins/ata/fu-ata-device.c fwupd-2.0.20/plugins/ata/fu-ata-device.c --- fwupd-2.0.8/plugins/ata/fu-ata-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ata/fu-ata-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,7 @@ #define FU_ATA_IDENTIFY_SIZE 512 /* bytes */ #define FU_ATA_BLOCK_SIZE 512 /* bytes */ -struct ata_tf { +typedef struct { guint8 dev; guint8 command; guint8 error; @@ -24,7 +24,7 @@ guint8 lbal; guint8 lbam; guint8 lbah; -}; +} FuAtaDeviceTf; #define ATA_USING_LBA (1 << 6) #define ATA_STAT_DRQ (1 << 3) @@ -329,7 +329,7 @@ gboolean has_oui_quirk = FALSE; guint16 xfer_min = 1; guint16 xfer_max = 0xffff; - guint16 id[FU_ATA_IDENTIFY_SIZE / 2]; + guint16 id[FU_ATA_IDENTIFY_SIZE / 2] = {0}; g_autofree gchar *name = NULL; g_autofree gchar *sku = NULL; @@ -478,7 +478,7 @@ } static guint64 -fu_ata_device_tf_to_pack_id(struct ata_tf *tf) +fu_ata_device_tf_to_pack_id(FuAtaDeviceTf *tf) { guint32 lba24 = (tf->lbah << 16) | (tf->lbam << 8) | (tf->lbal); guint32 lbah = tf->dev & 0x0f; @@ -514,7 +514,7 @@ static gboolean fu_ata_device_command(FuAtaDevice *self, - struct ata_tf *tf, + FuAtaDeviceTf *tf, gint dxfer_direction, guint timeout_ms, guint8 *dxferp, @@ -647,7 +647,7 @@ fu_ata_device_setup(FuDevice *device, GError **error) { FuAtaDevice *self = FU_ATA_DEVICE(device); - struct ata_tf tf = {0x0}; + FuAtaDeviceTf tf = {0x0}; guint8 id[FU_ATA_IDENTIFY_SIZE] = {0x0}; /* get ID block */ @@ -655,7 +655,7 @@ tf.command = ATA_OP_IDENTIFY; tf.nsect = 1; /* 512 bytes */ if (!fu_ata_device_command(self, &tf, SG_DXFER_FROM_DEV, 1000, id, sizeof(id), error)) { - g_prefix_error(error, "failed to IDENTIFY: "); + g_prefix_error_literal(error, "failed to IDENTIFY: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, "IDENTIFY", id, sizeof(id)); @@ -670,7 +670,7 @@ fu_ata_device_activate(FuDevice *device, FuProgress *progress, GError **error) { FuAtaDevice *self = FU_ATA_DEVICE(device); - struct ata_tf tf = {0x0}; + FuAtaDeviceTf tf = {0x0}; /* flush cache and put drive in standby to prepare to activate */ tf.dev = ATA_USING_LBA; @@ -682,7 +682,7 @@ NULL, 0, error)) { - g_prefix_error(error, "failed to flush cache immediate: "); + g_prefix_error_literal(error, "failed to flush cache immediate: "); return FALSE; } tf.command = ATA_OP_STANDBY_IMMEDIATE; @@ -693,7 +693,7 @@ NULL, 0, error)) { - g_prefix_error(error, "failed to standby immediate: "); + g_prefix_error_literal(error, "failed to standby immediate: "); return FALSE; } @@ -708,7 +708,7 @@ NULL, 0, error)) { - g_prefix_error(error, "failed to activate firmware: "); + g_prefix_error_literal(error, "failed to activate firmware: "); return FALSE; } @@ -724,7 +724,7 @@ guint32 data_sz, GError **error) { - struct ata_tf tf = {0x0}; + FuAtaDeviceTf tf = {0x0}; guint32 block_count = data_sz / FU_ATA_BLOCK_SIZE; guint32 buffer_offset = addr / FU_ATA_BLOCK_SIZE; @@ -887,7 +887,7 @@ } static void -fu_ata_device_set_progress(FuDevice *self, FuProgress *progress) +fu_ata_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -911,7 +911,7 @@ fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_SIGNED); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS); fu_device_set_summary(FU_DEVICE(self), "ATA drive"); - fu_device_add_icon(FU_DEVICE(self), "drive-harddisk"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_DRIVE_HARDDISK); fu_device_add_protocol(FU_DEVICE(self), "org.t13.ata"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); diff -Nru fwupd-2.0.8/plugins/ata/meson.build fwupd-2.0.20/plugins/ata/meson.build --- fwupd-2.0.8/plugins/ata/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ata/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginAta"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -39,6 +40,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', @@ -46,4 +48,3 @@ ) test('ata-self-test', e, env: env) # added to installed-tests endif -endif diff -Nru fwupd-2.0.8/plugins/aver-hid/README.md fwupd-2.0.20/plugins/aver-hid/README.md --- fwupd-2.0.8/plugins/aver-hid/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/aver-hid/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -50,10 +50,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.9.2`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Pierce Wang: @AVer-V001598 diff -Nru fwupd-2.0.8/plugins/aver-hid/fu-aver-hid-device.c fwupd-2.0.20/plugins/aver-hid/fu-aver-hid-device.c --- fwupd-2.0.8/plugins/aver-hid/fu-aver-hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/aver-hid/fu-aver-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -35,7 +35,7 @@ FU_AVER_HID_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, error)) { - g_prefix_error(error, "failed to send packet: "); + g_prefix_error_literal(error, "failed to send packet: "); return FALSE; } } @@ -47,13 +47,9 @@ FU_AVER_HID_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, error)) { - g_prefix_error(error, "failed to receive packet: "); + g_prefix_error_literal(error, "failed to receive packet: "); return FALSE; } - g_debug("custom-isp-cmd: %s [0x%x]", - fu_aver_hid_custom_isp_cmd_to_string( - fu_struct_aver_hid_res_isp_get_custom_isp_cmd(res)), - fu_struct_aver_hid_res_isp_get_custom_isp_cmd(res)); } return TRUE; } @@ -61,21 +57,24 @@ static gboolean fu_aver_hid_device_ensure_status(FuAverHidDevice *self, GError **error) { - g_autoptr(GByteArray) req = fu_struct_aver_hid_req_isp_new(); - g_autoptr(GByteArray) res = fu_struct_aver_hid_res_isp_status_new(); + g_autoptr(FuStructAverHidReqIsp) st_req = fu_struct_aver_hid_req_isp_new(); + g_autoptr(FuStructAverHidResIspStatus) st_res = fu_struct_aver_hid_res_isp_status_new(); - fu_struct_aver_hid_req_isp_set_custom_isp_cmd(req, FU_AVER_HID_CUSTOM_ISP_CMD_STATUS); - if (!fu_aver_hid_device_transfer(self, req, res, error)) + fu_struct_aver_hid_req_isp_set_custom_isp_cmd(st_req, FU_AVER_HID_CUSTOM_ISP_CMD_STATUS); + if (!fu_aver_hid_device_transfer(self, st_req->buf, st_res->buf, error)) return FALSE; - if (!fu_struct_aver_hid_res_isp_status_validate(res->data, res->len, 0x0, error)) + if (!fu_struct_aver_hid_res_isp_status_validate(st_res->buf->data, + st_res->buf->len, + 0x0, + error)) return FALSE; - if (fu_struct_aver_hid_res_isp_status_get_status(res) == FU_AVER_HID_STATUS_BUSY) { + if (fu_struct_aver_hid_res_isp_status_get_status(st_res) == FU_AVER_HID_STATUS_BUSY) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "device has status %s", fu_aver_hid_status_to_string( - fu_struct_aver_hid_res_isp_status_get_status(res))); + fu_struct_aver_hid_res_isp_status_get_status(st_res))); return FALSE; } return TRUE; @@ -85,11 +84,13 @@ fu_aver_hid_device_ensure_version(FuAverHidDevice *self, GError **error) { g_autofree gchar *ver = NULL; - g_autoptr(GByteArray) req = fu_struct_aver_hid_req_device_version_new(); - g_autoptr(GByteArray) res = fu_struct_aver_hid_res_device_version_new(); + g_autoptr(FuStructAverHidReqDeviceVersion) st_req = + fu_struct_aver_hid_req_device_version_new(); + g_autoptr(FuStructAverHidResDeviceVersion) st_res = + fu_struct_aver_hid_res_device_version_new(); g_autoptr(GError) error_local = NULL; - if (!fu_aver_hid_device_transfer(self, req, res, &error_local)) { + if (!fu_aver_hid_device_transfer(self, st_req->buf, st_res->buf, &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT)) { g_debug("ignoring %s", error_local->message); fu_device_set_version(FU_DEVICE(self), "0.0.0000.00"); @@ -98,9 +99,12 @@ g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } - if (!fu_struct_aver_hid_res_device_version_validate(res->data, res->len, 0x0, error)) + if (!fu_struct_aver_hid_res_device_version_validate(st_res->buf->data, + st_res->buf->len, + 0x0, + error)) return FALSE; - ver = fu_strsafe((const gchar *)fu_struct_aver_hid_res_device_version_get_ver(res, NULL), + ver = fu_strsafe((const gchar *)fu_struct_aver_hid_res_device_version_get_ver(st_res, NULL), FU_STRUCT_AVER_HID_RES_DEVICE_VERSION_SIZE_VER); fu_device_set_version(FU_DEVICE(self), ver); return TRUE; @@ -131,7 +135,7 @@ fu_aver_hid_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) firmware = fu_aver_hid_firmware_new(); @@ -151,8 +155,10 @@ fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { g_autoptr(FuChunk) chk = NULL; - g_autoptr(GByteArray) req = fu_struct_aver_hid_req_isp_file_dnload_new(); - g_autoptr(GByteArray) res = fu_struct_aver_hid_res_isp_status_new(); + g_autoptr(FuStructAverHidReqIspFileDnload) st_req = + fu_struct_aver_hid_req_isp_file_dnload_new(); + g_autoptr(FuStructAverHidResIspStatus) st_res = + fu_struct_aver_hid_res_isp_status_new(); /* prepare chunk */ chk = fu_chunk_array_index(chunks, i, error); @@ -162,15 +168,15 @@ /* copy in payload */ if (fu_device_has_private_flag(FU_DEVICE(self), FU_AVER_HID_FLAG_DUAL_ISP)) { fu_struct_aver_hid_req_isp_file_dnload_set_custom_isp_cmd( - req, + st_req, FU_AVER_HID_CUSTOM_ISP_CMD_ALL_FILE_DNLOAD); } else { fu_struct_aver_hid_req_isp_file_dnload_set_custom_isp_cmd( - req, + st_req, FU_AVER_HID_CUSTOM_ISP_CMD_FILE_DNLOAD); } - if (!fu_memcpy_safe(req->data, - req->len, + if (!fu_memcpy_safe(st_req->buf->data, + st_req->buf->len, FU_STRUCT_AVER_HID_REQ_ISP_FILE_DNLOAD_OFFSET_DATA, /* dst */ fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), @@ -182,21 +188,24 @@ /* resize the last packet */ if ((i == (fu_chunk_array_length(chunks) - 1)) && (fu_chunk_get_data_sz(chk) < FU_STRUCT_AVER_HID_REQ_ISP_FILE_DNLOAD_SIZE_DATA)) - fu_byte_array_set_size(req, 3 + fu_chunk_get_data_sz(chk), 0x0); - if (!fu_aver_hid_device_transfer(self, req, res, error)) + fu_byte_array_set_size(st_req->buf, 3 + fu_chunk_get_data_sz(chk), 0x0); + if (!fu_aver_hid_device_transfer(self, st_req->buf, st_res->buf, error)) return FALSE; - if (!fu_struct_aver_hid_res_isp_status_validate(res->data, res->len, 0x0, error)) + if (!fu_struct_aver_hid_res_isp_status_validate(st_res->buf->data, + st_res->buf->len, + 0x0, + error)) return FALSE; /* invalid chunk */ - if (fu_struct_aver_hid_res_isp_status_get_status(res) == + if (fu_struct_aver_hid_res_isp_status_get_status(st_res) == FU_AVER_HID_STATUS_FILEERR) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "device has status %s", fu_aver_hid_status_to_string( - fu_struct_aver_hid_res_isp_status_get_status(res))); + fu_struct_aver_hid_res_isp_status_get_status(st_res))); return FALSE; } @@ -212,21 +221,24 @@ fu_aver_hid_device_wait_for_ready_cb(FuDevice *device, gpointer user_data, GError **error) { FuAverHidDevice *self = FU_AVER_HID_DEVICE(device); - g_autoptr(GByteArray) req = fu_struct_aver_hid_req_isp_new(); - g_autoptr(GByteArray) res = fu_struct_aver_hid_res_isp_status_new(); + g_autoptr(FuStructAverHidReqIsp) st_req = fu_struct_aver_hid_req_isp_new(); + g_autoptr(FuStructAverHidResIspStatus) st_res = fu_struct_aver_hid_res_isp_status_new(); - fu_struct_aver_hid_req_isp_set_custom_isp_cmd(req, FU_AVER_HID_CUSTOM_ISP_CMD_STATUS); - if (!fu_aver_hid_device_transfer(self, req, res, error)) + fu_struct_aver_hid_req_isp_set_custom_isp_cmd(st_req, FU_AVER_HID_CUSTOM_ISP_CMD_STATUS); + if (!fu_aver_hid_device_transfer(self, st_req->buf, st_res->buf, error)) return FALSE; - if (!fu_struct_aver_hid_res_isp_status_validate(res->data, res->len, 0x0, error)) + if (!fu_struct_aver_hid_res_isp_status_validate(st_res->buf->data, + st_res->buf->len, + 0x0, + error)) return FALSE; - if (fu_struct_aver_hid_res_isp_status_get_status(res) != FU_AVER_HID_STATUS_READY) { + if (fu_struct_aver_hid_res_isp_status_get_status(st_res) != FU_AVER_HID_STATUS_READY) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "device has status %s", fu_aver_hid_status_to_string( - fu_struct_aver_hid_res_isp_status_get_status(res))); + fu_struct_aver_hid_res_isp_status_get_status(st_res))); return FALSE; } return TRUE; @@ -238,24 +250,28 @@ const gchar *name, GError **error) { - g_autoptr(GByteArray) req = fu_struct_aver_hid_req_isp_file_start_new(); - g_autoptr(GByteArray) res = fu_struct_aver_hid_res_isp_status_new(); + g_autoptr(FuStructAverHidReqIspFileStart) st_req = + fu_struct_aver_hid_req_isp_file_start_new(); + g_autoptr(FuStructAverHidResIspStatus) st_res = fu_struct_aver_hid_res_isp_status_new(); if (fu_device_has_private_flag(FU_DEVICE(self), FU_AVER_HID_FLAG_DUAL_ISP)) { fu_struct_aver_hid_req_isp_file_start_set_custom_isp_cmd( - req, + st_req, FU_AVER_HID_CUSTOM_ISP_CMD_ALL_FILE_START); } else { fu_struct_aver_hid_req_isp_file_start_set_custom_isp_cmd( - req, + st_req, FU_AVER_HID_CUSTOM_ISP_CMD_FILE_START); } - if (!fu_struct_aver_hid_req_isp_file_start_set_file_name(req, name, error)) + if (!fu_struct_aver_hid_req_isp_file_start_set_file_name(st_req, name, error)) return FALSE; - fu_struct_aver_hid_req_isp_file_start_set_file_size(req, sz); - if (!fu_aver_hid_device_transfer(self, req, res, error)) + fu_struct_aver_hid_req_isp_file_start_set_file_size(st_req, sz); + if (!fu_aver_hid_device_transfer(self, st_req->buf, st_res->buf, error)) return FALSE; - if (!fu_struct_aver_hid_res_isp_status_validate(res->data, res->len, 0x0, error)) + if (!fu_struct_aver_hid_res_isp_status_validate(st_res->buf->data, + st_res->buf->len, + 0x0, + error)) return FALSE; return TRUE; } @@ -263,25 +279,28 @@ static gboolean fu_aver_hid_device_isp_file_end(FuAverHidDevice *self, gsize sz, const gchar *name, GError **error) { - g_autoptr(GByteArray) req = fu_struct_aver_hid_req_isp_file_end_new(); - g_autoptr(GByteArray) res = fu_struct_aver_hid_res_isp_status_new(); + g_autoptr(FuStructAverHidReqIspFileEnd) st_req = fu_struct_aver_hid_req_isp_file_end_new(); + g_autoptr(FuStructAverHidResIspStatus) st_res = fu_struct_aver_hid_res_isp_status_new(); if (fu_device_has_private_flag(FU_DEVICE(self), FU_AVER_HID_FLAG_DUAL_ISP)) { fu_struct_aver_hid_req_isp_file_end_set_custom_isp_cmd( - req, + st_req, FU_AVER_HID_CUSTOM_ISP_CMD_ALL_FILE_END); } else { fu_struct_aver_hid_req_isp_file_end_set_custom_isp_cmd( - req, + st_req, FU_AVER_HID_CUSTOM_ISP_CMD_FILE_END); } - if (!fu_struct_aver_hid_req_isp_file_end_set_file_name(req, name, error)) + if (!fu_struct_aver_hid_req_isp_file_end_set_file_name(st_req, name, error)) return FALSE; - fu_struct_aver_hid_req_isp_file_end_set_end_flag(req, 1); - fu_struct_aver_hid_req_isp_file_end_set_file_size(req, sz); - if (!fu_aver_hid_device_transfer(self, req, res, error)) - return FALSE; - if (!fu_struct_aver_hid_res_isp_status_validate(res->data, res->len, 0x0, error)) + fu_struct_aver_hid_req_isp_file_end_set_end_flag(st_req, 1); + fu_struct_aver_hid_req_isp_file_end_set_file_size(st_req, sz); + if (!fu_aver_hid_device_transfer(self, st_req->buf, st_res->buf, error)) + return FALSE; + if (!fu_struct_aver_hid_res_isp_status_validate(st_res->buf->data, + st_res->buf->len, + 0x0, + error)) return FALSE; return TRUE; } @@ -290,22 +309,23 @@ fu_aver_hid_device_wait_for_untar_cb(FuDevice *device, gpointer user_data, GError **error) { FuAverHidDevice *self = FU_AVER_HID_DEVICE(device); - g_autoptr(GByteArray) req = fu_struct_aver_hid_req_isp_file_end_new(); - g_autoptr(GByteArray) res = fu_struct_aver_hid_res_isp_status_new(); + g_autoptr(FuStructAverHidReqIspFileEnd) st_req = fu_struct_aver_hid_req_isp_file_end_new(); + g_autoptr(FuStructAverHidResIspStatus) st_res = fu_struct_aver_hid_res_isp_status_new(); - fu_struct_aver_hid_req_isp_set_custom_isp_cmd(req, FU_AVER_HID_CUSTOM_ISP_CMD_STATUS); - if (!fu_aver_hid_device_transfer(self, req, res, error)) + fu_struct_aver_hid_req_isp_file_end_set_custom_isp_cmd(st_req, + FU_AVER_HID_CUSTOM_ISP_CMD_STATUS); + if (!fu_aver_hid_device_transfer(self, st_req->buf, st_res->buf, error)) return FALSE; g_info("isp status: %s", - fu_aver_hid_status_to_string(fu_struct_aver_hid_res_isp_status_get_status(res))); - if (fu_struct_aver_hid_res_isp_status_get_status(res) != FU_AVER_HID_STATUS_WAITUSR) { + fu_aver_hid_status_to_string(fu_struct_aver_hid_res_isp_status_get_status(st_res))); + if (fu_struct_aver_hid_res_isp_status_get_status(st_res) != FU_AVER_HID_STATUS_WAITUSR) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "device has status %s", fu_aver_hid_status_to_string( - fu_struct_aver_hid_res_isp_status_get_status(res))); + fu_struct_aver_hid_res_isp_status_get_status(st_res))); return FALSE; } return TRUE; @@ -314,21 +334,24 @@ static gboolean fu_aver_hid_device_isp_start(FuAverHidDevice *self, GError **error) { - g_autoptr(GByteArray) req = fu_struct_aver_hid_req_isp_new(); - g_autoptr(GByteArray) res = fu_struct_aver_hid_res_isp_status_new(); + g_autoptr(FuStructAverHidReqIspStart) st_req = fu_struct_aver_hid_req_isp_start_new(); + g_autoptr(FuStructAverHidResIspStatus) st_res = fu_struct_aver_hid_res_isp_status_new(); if (fu_device_has_private_flag(FU_DEVICE(self), FU_AVER_HID_FLAG_DUAL_ISP)) { fu_struct_aver_hid_req_isp_start_set_custom_isp_cmd( - req, + st_req, FU_AVER_HID_CUSTOM_ISP_CMD_ALL_START); } else { fu_struct_aver_hid_req_isp_start_set_custom_isp_cmd( - req, + st_req, FU_AVER_HID_CUSTOM_ISP_CMD_START); } - if (!fu_aver_hid_device_transfer(self, req, res, error)) + if (!fu_aver_hid_device_transfer(self, st_req->buf, st_res->buf, error)) return FALSE; - if (!fu_struct_aver_hid_res_isp_status_validate(res->data, res->len, 0x0, error)) + if (!fu_struct_aver_hid_res_isp_status_validate(st_res->buf->data, + st_res->buf->len, + 0x0, + error)) return FALSE; return TRUE; } @@ -336,9 +359,10 @@ static gboolean fu_aver_hid_device_isp_reboot(FuAverHidDevice *self, GError **error) { - g_autoptr(GByteArray) req = fu_struct_aver_hid_req_isp_new(); - fu_struct_aver_hid_req_isp_set_custom_isp_cmd(req, FU_AVER_HID_CUSTOM_ISP_CMD_ISP_REBOOT); - return fu_aver_hid_device_transfer(self, req, NULL, error); + g_autoptr(FuStructAverHidReqIsp) st_req = fu_struct_aver_hid_req_isp_new(); + fu_struct_aver_hid_req_isp_set_custom_isp_cmd(st_req, + FU_AVER_HID_CUSTOM_ISP_CMD_ISP_REBOOT); + return fu_aver_hid_device_transfer(self, st_req->buf, NULL, error); } static gboolean @@ -346,14 +370,14 @@ { FuAverHidDevice *self = FU_AVER_HID_DEVICE(device); FuProgress *progress = FU_PROGRESS(user_data); - g_autoptr(GByteArray) req = fu_struct_aver_hid_req_isp_new(); - g_autoptr(GByteArray) res = fu_struct_aver_hid_res_isp_status_new(); + g_autoptr(FuStructAverHidReqIsp) st_req = fu_struct_aver_hid_req_isp_new(); + g_autoptr(FuStructAverHidResIspStatus) st_res = fu_struct_aver_hid_res_isp_status_new(); - fu_struct_aver_hid_req_isp_set_custom_isp_cmd(req, FU_AVER_HID_CUSTOM_ISP_CMD_STATUS); - if (!fu_aver_hid_device_transfer(self, req, res, error)) + fu_struct_aver_hid_req_isp_set_custom_isp_cmd(st_req, FU_AVER_HID_CUSTOM_ISP_CMD_STATUS); + if (!fu_aver_hid_device_transfer(self, st_req->buf, st_res->buf, error)) return FALSE; - if (fu_struct_aver_hid_res_isp_status_get_status(res) == FU_AVER_HID_STATUS_ISPING) { - guint8 percentage = fu_struct_aver_hid_res_isp_status_get_progress(res); + if (fu_struct_aver_hid_res_isp_status_get_status(st_res) == FU_AVER_HID_STATUS_ISPING) { + guint8 percentage = fu_struct_aver_hid_res_isp_status_get_progress(st_res); if (percentage < 100) fu_progress_set_percentage(progress, percentage); g_set_error(error, @@ -361,16 +385,16 @@ FWUPD_ERROR_BUSY, "device has status %s", fu_aver_hid_status_to_string( - fu_struct_aver_hid_res_isp_status_get_status(res))); + fu_struct_aver_hid_res_isp_status_get_status(st_res))); return FALSE; } - if (fu_struct_aver_hid_res_isp_status_get_status(res) != FU_AVER_HID_STATUS_REBOOT) { + if (fu_struct_aver_hid_res_isp_status_get_status(st_res) != FU_AVER_HID_STATUS_REBOOT) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "device has status %s", fu_aver_hid_status_to_string( - fu_struct_aver_hid_res_isp_status_get_status(res))); + fu_struct_aver_hid_res_isp_status_get_status(st_res))); return FALSE; } return TRUE; @@ -483,7 +507,7 @@ } static void -fu_aver_hid_device_set_progress(FuDevice *self, FuProgress *progress) +fu_aver_hid_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/aver-hid/fu-aver-hid-firmware.c fwupd-2.0.20/plugins/aver-hid/fu-aver-hid-firmware.c --- fwupd-2.0.8/plugins/aver-hid/fu-aver-hid-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/aver-hid/fu-aver-hid-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -33,7 +33,7 @@ static gboolean fu_aver_hid_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuArchive) archive = NULL; diff -Nru fwupd-2.0.8/plugins/aver-hid/fu-aver-hid-plugin.c fwupd-2.0.20/plugins/aver-hid/fu-aver-hid-plugin.c --- fwupd-2.0.8/plugins/aver-hid/fu-aver-hid-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/aver-hid/fu-aver-hid-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -20,12 +20,14 @@ static void fu_aver_hid_plugin_init(FuAverHidPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void fu_aver_hid_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_AVER_HID_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_AVER_SAFEISP_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_AVER_HID_FIRMWARE); diff -Nru fwupd-2.0.8/plugins/aver-hid/fu-aver-hid.rs fwupd-2.0.20/plugins/aver-hid/fu-aver-hid.rs --- fwupd-2.0.8/plugins/aver-hid/fu-aver-hid.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/aver-hid/fu-aver-hid.rs 2026-02-26 11:36:18.000000000 +0000 @@ -16,7 +16,7 @@ Stop, } -#[derive(ToString)] +#[repr(u8)] enum FuAverHidCustomIspCmd { Status = 0x01, FileStart, @@ -35,7 +35,7 @@ AllStart, } -#[derive(ToString)] +#[repr(u8)] enum FuAverSafeispCustomCmd { GetVersion = 0x14, Support = 0x29, @@ -72,8 +72,8 @@ struct FuStructAverHidReqIsp { report_id_custom_command: u8 == 0x08, custom_cmd_isp: u8 == 0x10, - custom_isp_cmd: u8, - data: [u8; 508] = 0xFF, + custom_isp_cmd: FuAverHidCustomIspCmd, + data: [u8; 508] = [0xFF; 508], end: u8 == 0x00, } @@ -82,11 +82,11 @@ struct FuStructAverHidReqIspFileStart { report_id_custom_command: u8 == 0x08, custom_cmd_isp: u8 == 0x10, - custom_isp_cmd: u8, + custom_isp_cmd: FuAverHidCustomIspCmd, file_name: [char; 52], file_size: u32le, free_space: u32le, - _reserved: [u8; 448] = 0xFF, + _reserved: [u8; 448] = [0xFF; 448], end: u8 == 0x00, } @@ -95,8 +95,8 @@ struct FuStructAverHidReqIspFileDnload { report_id_custom_command: u8 == 0x08, custom_cmd_isp: u8 == 0x10, - custom_isp_cmd: u8, - data: [u8; 508] = 0xFF, + custom_isp_cmd: FuAverHidCustomIspCmd, + data: [u8; 508] = [0xFF; 508], } #[derive(Setters, Getters, New, Default)] @@ -104,23 +104,23 @@ struct FuStructAverHidReqIspFileEnd { report_id_custom_command: u8 == 0x08, custom_cmd_isp: u8 == 0x10, - custom_isp_cmd: u8, + custom_isp_cmd: FuAverHidCustomIspCmd, file_name: [char; 51], end_flag: u8, file_size: u32le, free_space: u32le, - _reserved: [u8; 448] = 0xFF, + _reserved: [u8; 448] = [0xFF; 448], end: u8 == 0x00, } -#[derive(Getters, Setters, Default)] +#[derive(Getters, New, Default)] #[repr(C, packed)] struct FuStructAverHidReqIspStart { report_id_custom_command: u8 == 0x08, custom_cmd_isp: u8 == 0x10, - custom_isp_cmd: u8, - isp_cmd: [u8; 60], - _reserved: [u8; 448] = 0xFF, + custom_isp_cmd: FuAverHidCustomIspCmd, + isp_cmd: [u8; 60] = [0xFF; 60], + _reserved: [u8; 448] = [0xFF; 448], end: u8 == 0x00, } @@ -130,7 +130,7 @@ report_id_custom_command: u8 == 0x08, custom_cmd_isp: u8 == 0x25, ver: [u8; 11], - _reserved: [u8; 498] = 0xFF, + _reserved: [u8; 498] = [0xFF; 498], end: u8 == 0x00, } @@ -139,11 +139,11 @@ struct FuStructAverHidResIspStatus { report_id_custom_command: u8 == 0x09, custom_cmd_isp: u8 == 0x10, - custom_isp_cmd: u8, + custom_isp_cmd: FuAverHidCustomIspCmd, status: u8, status_string: [char; 58], progress: u8, - _reserved: [u8; 448] = 0xFF, + _reserved: [u8; 448] = [0xFF; 448], end: u8 == 0x00, } @@ -152,8 +152,8 @@ struct FuStructAverHidResIsp { report_id_custom_command: u8 == 0x09, custom_cmd_isp: u8 == 0x10, - custom_isp_cmd: u8, - _reserved: [u8; 508] = 0xFF, + custom_isp_cmd: FuAverHidCustomIspCmd, + _reserved: [u8; 508] = [0xFF; 508], end: u8 == 0x00, } @@ -163,7 +163,7 @@ report_id_custom_command: u8 == 0x09, custom_cmd_isp: u8 == 0x25, ver: [u8; 11], - _reserved: [u8; 498] = 0xFF, + _reserved: [u8; 498] = [0xFF; 498], end: u8 == 0x00, } @@ -171,11 +171,11 @@ #[repr(C, packed)] struct FuStructAverSafeispReq { report_id_custom_command: u8 == 0x08, - custom_cmd: u8, + custom_cmd: FuAverSafeispCustomCmd, custom_res: u16le, custom_parm0: u32le = 0x00, custom_parm1: u32le = 0x00, - data: [u8; 1012] = 0x00, + data: [u8; 1012] = [0x00; 1012], }; #[derive(New, Getters, Validate, Default)] @@ -186,14 +186,14 @@ custom_res: u16le, custom_parm0: u32le, custom_parm1: u32le, - data: [u8; 4] = 0x00, + data: [u8; 4] = [0x00; 4], }; -#[derive(Getters, Validate, Default)] +#[derive(New, Getters, Validate, Default)] #[repr(C, packed)] struct FuStructAverSafeispResDeviceVersion { report_id_custom_command: u8 == 0x09, - custom_cmd: u8 == 0x14, + custom_cmd: FuAverSafeispCustomCmd == GetVersion, ver: [u8; 11], - _reserved: [u8; 3] = 0x00, + _reserved: [u8; 3] = [0x00; 3], } diff -Nru fwupd-2.0.8/plugins/aver-hid/fu-aver-safeisp-device.c fwupd-2.0.20/plugins/aver-hid/fu-aver-safeisp-device.c --- fwupd-2.0.8/plugins/aver-hid/fu-aver-safeisp-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/aver-hid/fu-aver-safeisp-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -35,7 +35,7 @@ FU_AVER_SAFEISP_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, error)) { - g_prefix_error(error, "failed to send packet: "); + g_prefix_error_literal(error, "failed to send packet: "); return FALSE; } } @@ -47,13 +47,9 @@ FU_AVER_SAFEISP_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, error)) { - g_prefix_error(error, "failed to receive packet: "); + g_prefix_error_literal(error, "failed to receive packet: "); return FALSE; } - g_debug("custom-isp-cmd: %s [0x%x]", - fu_aver_safeisp_custom_cmd_to_string( - fu_struct_aver_safeisp_res_get_custom_cmd(res)), - fu_struct_aver_safeisp_res_get_custom_cmd(res)); } return TRUE; } @@ -62,16 +58,21 @@ fu_aver_safeisp_device_ensure_version(FuAverSafeispDevice *self, GError **error) { g_autofree gchar *ver = NULL; - g_autoptr(GByteArray) req = fu_struct_aver_safeisp_req_new(); - g_autoptr(GByteArray) res = fu_struct_aver_safeisp_res_new(); - fu_struct_aver_safeisp_req_set_custom_cmd(req, FU_AVER_SAFEISP_CUSTOM_CMD_GET_VERSION); - if (!fu_aver_safeisp_device_transfer(self, req, res, error)) - return FALSE; - if (!fu_struct_aver_safeisp_res_device_version_validate(res->data, res->len, 0x0, error)) - return FALSE; - ver = - fu_strsafe((const gchar *)fu_struct_aver_safeisp_res_device_version_get_ver(res, NULL), - FU_STRUCT_AVER_SAFEISP_RES_DEVICE_VERSION_SIZE_VER); + g_autoptr(FuStructAverSafeispReq) st_req = fu_struct_aver_safeisp_req_new(); + g_autoptr(FuStructAverSafeispResDeviceVersion) st_res = + fu_struct_aver_safeisp_res_device_version_new(); + + fu_struct_aver_safeisp_req_set_custom_cmd(st_req, FU_AVER_SAFEISP_CUSTOM_CMD_GET_VERSION); + if (!fu_aver_safeisp_device_transfer(self, st_req->buf, st_res->buf, error)) + return FALSE; + if (!fu_struct_aver_safeisp_res_device_version_validate(st_res->buf->data, + st_res->buf->len, + 0x0, + error)) + return FALSE; + ver = fu_strsafe( + (const gchar *)fu_struct_aver_safeisp_res_device_version_get_ver(st_res, NULL), + FU_STRUCT_AVER_SAFEISP_RES_DEVICE_VERSION_SIZE_VER); fu_device_set_version(FU_DEVICE(self), ver); return TRUE; } @@ -95,15 +96,15 @@ static gboolean fu_aver_safeisp_device_support(FuAverSafeispDevice *self, GError **error) { - g_autoptr(GByteArray) req = fu_struct_aver_safeisp_req_new(); - g_autoptr(GByteArray) res = fu_struct_aver_safeisp_res_new(); + g_autoptr(FuStructAverSafeispReq) st_req = fu_struct_aver_safeisp_req_new(); + g_autoptr(FuStructAverSafeispRes) st_res = fu_struct_aver_safeisp_res_new(); - fu_struct_aver_safeisp_req_set_custom_cmd(req, FU_AVER_SAFEISP_CUSTOM_CMD_SUPPORT); - if (!fu_aver_safeisp_device_transfer(self, req, res, error)) + fu_struct_aver_safeisp_req_set_custom_cmd(st_req, FU_AVER_SAFEISP_CUSTOM_CMD_SUPPORT); + if (!fu_aver_safeisp_device_transfer(self, st_req->buf, st_res->buf, error)) return FALSE; - if (!fu_struct_aver_safeisp_res_validate(res->data, res->len, 0x0, error)) + if (!fu_struct_aver_safeisp_res_validate(st_res->buf->data, st_res->buf->len, 0x0, error)) return FALSE; - if (fu_struct_aver_safeisp_res_get_custom_cmd(res) != FU_AVER_SAFEISP_ACK_STATUS_SUPPORT) + if (fu_struct_aver_safeisp_res_get_custom_cmd(st_res) != FU_AVER_SAFEISP_ACK_STATUS_SUPPORT) return FALSE; return TRUE; } @@ -114,15 +115,16 @@ gsize size, GError **error) { - g_autoptr(GByteArray) req = fu_struct_aver_safeisp_req_new(); - g_autoptr(GByteArray) res = fu_struct_aver_safeisp_res_new(); + g_autoptr(FuStructAverSafeispReq) st_req = fu_struct_aver_safeisp_req_new(); + g_autoptr(FuStructAverSafeispRes) st_res = fu_struct_aver_safeisp_res_new(); - fu_struct_aver_safeisp_req_set_custom_cmd(req, FU_AVER_SAFEISP_CUSTOM_CMD_UPLOAD_PREPARE); - fu_struct_aver_safeisp_req_set_custom_parm0(req, partition); - fu_struct_aver_safeisp_req_set_custom_parm1(req, size); - if (!fu_aver_safeisp_device_transfer(self, req, res, error)) + fu_struct_aver_safeisp_req_set_custom_cmd(st_req, + FU_AVER_SAFEISP_CUSTOM_CMD_UPLOAD_PREPARE); + fu_struct_aver_safeisp_req_set_custom_parm0(st_req, partition); + fu_struct_aver_safeisp_req_set_custom_parm1(st_req, size); + if (!fu_aver_safeisp_device_transfer(self, st_req->buf, st_res->buf, error)) return FALSE; - if (!fu_struct_aver_safeisp_res_validate(res->data, res->len, 0x0, error)) + if (!fu_struct_aver_safeisp_res_validate(st_res->buf->data, st_res->buf->len, 0x0, error)) return FALSE; return TRUE; } @@ -133,15 +135,15 @@ gsize param1, GError **error) { - g_autoptr(GByteArray) req = fu_struct_aver_safeisp_req_new(); - g_autoptr(GByteArray) res = fu_struct_aver_safeisp_res_new(); + g_autoptr(FuStructAverSafeispReq) st_req = fu_struct_aver_safeisp_req_new(); + g_autoptr(FuStructAverSafeispRes) st_res = fu_struct_aver_safeisp_res_new(); - fu_struct_aver_safeisp_req_set_custom_cmd(req, FU_AVER_SAFEISP_CUSTOM_CMD_ERASE_TEMP); - fu_struct_aver_safeisp_req_set_custom_parm0(req, param0); - fu_struct_aver_safeisp_req_set_custom_parm1(req, param1); - if (!fu_aver_safeisp_device_transfer(self, req, res, error)) + fu_struct_aver_safeisp_req_set_custom_cmd(st_req, FU_AVER_SAFEISP_CUSTOM_CMD_ERASE_TEMP); + fu_struct_aver_safeisp_req_set_custom_parm0(st_req, param0); + fu_struct_aver_safeisp_req_set_custom_parm1(st_req, param1); + if (!fu_aver_safeisp_device_transfer(self, st_req->buf, st_res->buf, error)) return FALSE; - if (!fu_struct_aver_safeisp_res_validate(res->data, res->len, 0x0, error)) + if (!fu_struct_aver_safeisp_res_validate(st_res->buf->data, st_res->buf->len, 0x0, error)) return FALSE; return TRUE; } @@ -158,8 +160,8 @@ fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { g_autoptr(FuChunk) chk = NULL; - g_autoptr(GByteArray) req = fu_struct_aver_safeisp_req_new(); - g_autoptr(GByteArray) res = fu_struct_aver_safeisp_res_new(); + g_autoptr(FuStructAverSafeispReq) st_req = fu_struct_aver_safeisp_req_new(); + g_autoptr(FuStructAverSafeispRes) st_res = fu_struct_aver_safeisp_res_new(); /* prepare chunk */ chk = fu_chunk_array_index(chunks, i, error); @@ -169,11 +171,11 @@ /* copy in payload */ if (partition == ISP_CX3) { fu_struct_aver_safeisp_req_set_custom_cmd( - req, + st_req, FU_AVER_SAFEISP_CUSTOM_CMD_UPLOAD_TO_CX3); } else if (partition == ISP_M12) { fu_struct_aver_safeisp_req_set_custom_cmd( - req, + st_req, FU_AVER_SAFEISP_CUSTOM_CMD_UPLOAD_TO_M12MO); } else { g_set_error(error, @@ -184,11 +186,11 @@ return FALSE; } - fu_struct_aver_safeisp_req_set_custom_parm0(req, fu_chunk_get_address(chk)); - fu_struct_aver_safeisp_req_set_custom_parm1(req, fu_chunk_get_data_sz(chk)); + fu_struct_aver_safeisp_req_set_custom_parm0(st_req, fu_chunk_get_address(chk)); + fu_struct_aver_safeisp_req_set_custom_parm1(st_req, fu_chunk_get_data_sz(chk)); - if (!fu_memcpy_safe(req->data, - req->len, + if (!fu_memcpy_safe(st_req->buf->data, + st_req->buf->len, FU_STRUCT_AVER_SAFEISP_REQ_OFFSET_DATA, /* dst */ fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), @@ -200,12 +202,16 @@ /* resize the last packet */ if ((i == (fu_chunk_array_length(chunks) - 1)) && (fu_chunk_get_data_sz(chk) < 512)) { - fu_byte_array_set_size(req, 12 + fu_chunk_get_data_sz(chk), 0x0); - fu_struct_aver_safeisp_req_set_custom_parm1(req, fu_chunk_get_data_sz(chk)); + fu_byte_array_set_size(st_req->buf, 12 + fu_chunk_get_data_sz(chk), 0x0); + fu_struct_aver_safeisp_req_set_custom_parm1(st_req, + fu_chunk_get_data_sz(chk)); } - if (!fu_aver_safeisp_device_transfer(self, req, res, error)) + if (!fu_aver_safeisp_device_transfer(self, st_req->buf, st_res->buf, error)) return FALSE; - if (!fu_struct_aver_safeisp_res_validate(res->data, res->len, 0x0, error)) + if (!fu_struct_aver_safeisp_res_validate(st_res->buf->data, + st_res->buf->len, + 0x0, + error)) return FALSE; /* update progress */ @@ -222,19 +228,19 @@ gsize param1, GError **error) { - g_autoptr(GByteArray) req = fu_struct_aver_safeisp_req_new(); - g_autoptr(GByteArray) res = fu_struct_aver_safeisp_res_new(); + g_autoptr(FuStructAverSafeispReq) st_req = fu_struct_aver_safeisp_req_new(); + g_autoptr(FuStructAverSafeispRes) st_res = fu_struct_aver_safeisp_res_new(); fu_struct_aver_safeisp_req_set_custom_cmd( - req, + st_req, FU_AVER_SAFEISP_CUSTOM_CMD_UPLOAD_COMPARE_CHECKSUM); - fu_struct_aver_safeisp_req_set_custom_parm0(req, param0); - fu_struct_aver_safeisp_req_set_custom_parm1(req, param1); - if (!fu_aver_safeisp_device_transfer(self, req, res, error)) + fu_struct_aver_safeisp_req_set_custom_parm0(st_req, param0); + fu_struct_aver_safeisp_req_set_custom_parm1(st_req, param1); + if (!fu_aver_safeisp_device_transfer(self, st_req->buf, st_res->buf, error)) return FALSE; - if (!fu_struct_aver_safeisp_res_validate(res->data, res->len, 0x0, error)) + if (!fu_struct_aver_safeisp_res_validate(st_res->buf->data, st_res->buf->len, 0x0, error)) return FALSE; - if (fu_struct_aver_safeisp_req_get_custom_cmd(res) != FU_AVER_SAFEISP_ACK_STATUS_SUCCESS) + if (fu_struct_aver_safeisp_res_get_custom_cmd(st_res) != FU_AVER_SAFEISP_ACK_STATUS_SUCCESS) return FALSE; return TRUE; } @@ -242,14 +248,11 @@ static gboolean fu_aver_safeisp_device_update(FuAverSafeispDevice *self, gsize param0, gsize param1, GError **error) { - g_autoptr(GByteArray) req = fu_struct_aver_safeisp_req_new(); - - fu_struct_aver_safeisp_req_set_custom_cmd(req, FU_AVER_SAFEISP_CUSTOM_CMD_UPDATE_START); - fu_struct_aver_safeisp_req_set_custom_parm0(req, param0); - fu_struct_aver_safeisp_req_set_custom_parm1(req, param1); - if (!fu_aver_safeisp_device_transfer(self, req, NULL, error)) - return FALSE; - return TRUE; + g_autoptr(FuStructAverSafeispReq) st_req = fu_struct_aver_safeisp_req_new(); + fu_struct_aver_safeisp_req_set_custom_cmd(st_req, FU_AVER_SAFEISP_CUSTOM_CMD_UPDATE_START); + fu_struct_aver_safeisp_req_set_custom_parm0(st_req, param0); + fu_struct_aver_safeisp_req_set_custom_parm1(st_req, param1); + return fu_aver_safeisp_device_transfer(self, st_req->buf, NULL, error); } static gboolean @@ -393,7 +396,7 @@ } static void -fu_aver_safeisp_device_set_progress(FuDevice *self, FuProgress *progress) +fu_aver_safeisp_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/aver-hid/tests/aver-fone540.json fwupd-2.0.20/plugins/aver-hid/tests/aver-fone540.json --- fwupd-2.0.8/plugins/aver-hid/tests/aver-fone540.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/aver-hid/tests/aver-fone540.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/3dca7723feba4f86d6cbd1d3cb39e0d2524d59bf03e3c904465edc88ca8254c6-0.0.7004.09.cab", - "emulation-url": "https://fwupd.org/downloads/bbe19a7f6ad1025fc9da37c71c2a155884403876c5bc0c4ef64174319f91f398-0.0.7004.09.zip", + "url": "3dca7723feba4f86d6cbd1d3cb39e0d2524d59bf03e3c904465edc88ca8254c6-0.0.7004.09.cab", + "emulation-url": "bbe19a7f6ad1025fc9da37c71c2a155884403876c5bc0c4ef64174319f91f398-0.0.7004.09.zip", "components": [ { "version": "0.0.7004.09", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/e16c8a7a9dd19921ace461334dcc3148c52edf95145a3047db7cc872113f82c8-0.0.7106.12.cab", - "emulation-url": "https://fwupd.org/downloads/d93a8cdaa112ad2fa43da573aa92938bc261220b97c80b99b2f08aedff8ca959-0.0.7106.12.zip", + "url": "e16c8a7a9dd19921ace461334dcc3148c52edf95145a3047db7cc872113f82c8-0.0.7106.12.cab", + "emulation-url": "d93a8cdaa112ad2fa43da573aa92938bc261220b97c80b99b2f08aedff8ca959-0.0.7106.12.zip", "components": [ { "version": "0.0.7106.12", diff -Nru fwupd-2.0.8/plugins/bcm57xx/README.md fwupd-2.0.20/plugins/bcm57xx/README.md --- fwupd-2.0.8/plugins/bcm57xx/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -27,10 +27,6 @@ device without disconnecting the working kernel driver. Once complete the APE is reset which may cause a brief link reconnection. -On flash failure the device is nonfunctional, but is recoverable using direct -BAR writes, which is typically much slower than updating the device using the -kernel driver and the ethtool API. - ## Vendor ID Security The vendor ID is set from the PCI vendor, in this instance set to `PCI:0x14E4` @@ -42,10 +38,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.5.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Evan Lojewski: @meklort diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-common.c fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-common.c --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -81,18 +81,18 @@ } void -fu_bcm57xx_veritem_free(Bcm57xxVeritem *veritem) +fu_bcm57xx_veritem_free(FuBcm57xxVeritem *veritem) { g_free(veritem->branch); g_free(veritem->version); g_free(veritem); } -Bcm57xxVeritem * +FuBcm57xxVeritem * fu_bcm57xx_veritem_new(const guint8 *buf, gsize bufsz) { g_autofree gchar *tmp = NULL; - g_autoptr(Bcm57xxVeritem) veritem = g_new0(Bcm57xxVeritem, 1); + g_autoptr(FuBcm57xxVeritem) veritem = g_new0(FuBcm57xxVeritem, 1); struct { const gchar *prefix; const gchar *branch; diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-common.h fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-common.h --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -26,23 +26,6 @@ #define BCM_NVRAM_INFO2_BASE 0x200 #define BCM_NVRAM_STAGE1_BASE 0x28c -#define BCM_NVRAM_HEADER_MAGIC 0x00 -#define BCM_NVRAM_HEADER_PHYS_ADDR 0x04 -#define BCM_NVRAM_HEADER_SIZE_WRDS 0x08 -#define BCM_NVRAM_HEADER_OFFSET 0x0C -#define BCM_NVRAM_HEADER_CRC 0x10 -#define BCM_NVRAM_HEADER_SZ 0x14 - -#define BCM_NVRAM_INFO_MAC_ADDR0 0x00 -#define BCM_NVRAM_INFO_VENDOR 0x2E -#define BCM_NVRAM_INFO_DEVICE 0x2C -#define BCM_NVRAM_INFO_SZ 0x8C - -#define BCM_NVRAM_DIRECTORY_ADDR 0x00 -#define BCM_NVRAM_DIRECTORY_SIZE_WRDS 0x04 -#define BCM_NVRAM_DIRECTORY_OFFSET 0x08 -#define BCM_NVRAM_DIRECTORY_SZ 0x0c - #define BCM_NVRAM_VPD_SZ 0x100 #define BCM_NVRAM_INFO2_SZ 0x8c @@ -54,7 +37,7 @@ gchar *branch; gchar *version; FwupdVersionFormat verfmt; -} Bcm57xxVeritem; +} FuBcm57xxVeritem; gboolean fu_bcm57xx_verify_crc(GInputStream *stream, GError **error); @@ -63,7 +46,7 @@ /* parses stage1 version */ void -fu_bcm57xx_veritem_free(Bcm57xxVeritem *veritem); -Bcm57xxVeritem * +fu_bcm57xx_veritem_free(FuBcm57xxVeritem *veritem); +FuBcm57xxVeritem * fu_bcm57xx_veritem_new(const guint8 *buf, gsize bufsz); -G_DEFINE_AUTOPTR_CLEANUP_FUNC(Bcm57xxVeritem, fu_bcm57xx_veritem_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuBcm57xxVeritem, fu_bcm57xx_veritem_free) diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-device.c fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-device.c --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,7 +25,6 @@ #include "fu-bcm57xx-device.h" #include "fu-bcm57xx-dict-image.h" #include "fu-bcm57xx-firmware.h" -#include "fu-bcm57xx-recovery-device.h" #define FU_BCM57XX_BLOCK_SZ 0x4000 /* 16kb */ @@ -84,7 +83,7 @@ 500, /* ms */ FU_IOCTL_FLAG_NONE, error)) { - g_prefix_error(error, "failed to SIOCETHTOOL: "); + g_prefix_error_literal(error, "failed to SIOCETHTOOL: "); return FALSE; } @@ -135,7 +134,7 @@ (guint8 *)eeprom, eepromsz, error)) { - g_prefix_error(error, "cannot write eeprom: "); + g_prefix_error_literal(error, "cannot write eeprom: "); return FALSE; } @@ -190,7 +189,7 @@ (guint8 *)eeprom, eepromsz, error)) { - g_prefix_error(error, "cannot read eeprom: "); + g_prefix_error_literal(error, "cannot read eeprom: "); return FALSE; } @@ -236,7 +235,7 @@ (guint8 *)&drvinfo, sizeof(drvinfo), error)) { - g_prefix_error(error, "cannot get driver information: "); + g_prefix_error_literal(error, "cannot get driver information: "); return FALSE; } g_debug("FW version %s", drvinfo.fw_version); @@ -262,10 +261,10 @@ /* success */ return TRUE; #else - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Not supported as not found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported as not found"); return FALSE; #endif } @@ -307,7 +306,7 @@ fw = fu_bcm57xx_device_dump_firmware(device, progress, error); if (fw == NULL) return NULL; - if (!fu_firmware_parse_bytes(firmware, fw, 0x0, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + if (!fu_firmware_parse_bytes(firmware, fw, 0x0, FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return NULL; /* remove images that will contain user-data */ @@ -324,7 +323,7 @@ fu_bcm57xx_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint dict_cnt = 0; @@ -340,12 +339,12 @@ /* try to parse NVRAM, stage1 or APE */ if (!fu_firmware_parse_stream(firmware_tmp, stream, 0x0, flags, error)) { - g_prefix_error(error, "failed to parse new firmware: "); + g_prefix_error_literal(error, "failed to parse new firmware: "); return NULL; } /* for full NVRAM image, verify if correct device */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) == 0) { guint16 vid = fu_bcm57xx_firmware_get_vendor(FU_BCM57XX_FIRMWARE(firmware_tmp)); guint16 did = fu_bcm57xx_firmware_get_model(FU_BCM57XX_FIRMWARE(firmware_tmp)); if (vid != 0x0 && did != 0x0 && @@ -368,8 +367,12 @@ fw_old = fu_bcm57xx_device_dump_firmware(device, progress, error); if (fw_old == NULL) return NULL; - if (!fu_firmware_parse_bytes(firmware, fw_old, 0x0, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) { - g_prefix_error(error, "failed to parse existing firmware: "); + if (!fu_firmware_parse_bytes(firmware, + fw_old, + 0x0, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, + error)) { + g_prefix_error_literal(error, "failed to parse existing firmware: "); return NULL; } str_existing = fu_firmware_to_string(firmware); @@ -377,14 +380,20 @@ /* merge in all the provided images into the existing firmware */ img_stage1 = fu_firmware_get_image_by_id(firmware_tmp, "stage1", NULL); - if (img_stage1 != NULL) - fu_firmware_add_image(firmware, img_stage1); + if (img_stage1 != NULL) { + if (!fu_firmware_add_image(firmware, img_stage1, error)) + return NULL; + } img_stage2 = fu_firmware_get_image_by_id(firmware_tmp, "stage2", NULL); - if (img_stage2 != NULL) - fu_firmware_add_image(firmware, img_stage2); + if (img_stage2 != NULL) { + if (!fu_firmware_add_image(firmware, img_stage2, error)) + return NULL; + } img_ape = fu_firmware_get_image_by_id(firmware_tmp, "ape", NULL); - if (img_ape != NULL) - fu_firmware_add_image(firmware, img_ape); + if (img_ape != NULL) { + if (!fu_firmware_add_image(firmware, img_ape, error)) + return NULL; + } /* the src and dst dictionaries may be in different order */ images = fu_firmware_get_images(firmware); @@ -518,7 +527,7 @@ } else { guint8 bufver[16] = {0x0}; guint32 veraddr = 0; - g_autoptr(Bcm57xxVeritem) veritem = NULL; + g_autoptr(FuBcm57xxVeritem) veritem = NULL; /* fall back to the string, e.g. '5719-v1.43' */ if (!fu_bcm57xx_device_nvram_read(self, @@ -567,7 +576,7 @@ FWUPD_ERROR_NOT_SUPPORTED, "failed to open socket: %s", #ifdef HAVE_ERRNO_H - g_strerror(errno)); + fwupd_strerror(errno)); #else "unspecified error"); #endif @@ -586,7 +595,7 @@ } static void -fu_bcm57xx_device_set_progress(FuDevice *self, FuProgress *progress) +fu_bcm57xx_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -642,7 +651,7 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN); fu_device_add_request_flag(FU_DEVICE(self), FWUPD_REQUEST_FLAG_NON_GENERIC_MESSAGE); fu_device_add_protocol(FU_DEVICE(self), "com.broadcom.bcm57xx"); - fu_device_add_icon(FU_DEVICE(self), "network-wired"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_NETWORK_WIRED); /* other values are set from a quirk */ fu_device_set_firmware_size(FU_DEVICE(self), BCM_FIRMWARE_SIZE); diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-dict-image.c fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-dict-image.c --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-dict-image.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-dict-image.c 2026-02-26 11:36:18.000000000 +0000 @@ -30,7 +30,7 @@ static gboolean fu_bcm57xx_dict_image_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(GInputStream) stream_nocrc = NULL; @@ -45,7 +45,7 @@ "dict image is too small"); return FALSE; } - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { if (!fu_bcm57xx_verify_crc(stream, error)) return FALSE; } diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-firmware.c fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-firmware.c --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,6 +12,7 @@ #include "fu-bcm57xx-firmware.h" #include "fu-bcm57xx-stage1-image.h" #include "fu-bcm57xx-stage2-image.h" +#include "fu-bcm57xx-struct.h" struct _FuBcm57xxFirmware { FuFirmware parent_instance; @@ -55,43 +56,36 @@ /* get address */ return fu_input_stream_read_u32(stream, - BCM_NVRAM_HEADER_PHYS_ADDR, + FU_STRUCT_BCM57XX_NVRAM_HEADER_OFFSET_PHYS_ADDR, &self->phys_addr, G_BIG_ENDIAN, error); } static FuFirmware * -fu_bcm57xx_firmware_parse_info(FuBcm57xxFirmware *self, GInputStream *stream, GError **error) +fu_bcm57xx_firmware_parse_info(FuBcm57xxFirmware *self, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) { - guint32 mac_addr0 = 0; + guint32 mac_addr0; g_autoptr(FuFirmware) img = fu_firmware_new(); + g_autoptr(FuStructBcm57xxNvramInfo) st = NULL; - /* if the MAC is set non-zero this is an actual backup rather than a container */ - if (!fu_input_stream_read_u32(stream, - BCM_NVRAM_INFO_MAC_ADDR0, - &mac_addr0, - G_BIG_ENDIAN, - error)) + st = fu_struct_bcm57xx_nvram_info_parse_stream(stream, 0x0, error); + if (st == NULL) return NULL; + + /* if the MAC is set non-zero this is an actual backup rather than a container */ + mac_addr0 = fu_struct_bcm57xx_nvram_info_get_mac_addr(st, 0); self->is_backup = mac_addr0 != 0x0 && mac_addr0 != 0xffffffff; /* read vendor + model */ - if (!fu_input_stream_read_u16(stream, - BCM_NVRAM_INFO_VENDOR, - &self->vendor, - G_BIG_ENDIAN, - error)) - return NULL; - if (!fu_input_stream_read_u16(stream, - BCM_NVRAM_INFO_DEVICE, - &self->model, - G_BIG_ENDIAN, - error)) - return NULL; + self->vendor = fu_struct_bcm57xx_nvram_info_get_vendor(st); + self->model = fu_struct_bcm57xx_nvram_info_get_device(st); /* success */ - if (!fu_firmware_parse_stream(img, stream, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_stream(img, stream, 0x0, flags, error)) return NULL; fu_firmware_set_id(img, "info"); return g_steal_pointer(&img); @@ -101,30 +95,25 @@ fu_bcm57xx_firmware_parse_stage1(FuBcm57xxFirmware *self, GInputStream *stream, guint32 *out_stage1_sz, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize streamsz = 0; guint32 stage1_wrds = 0; guint32 stage1_sz; guint32 stage1_off = 0; + g_autoptr(FuStructBcm57xxNvramHeader) st = NULL; g_autoptr(FuFirmware) img = fu_bcm57xx_stage1_image_new(); g_autoptr(GInputStream) stream_tmp = NULL; if (!fu_input_stream_size(stream, &streamsz, error)) return NULL; - if (!fu_input_stream_read_u32(stream, - BCM_NVRAM_HEADER_BASE + BCM_NVRAM_HEADER_SIZE_WRDS, - &stage1_wrds, - G_BIG_ENDIAN, - error)) - return NULL; - if (!fu_input_stream_read_u32(stream, - BCM_NVRAM_HEADER_BASE + BCM_NVRAM_HEADER_OFFSET, - &stage1_off, - G_BIG_ENDIAN, - error)) + st = fu_struct_bcm57xx_nvram_header_parse_stream(stream, BCM_NVRAM_HEADER_BASE, error); + if (st == NULL) return NULL; + stage1_wrds = fu_struct_bcm57xx_nvram_header_get_size_wrds(st); + stage1_off = fu_struct_bcm57xx_nvram_header_get_offset(st); + stage1_sz = (stage1_wrds * sizeof(guint32)); if (stage1_off != BCM_NVRAM_STAGE1_BASE) { g_set_error(error, @@ -152,7 +141,7 @@ if (!fu_firmware_parse_stream(img, stream_tmp, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return NULL; @@ -170,7 +159,7 @@ fu_bcm57xx_firmware_parse_stage2(FuBcm57xxFirmware *self, GInputStream *stream, guint32 stage1_sz, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize streamsz = 0; @@ -207,7 +196,7 @@ if (!fu_firmware_parse_stream(img, stream_tmp, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return NULL; @@ -221,7 +210,7 @@ fu_bcm57xx_firmware_parse_dict(FuBcm57xxFirmware *self, GInputStream *stream, guint idx, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize streamsz = 0; @@ -229,29 +218,18 @@ guint32 dict_info = 0x0; guint32 dict_off = 0x0; guint32 dict_sz; - guint32 base = BCM_NVRAM_DIRECTORY_BASE + (idx * BCM_NVRAM_DIRECTORY_SZ); + guint32 base = BCM_NVRAM_DIRECTORY_BASE + (idx * FU_STRUCT_BCM57XX_NVRAM_DIRECTORY_SIZE); g_autoptr(FuFirmware) img = fu_bcm57xx_dict_image_new(); + g_autoptr(FuStructBcm57xxNvramDirectory) st = NULL; g_autoptr(GInputStream) stream_tmp = NULL; /* header */ - if (!fu_input_stream_read_u32(stream, - base + BCM_NVRAM_DIRECTORY_ADDR, - &dict_addr, - G_BIG_ENDIAN, - error)) - return FALSE; - if (!fu_input_stream_read_u32(stream, - base + BCM_NVRAM_DIRECTORY_SIZE_WRDS, - &dict_info, - G_BIG_ENDIAN, - error)) - return FALSE; - if (!fu_input_stream_read_u32(stream, - base + BCM_NVRAM_DIRECTORY_OFFSET, - &dict_off, - G_BIG_ENDIAN, - error)) + st = fu_struct_bcm57xx_nvram_directory_parse_stream(stream, base, error); + if (st == NULL) return FALSE; + dict_addr = fu_struct_bcm57xx_nvram_directory_get_addr(st); + dict_info = fu_struct_bcm57xx_nvram_directory_get_size_wrds(st); + dict_off = fu_struct_bcm57xx_nvram_directory_get_offset(st); /* no dict stored */ if (dict_addr == 0 && dict_info == 0 && dict_off == 0) @@ -270,8 +248,7 @@ if (dict_sz == 0) { g_autoptr(GBytes) blob = g_bytes_new(NULL, 0); fu_firmware_set_bytes(img, blob); - fu_firmware_add_image(FU_FIRMWARE(self), img); - return TRUE; + return fu_firmware_add_image(FU_FIRMWARE(self), img, error); } /* check against image size */ @@ -292,13 +269,12 @@ if (!fu_firmware_parse_stream(img, stream_tmp, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return FALSE; /* success */ - fu_firmware_add_image(FU_FIRMWARE(self), img); - return TRUE; + return fu_firmware_add_image(FU_FIRMWARE(self), img, error); } static gboolean @@ -310,7 +286,7 @@ guint32 magic = 0; if (!fu_input_stream_read_u32(stream, 0x0, &magic, G_BIG_ENDIAN, error)) { - g_prefix_error(error, "failed to read magic: "); + g_prefix_error_literal(error, "failed to read magic: "); return FALSE; } if (magic != BCM_APE_HEADER_MAGIC && magic != BCM_STAGE1_HEADER_MAGIC_BROADCOM && @@ -330,7 +306,7 @@ static gboolean fu_bcm57xx_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuBcm57xxFirmware *self = FU_BCM57XX_FIRMWARE(firmware); @@ -358,8 +334,7 @@ fu_bcm57xx_dict_image_set_kind(FU_BCM57XX_DICT_IMAGE(img), 0x0); fu_firmware_set_addr(img, BCM_CODE_DIRECTORY_ADDR_APE); fu_firmware_set_id(img, "ape"); - fu_firmware_add_image(firmware, img); - return TRUE; + return fu_firmware_add_image(firmware, img, error); } /* standalone stage1 */ @@ -368,8 +343,7 @@ if (!fu_firmware_set_stream(img_stage1_standalone, stream, error)) return FALSE; fu_firmware_set_id(img_stage1_standalone, "stage1"); - fu_firmware_add_image(firmware, img_stage1_standalone); - return TRUE; + return fu_firmware_add_image(firmware, img_stage1_standalone, error); } /* not full NVRAM image */ @@ -390,27 +364,32 @@ return FALSE; /* NVRAM header */ - stream_header = - fu_partial_input_stream_new(stream, BCM_NVRAM_HEADER_BASE, BCM_NVRAM_HEADER_SZ, error); + stream_header = fu_partial_input_stream_new(stream, + BCM_NVRAM_HEADER_BASE, + FU_STRUCT_BCM57XX_NVRAM_HEADER_SIZE, + error); if (stream_header == NULL) return FALSE; if (!fu_bcm57xx_firmware_parse_header(self, stream_header, error)) { - g_prefix_error(error, "failed to parse header: "); + g_prefix_error_literal(error, "failed to parse header: "); return FALSE; } /* info */ - stream_info = - fu_partial_input_stream_new(stream, BCM_NVRAM_INFO_BASE, BCM_NVRAM_INFO_SZ, error); + stream_info = fu_partial_input_stream_new(stream, + BCM_NVRAM_INFO_BASE, + FU_STRUCT_BCM57XX_NVRAM_INFO_SIZE, + error); if (stream_info == NULL) return FALSE; - img_info = fu_bcm57xx_firmware_parse_info(self, stream_info, error); + img_info = fu_bcm57xx_firmware_parse_info(self, stream_info, flags, error); if (img_info == NULL) { - g_prefix_error(error, "failed to parse info: "); + g_prefix_error_literal(error, "failed to parse info: "); return FALSE; } fu_firmware_set_offset(img_info, BCM_NVRAM_INFO_BASE); - fu_firmware_add_image(firmware, img_info); + if (!fu_firmware_add_image(firmware, img_info, error)) + return FALSE; /* VPD */ stream_vpd = @@ -418,12 +397,13 @@ if (stream_vpd == NULL) return FALSE; if (!fu_firmware_parse_stream(img_vpd, stream_vpd, 0x0, flags, error)) { - g_prefix_error(error, "failed to parse VPD: "); + g_prefix_error_literal(error, "failed to parse VPD: "); return FALSE; } fu_firmware_set_id(img_vpd, "vpd"); fu_firmware_set_offset(img_vpd, BCM_NVRAM_VPD_BASE); - fu_firmware_add_image(firmware, img_vpd); + if (!fu_firmware_add_image(firmware, img_vpd, error)) + return FALSE; /* info2 */ stream_info2 = @@ -431,28 +411,31 @@ if (stream_info2 == NULL) return FALSE; if (!fu_firmware_parse_stream(img_info2, stream_info2, 0x0, flags, error)) { - g_prefix_error(error, "failed to parse info2: "); + g_prefix_error_literal(error, "failed to parse info2: "); return FALSE; } fu_firmware_set_id(img_info2, "info2"); fu_firmware_set_offset(img_info2, BCM_NVRAM_INFO2_BASE); - fu_firmware_add_image(firmware, img_info2); + if (!fu_firmware_add_image(firmware, img_info2, error)) + return FALSE; /* stage1 */ img_stage1 = fu_bcm57xx_firmware_parse_stage1(self, stream, &stage1_sz, flags, error); if (img_stage1 == NULL) { - g_prefix_error(error, "failed to parse stage1: "); + g_prefix_error_literal(error, "failed to parse stage1: "); return FALSE; } - fu_firmware_add_image(firmware, img_stage1); + if (!fu_firmware_add_image(firmware, img_stage1, error)) + return FALSE; /* stage2 */ img_stage2 = fu_bcm57xx_firmware_parse_stage2(self, stream, stage1_sz, flags, error); if (img_stage2 == NULL) { - g_prefix_error(error, "failed to parse stage2: "); + g_prefix_error_literal(error, "failed to parse stage2: "); return FALSE; } - fu_firmware_add_image(firmware, img_stage2); + if (!fu_firmware_add_image(firmware, img_stage2, error)) + return FALSE; /* dictionaries, e.g. APE */ for (guint i = 0; i < 8; i++) { @@ -582,12 +565,10 @@ if (blob_info == NULL) return NULL; } else { - g_autoptr(GByteArray) tmp = g_byte_array_sized_new(BCM_NVRAM_INFO_SZ); - for (gsize i = 0; i < BCM_NVRAM_INFO_SZ; i++) - fu_byte_array_append_uint8(tmp, 0x0); - fu_memwrite_uint16(tmp->data + BCM_NVRAM_INFO_VENDOR, self->vendor, G_BIG_ENDIAN); - fu_memwrite_uint16(tmp->data + BCM_NVRAM_INFO_DEVICE, self->model, G_BIG_ENDIAN); - blob_info = g_bytes_new(tmp->data, tmp->len); + g_autoptr(FuStructBcm57xxNvramInfo) st = fu_struct_bcm57xx_nvram_info_new(); + fu_struct_bcm57xx_nvram_info_set_device(st, self->model); + fu_struct_bcm57xx_nvram_info_set_vendor(st, self->vendor); + blob_info = fu_struct_bcm57xx_nvram_info_to_bytes(st); } fu_byte_array_append_bytes(buf, blob_info); @@ -643,12 +624,6 @@ return self->model; } -gboolean -fu_bcm57xx_firmware_is_backup(FuBcm57xxFirmware *self) -{ - return self->is_backup; -} - static void fu_bcm57xx_firmware_init(FuBcm57xxFirmware *self) { diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-firmware.h fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-firmware.h --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-firmware.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -17,5 +17,3 @@ fu_bcm57xx_firmware_get_vendor(FuBcm57xxFirmware *self); guint16 fu_bcm57xx_firmware_get_model(FuBcm57xxFirmware *self); -gboolean -fu_bcm57xx_firmware_is_backup(FuBcm57xxFirmware *self); diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-plugin.c fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-plugin.c --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,15 +10,14 @@ #include "fu-bcm57xx-dict-image.h" #include "fu-bcm57xx-firmware.h" #include "fu-bcm57xx-plugin.h" -#include "fu-bcm57xx-recovery-device.h" #include "fu-bcm57xx-stage1-image.h" #include "fu-bcm57xx-stage2-image.h" -struct _FuBcm57XxPlugin { +struct _FuBcm57xxPlugin { FuPlugin parent_instance; }; -G_DEFINE_TYPE(FuBcm57XxPlugin, fu_bcm57xx_plugin, FU_TYPE_PLUGIN) +G_DEFINE_TYPE(FuBcm57xxPlugin, fu_bcm57xx_plugin, FU_TYPE_PLUGIN) static gboolean fu_bcm57xx_plugin_backend_device_added(FuPlugin *plugin, @@ -32,6 +31,10 @@ g_autoptr(FuDevice) dev = NULL; g_autoptr(FuDeviceLocker) locker = NULL; + /* not us */ + if (!FU_IS_UDEV_DEVICE(device)) + return TRUE; + /* only enumerate number 0 */ if (fu_udev_device_get_number(FU_UDEV_DEVICE(device)) != 0) { g_set_error_literal(error, @@ -41,10 +44,11 @@ return FALSE; } - /* is in recovery mode if has no ethtool interface */ + /* has no ethtool interface */ if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) { dev = g_object_new(FU_TYPE_BCM57XX_DEVICE, "iface", "enp81s0f0", NULL); } else { + g_autofree gchar *ethtool_iface = NULL; fn = g_build_filename(fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device)), "net", NULL); @@ -56,12 +60,15 @@ } ifaces = fu_path_glob(fn, "en*", NULL); if (ifaces == NULL || ifaces->len == 0) { - dev = g_object_new(FU_TYPE_BCM57XX_RECOVERY_DEVICE, NULL); - } else { - g_autofree gchar *ethtool_iface = - g_path_get_basename(g_ptr_array_index(ifaces, 0)); - dev = g_object_new(FU_TYPE_BCM57XX_DEVICE, "iface", ethtool_iface, NULL); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no interfaces detected for %s", + fn); + return FALSE; } + ethtool_iface = g_path_get_basename(g_ptr_array_index(ifaces, 0)); + dev = g_object_new(FU_TYPE_BCM57XX_DEVICE, "iface", ethtool_iface, NULL); } fu_device_incorporate(dev, device, FU_DEVICE_INCORPORATE_FLAG_ALL); locker = fu_device_locker_new(dev, error); @@ -72,7 +79,7 @@ } static void -fu_bcm57xx_plugin_init(FuBcm57XxPlugin *self) +fu_bcm57xx_plugin_init(FuBcm57xxPlugin *self) { } @@ -89,16 +96,14 @@ FuPlugin *plugin = FU_PLUGIN(obj); fu_plugin_add_udev_subsystem(plugin, "pci"); fu_plugin_add_device_gtype(plugin, FU_TYPE_BCM57XX_DEVICE); - fu_plugin_add_device_gtype(plugin, FU_TYPE_BCM57XX_RECOVERY_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_BCM57XX_FIRMWARE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_BCM57XX_DICT_IMAGE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_BCM57XX_STAGE1_IMAGE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_BCM57XX_STAGE2_IMAGE); - fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_BETTER_THAN, "optionrom"); } static void -fu_bcm57xx_plugin_class_init(FuBcm57XxPluginClass *klass) +fu_bcm57xx_plugin_class_init(FuBcm57xxPluginClass *klass) { FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); GObjectClass *object_class = G_OBJECT_CLASS(klass); diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-plugin.h fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-plugin.h --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-plugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,4 +8,4 @@ #include -G_DECLARE_FINAL_TYPE(FuBcm57XxPlugin, fu_bcm57xx_plugin, FU, BCM57XX_PLUGIN, FuPlugin) +G_DECLARE_FINAL_TYPE(FuBcm57xxPlugin, fu_bcm57xx_plugin, FU, BCM57XX_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-recovery-device.c fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-recovery-device.c --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-recovery-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-recovery-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,902 +0,0 @@ -/* - * Copyright 2018 Evan Lojewski - * Copyright 2020 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#ifdef HAVE_MMAN_H -#include -#endif - -#ifdef HAVE_VALGRIND -#include -#endif /* HAVE_VALGRIND */ - -#include "fu-bcm57xx-common.h" -#include "fu-bcm57xx-firmware.h" -#include "fu-bcm57xx-recovery-device.h" - -/* offsets into BAR[0] */ -#define REG_DEVICE_PCI_VENDOR_DEVICE_ID 0x6434 -#define REG_NVM_SOFTWARE_ARBITRATION 0x7020 -#define REG_NVM_ACCESS 0x7024 -#define REG_NVM_COMMAND 0x7000 -#define REG_NVM_ADDR 0x700c -#define REG_NVM_READ 0x7010 -#define REG_NVM_WRITE 0x7008 - -/* offsets into BAR[2] */ -#define REG_APE_MODE 0x0 - -typedef struct { - guint8 *buf; - gsize bufsz; -} FuBcm57xxMmap; - -#define FU_BCM57XX_BAR_DEVICE 0 -#define FU_BCM57XX_BAR_APE 1 -#define FU_BCM57XX_BAR_MAX 3 - -struct _FuBcm57xxRecoveryDevice { - FuUdevDevice parent_instance; - FuBcm57xxMmap bar[FU_BCM57XX_BAR_MAX]; -}; - -typedef union { - guint32 r32; - struct { - guint32 reserved_0_0 : 1; - guint32 Reset : 1; - guint32 reserved_2_2 : 1; - guint32 Done : 1; - guint32 Doit : 1; - guint32 Wr : 1; - guint32 Erase : 1; - guint32 First : 1; - guint32 Last : 1; - guint32 reserved_15_9 : 7; - guint32 WriteEnableCommand : 1; - guint32 WriteDisableCommand : 1; - guint32 reserved_31_18 : 14; - } __attribute__((packed)) bits; /* nocheck:blocked */ -} BcmRegNVMCommand; - -typedef union { - guint32 r32; - struct { - guint32 ReqSet0 : 1; - guint32 ReqSet1 : 1; - guint32 ReqSet2 : 1; - guint32 ReqSet3 : 1; - guint32 ReqClr0 : 1; - guint32 ReqClr1 : 1; - guint32 ReqClr2 : 1; - guint32 ReqClr3 : 1; - guint32 ArbWon0 : 1; - guint32 ArbWon1 : 1; - guint32 ArbWon2 : 1; - guint32 ArbWon3 : 1; - guint32 Req0 : 1; - guint32 Req1 : 1; - guint32 Req2 : 1; - guint32 Req3 : 1; - guint32 reserved_31_16 : 16; - } __attribute__((packed)) bits; /* nocheck:blocked */ -} BcmRegNVMSoftwareArbitration; - -typedef union { - guint32 r32; - struct { - guint32 Enable : 1; - guint32 WriteEnable : 1; - guint32 reserved_31_2 : 30; - } __attribute__((packed)) bits; /* nocheck:blocked */ -} BcmRegNVMAccess; - -typedef union { - guint32 r32; - struct { - guint32 Reset : 1; - guint32 Halt : 1; - guint32 FastBoot : 1; - guint32 HostDiag : 1; - guint32 reserved_4_4 : 1; - guint32 Event1 : 1; - guint32 Event2 : 1; - guint32 GRCint : 1; - guint32 reserved_8_8 : 1; - guint32 SwapATBdword : 1; - guint32 reserved_10_10 : 1; - guint32 SwapARBdword : 1; - guint32 reserved_13_12 : 2; - guint32 Channel0Enable : 1; - guint32 Channel2Enable : 1; - guint32 reserved_17_16 : 2; - guint32 MemoryECC : 1; - guint32 ICodePIPRdDisable : 1; - guint32 reserved_29_20 : 10; - guint32 Channel1Enable : 1; - guint32 Channel3Enable : 1; - } __attribute__((packed)) bits; /* nocheck:blocked */ -} BcmRegAPEMode; - -G_DEFINE_TYPE(FuBcm57xxRecoveryDevice, fu_bcm57xx_recovery_device, FU_TYPE_UDEV_DEVICE) - -#ifdef __ppc64__ -#define BARRIER() __asm__ volatile("sync 0\neieio\n" : : : "memory") -#else -#define BARRIER() __asm__ volatile("" : : : "memory"); -#endif - -static gboolean -fu_bcm57xx_recovery_device_bar_read(FuBcm57xxRecoveryDevice *self, - guint bar, - gsize offset, - guint32 *val, - GError **error) -{ - /* this should never happen */ - if (self->bar[bar].buf == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "BAR[%u] is not mapped!", - bar); - return FALSE; - } - - BARRIER(); - return fu_memcpy_safe((guint8 *)val, - sizeof(*val), - 0x0, /* dst */ - self->bar[bar].buf, - self->bar[bar].bufsz, - offset, - sizeof(*val), - error); -} - -static gboolean -fu_bcm57xx_recovery_device_bar_write(FuBcm57xxRecoveryDevice *self, - guint bar, - gsize offset, - guint32 val, - GError **error) -{ - /* this should never happen */ - if (self->bar[bar].buf == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "BAR[%u] is not mapped!", - bar); - return FALSE; - } - - BARRIER(); - if (!fu_memcpy_safe(self->bar[bar].buf, - self->bar[bar].bufsz, - offset, /* dst */ - (const guint8 *)&val, - sizeof(val), - 0x0, /* src */ - sizeof(val), - error)) - return FALSE; - BARRIER(); - return TRUE; -} - -static gboolean -fu_bcm57xx_recovery_device_nvram_disable(FuBcm57xxRecoveryDevice *self, GError **error) -{ - BcmRegNVMAccess tmp; - if (!fu_bcm57xx_recovery_device_bar_read(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_ACCESS, - &tmp.r32, - error)) - return FALSE; - tmp.bits.Enable = FALSE; - tmp.bits.WriteEnable = FALSE; - return fu_bcm57xx_recovery_device_bar_write(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_ACCESS, - tmp.r32, - error); -} - -static gboolean -fu_bcm57xx_recovery_device_nvram_enable(FuBcm57xxRecoveryDevice *self, GError **error) -{ - BcmRegNVMAccess tmp; - if (!fu_bcm57xx_recovery_device_bar_read(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_ACCESS, - &tmp.r32, - error)) - return FALSE; - tmp.bits.Enable = TRUE; - tmp.bits.WriteEnable = FALSE; - return fu_bcm57xx_recovery_device_bar_write(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_ACCESS, - tmp.r32, - error); -} - -static gboolean -fu_bcm57xx_recovery_device_nvram_enable_write(FuBcm57xxRecoveryDevice *self, GError **error) -{ - BcmRegNVMAccess tmp; - if (!fu_bcm57xx_recovery_device_bar_read(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_ACCESS, - &tmp.r32, - error)) - return FALSE; - tmp.bits.Enable = TRUE; - tmp.bits.WriteEnable = TRUE; - return fu_bcm57xx_recovery_device_bar_write(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_ACCESS, - tmp.r32, - error); -} - -static gboolean -fu_bcm57xx_recovery_device_nvram_acquire_lock(FuBcm57xxRecoveryDevice *self, GError **error) -{ - BcmRegNVMSoftwareArbitration tmp = {0}; - g_autoptr(GTimer) timer = g_timer_new(); - tmp.bits.ReqSet1 = 1; - if (!fu_bcm57xx_recovery_device_bar_write(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_SOFTWARE_ARBITRATION, - tmp.r32, - error)) - return FALSE; - do { - if (!fu_bcm57xx_recovery_device_bar_read(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_SOFTWARE_ARBITRATION, - &tmp.r32, - error)) - return FALSE; - if (tmp.bits.ArbWon1) - return TRUE; - if (g_timer_elapsed(timer, NULL) > 0.2) - break; - } while (TRUE); - - /* timed out */ - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_TIMED_OUT, - "timed out trying to acquire lock #1"); - return FALSE; -} - -static gboolean -fu_bcm57xx_recovery_device_nvram_release_lock(FuBcm57xxRecoveryDevice *self, GError **error) -{ - BcmRegNVMSoftwareArbitration tmp = {0}; - tmp.r32 = 0; - tmp.bits.ReqClr1 = 1; - return fu_bcm57xx_recovery_device_bar_write(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_SOFTWARE_ARBITRATION, - tmp.r32, - error); -} - -static gboolean -fu_bcm57xx_recovery_device_nvram_wait_done(FuBcm57xxRecoveryDevice *self, GError **error) -{ - BcmRegNVMCommand tmp = {0}; - g_autoptr(GTimer) timer = g_timer_new(); - do { - if (!fu_bcm57xx_recovery_device_bar_read(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_COMMAND, - &tmp.r32, - error)) - return FALSE; - if (tmp.bits.Done) - return TRUE; - if (g_timer_elapsed(timer, NULL) > 0.2) - break; - } while (TRUE); - - /* timed out */ - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT, "timed out"); - return FALSE; -} - -static gboolean -fu_bcm57xx_recovery_device_nvram_clear_done(FuBcm57xxRecoveryDevice *self, GError **error) -{ - BcmRegNVMCommand tmp = {0}; - tmp.bits.Done = 1; - return fu_bcm57xx_recovery_device_bar_write(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_COMMAND, - tmp.r32, - error); -} - -static gboolean -fu_bcm57xx_recovery_device_nvram_read(FuBcm57xxRecoveryDevice *self, - guint32 address, - guint32 *buf, - gsize bufsz, - FuProgress *progress, - GError **error) -{ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_set_steps(progress, bufsz); - for (guint i = 0; i < bufsz; i++) { - BcmRegNVMCommand tmp = {0}; - guint32 val32 = 0; - if (!fu_bcm57xx_recovery_device_nvram_clear_done(self, error)) - return FALSE; - if (!fu_bcm57xx_recovery_device_bar_write(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_ADDR, - address, - error)) - return FALSE; - tmp.bits.Doit = 1; - tmp.bits.First = i == 0; - tmp.bits.Last = i == bufsz - 1; - - if (!fu_bcm57xx_recovery_device_bar_write(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_COMMAND, - tmp.r32, - error)) - return FALSE; - if (!fu_bcm57xx_recovery_device_nvram_wait_done(self, error)) { - g_prefix_error(error, "failed to read @0x%x: ", address); - return FALSE; - } - if (!fu_bcm57xx_recovery_device_bar_read(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_READ, - &val32, - error)) - return FALSE; - buf[i] = GUINT32_FROM_BE(val32); /* nocheck:blocked */ - address += sizeof(guint32); - fu_progress_step_done(progress); - } - - /* success */ - return TRUE; -} - -static gboolean -fu_bcm57xx_recovery_device_nvram_write(FuBcm57xxRecoveryDevice *self, - guint32 address, - const guint32 *buf, - gsize bufsz_dwrds, - FuProgress *progress, - GError **error) -{ - const guint32 page_size_dwrds = 64; - - /* can only write in pages of 64 dwords */ - if (bufsz_dwrds % page_size_dwrds != 0 || - (address * sizeof(guint32)) % page_size_dwrds != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "can only write aligned with page size 0x%x", - page_size_dwrds); - return FALSE; - } - - fu_progress_set_id(progress, G_STRLOC); - fu_progress_set_steps(progress, bufsz_dwrds); - for (guint i = 0; i < bufsz_dwrds; i++) { - BcmRegNVMCommand tmp = {0}; - if (!fu_bcm57xx_recovery_device_nvram_clear_done(self, error)) - return FALSE; - if (!fu_bcm57xx_recovery_device_bar_write( - self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_WRITE, - GUINT32_TO_BE(buf[i]), /* nocheck:blocked */ - error)) - return FALSE; - if (!fu_bcm57xx_recovery_device_bar_write(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_ADDR, - address, - error)) - return FALSE; - tmp.bits.Wr = TRUE; - tmp.bits.Doit = TRUE; - tmp.bits.First = i % page_size_dwrds == 0; - tmp.bits.Last = (i + 1) % page_size_dwrds == 0; - if (!fu_bcm57xx_recovery_device_bar_write(self, - FU_BCM57XX_BAR_DEVICE, - REG_NVM_COMMAND, - tmp.r32, - error)) - return FALSE; - if (!fu_bcm57xx_recovery_device_nvram_wait_done(self, error)) { - g_prefix_error(error, "failed to write @0x%x: ", address); - return FALSE; - } - address += sizeof(guint32); - fu_progress_step_done(progress); - } - - /* success */ - return TRUE; -} - -static gboolean -fu_bcm57xx_recovery_device_detach(FuDevice *device, FuProgress *progress, GError **error) -{ - /* unbind tg3 */ - return fu_device_unbind_driver(device, error); -} - -static gboolean -fu_bcm57xx_recovery_device_attach(FuDevice *device, FuProgress *progress, GError **error) -{ - g_autoptr(GError) error_local = NULL; - - /* bind tg3, which might fail if the module is not compiled */ - if (!fu_device_bind_driver(device, "pci", "tg3", &error_local)) { - if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { - g_warning("failed to bind tg3: %s", error_local->message); - } else { - g_propagate_prefixed_error(error, - g_steal_pointer(&error_local), - "failed to bind tg3: "); - return FALSE; - } - } - - /* success */ - return TRUE; -} - -static gboolean -fu_bcm57xx_recovery_device_activate(FuDevice *device, FuProgress *progress, GError **error) -{ - BcmRegAPEMode mode = {0}; - FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE(device); - - /* halt */ - mode.bits.Halt = 1; - mode.bits.FastBoot = 0; - if (!fu_bcm57xx_recovery_device_bar_write(self, - FU_BCM57XX_BAR_APE, - REG_APE_MODE, - mode.r32, - error)) - return FALSE; - - /* boot */ - mode.bits.Halt = 0; - mode.bits.FastBoot = 0; - mode.bits.Reset = 1; - return fu_bcm57xx_recovery_device_bar_write(self, - FU_BCM57XX_BAR_APE, - REG_APE_MODE, - mode.r32, - error); -} - -static GBytes * -fu_bcm57xx_recovery_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) -{ - FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE(device); - gsize bufsz_dwrds = fu_device_get_firmware_size_max(FU_DEVICE(self)) / sizeof(guint32); - g_autofree guint32 *buf_dwrds = g_new0(guint32, bufsz_dwrds); - g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(FuDeviceLocker) locker2 = NULL; - - /* read from hardware */ - fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); - locker = fu_device_locker_new_full( - self, - (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_acquire_lock, - (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_release_lock, - error); - if (locker == NULL) - return NULL; - locker2 = - fu_device_locker_new_full(self, - (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_enable, - (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_disable, - error); - if (locker2 == NULL) - return NULL; - if (!fu_bcm57xx_recovery_device_nvram_read(self, - 0x0, - buf_dwrds, - bufsz_dwrds, - progress, - error)) - return NULL; - if (!fu_device_locker_close(locker2, error)) - return NULL; - return g_bytes_new(buf_dwrds, bufsz_dwrds * sizeof(guint32)); -} - -static FuFirmware * -fu_bcm57xx_recovery_device_prepare_firmware(FuDevice *device, - GInputStream *stream, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - g_autoptr(FuFirmware) firmware_bin = fu_firmware_new(); - g_autoptr(FuFirmware) firmware_tmp = fu_bcm57xx_firmware_new(); - - /* check is a NVRAM backup */ - if (!fu_firmware_parse_stream(firmware_tmp, stream, 0x0, flags, error)) { - g_prefix_error(error, "failed to parse new firmware: "); - return NULL; - } - if (!fu_bcm57xx_firmware_is_backup(FU_BCM57XX_FIRMWARE(firmware_tmp))) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "can only recover with backup firmware"); - return NULL; - } - if (!fu_firmware_parse_stream(firmware_bin, stream, 0x0, flags, error)) - return NULL; - return g_steal_pointer(&firmware_bin); -} - -static gboolean -fu_bcm57xx_recovery_device_write_firmware(FuDevice *device, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE(device); - const guint8 *buf; - gsize bufsz = 0; - gsize bufsz_dwrds; - g_autofree guint32 *buf_dwrds = NULL; - g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(FuDeviceLocker) locker2 = NULL; - g_autoptr(GBytes) blob = NULL; - - /* progress */ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 1, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 95, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, NULL); - - /* build the images into one linear blob of the correct size */ - blob = fu_firmware_write(firmware, error); - if (blob == NULL) - return FALSE; - fu_progress_step_done(progress); - - /* align into uint32_t buffer */ - buf = g_bytes_get_data(blob, &bufsz); - bufsz_dwrds = bufsz / sizeof(guint32); - buf_dwrds = g_new0(guint32, bufsz_dwrds); - if (!fu_memcpy_safe((guint8 *)buf_dwrds, - bufsz_dwrds * sizeof(guint32), - 0x0, /* dst */ - buf, - bufsz, - 0x0, /* src */ - bufsz, - error)) - return FALSE; - - /* hit hardware */ - locker = fu_device_locker_new_full( - self, - (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_acquire_lock, - (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_release_lock, - error); - if (locker == NULL) - return FALSE; - locker2 = fu_device_locker_new_full( - self, - (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_enable_write, - (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_disable, - error); - if (locker2 == NULL) - return FALSE; - if (!fu_bcm57xx_recovery_device_nvram_write(self, - 0x0, - buf_dwrds, - bufsz_dwrds, - fu_progress_get_child(progress), - error)) - return FALSE; - if (!fu_device_locker_close(locker2, error)) - return FALSE; - if (!fu_device_locker_close(locker, error)) - return FALSE; - fu_progress_step_done(progress); - - /* reset APE */ - if (!fu_device_activate(device, fu_progress_get_child(progress), error)) - return FALSE; - fu_progress_step_done(progress); - - /* success */ - return TRUE; -} - -static gboolean -fu_bcm57xx_recovery_device_setup(FuDevice *device, GError **error) -{ - FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE(device); - guint32 fwversion = 0; - g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(FuDeviceLocker) locker2 = NULL; - g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); - - /* progress */ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "enable"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 80, "nvram"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "veraddr"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "version"); - - locker = fu_device_locker_new_full( - self, - (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_acquire_lock, - (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_release_lock, - error); - if (locker == NULL) - return FALSE; - locker2 = - fu_device_locker_new_full(self, - (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_enable, - (FuDeviceLockerFunc)fu_bcm57xx_recovery_device_nvram_disable, - error); - if (locker2 == NULL) - return FALSE; - fu_progress_step_done(progress); - - /* get NVRAM version */ - if (!fu_bcm57xx_recovery_device_nvram_read(self, - BCM_NVRAM_STAGE1_BASE + BCM_NVRAM_STAGE1_VERSION, - &fwversion, - 1, - fu_progress_get_child(progress), - error)) - return FALSE; - fu_progress_step_done(progress); - if (fwversion != 0x0) { - /* this is only set on the OSS firmware */ - fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); - fu_device_set_version_raw(device, GUINT32_FROM_BE(fwversion)); /* nocheck:blocked */ - fu_device_set_branch(device, BCM_FW_BRANCH_OSS_FIRMWARE); - fu_progress_step_done(progress); - fu_progress_step_done(progress); - } else { - guint32 bufver[4] = {0x0}; - guint32 veraddr = 0; - g_autoptr(Bcm57xxVeritem) veritem = NULL; - - /* fall back to the string, e.g. '5719-v1.43' */ - if (!fu_bcm57xx_recovery_device_nvram_read(self, - BCM_NVRAM_STAGE1_BASE + - BCM_NVRAM_STAGE1_VERADDR, - &veraddr, - 1, - fu_progress_get_child(progress), - error)) - return FALSE; - fu_progress_step_done(progress); - veraddr = GUINT32_FROM_BE(veraddr); /* nocheck:blocked */ - if (veraddr > BCM_PHYS_ADDR_DEFAULT) - veraddr -= BCM_PHYS_ADDR_DEFAULT; - if (!fu_bcm57xx_recovery_device_nvram_read(self, - BCM_NVRAM_STAGE1_BASE + veraddr, - bufver, - 4, - fu_progress_get_child(progress), - error)) - return FALSE; - fu_progress_step_done(progress); - veritem = fu_bcm57xx_veritem_new((guint8 *)bufver, sizeof(bufver)); - if (veritem != NULL) { - fu_device_set_version(device, veritem->version); /* nocheck:set-version */ - fu_device_set_branch(device, veritem->branch); - fu_device_set_version_format(device, veritem->verfmt); - } - } - - return TRUE; -} - -static gboolean -fu_bcm57xx_recovery_device_open(FuDevice *device, GError **error) -{ -#ifdef HAVE_MMAN_H - FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE(device); - FuUdevDevice *udev_device = FU_UDEV_DEVICE(device); - const gchar *sysfs_path = fu_udev_device_get_sysfs_path(udev_device); -#endif - -#ifdef RUNNING_ON_VALGRIND - /* this can't work */ - if (RUNNING_ON_VALGRIND) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "cannot mmap'ing BARs when using valgrind"); - return FALSE; - } -#endif - -#ifdef HAVE_MMAN_H - /* map BARs */ - for (guint i = 0; i < FU_BCM57XX_BAR_MAX; i++) { - int memfd; - struct stat st; - g_autofree gchar *fn = NULL; - g_autofree gchar *resfn = NULL; - - /* open 64 bit resource */ - resfn = g_strdup_printf("resource%u", i * 2); - fn = g_build_filename(sysfs_path, resfn, NULL); - memfd = open(fn, O_RDWR | O_SYNC); - if (memfd < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "error opening %s", - fn); - return FALSE; - } - if (fstat(memfd, &st) < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "could not stat %s", - fn); - close(memfd); - return FALSE; - } - - /* mmap */ - g_debug("mapping BAR[%u] %s for 0x%x bytes", i, fn, (guint)st.st_size); - self->bar[i].buf = - (guint8 *)mmap(0, st.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, memfd, 0); - self->bar[i].bufsz = st.st_size; - close(memfd); - if (self->bar[i].buf == MAP_FAILED) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "could not mmap %s: %s", - fn, - g_strerror(errno)); - return FALSE; - } - } - - /* success */ - return TRUE; -#else - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "mmap() not supported as sys/mman.h not available"); - return FALSE; -#endif -} - -static gboolean -fu_bcm57xx_recovery_device_close(FuDevice *device, GError **error) -{ -#ifdef HAVE_MMAN_H - FuBcm57xxRecoveryDevice *self = FU_BCM57XX_RECOVERY_DEVICE(device); - - /* unmap BARs */ - for (guint i = 0; i < FU_BCM57XX_BAR_MAX; i++) { - if (self->bar[i].buf == NULL) - continue; - g_debug("unmapping BAR[%u]", i); - munmap(self->bar[i].buf, self->bar[i].bufsz); - self->bar[i].buf = NULL; - self->bar[i].bufsz = 0; - } - - /* success */ - return TRUE; -#else - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "munmap() not supported as sys/mman.h not available"); - return FALSE; -#endif -} - -static void -fu_bcm57xx_recovery_device_set_progress(FuDevice *self, FuProgress *progress) -{ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); -} - -static gchar * -fu_bcm57xx_recovery_device_convert_version(FuDevice *device, guint64 version_raw) -{ - return fu_version_from_uint32(version_raw, fu_device_get_version_format(device)); -} - -static void -fu_bcm57xx_recovery_device_init(FuBcm57xxRecoveryDevice *self) -{ - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_BACKUP_BEFORE_INSTALL); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - fu_device_add_protocol(FU_DEVICE(self), "com.broadcom.bcm57xx"); - fu_device_add_icon(FU_DEVICE(self), "network-wired"); - fu_device_set_logical_id(FU_DEVICE(self), "recovery"); - - /* other values are set from a quirk */ - fu_device_set_firmware_size(FU_DEVICE(self), BCM_FIRMWARE_SIZE); - - /* no BARs mapped */ - for (guint i = 0; i < FU_BCM57XX_BAR_MAX; i++) { - self->bar[i].buf = NULL; - self->bar[i].bufsz = 0; - } -} - -static gboolean -fu_bcm57xx_recovery_device_probe(FuDevice *device, GError **error) -{ - return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "pci", error); -} - -static void -fu_bcm57xx_recovery_device_class_init(FuBcm57xxRecoveryDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->activate = fu_bcm57xx_recovery_device_activate; - device_class->prepare_firmware = fu_bcm57xx_recovery_device_prepare_firmware; - device_class->setup = fu_bcm57xx_recovery_device_setup; - device_class->reload = fu_bcm57xx_recovery_device_setup; - device_class->open = fu_bcm57xx_recovery_device_open; - device_class->close = fu_bcm57xx_recovery_device_close; - device_class->write_firmware = fu_bcm57xx_recovery_device_write_firmware; - device_class->dump_firmware = fu_bcm57xx_recovery_device_dump_firmware; - device_class->attach = fu_bcm57xx_recovery_device_attach; - device_class->detach = fu_bcm57xx_recovery_device_detach; - device_class->probe = fu_bcm57xx_recovery_device_probe; - device_class->set_progress = fu_bcm57xx_recovery_device_set_progress; - device_class->convert_version = fu_bcm57xx_recovery_device_convert_version; -} diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-recovery-device.h fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-recovery-device.h --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-recovery-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-recovery-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -/* - * Copyright 2020 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_BCM57XX_RECOVERY_DEVICE (fu_bcm57xx_recovery_device_get_type()) -G_DECLARE_FINAL_TYPE(FuBcm57xxRecoveryDevice, - fu_bcm57xx_recovery_device, - FU, - BCM57XX_RECOVERY_DEVICE, - FuUdevDevice) diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-stage1-image.c fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-stage1-image.c --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-stage1-image.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-stage1-image.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,7 +18,7 @@ static gboolean fu_bcm57xx_stage1_image_parse(FuFirmware *image, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize streamsz = 0; @@ -26,7 +26,7 @@ g_autoptr(GInputStream) stream_nocrc = NULL; /* verify CRC */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { if (!fu_bcm57xx_verify_crc(stream, error)) return FALSE; } @@ -52,7 +52,7 @@ return FALSE; if (veraddr != 0x0) { guint32 bufver[4] = {'\0'}; - g_autoptr(Bcm57xxVeritem) veritem = NULL; + g_autoptr(FuBcm57xxVeritem) veritem = NULL; if (veraddr < BCM_PHYS_ADDR_DEFAULT + sizeof(bufver)) { g_set_error(error, FWUPD_ERROR, diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-stage2-image.c fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-stage2-image.c --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx-stage2-image.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx-stage2-image.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,12 +18,12 @@ static gboolean fu_bcm57xx_stage2_image_parse(FuFirmware *image, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize streamsz = 0; g_autoptr(GInputStream) stream_nocrc = NULL; - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { if (!fu_bcm57xx_verify_crc(stream, error)) return FALSE; } diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx.rs fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx.rs --- fwupd-2.0.8/plugins/bcm57xx/fu-bcm57xx.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-bcm57xx.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,29 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[derive(ParseStream)] +#[repr(C, packed)] +struct FuStructBcm57xxNvramHeader { + magic: u32be, + phys_addr: u32be, + size_wrds: u32be, + offset: u32be, + crc: u32be, +} + +#[derive(ParseStream)] +#[repr(C, packed)] +struct FuStructBcm57xxNvramDirectory { + addr: u32be, + size_wrds: u32be, + offset: u32be, +} + +#[derive(ParseStream, New, ToBytes)] +#[repr(C, packed)] +struct FuStructBcm57xxNvramInfo { + mac_addr: [u32be; 11], + device: u16be, + vendor: u16be, + _reserved: [u8; 92], +} diff -Nru fwupd-2.0.8/plugins/bcm57xx/fu-self-test.c fwupd-2.0.20/plugins/bcm57xx/fu-self-test.c --- fwupd-2.0.8/plugins/bcm57xx/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -36,9 +36,9 @@ static void fu_bcm57xx_common_veritem_func(void) { - g_autoptr(Bcm57xxVeritem) veritem1 = NULL; - g_autoptr(Bcm57xxVeritem) veritem2 = NULL; - g_autoptr(Bcm57xxVeritem) veritem3 = NULL; + g_autoptr(FuBcm57xxVeritem) veritem1 = NULL; + g_autoptr(FuBcm57xxVeritem) veritem2 = NULL; + g_autoptr(FuBcm57xxVeritem) veritem3 = NULL; guint8 bufver[16] = {0x0}; fu_bcm57xx_create_verbuf(bufver, sizeof(bufver), "5719-v1.43"); @@ -84,7 +84,12 @@ blob = fu_bytes_get_contents(fn, &error); g_assert_no_error(error); g_assert_nonnull(blob); - ret = fu_firmware_parse_bytes(firmware, blob, 0x0, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + ret = fu_firmware_parse_bytes(firmware, + blob, + 0x0, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH | + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, + &error); g_assert_no_error(error); g_assert_true(ret); images = fu_firmware_get_images(firmware); diff -Nru fwupd-2.0.8/plugins/bcm57xx/meson.build fwupd-2.0.20/plugins/bcm57xx/meson.build --- fwupd-2.0.8/plugins/bcm57xx/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,16 +1,19 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginBcm57xx"'] plugins += {meson.current_source_dir().split('/')[-1]: true} plugin_quirks += files('bcm57xx.quirk') plugin_builtin_bcm57xx = static_library('fu_plugin_bcm57xx', + rustgen.process( + 'fu-bcm57xx.rs', # fuzzing + ), sources: [ 'fu-bcm57xx-plugin.c', 'fu-bcm57xx-common.c', # fuzzing 'fu-bcm57xx-device.c', 'fu-bcm57xx-dict-image.c', # fuzzing 'fu-bcm57xx-firmware.c', # fuzzing - 'fu-bcm57xx-recovery-device.c', 'fu-bcm57xx-stage1-image.c', # fuzzing 'fu-bcm57xx-stage2-image.c', # fuzzing ], @@ -49,6 +52,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', @@ -56,4 +60,3 @@ ) test('bcm57xx-self-test', e, env: env) endif -endif diff -Nru fwupd-2.0.8/plugins/bcm57xx/tests/dell-kh08p.json fwupd-2.0.20/plugins/bcm57xx/tests/dell-kh08p.json --- fwupd-2.0.8/plugins/bcm57xx/tests/dell-kh08p.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bcm57xx/tests/dell-kh08p.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/6cf165037a381eb29c183319e031def6b87e3ce955781ecf73f28751a1365db2-kh08p-bcm5719-0.4.62.cab", + "url": "6cf165037a381eb29c183319e031def6b87e3ce955781ecf73f28751a1365db2-kh08p-bcm5719-0.4.62.cab", "components": [ { "version": "0.4.62", @@ -14,7 +14,7 @@ ] }, { - "url": "https://fwupd.org/downloads/c786be1c525ad062c5af8983474a9412f83f5251efb767fe9cb414a3a124b8ce-kh08p-bcm5719-0.4.64.cab", + "url": "c786be1c525ad062c5af8983474a9412f83f5251efb767fe9cb414a3a124b8ce-kh08p-bcm5719-0.4.64.cab", "components": [ { "version": "0.4.64", diff -Nru fwupd-2.0.8/plugins/bios/fu-bios-plugin.c fwupd-2.0.20/plugins/bios/fu-bios-plugin.c --- fwupd-2.0.8/plugins/bios/fu-bios-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bios/fu-bios-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -22,10 +22,23 @@ vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BIOS_VENDOR); if (g_strcmp0(vendor, "coreboot") == 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "system uses coreboot"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "system uses coreboot"); return FALSE; } + /* check if UEFI is supported by the hardware */ + if (!fu_context_has_flag(ctx, FU_CONTEXT_FLAG_SMBIOS_UEFI_ENABLED)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "system does not support UEFI"); + return FALSE; + } + + /* success */ return TRUE; } @@ -34,7 +47,6 @@ { FuContext *ctx = fu_plugin_get_context(plugin); FuEfivars *efivars = fu_context_get_efivars(ctx); - g_autofree gchar *sysfsfwdir = NULL; g_autofree gchar *esrt_path = NULL; g_autoptr(GError) error_local = NULL; @@ -46,11 +58,13 @@ } /* get the directory of ESRT entries */ - sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - esrt_path = g_build_filename(sysfsfwdir, "efi", "esrt", NULL); + esrt_path = fu_path_build(FU_PATH_KIND_SYSFSDIR_FW, "efi", "esrt", NULL); if (!g_file_test(esrt_path, G_FILE_TEST_IS_DIR)) { - fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED); - fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); + /* don't show the warning in a hypervisor as capsule updates are not expected */ + if (!fu_context_has_flag(ctx, FU_CONTEXT_FLAG_IS_HYPERVISOR)) { + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED); + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_USER_WARNING); + } return TRUE; } diff -Nru fwupd-2.0.8/plugins/bios/meson.build fwupd-2.0.20/plugins/bios/meson.build --- fwupd-2.0.8/plugins/bios/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bios/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if allow_uefi_capsule and (host_cpu == 'x86' or host_cpu == 'x86_64') +allow_uefi or subdir_done() +host_cpu in ['x86', 'x86_64'] or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginBios"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -11,4 +13,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/bnr-dp/README.md fwupd-2.0.20/plugins/bnr-dp/README.md --- fwupd-2.0.8/plugins/bnr-dp/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bnr-dp/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -24,6 +24,8 @@ These devices build their GUIDs by adding the `OUI` from the DpAux DPCD with additional B&R-specific device, variant and revision metadata, e.g. +- `DPAUX\OUI_00006065&DEV_00002F1A` +- `DPAUX\OUI_00006065&DEV_00002F1A&VARIANT_00000000` - `DPAUX\OUI_00006065&DEV_00002F1A&VARIANT_00000000&HW_REV_A0` ## Update Behavior @@ -51,10 +53,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.7`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people -should be consulted before making major or functional changes: - -- Thomas Mühlbacher: @tmuehlbacher-bnr diff -Nru fwupd-2.0.8/plugins/bnr-dp/fu-bnr-dp-device.c fwupd-2.0.20/plugins/bnr-dp/fu-bnr-dp-device.c --- fwupd-2.0.8/plugins/bnr-dp/fu-bnr-dp-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bnr-dp/fu-bnr-dp-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -71,7 +71,7 @@ guint8 error_byte = fu_struct_bnr_dp_aux_status_get_error(st_status); guint8 error_code = error_byte & 0x0F; - if (error_byte & FU_BNR_DP_AUX_STATUS_FLAGS_ERROR) { + if (error_byte & FU_BNR_DP_AUX_STATUS_FLAG_ERROR) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_READ, @@ -88,7 +88,7 @@ { guint8 error_byte = fu_struct_bnr_dp_aux_status_get_error(st_status); - if (error_byte & FU_BNR_DP_AUX_STATUS_FLAGS_BUSY) { + if (error_byte & FU_BNR_DP_AUX_STATUS_FLAG_BUSY) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "device is busy"); return FALSE; } @@ -108,14 +108,14 @@ g_autoptr(FuStructBnrDpAuxTxHeader) st_header = fu_struct_bnr_dp_aux_tx_header_new(); if (!fu_struct_bnr_dp_aux_tx_header_set_request(st_header, st_request, error)) { - g_prefix_error(error, "failed to set request: "); + g_prefix_error_literal(error, "failed to set request: "); return FALSE; } /* write optional data */ checksum = fu_bnr_dp_device_xor_checksum(FU_BNR_DP_CHECKSUM_INIT_TX, - st_request->data, - st_request->len); + st_request->buf->data, + st_request->buf->len); if (buf != NULL && bufsz > 0) { if (!fu_dpaux_device_write(FU_DPAUX_DEVICE(self), FU_BNR_DP_DEVICE_DATA_OFFSET, @@ -123,7 +123,7 @@ bufsz, FU_BNR_DP_DEVICE_DPAUX_TIMEOUT_MSEC, error)) { - g_prefix_error(error, "failed to write request: "); + g_prefix_error_literal(error, "failed to write request: "); return FALSE; } @@ -134,8 +134,8 @@ /* write header to kick off processing by the device */ return fu_dpaux_device_write(FU_DPAUX_DEVICE(self), FU_BNR_DP_DEVICE_HEADER_OFFSET, - st_header->data, - st_header->len, + st_header->buf->data, + st_header->buf->len, FU_BNR_DP_DEVICE_DPAUX_TIMEOUT_MSEC, error); } @@ -169,8 +169,8 @@ return FALSE; actual_checksum = fu_bnr_dp_device_xor_checksum(FU_BNR_DP_CHECKSUM_INIT_RX, - st_response->data, - st_response->len); + st_response->buf->data, + st_response->buf->len); /* read command output data */ g_byte_array_set_size(data, fu_struct_bnr_dp_aux_response_get_data_len(st_response)); @@ -369,7 +369,7 @@ FU_BNR_DP_DEVICE_DATA_CHUNK_SIZE, error); if (st_request == NULL) { - g_prefix_error(error, "failed to build request: "); + g_prefix_error_literal(error, "failed to build request: "); return FALSE; } @@ -409,7 +409,7 @@ return TRUE; } -static FuStructBnrDpPayloadHeader * +static FuStructBnrDpFactoryData * fu_bnr_dp_device_factory_data(FuBnrDpDevice *self, FuBnrDpModuleNumber module_number, GError **error) @@ -545,6 +545,11 @@ oui = g_strdup_printf("%06X", fu_dpaux_device_get_dpcd_ieee_oui(FU_DPAUX_DEVICE(device))); fu_device_build_vendor_id(FU_DEVICE(self), "OUI", oui); + + if (!fu_device_build_instance_id(device, error, "DPAUX", "OUI", "DEV", NULL)) + return FALSE; + if (!fu_device_build_instance_id(device, error, "DPAUX", "OUI", "DEV", "VARIANT", NULL)) + return FALSE; return fu_device_build_instance_id(device, error, "DPAUX", @@ -580,7 +585,7 @@ /* the flash is 3 * `FU_BNR_DP_FW_SIZE`; first third is boot loader, then low and high * images */ - if ((flags & FU_BNR_DP_PAYLOAD_FLAGS_BOOT_AREA) == (guint)FU_BNR_DP_BOOT_AREA_HIGH) + if ((flags & FU_BNR_DP_PAYLOAD_FLAG_BOOT_AREA) == (guint)FU_BNR_DP_BOOT_AREA_HIGH) offset *= 2; image = fu_bnr_dp_device_read_data(self, @@ -641,7 +646,7 @@ fu_bnr_dp_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuBnrDpDevice *self = FU_BNR_DP_DEVICE(device); @@ -761,7 +766,7 @@ fu_progress_get_child(progress), error); if (read_back == NULL) { - g_prefix_error(error, "failed to read data: "); + g_prefix_error_literal(error, "failed to read data: "); return FALSE; } if (!fu_memcmp_safe(g_bytes_get_data(bytes, NULL), @@ -779,7 +784,7 @@ /* apply new firmware by resetting the device */ if (!fu_bnr_dp_device_reset(self, FU_BNR_DP_MODULE_NUMBER_RECEIVER, error)) { - g_prefix_error(error, "failed to reset: "); + g_prefix_error_literal(error, "failed to reset: "); return FALSE; } @@ -795,9 +800,9 @@ } boot_area_pre = (fu_struct_bnr_dp_info_flags_get_inner(st_info_flags_pre) & - FU_BNR_DP_INFO_FLAGS_BOOT_AREA); + FU_BNR_DP_INFO_FLAG_BOOT_AREA); boot_area_post = (fu_struct_bnr_dp_info_flags_get_inner(st_info_flags_post) & - FU_BNR_DP_INFO_FLAGS_BOOT_AREA); + FU_BNR_DP_INFO_FLAG_BOOT_AREA); if (boot_area_pre == boot_area_post) { g_set_error( error, @@ -813,13 +818,13 @@ } static gchar * -fu_bnr_dp_device_convert_version(FuDevice *self, guint64 version_raw) +fu_bnr_dp_device_convert_version(FuDevice *device, guint64 version_raw) { return fu_bnr_dp_version_to_string(version_raw); } static void -fu_bnr_dp_device_set_progress(FuDevice *self, FuProgress *progress) +fu_bnr_dp_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -833,9 +838,10 @@ fu_bnr_dp_device_init(FuBnrDpDevice *self) { fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); - fu_device_set_vendor(FU_DEVICE(self), "B&R Industrial Automation GmbH"); + fu_device_set_vendor(FU_DEVICE(self), "B&R"); fu_device_add_protocol(FU_DEVICE(self), "com.br-automation.dpaux"); - fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_set_summary(FU_DEVICE(self), "DisplayPort receiver"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_VIDEO_DISPLAY); fu_device_set_firmware_size_max(FU_DEVICE(self), FU_BNR_DP_FIRMWARE_SIZE_MAX); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); diff -Nru fwupd-2.0.8/plugins/bnr-dp/fu-bnr-dp-firmware.c fwupd-2.0.20/plugins/bnr-dp/fu-bnr-dp-firmware.c --- fwupd-2.0.8/plugins/bnr-dp/fu-bnr-dp-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bnr-dp/fu-bnr-dp-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,19 +13,19 @@ #include "fu-bnr-dp-struct.h" struct _FuBnrDpFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; /* mandatory XML header attributes, not part of payload. additionally, "Ver" (version) is * also mandatory */ - guint64 device_id; /* Dev */ - gchar *usage; /* Use */ - gchar function; /* Fct */ - guint64 variant; /* Var */ - guint64 payload_length; /* Len */ - guint16 payload_checksum; /* Chk */ - gchar *material; /* Mat */ - gchar *creation_date; /* Date (nullable) */ - gchar *comment; /* Rem (nullable) */ + guint64 device_id; /* dev */ + gchar *usage; /* use */ + gchar function; /* fct */ + guint64 variant; /* var */ + guint64 payload_length; /* len */ + guint16 payload_checksum; /* chk */ + gchar *material; /* mat */ + gchar *creation_date; /* date (nullable) */ + gchar *comment; /* rem (nullable) */ }; G_DEFINE_TYPE(FuBnrDpFirmware, fu_bnr_dp_firmware, FU_TYPE_FIRMWARE) @@ -47,7 +47,7 @@ } static gchar * -fu_bnr_dp_firmware_convert_version(FuFirmware *self, guint64 version_raw) +fu_bnr_dp_firmware_convert_version(FuFirmware *firmware, guint64 version_raw) { return fu_bnr_dp_version_to_string(version_raw); } @@ -270,7 +270,7 @@ static gboolean fu_bnr_dp_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuBnrDpFirmware *self = FU_BNR_DP_FIRMWARE(firmware); @@ -289,6 +289,7 @@ if (!fu_input_stream_find(stream, header_separator, sizeof(header_separator), + 0x0, &separator_idx, error)) return FALSE; @@ -448,7 +449,7 @@ /* check that the current CRC was correct */ crc = fu_crc16(FU_CRC_KIND_B16_BNR, - st_header->data, + st_header->buf->data, FU_STRUCT_BNR_DP_PAYLOAD_HEADER_SIZE - sizeof(crc)); if (fu_struct_bnr_dp_payload_header_get_crc(st_header) != crc) { g_set_error( @@ -469,16 +470,16 @@ fu_struct_bnr_dp_payload_header_set_flags( st_header, fu_struct_bnr_dp_payload_header_get_flags(st_header) & - ~FU_BNR_DP_PAYLOAD_FLAGS_CRC_ERROR); + ~FU_BNR_DP_PAYLOAD_FLAG_CRC_ERROR); /* update checksum */ crc = fu_crc16(FU_CRC_KIND_B16_BNR, - st_header->data, + st_header->buf->data, FU_STRUCT_BNR_DP_PAYLOAD_HEADER_SIZE - sizeof(crc)); fu_struct_bnr_dp_payload_header_set_crc(st_header, crc); - patch = g_bytes_new(st_header->data, st_header->len); + patch = fu_struct_bnr_dp_payload_header_to_bytes(st_header); fu_firmware_add_patch(FU_FIRMWARE(self), FU_BNR_DP_FIRMWARE_HEADER_OFFSET, patch); return TRUE; @@ -490,7 +491,7 @@ const FuStructBnrDpFactoryData *st_factory_data, const FuStructBnrDpPayloadHeader *st_active_header, const FuStructBnrDpPayloadHeader *st_fw_header, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint64 active_version = 0; diff -Nru fwupd-2.0.8/plugins/bnr-dp/fu-bnr-dp-firmware.h fwupd-2.0.20/plugins/bnr-dp/fu-bnr-dp-firmware.h --- fwupd-2.0.8/plugins/bnr-dp/fu-bnr-dp-firmware.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bnr-dp/fu-bnr-dp-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -39,5 +39,5 @@ const FuStructBnrDpFactoryData *st_factory_data, const FuStructBnrDpPayloadHeader *st_active_header, const FuStructBnrDpPayloadHeader *st_fw_header, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) G_GNUC_NON_NULL(1, 2, 3, 4); diff -Nru fwupd-2.0.8/plugins/bnr-dp/fu-bnr-dp.rs fwupd-2.0.20/plugins/bnr-dp/fu-bnr-dp.rs --- fwupd-2.0.8/plugins/bnr-dp/fu-bnr-dp.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bnr-dp/fu-bnr-dp.rs 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,7 @@ CrcError = 1 << 1, } -#[derive(Default, Setters, Parse)] +#[derive(Default, Setters, Parse, ToBytes)] #[repr(C, packed)] struct FuStructBnrDpPayloadHeader { id: [char; 4] == "DP0R", diff -Nru fwupd-2.0.8/plugins/bnr-dp/fu-self-test.c fwupd-2.0.20/plugins/bnr-dp/fu-self-test.c --- fwupd-2.0.8/plugins/bnr-dp/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bnr-dp/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -31,7 +31,7 @@ g_assert_true(ret); csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); g_assert_no_error(error); - g_assert_cmpstr(csum1, ==, "e5d645902551f55258827223905fb097cf3af58c"); + g_assert_cmpstr(csum1, ==, "b83504af44c2a53561f9e5f25fb133903e1c19fc"); /* ensure we can round-trip */ xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); diff -Nru fwupd-2.0.8/plugins/bnr-dp/meson.build fwupd-2.0.20/plugins/bnr-dp/meson.build --- fwupd-2.0.8/plugins/bnr-dp/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bnr-dp/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,5 @@ +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginBnrDp"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -39,6 +41,7 @@ plugin_libs, ], install: true, + install_tag: 'tests', install_dir: installed_test_bindir, install_rpath: libdir_pkg, c_args: ['-DSRCDIR="@0@"'.format(meson.current_source_dir())], diff -Nru fwupd-2.0.8/plugins/bnr-dp/tests/bnr-dp.json fwupd-2.0.20/plugins/bnr-dp/tests/bnr-dp.json --- fwupd-2.0.8/plugins/bnr-dp/tests/bnr-dp.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/bnr-dp/tests/bnr-dp.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/5a909ed2e4997518a8be94003042a53ed2d3bb7676f23745d6da0b21875cf03f-bnr-dp-0.13-test.cab", - "emulation-url": "https://fwupd.org/downloads/2d65498250eab4800c250b9499c3595be165762154f41dbfa1118797f5004636-bnr-dp-0.13-dpaux-dpcd-check-minimal.zip", + "url": "e4bf3652666625df2bbcfc851709090d5765ed37323f6becb1f05b4b45be1e2f-bnr-dp-0.13-test.cab", + "emulation-url": "2d65498250eab4800c250b9499c3595be165762154f41dbfa1118797f5004636-bnr-dp-0.13-dpaux-dpcd-check-minimal.zip", "components": [ { "version": "0.13", diff -Nru fwupd-2.0.8/plugins/ccgx/README.md fwupd-2.0.20/plugins/ccgx/README.md --- fwupd-2.0.8/plugins/ccgx/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -123,10 +123,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.4.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Brent Wu: @IfxBrent diff -Nru fwupd-2.0.8/plugins/ccgx/fu-ccgx-firmware.c fwupd-2.0.20/plugins/ccgx/fu-ccgx-firmware.c --- fwupd-2.0.8/plugins/ccgx/fu-ccgx-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx/fu-ccgx-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,7 +13,7 @@ #include "fu-ccgx-firmware.h" struct _FuCcgxFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; GPtrArray *records; guint16 app_type; guint16 silicon_id; @@ -80,7 +80,7 @@ static gboolean fu_ccgx_firmware_add_record(FuCcgxFirmware *self, GString *token, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint16 buflen; @@ -125,7 +125,7 @@ rcd->data = g_bytes_new(data->data, data->len); /* verify 2s complement checksum */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint8 checksum_file; if (!fu_firmware_strparse_uint8_safe(token->str, token->len, @@ -161,7 +161,7 @@ } static gboolean -fu_ccgx_firmware_parse_md_block(FuCcgxFirmware *self, FwupdInstallFlags flags, GError **error) +fu_ccgx_firmware_parse_md_block(FuCcgxFirmware *self, FuFirmwareParseFlags flags, GError **error) { FuCcgxFirmwareRecord *rcd; gsize bufsz = 0; @@ -170,7 +170,7 @@ guint32 rcd_version_idx = 0; guint32 version = 0; guint8 checksum_calc = 0; - g_autoptr(GByteArray) st_metadata = NULL; + g_autoptr(FuStructCcgxMetadataHdr) st_metadata = NULL; /* sanity check */ if (self->records->len == 0) { @@ -232,7 +232,7 @@ return FALSE; } checksum_calc = 1 + ~checksum_calc; - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { if (fu_struct_ccgx_metadata_hdr_get_fw_checksum(st_metadata) != checksum_calc) { g_set_error(error, FWUPD_ERROR, @@ -281,7 +281,7 @@ typedef struct { FuCcgxFirmware *self; - FwupdInstallFlags flags; + FuFirmwareParseFlags flags; } FuCcgxFirmwareTokenHelper; static gboolean @@ -345,7 +345,7 @@ static gboolean fu_ccgx_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuCcgxFirmware *self = FU_CCGX_FIRMWARE(firmware); @@ -363,7 +363,7 @@ /* parse metadata block */ if (!fu_ccgx_firmware_parse_md_block(self, flags, error)) { - g_prefix_error(error, "failed to parse metadata: "); + g_prefix_error_literal(error, "failed to parse metadata: "); return FALSE; } @@ -410,7 +410,7 @@ const guint8 *fwbuf; g_autoptr(GByteArray) buf = g_byte_array_new(); g_autoptr(GByteArray) mdbuf = g_byte_array_new(); - g_autoptr(GByteArray) st_metadata = fu_struct_ccgx_metadata_hdr_new(); + g_autoptr(FuStructCcgxMetadataHdr) st_metadata = fu_struct_ccgx_metadata_hdr_new(); g_autoptr(GBytes) fw = NULL; g_autoptr(FuChunkArray) chunks = NULL; g_autoptr(GString) str = g_string_new(NULL); @@ -460,10 +460,10 @@ if (!fu_memcpy_safe(mdbuf->data, mdbuf->len, 0x40, /* dst */ - st_metadata->data, - st_metadata->len, + st_metadata->buf->data, + st_metadata->buf->len, 0x0, /* src */ - st_metadata->len, + st_metadata->buf->len, error)) return NULL; fu_ccgx_firmware_write_record(str, diff -Nru fwupd-2.0.8/plugins/ccgx/fu-ccgx-hid-device.c fwupd-2.0.20/plugins/ccgx/fu-ccgx-hid-device.c --- fwupd-2.0.8/plugins/ccgx/fu-ccgx-hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx/fu-ccgx-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -31,7 +31,7 @@ FU_CCGX_HID_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "switch to HPI mode error: "); + g_prefix_error_literal(error, "switch to HPI mode error: "); return FALSE; } return TRUE; @@ -77,7 +77,7 @@ } static void -fu_ccgx_hid_device_set_progress(FuDevice *self, FuProgress *progress) +fu_ccgx_hid_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); diff -Nru fwupd-2.0.8/plugins/ccgx/fu-ccgx-hpi-common.h fwupd-2.0.20/plugins/ccgx/fu-ccgx-hpi-common.h --- fwupd-2.0.8/plugins/ccgx/fu-ccgx-hpi-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx/fu-ccgx-hpi-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,6 +8,11 @@ #include +/* + * NOTE: DO NOT ALLOW ANY MORE MAGIC CONSTANTS IN THIS FILE + * nocheck:magic-defines=33 + */ + #define I2C_READ_WRITE_DELAY_MS 10 /* ms */ #define CY_SCB_INDEX_POS 15 @@ -33,79 +38,6 @@ /* max i2c frequency */ #define FU_CCGX_HPI_FREQ 400000 -typedef enum { - CY_GET_VERSION_CMD = 0xB0, /* get the version of the boot-loader - * value = 0, index = 0, length = 4; - * data_in = 32 bit version */ - CY_GET_SIGNATURE_CMD = 0xBD, /* get the signature of the firmware - * It is suppose to be 'CYUS' for normal firmware - * and 'CYBL' for Bootloader */ - CY_UART_GET_CONFIG_CMD = 0xC0, /* retrieve the 16 byte UART configuration information - * MS bit of value indicates the SCB index - * length = 16, data_in = 16 byte configuration */ - CY_UART_SET_CONFIG_CMD, /* update the 16 byte UART configuration information - * MS bit of value indicates the SCB index. - * length = 16, data_out = 16 byte configuration information */ - CY_SPI_GET_CONFIG_CMD, /* retrieve the 16 byte SPI configuration information - * MS bit of value indicates the SCB index - * length = 16, data_in = 16 byte configuration */ - CY_SPI_SET_CONFIG_CMD, /* update the 16 byte SPI configuration information - * MS bit of value indicates the SCB index - * length = 16, data_out = 16 byte configuration information */ - CY_I2C_GET_CONFIG_CMD, /* retrieve the 16 byte I2C configuration information - * MS bit of value indicates the SCB index - * length = 16, data_in = 16 byte configuration */ - CY_I2C_SET_CONFIG_CMD = - 0xC5, /* update the 16 byte I2C configuration information - * MS bit of value indicates the SCB index - * length = 16, data_out = 16 byte configuration information */ - CY_I2C_WRITE_CMD, /* perform I2C write operation - * value = bit0 - start, bit1 - stop, bit3 - start on idle, - * bits[14:8] - target address, bit15 - scbIndex. length = 0 the - * data is provided over the bulk endpoints */ - CY_I2C_READ_CMD, /* perform I2C read operation. - * value = bit0 - start, bit1 - stop, bit2 - Nak last byte, - * bit3 - start on idle, bits[14:8] - target address, bit15 - scbIndex, - * length = 0. The data is provided over the bulk endpoints */ - CY_I2C_GET_STATUS_CMD, /* retrieve the I2C bus status. - * value = bit0 - 0: TX 1: RX, bit15 - scbIndex, length = 3, - * data_in = byte0: bit0 - flag, bit1 - bus_state, bit2 - SDA state, - * bit3 - TX underflow, bit4 - arbitration error, bit5 - NAK - * bit6 - bus error, - * byte[2:1] Data count remaining */ - CY_I2C_RESET_CMD, /* the command cleans up the I2C state machine and frees the bus - * value = bit0 - 0: TX path, 1: RX path; bit15 - scbIndex, - * length = 0 */ - CY_SPI_READ_WRITE_CMD = 0xCA, /* the command starts a read / write operation at SPI - * value = bit 0 - RX enable, bit 1 - TX enable, bit 15 - - * scbIndex; index = length of transfer */ - CY_SPI_RESET_CMD, /* the command resets the SPI pipes and allows it to receive new - * request - * value = bit 15 - scbIndex */ - CY_SPI_GET_STATUS_CMD, /* the command returns the current transfer status - * the count will match the TX pipe status at SPI end - * for completion of read, read all data - * at the USB end signifies the end of transfer - * value = bit 15 - scbIndex */ - CY_JTAG_ENABLE_CMD = 0xD0, /* enable JTAG module */ - CY_JTAG_DISABLE_CMD, /* disable JTAG module */ - CY_JTAG_READ_CMD, /* jtag read vendor command */ - CY_JTAG_WRITE_CMD, /* jtag write vendor command */ - CY_GPIO_GET_CONFIG_CMD = 0xD8, /* get the GPIO configuration */ - CY_GPIO_SET_CONFIG_CMD, /* set the GPIO configuration */ - CY_GPIO_GET_VALUE_CMD, /* get GPIO value */ - CY_GPIO_SET_VALUE_CMD, /* set the GPIO value */ - CY_PROG_USER_FLASH_CMD = 0xE0, /* program user flash area. The total space available is 512 - * bytes this can be accessed by the user from USB. The flash - * area address offset is from 0x0000 to 0x00200 and can be - * written to page wise (128 byte) */ - CY_READ_USER_FLASH_CMD, /* read user flash area. The total space available is 512 bytes - * this can be accessed by the user from USB. The flash area - * address offset is from 0x0000 to 0x00200 and can be written to - * page wise (128 byte) */ - CY_DEVICE_RESET_CMD = 0xE3, /* performs a device reset from firmware */ -} CyVendorCommand; - typedef struct __attribute__((packed)) { /* nocheck:blocked */ guint32 frequency; /* frequency of operation. Only valid values are 100KHz and 400KHz */ guint8 target_address; /* target address to be used when in target mode */ @@ -116,119 +48,13 @@ guint8 is_loop_back; /* whether to loop back TX data to RX. Valid only for debug purposes */ guint8 reserved[6]; -} CyI2CConfig; - -typedef enum { - CY_I2C_DATA_CONFIG_NONE = 0, - CY_I2C_DATA_CONFIG_STOP = 1 << 0, - CY_I2C_DATA_CONFIG_NAK = 1 << 1, /* only for read */ -} CyI2CDataConfigBits; - -typedef enum { - HPI_DEV_REG_DEVICE_MODE = 0, - HPI_DEV_REG_BOOT_MODE_REASON, - HPI_DEV_REG_SI_ID, - HPI_DEV_REG_SI_ID_LSB, - HPI_DEV_REG_BL_LAST_ROW, - HPI_DEV_REG_BL_LAST_ROW_LSB, - HPI_DEV_REG_INTR_ADDR, - HPI_DEV_REG_JUMP_TO_BOOT, - HPI_DEV_REG_RESET_ADDR, - HPI_DEV_REG_RESET_CMD, - HPI_DEV_REG_ENTER_FLASH_MODE, - HPI_DEV_REG_VALIDATE_FW_ADDR, - HPI_DEV_REG_FLASH_READ_WRITE, - HPI_DEV_REG_FLASH_READ_WRITE_CMD, - HPI_DEV_REG_FLASH_ROW, - HPI_DEV_REG_FLASH_ROW_LSB, - HPI_DEV_REG_ALL_VERSION, - HPI_DEV_REG_ALL_VERSION_BYTE_1, - HPI_DEV_REG_ALL_VERSION_BYTE_2, - HPI_DEV_REG_ALL_VERSION_BYTE_3, - HPI_DEV_REG_ALL_VERSION_BYTE_4, - HPI_DEV_REG_ALL_VERSION_BYTE_5, - HPI_DEV_REG_ALL_VERSION_BYTE_6, - HPI_DEV_REG_ALL_VERSION_BYTE_7, - HPI_DEV_REG_ALL_VERSION_BYTE_8, - HPI_DEV_REG_ALL_VERSION_BYTE_9, - HPI_DEV_REG_ALL_VERSION_BYTE_10, - HPI_DEV_REG_ALL_VERSION_BYTE_11, - HPI_DEV_REG_ALL_VERSION_BYTE_12, - HPI_DEV_REG_ALL_VERSION_BYTE_13, - HPI_DEV_REG_ALL_VERSION_BYTE_14, - HPI_DEV_REG_ALL_VERSION_BYTE_15, - HPI_DEV_REG_FW_2_VERSION, - HPI_DEV_REG_FW_2_VERSION_BYTE_1, - HPI_DEV_REG_FW_2_VERSION_BYTE_2, - HPI_DEV_REG_FW_2_VERSION_BYTE_3, - HPI_DEV_REG_FW_2_VERSION_BYTE_4, - HPI_DEV_REG_FW_2_VERSION_BYTE_5, - HPI_DEV_REG_FW_2_VERSION_BYTE_6, - HPI_DEV_REG_FW_2_VERSION_BYTE_7, - HPI_DEV_REG_FW_BIN_LOC, - HPI_DEV_REG_FW_1_BIN_LOC_LSB, - HPI_DEV_REG_FW_2_BIN_LOC_MSB, - HPI_DEV_REG_FW_2_BIN_LOC_LSB, - HPI_DEV_REG_PORT_ENABLE, - HPI_DEV_SPACE_REG_LEN, - HPI_DEV_REG_RESPONSE = 0x007E, - HPI_DEV_REG_FLASH_MEM = 0x0200 -} HPIDevReg; - -typedef enum { - HPI_REG_SECTION_DEV = 0, /* device information */ - HPI_REG_SECTION_PORT_0, /* USB-PD Port 0 related */ - HPI_REG_SECTION_PORT_1, /* USB-PD Port 1 related */ - HPI_REG_SECTION_ALL /* select all registers */ -} HPIRegSection; +} FuCcgxI2cConfig; typedef struct __attribute__((packed)) { /* nocheck:blocked */ guint16 event_code; guint16 event_length; guint8 event_data[128]; -} HPIEvent; - -typedef enum { - HPI_REG_PART_REG = 0, /* register region */ - HPI_REG_PART_DATA = 1, /* data memory */ - HPI_REG_PART_FLASH = 2, /* flash memory */ - HPI_REG_PART_PDDATA_READ = 4, /* read data memory */ - HPI_REG_PART_PDDATA_WRITE = 8, /* write data memory */ -} HPIRegPart; - -typedef enum { - FU_CCGX_PD_RESP_REG_DEVICE_MODE_ADDR, - FU_CCGX_PD_RESP_BOOT_MODE_REASON, - FU_CCGX_PD_RESP_SILICON_ID, - FU_CCGX_PD_RESP_BL_LAST_ROW = 0x04, - FU_CCGX_PD_RESP_REG_INTR_REG_ADDR = 0x06, - FU_CCGX_PD_RESP_JUMP_TO_BOOT_REG_ADDR, - FU_CCGX_PD_RESP_REG_RESET_ADDR, - FU_CCGX_PD_RESP_REG_ENTER_FLASH_MODE_ADDR = 0x0A, - FU_CCGX_PD_RESP_REG_VALIDATE_FW_ADDR, - FU_CCGX_PD_RESP_REG_FLASH_READ_WRITE_ADDR, - FU_CCGX_PD_RESP_GET_VERSION = 0x10, - FU_CCGX_PD_RESP_REG_DBG_PD_INIT = 0x12, - FU_CCGX_PD_RESP_REG_U_VDM_CTRL_ADDR = 0x20, - FU_CCGX_PD_RESP_REG_READ_PD_PROFILE = 0x22, - FU_CCGX_PD_RESP_REG_EFFECTIVE_SOURCE_PDO_MASK = 0x24, - FU_CCGX_PD_RESP_REG_EFFECTIVE_SINK_PDO_MASK, - FU_CCGX_PD_RESP_REG_SELECT_SOURCE_PDO, - FU_CCGX_PD_RESP_REG_SELECT_SINK_PDO, - FU_CCGX_PD_RESP_REG_PD_CONTROL, - FU_CCGX_PD_RESP_REG_PD_STATUS = 0x2C, - FU_CCGX_PD_RESP_REG_TYPE_C_STATUS = 0x30, - FU_CCGX_PD_RESP_REG_CURRENT_PDO = 0x34, - FU_CCGX_PD_RESP_REG_CURRENT_RDO = 0x38, - FU_CCGX_PD_RESP_REG_CURRENT_CABLE_VDO = 0x3C, - FU_CCGX_PD_RESP_REG_DISPLAY_PORT_STATUS = 0x40, - FU_CCGX_PD_RESP_REG_DISPLAY_PORT_CONFIG = 0x44, - FU_CCGX_PD_RESP_REG_ALTERNATE_MODE_MUX_SELECTION = 0X45, - FU_CCGX_PD_RESP_REG_EVENT_MASK = 0x48, - FU_CCGX_PD_RESP_REG_RESPONSE_ADDR = 0x7E, - FU_CCGX_PD_RESP_REG_BOOTDATA_MEMORY_ADDR = 0x80, - FU_CCGX_PD_RESP_REG_FWDATA_MEMORY_ADDR = 0xC0, -} CyPDReg; +} FuCcgxHpiEvent; #define FU_CCGX_PD_RESP_BRIDGE_MODE_CMD_SIG 0x42 #define FU_CCGX_PD_RESP_GET_SILICON_ID_CMD_SIG 0x53 @@ -269,52 +95,3 @@ #define PD_I2CM_USB_EP_BULK_OUT 0x02 #define PD_I2CM_USB_EP_BULK_IN 0x83 #define PD_I2CM_USB_EP_INTR_IN 0x84 - -typedef enum { - HPI_RESPONSE_NO_RESPONSE, - HPI_RESPONSE_SUCCESS = 0x02, - HPI_RESPONSE_FLASH_DATA_AVAILABLE, - HPI_RESPONSE_INVALID_COMMAND = 0x05, - HPI_RESPONSE_FLASH_UPDATE_FAILED = 0x07, - HPI_RESPONSE_INVALID_FW, - HPI_RESPONSE_INVALID_ARGUMENT, - HPI_RESPONSE_NOT_SUPPORTED, - HPI_RESPONSE_PD_TRANSACTION_FAILED = 0x0C, - HPI_RESPONSE_PD_COMMAND_FAILED, - HPI_RESPONSE_UNDEFINED_ERROR = 0x0F, - HPI_EVENT_RESET_COMPLETE = 0x80, - HPI_EVENT_MSG_OVERFLOW, - HPI_EVENT_OC_DETECT, - HPI_EVENT_OV_DETECT, - HPI_EVENT_CONNECT_DETECT, - HPI_EVENT_DISCONNECT_DETECT, - HPI_EVENT_NEGOTIATION_COMPLETE, - HPI_EVENT_SWAP_COMPLETE, - HPI_EVENT_PS_RDY_RECEIVED = 0x8A, - HPI_EVENT_GOTO_MIN_RECEIVED, - HPI_EVENT_ACCEPT_RECEIVED, - HPI_EVENT_REJECT_RECEIVED, - HPI_EVENT_WAIT_RECEIVED, - HPI_EVENT_HARD_RESET_RECEIVED, - HPI_EVENT_VDM_RECEIVED = 0x90, - HPI_EVENT_SOURCE_CAP_RECEIVED, - HPI_EVENT_SINK_CAP_RECEIVED, - HPI_EVENT_DP_MODE_ENTERED, - HPI_EVENT_DP_STATUS_UPDATE, - HPI_EVENT_DP_SID_NOT_FOUND = 0x96, - HPI_EVENT_DP_MANY_SID_FOUND, - HPI_EVENT_DP_NO_CABLE_SUPPORT, - HPI_EVENT_DP_NO_UFP_SUPPORT, - HPI_EVENT_HARD_RESET_SENT, - HPI_EVENT_SOFT_RESET_SENT, - HPI_EVENT_CABLE_RESET_SENT, - HPI_EVENT_SOURCE_DISABLED, - HPI_EVENT_SENDER_TIMEOUT, - HPI_EVENT_VDM_NO_RESPONSE, - HPI_EVENT_UNEXPECTED_VOLTAGE, - HPI_EVENT_ERROR_RECOVERY, - HPI_EVENT_EMCA_DETECT = 0xA6, - HPI_EVENT_RP_CHANGE_DETECT = 0xAA, - HPI_EVENT_TB_ENTERED = 0xB0, - HPI_EVENT_TB_EXITED -} HPIResp; diff -Nru fwupd-2.0.8/plugins/ccgx/fu-ccgx-hpi-device.c fwupd-2.0.20/plugins/ccgx/fu-ccgx-hpi-device.c --- fwupd-2.0.8/plugins/ccgx/fu-ccgx-hpi-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx/fu-ccgx-hpi-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -104,7 +104,7 @@ FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - CY_I2C_RESET_CMD, + FU_CCGX_HPI_VENDOR_CMD_I2C_RESET, (self->scb_index << CY_SCB_INDEX_POS) | helper->mode, 0x0, NULL, @@ -132,7 +132,7 @@ FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - CY_I2C_GET_STATUS_CMD, + FU_CCGX_HPI_VENDOR_CMD_I2C_GET_STATUS, (((guint16)self->scb_index) << CY_SCB_INDEX_POS) | mode, 0x0, buf, @@ -168,14 +168,16 @@ } static gboolean -fu_ccgx_hpi_device_get_i2c_config(FuCcgxHpiDevice *self, CyI2CConfig *i2c_config, GError **error) +fu_ccgx_hpi_device_get_i2c_config(FuCcgxHpiDevice *self, + FuCcgxI2cConfig *i2c_config, + GError **error) { g_autoptr(GError) error_local = NULL; if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - CY_I2C_GET_CONFIG_CMD, + FU_CCGX_HPI_VENDOR_CMD_I2C_GET_CONFIG, ((guint16)self->scb_index) << CY_SCB_INDEX_POS, 0x0, (guint8 *)i2c_config, @@ -195,14 +197,16 @@ } static gboolean -fu_ccgx_hpi_device_set_i2c_config(FuCcgxHpiDevice *self, CyI2CConfig *i2c_config, GError **error) +fu_ccgx_hpi_device_set_i2c_config(FuCcgxHpiDevice *self, + FuCcgxI2cConfig *i2c_config, + GError **error) { g_autoptr(GError) error_local = NULL; if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - CY_I2C_SET_CONFIG_CMD, + FU_CCGX_HPI_VENDOR_CMD_I2C_SET_CONFIG, ((guint16)self->scb_index) << CY_SCB_INDEX_POS, 0x0, (guint8 *)i2c_config, @@ -276,13 +280,13 @@ fu_ccgx_hpi_device_i2c_read(FuCcgxHpiDevice *self, guint8 *buf, gsize bufsz, - CyI2CDataConfigBits cfg_bits, + FuCcgxI2cDataConfig cfg_bits, GError **error) { guint8 target_address = 0; if (!fu_ccgx_hpi_device_check_i2c_status(self, CY_I2C_MODE_READ, error)) { - g_prefix_error(error, "i2c read error: "); + g_prefix_error_literal(error, "i2c read error: "); return FALSE; } target_address = (self->target_address & 0x7F) | (self->scb_index << 7); @@ -290,7 +294,7 @@ FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - CY_I2C_READ_CMD, + FU_CCGX_HPI_VENDOR_CMD_I2C_READ, (((guint16)target_address) << 8) | cfg_bits, bufsz, NULL, @@ -299,7 +303,7 @@ FU_CCGX_HPI_WAIT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "i2c read error: control xfer: "); + g_prefix_error_literal(error, "i2c read error: control xfer: "); return FALSE; } if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), @@ -310,14 +314,14 @@ FU_CCGX_HPI_WAIT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "i2c read error: bulk xfer: "); + g_prefix_error_literal(error, "i2c read error: bulk xfer: "); return FALSE; } /* 10 msec delay */ fu_device_sleep(FU_DEVICE(self), I2C_READ_WRITE_DELAY_MS); if (!fu_ccgx_hpi_device_wait_for_notify(self, NULL, error)) { - g_prefix_error(error, "i2c read error: "); + g_prefix_error_literal(error, "i2c read error: "); return FALSE; } return TRUE; @@ -327,13 +331,13 @@ fu_ccgx_hpi_device_i2c_write(FuCcgxHpiDevice *self, guint8 *buf, gsize bufsz, - CyI2CDataConfigBits cfg_bits, + FuCcgxI2cDataConfig cfg_bits, GError **error) { guint8 target_address; if (!fu_ccgx_hpi_device_check_i2c_status(self, CY_I2C_MODE_WRITE, error)) { - g_prefix_error(error, "i2c get status error: "); + g_prefix_error_literal(error, "i2c get status error: "); return FALSE; } target_address = (self->target_address & 0x7F) | (self->scb_index << 7); @@ -341,9 +345,9 @@ FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - CY_I2C_WRITE_CMD, + FU_CCGX_HPI_VENDOR_CMD_I2C_WRITE, ((guint16)target_address << 8) | - (cfg_bits & CY_I2C_DATA_CONFIG_STOP), + (cfg_bits & FU_CCGX_I2C_DATA_CONFIG_STOP), bufsz, /* idx */ NULL, 0x0, @@ -351,7 +355,7 @@ FU_CCGX_HPI_WAIT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "i2c write error: control xfer: "); + g_prefix_error_literal(error, "i2c write error: control xfer: "); return FALSE; } if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), @@ -362,14 +366,14 @@ FU_CCGX_HPI_WAIT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "i2c write error: bulk xfer: "); + g_prefix_error_literal(error, "i2c write error: bulk xfer: "); return FALSE; } /* 10 msec delay */ fu_device_sleep(FU_DEVICE(self), I2C_READ_WRITE_DELAY_MS); if (!fu_ccgx_hpi_device_wait_for_notify(self, NULL, error)) { - g_prefix_error(error, "i2c wait for notification error: "); + g_prefix_error_literal(error, "i2c wait for notification error: "); return FALSE; } return TRUE; @@ -379,14 +383,14 @@ fu_ccgx_hpi_device_i2c_write_no_resp(FuCcgxHpiDevice *self, guint8 *buf, gsize bufsz, - CyI2CDataConfigBits cfg_bits, + FuCcgxI2cDataConfig cfg_bits, GError **error) { guint8 target_address = 0; g_autoptr(GError) error_local = NULL; if (!fu_ccgx_hpi_device_check_i2c_status(self, CY_I2C_MODE_WRITE, error)) { - g_prefix_error(error, "i2c write error: "); + g_prefix_error_literal(error, "i2c write error: "); return FALSE; } target_address = (self->target_address & 0x7F) | (self->scb_index << 7); @@ -394,9 +398,9 @@ FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - CY_I2C_WRITE_CMD, + FU_CCGX_HPI_VENDOR_CMD_I2C_WRITE, ((guint16)target_address << 8) | - (cfg_bits & CY_I2C_DATA_CONFIG_STOP), + (cfg_bits & FU_CCGX_I2C_DATA_CONFIG_STOP), bufsz, NULL, 0x0, @@ -404,7 +408,7 @@ FU_CCGX_HPI_WAIT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "i2c write error: control xfer: "); + g_prefix_error_literal(error, "i2c write error: control xfer: "); return FALSE; } @@ -434,17 +438,17 @@ if (!fu_ccgx_hpi_device_i2c_write(self, bufhw, self->hpi_addrsz, - CY_I2C_DATA_CONFIG_NAK, + FU_CCGX_I2C_DATA_CONFIG_NAK, error)) { - g_prefix_error(error, "write error: "); + g_prefix_error_literal(error, "write error: "); return FALSE; } if (!fu_ccgx_hpi_device_i2c_read(self, helper->buf, helper->bufsz, - CY_I2C_DATA_CONFIG_STOP | CY_I2C_DATA_CONFIG_NAK, + FU_CCGX_I2C_DATA_CONFIG_STOP | FU_CCGX_I2C_DATA_CONFIG_NAK, error)) { - g_prefix_error(error, "read error: "); + g_prefix_error_literal(error, "read error: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), HPI_CMD_REG_READ_WRITE_DELAY_MS); @@ -453,7 +457,7 @@ static gboolean fu_ccgx_hpi_device_reg_read(FuCcgxHpiDevice *self, - guint16 addr, + FuCcgxPdRespReg addr, guint8 *buf, gsize bufsz, GError **error) @@ -484,9 +488,10 @@ if (!fu_ccgx_hpi_device_i2c_write(self, bufhw, helper->bufsz + self->hpi_addrsz, - CY_I2C_DATA_CONFIG_STOP | CY_I2C_DATA_CONFIG_NAK, + FU_CCGX_I2C_DATA_CONFIG_STOP | + FU_CCGX_I2C_DATA_CONFIG_NAK, error)) { - g_prefix_error(error, "reg write error: "); + g_prefix_error_literal(error, "reg write error: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), HPI_CMD_REG_READ_WRITE_DELAY_MS); @@ -527,9 +532,10 @@ if (!fu_ccgx_hpi_device_i2c_write_no_resp(self, bufhw, bufsz + self->hpi_addrsz, - CY_I2C_DATA_CONFIG_STOP | CY_I2C_DATA_CONFIG_NAK, + FU_CCGX_I2C_DATA_CONFIG_STOP | + FU_CCGX_I2C_DATA_CONFIG_NAK, error)) { - g_prefix_error(error, "reg write no-resp error: "); + g_prefix_error_literal(error, "reg write no-resp error: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), HPI_CMD_REG_READ_WRITE_DELAY_MS); @@ -537,19 +543,19 @@ } static gboolean -fu_ccgx_hpi_device_clear_intr(FuCcgxHpiDevice *self, HPIRegSection section, GError **error) +fu_ccgx_hpi_device_clear_intr(FuCcgxHpiDevice *self, FuCcgxHpiRegSection section, GError **error) { guint8 intr = 0; for (guint8 i = 0; i <= self->num_ports; i++) { - if (i == section || section == HPI_REG_SECTION_ALL) + if (i == section || section == FU_CCGX_HPI_REG_SECTION_ALL) FU_BIT_SET(intr, i); } if (!fu_ccgx_hpi_device_reg_write(self, - HPI_DEV_REG_INTR_ADDR, + FU_CCGX_HPI_DEV_REG_INTR_ADDR, &intr, sizeof(intr), error)) { - g_prefix_error(error, "failed to clear interrupt: "); + g_prefix_error_literal(error, "failed to clear interrupt: "); return FALSE; } return TRUE; @@ -563,18 +569,19 @@ static gboolean fu_ccgx_hpi_device_read_event_reg(FuCcgxHpiDevice *self, - HPIRegSection section, - HPIEvent *event, + FuCcgxHpiRegSection section, + FuCcgxHpiEvent *event, GError **error) { - if (section != HPI_REG_SECTION_DEV) { + if (section != FU_CCGX_HPI_REG_SECTION_DEV) { guint16 reg_addr; guint8 buf[4] = {0x0}; /* first read the response register */ - reg_addr = fu_ccgx_hpi_device_reg_addr_gen(section, HPI_REG_PART_PDDATA_READ, 0); + reg_addr = + fu_ccgx_hpi_device_reg_addr_gen(section, FU_CCGX_HPI_REG_PART_PDDATA_READ, 0); if (!fu_ccgx_hpi_device_reg_read(self, reg_addr, buf, sizeof(buf), error)) { - g_prefix_error(error, "read response reg error: "); + g_prefix_error_literal(error, "read response reg error: "); return FALSE; } @@ -583,14 +590,14 @@ memcpy((guint8 *)event, buf, sizeof(buf)); /* nocheck:blocked */ if (event->event_length != 0) { reg_addr = fu_ccgx_hpi_device_reg_addr_gen(section, - HPI_REG_PART_PDDATA_READ, + FU_CCGX_HPI_REG_PART_PDDATA_READ, sizeof(buf)); if (!fu_ccgx_hpi_device_reg_read(self, reg_addr, event->event_data, event->event_length, error)) { - g_prefix_error(error, "read event data error: "); + g_prefix_error_literal(error, "read event data error: "); return FALSE; } } @@ -601,7 +608,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "read response reg error: "); + g_prefix_error_literal(error, "read response reg error: "); return FALSE; } event->event_code = buf[0]; @@ -613,7 +620,7 @@ event->event_data, event->event_length, error)) { - g_prefix_error(error, "read event data error: "); + g_prefix_error_literal(error, "read event data error: "); return FALSE; } } @@ -625,8 +632,8 @@ static gboolean fu_ccgx_hpi_device_app_read_intr_reg(FuCcgxHpiDevice *self, - HPIRegSection section, - HPIEvent *event_array, + FuCcgxHpiRegSection section, + FuCcgxHpiEvent *event_array, guint8 *event_count, GError **error) { @@ -634,25 +641,25 @@ guint8 event_count_tmp = 0; guint8 intr_reg = 0; - reg_addr = fu_ccgx_hpi_device_reg_addr_gen(HPI_REG_SECTION_DEV, - HPI_REG_PART_REG, - HPI_DEV_REG_INTR_ADDR); + reg_addr = fu_ccgx_hpi_device_reg_addr_gen(FU_CCGX_HPI_REG_SECTION_DEV, + FU_CCGX_HPI_REG_PART_REG, + FU_CCGX_HPI_DEV_REG_INTR_ADDR); if (!fu_ccgx_hpi_device_reg_read(self, reg_addr, &intr_reg, sizeof(intr_reg), error)) { - g_prefix_error(error, "read intr reg error: "); + g_prefix_error_literal(error, "read intr reg error: "); return FALSE; } /* device section will not come here */ for (guint8 i = 0; i <= self->num_ports; i++) { /* check if this section is needed */ - if (section == i || section == HPI_REG_SECTION_ALL) { + if (section == i || section == FU_CCGX_HPI_REG_SECTION_ALL) { /* check whether this section has any event/response */ if ((1 << i) & intr_reg) { if (!fu_ccgx_hpi_device_read_event_reg(self, section, &event_array[i], error)) { - g_prefix_error(error, "read event error: "); + g_prefix_error_literal(error, "read event error: "); return FALSE; } event_count_tmp++; @@ -666,8 +673,8 @@ static gboolean fu_ccgx_hpi_device_wait_for_event(FuCcgxHpiDevice *self, - HPIRegSection section, - HPIEvent *event_array, + FuCcgxHpiRegSection section, + FuCcgxHpiEvent *event_array, guint32 timeout_ms, GError **error) { @@ -695,14 +702,14 @@ static gboolean fu_ccgx_hpi_device_get_event(FuCcgxHpiDevice *self, - HPIRegSection reg_section, + FuCcgxHpiRegSection reg_section, FuCcgxPdResp *event, guint32 io_timeout, GError **error) { - HPIEvent event_array[HPI_REG_SECTION_ALL + 1] = {0x0}; + FuCcgxHpiEvent event_array[FU_CCGX_HPI_REG_SECTION_ALL + 1] = {0x0}; if (!fu_ccgx_hpi_device_wait_for_event(self, reg_section, event_array, io_timeout, error)) { - g_prefix_error(error, "failed to get event: "); + g_prefix_error_literal(error, "failed to get event: "); return FALSE; } *event = event_array[reg_section].event_code; @@ -712,10 +719,10 @@ static gboolean fu_ccgx_hpi_device_clear_all_events(FuCcgxHpiDevice *self, guint32 io_timeout, GError **error) { - HPIEvent event_array[HPI_REG_SECTION_ALL + 1] = {0x0}; + FuCcgxHpiEvent event_array[FU_CCGX_HPI_REG_SECTION_ALL + 1] = {0x0}; if (io_timeout == 0) { return fu_ccgx_hpi_device_app_read_intr_reg(self, - HPI_REG_SECTION_ALL, + FU_CCGX_HPI_REG_SECTION_ALL, event_array, NULL, error); @@ -754,15 +761,15 @@ fw_index, 1, error)) { - g_prefix_error(error, "validate fw error: "); + g_prefix_error_literal(error, "validate fw error: "); return FALSE; } if (!fu_ccgx_hpi_device_get_event(self, - HPI_REG_SECTION_DEV, + FU_CCGX_HPI_REG_SECTION_DEV, &hpi_event, HPI_CMD_COMMAND_RESPONSE_TIME_MS, error)) { - g_prefix_error(error, "validate fw resp error: "); + g_prefix_error_literal(error, "validate fw resp error: "); return FALSE; } if (hpi_event != FU_CCGX_PD_RESP_SUCCESS) { @@ -801,15 +808,15 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "enter flash mode error: "); + g_prefix_error_literal(error, "enter flash mode error: "); return FALSE; } if (!fu_ccgx_hpi_device_get_event(self, - HPI_REG_SECTION_DEV, + FU_CCGX_HPI_REG_SECTION_DEV, &hpi_event, HPI_CMD_COMMAND_RESPONSE_TIME_MS, error)) { - g_prefix_error(error, "enter flash mode resp error: "); + g_prefix_error_literal(error, "enter flash mode resp error: "); return FALSE; } if (hpi_event != FU_CCGX_PD_RESP_SUCCESS) { @@ -852,15 +859,15 @@ &buf, sizeof(buf), error)) { - g_prefix_error(error, "leave flash mode error: "); + g_prefix_error_literal(error, "leave flash mode error: "); return FALSE; } if (!fu_ccgx_hpi_device_get_event(self, - HPI_REG_SECTION_DEV, + FU_CCGX_HPI_REG_SECTION_DEV, &hpi_event, HPI_CMD_COMMAND_RESPONSE_TIME_MS, error)) { - g_prefix_error(error, "leave flash mode resp error: "); + g_prefix_error_literal(error, "leave flash mode resp error: "); return FALSE; } if (hpi_event != FU_CCGX_PD_RESP_SUCCESS) { @@ -906,10 +913,10 @@ return FALSE; /* write data to memory */ - addr_tmp = self->hpi_addrsz > 1 ? HPI_DEV_REG_FLASH_MEM + addr_tmp = self->hpi_addrsz > 1 ? FU_CCGX_HPI_DEV_REG_FLASH_MEM : FU_CCGX_PD_RESP_REG_BOOTDATA_MEMORY_ADDR; if (!fu_ccgx_hpi_device_reg_write(self, addr_tmp, helper->buf, helper->bufsz, error)) { - g_prefix_error(error, "write buf to memory error: "); + g_prefix_error_literal(error, "write buf to memory error: "); return FALSE; } if (!fu_ccgx_hpi_device_reg_write(self, @@ -917,17 +924,17 @@ bufhw, sizeof(bufhw), error)) { - g_prefix_error(error, "write flash error: "); + g_prefix_error_literal(error, "write flash error: "); return FALSE; } /* wait until flash is written */ if (!fu_ccgx_hpi_device_get_event(self, - HPI_REG_SECTION_DEV, + FU_CCGX_HPI_REG_SECTION_DEV, &hpi_event, HPI_CMD_COMMAND_RESPONSE_TIME_MS, error)) { - g_prefix_error(error, "write flash resp error: "); + g_prefix_error_literal(error, "write flash resp error: "); return FALSE; } if (hpi_event != FU_CCGX_PD_RESP_SUCCESS) { @@ -983,17 +990,17 @@ bufhw, sizeof(bufhw), error)) { - g_prefix_error(error, "read flash error: "); + g_prefix_error_literal(error, "read flash error: "); return FALSE; } /* wait until flash is read */ if (!fu_ccgx_hpi_device_get_event(self, - HPI_REG_SECTION_DEV, + FU_CCGX_HPI_REG_SECTION_DEV, &hpi_event, HPI_CMD_COMMAND_RESPONSE_TIME_MS, error)) { - g_prefix_error(error, "read flash resp error: "); + g_prefix_error_literal(error, "read flash resp error: "); return FALSE; } if (hpi_event != FU_CCGX_PD_RESP_FLASH_DATA_AVAILABLE) { @@ -1005,10 +1012,10 @@ hpi_event); return FALSE; } - addr_tmp = self->hpi_addrsz > 1 ? HPI_DEV_REG_FLASH_MEM + addr_tmp = self->hpi_addrsz > 1 ? FU_CCGX_HPI_DEV_REG_FLASH_MEM : FU_CCGX_PD_RESP_REG_BOOTDATA_MEMORY_ADDR; if (!fu_ccgx_hpi_device_reg_read(self, addr_tmp, helper->buf, helper->bufsz, error)) { - g_prefix_error(error, "read data from memory error: "); + g_prefix_error_literal(error, "read data from memory error: "); return FALSE; } return TRUE; @@ -1050,11 +1057,11 @@ if (!fu_ccgx_hpi_device_clear_all_events(self, HPI_CMD_COMMAND_CLEAR_EVENT_TIME_MS, error)) return FALSE; if (!fu_ccgx_hpi_device_reg_write(self, - FU_CCGX_PD_RESP_JUMP_TO_BOOT_REG_ADDR, + FU_CCGX_PD_RESP_REG_JUMP_TO_BOOT_REG_ADDR, buf, sizeof(buf), error)) { - g_prefix_error(error, "jump to alt mode error: "); + g_prefix_error_literal(error, "jump to alt mode error: "); return FALSE; } @@ -1081,7 +1088,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "reset device error: "); + g_prefix_error_literal(error, "reset device error: "); return FALSE; } fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); @@ -1093,7 +1100,7 @@ fu_ccgx_hpi_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); @@ -1117,7 +1124,7 @@ fw_silicon_id); return NULL; } - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) == 0) { fw_app_type = fu_ccgx_firmware_get_app_type(FU_CCGX_FIRMWARE(firmware)); if (fw_app_type != self->fw_app_type) { g_set_error(error, @@ -1209,7 +1216,7 @@ return FALSE; buf = g_malloc0(self->flash_row_size); if (!fu_ccgx_hpi_device_read_flash(self, addr, buf, self->flash_row_size, error)) { - g_prefix_error(error, "fw metadata read error: "); + g_prefix_error_literal(error, "fw metadata read error: "); return FALSE; } return fu_memcpy_safe(st_metadata->data, @@ -1238,7 +1245,7 @@ return FALSE; buf = g_malloc0(self->flash_row_size); if (!fu_ccgx_hpi_device_read_flash(self, addr, buf, self->flash_row_size, error)) { - g_prefix_error(error, "fw metadata read existing error: "); + g_prefix_error_literal(error, "fw metadata read existing error: "); return FALSE; } if (!fu_memcpy_safe(buf, @@ -1251,7 +1258,7 @@ error)) return FALSE; if (!fu_ccgx_hpi_device_write_flash(self, addr, buf, self->flash_row_size, error)) { - g_prefix_error(error, "fw metadata write error: "); + g_prefix_error_literal(error, "fw metadata write error: "); return FALSE; } return TRUE; @@ -1268,7 +1275,7 @@ GPtrArray *records = fu_ccgx_firmware_get_records(FU_CCGX_FIRMWARE(firmware)); FuCcgxFwMode fw_mode_alt = fu_ccgx_fw_mode_get_alternate(self->fw_mode); g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(GByteArray) st_metadata = fu_struct_ccgx_metadata_hdr_new(); + g_autoptr(FuStructCcgxMetadataHdr) st_metadata = fu_struct_ccgx_metadata_hdr_new(); /* progress */ fu_progress_set_id(progress, G_STRLOC); @@ -1279,7 +1286,7 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "leave-flash"); /* enter flash mode */ - locker = fu_device_locker_new_full(self, + locker = fu_device_locker_new_full(device, (FuDeviceLockerFunc)fu_ccgx_hpi_device_enter_flash_mode, (FuDeviceLockerFunc)fu_ccgx_hpi_device_leave_flash_mode, error); @@ -1287,10 +1294,10 @@ return FALSE; /* invalidate metadata for alternate image */ - if (!fu_ccgx_hpi_device_load_metadata(self, fw_mode_alt, st_metadata, error)) + if (!fu_ccgx_hpi_device_load_metadata(self, fw_mode_alt, st_metadata->buf, error)) return FALSE; fu_struct_ccgx_metadata_hdr_set_metadata_valid(st_metadata, 0x0); - if (!fu_ccgx_hpi_device_save_metadata(self, fw_mode_alt, st_metadata, error)) + if (!fu_ccgx_hpi_device_save_metadata(self, fw_mode_alt, st_metadata->buf, error)) return FALSE; fu_progress_step_done(progress); @@ -1317,7 +1324,7 @@ /* validate fw */ if (!fu_ccgx_hpi_device_validate_fw(self, fw_mode_alt, error)) { - g_prefix_error(error, "fw validate error: "); + g_prefix_error_literal(error, "fw validate error: "); return FALSE; } fu_progress_step_done(progress); @@ -1337,11 +1344,11 @@ guint8 buf[2] = {0x0}; if (!fu_ccgx_hpi_device_reg_read(self, - FU_CCGX_PD_RESP_SILICON_ID, + FU_CCGX_PD_RESP_REG_SILICON_ID, buf, sizeof(buf), error)) { - g_prefix_error(error, "get silicon id error: "); + g_prefix_error_literal(error, "get silicon id error: "); return FALSE; } if (!fu_memread_uint16_safe(buf, @@ -1390,7 +1397,7 @@ fu_ccgx_hpi_device_setup(FuDevice *device, GError **error) { FuCcgxHpiDevice *self = FU_CCGX_HPI_DEVICE(device); - CyI2CConfig i2c_config = {0x0}; + FuCcgxI2cConfig i2c_config = {0x0}; guint32 hpi_event = 0; guint8 mode = 0; g_autoptr(GError) error_local = NULL; @@ -1401,14 +1408,14 @@ /* set the new config */ if (!fu_ccgx_hpi_device_get_i2c_config(self, &i2c_config, error)) { - g_prefix_error(error, "get config error: "); + g_prefix_error_literal(error, "get config error: "); return FALSE; } i2c_config.frequency = FU_CCGX_HPI_FREQ; i2c_config.is_initiator = TRUE; i2c_config.is_msb_first = TRUE; if (!fu_ccgx_hpi_device_set_i2c_config(self, &i2c_config, error)) { - g_prefix_error(error, "set config error: "); + g_prefix_error_literal(error, "set config error: "); return FALSE; } if (!fu_ccgx_hpi_device_reg_read(self, @@ -1416,7 +1423,7 @@ &mode, 1, error)) { - g_prefix_error(error, "get device mode error: "); + g_prefix_error_literal(error, "get device mode error: "); return FALSE; } self->hpi_addrsz = mode & 0x80 ? 2 : 1; @@ -1438,11 +1445,11 @@ bufsz = self->hpi_addrsz == 1 ? HPI_DEVICE_VERSION_SIZE_HPIV1 : HPI_DEVICE_VERSION_SIZE_HPIV2; if (!fu_ccgx_hpi_device_reg_read(self, - FU_CCGX_PD_RESP_GET_VERSION, + FU_CCGX_PD_RESP_REG_GET_VERSION, bufver, bufsz, error)) { - g_prefix_error(error, "get version error: "); + g_prefix_error_literal(error, "get version error: "); return FALSE; } @@ -1490,7 +1497,7 @@ /* if we are coming back from reset, wait for hardware to settle */ if (!fu_ccgx_hpi_device_get_event(self, - HPI_REG_SECTION_DEV, + FU_CCGX_HPI_REG_SECTION_DEV, &hpi_event, HPI_CMD_SETUP_EVENT_WAIT_TIME_MS, &error_local)) { @@ -1560,7 +1567,7 @@ } static void -fu_ccgx_hpi_device_set_progress(FuDevice *self, FuProgress *progress) +fu_ccgx_hpi_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); diff -Nru fwupd-2.0.8/plugins/ccgx/fu-ccgx-plugin.c fwupd-2.0.20/plugins/ccgx/fu-ccgx-plugin.c --- fwupd-2.0.8/plugins/ccgx/fu-ccgx-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx/fu-ccgx-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,6 +21,7 @@ static void fu_ccgx_plugin_init(FuCcgxPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void @@ -31,6 +32,7 @@ fu_context_add_quirk_key(ctx, "CcgxFlashRowSize"); fu_context_add_quirk_key(ctx, "CcgxFlashSize"); fu_context_add_quirk_key(ctx, "CcgxImageKind"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_CCGX_FIRMWARE); fu_plugin_add_device_gtype(plugin, FU_TYPE_CCGX_HID_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_CCGX_PURE_HID_DEVICE); diff -Nru fwupd-2.0.8/plugins/ccgx/fu-ccgx-pure-hid-device.c fwupd-2.0.20/plugins/ccgx/fu-ccgx-pure-hid-device.c --- fwupd-2.0.8/plugins/ccgx/fu-ccgx-pure-hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx/fu-ccgx-pure-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -34,19 +34,16 @@ guint8 param2, GError **error) { - g_autoptr(GByteArray) cmd = fu_struct_ccgx_pure_hid_command_new(); - fu_struct_ccgx_pure_hid_command_set_cmd(cmd, param1); - fu_struct_ccgx_pure_hid_command_set_opt(cmd, param2); - if (!fu_hid_device_set_report(FU_HID_DEVICE(self), - FU_CCGX_PURE_HID_REPORT_ID_COMMAND, - cmd->data, - cmd->len, - FU_CCGX_PURE_HID_DEVICE_TIMEOUT, - FU_HID_DEVICE_FLAG_NONE, - error)) { - return FALSE; - } - return TRUE; + g_autoptr(FuStructCcgxPureHidCommand) st = fu_struct_ccgx_pure_hid_command_new(); + fu_struct_ccgx_pure_hid_command_set_cmd(st, param1); + fu_struct_ccgx_pure_hid_command_set_opt(st, param2); + return fu_hid_device_set_report(FU_HID_DEVICE(self), + FU_CCGX_PURE_HID_REPORT_ID_COMMAND, + st->buf->data, + st->buf->len, + FU_CCGX_PURE_HID_DEVICE_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error); } static gboolean @@ -56,7 +53,7 @@ FU_CCGX_PURE_HID_COMMAND_FLASH, FU_CCGX_PD_RESP_ENTER_FLASHING_MODE_CMD_SIG, error)) { - g_prefix_error(error, "flashing enable command error: "); + g_prefix_error_literal(error, "flashing enable command error: "); return FALSE; } return TRUE; @@ -82,7 +79,7 @@ FU_CCGX_PURE_HID_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_IS_FEATURE, error)) { - g_prefix_error(error, "magic enable command error: "); + g_prefix_error_literal(error, "magic enable command error: "); return FALSE; } @@ -103,7 +100,7 @@ guint8 buf[0x40] = {FU_CCGX_PURE_HID_REPORT_ID_INFO, 0}; guint version = 0; g_autofree gchar *bl_ver = NULL; - g_autoptr(GByteArray) st_info = NULL; + g_autoptr(FuStructCcgxPureHidFwInfo) st_info = NULL; if (!fu_hid_device_get_report(FU_HID_DEVICE(self), buf[0], @@ -200,7 +197,7 @@ fu_ccgx_pure_hid_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuCcgxPureHidDevice *self = FU_CCGX_PURE_HID_DEVICE(device); @@ -313,13 +310,13 @@ gsize row_len, GError **error) { - g_autoptr(GByteArray) st_hdr = fu_struct_ccgx_pure_hid_write_hdr_new(); + g_autoptr(FuStructCcgxPureHidWriteHdr) st_hdr = fu_struct_ccgx_pure_hid_write_hdr_new(); fu_struct_ccgx_pure_hid_write_hdr_set_pd_resp(st_hdr, FU_CCGX_PD_RESP_FLASH_READ_WRITE_CMD_SIG); fu_struct_ccgx_pure_hid_write_hdr_set_addr(st_hdr, address); - if (!fu_memcpy_safe(st_hdr->data, - st_hdr->len, + if (!fu_memcpy_safe(st_hdr->buf->data, + st_hdr->buf->len, FU_STRUCT_CCGX_PURE_HID_WRITE_HDR_OFFSET_DATA, row, row_len, @@ -329,13 +326,13 @@ return FALSE; if (!fu_hid_device_set_report(FU_HID_DEVICE(self), - st_hdr->data[0], - st_hdr->data, - st_hdr->len, + st_hdr->buf->data[0], + st_hdr->buf->data, + st_hdr->buf->len, FU_CCGX_PURE_HID_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "write row command error: "); + g_prefix_error_literal(error, "write row command error: "); return FALSE; } return TRUE; @@ -377,7 +374,7 @@ FU_CCGX_PURE_HID_COMMAND_SET_BOOT, fw_mode, error)) { - g_prefix_error(error, "bootswitch command error: "); + g_prefix_error_literal(error, "bootswitch command error: "); return FALSE; } @@ -385,7 +382,7 @@ FU_CCGX_PURE_HID_COMMAND_JUMP, FU_CCGX_PD_RESP_DEVICE_RESET_CMD_SIG, error)) { - g_prefix_error(error, "reset command error: "); + g_prefix_error_literal(error, "reset command error: "); return FALSE; } @@ -395,7 +392,7 @@ } static void -fu_ccgx_pure_hid_device_set_progress(FuDevice *self, FuProgress *progress) +fu_ccgx_pure_hid_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/ccgx/fu-ccgx.rs fwupd-2.0.20/plugins/ccgx/fu-ccgx.rs --- fwupd-2.0.8/plugins/ccgx/fu-ccgx.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx/fu-ccgx.rs 2026-02-26 11:36:18.000000000 +0000 @@ -89,3 +89,175 @@ SenderResponseTimerTimeout, NoVdmResponseReceived, } + +enum FuCcgxHpiVendorCmd { + GetVersion = 0xB0, // get the version of the boot-loader + // value = 0, index = 0, length = 4; + // data_in = 32 bit version + GetSignature = 0xBD, // gsupposes to be 'CYUS' for normal firmware + // and 'CYBL' for Bootloader + UartGetConfig = 0xC0, // retrieve the 16 byte UART configuration information + // MS bit of value indicates the SCB index + // length = 16, data_in = 16 byte configuration + UartSetConfig, // update the 16 byte UART configuration information + // MS bit of value indicates the SCB index. + // length = 16, data_out = 16 byte configuration information + SpiGetConfig, // retrieve the 16 byte SPI configuration information + // MS bit of value indicates the SCB index + // length = 16, data_in = 16 byte configuration + SpiSetConfig, // update the 16 byte SPI configuration information + // MS bit of value indicates the SCB index + // length = 16, data_out = 16 byte configuration information + I2cGetConfig, // retrieve the 16 byte I2C configuration information + // MS bit of value indicates the SCB index + // length = 16, data_in = 16 byte configuration + I2cSetConfig = 0xc5, // update the 16 byte I2C configuration information + // MS bit of value indicates the SCB index + // length = 16, data_out = 16 byte configuration information + I2cWrite, // perform I2C write operation + // value = bit0 - start, bit1 - stop, bit3 - start on idle, + // bits[14:8] - target address, bit15 - scbIndex. length = 0 the + // data is provided over the bulk endpoints + I2cRead, // value = bit0 - start, bit1 - stop, bit2 - Nak last byte, + // bit3 - start on idle, bits[14:8] - target address, bit15 - scbIndex, + // length = 0. The data is provided over the bulk endpoints + I2cGetStatus, // value = bit0 - 0: TX 1: RX, bit15 - scbIndex, length = 3, + // data_in = byte0: bit0 - flag, bit1 - bus_state, bit2 - SDA state, + // bit3 - TX underflow, bit4 - arbitration error, bit5 - NAK + // bit6 - bus error, + // byte[2:1] Data count remaining + I2cReset, // the command cleans up the I2C state machine and frees the bus + // value = bit0 - 0: TX path, 1: RX path; bit15 - scbIndex, + // length = 0 + SpiReadWrite = 0xCA, // the command starts a read / write operation at SPI + // value = bit 0 - RX enable, bit 1 - TX enable, bit 15 - + // scbIndex; index = length of transfer + SpiReset, // the command resets the SPI pipes and allows it to receive new + // request + // value = bit 15 - scbIndex + SpiGetStatus, // the command returns the current transfer status + // the count will match the TX pipe status at SPI end + // for completion of read, read all data + // at the USB end signifies the end of transfer + // value = bit 15 - scbIndex + JtagEnable = 0xD0, + JtagDisable, + JtagRead, + JtagWrite, + GpioGetConfig = 0xD8, + GpioSetConfig, + GpioGetValue, + GpioSetValue, + ProgUserFlash = 0xE0, // The total space available is 512 + // bytes this can be accessed by the user from USB. The flash + // area address offset is from 0x0000 to 0x00200 and can be + // written to page wise (128 byte) + ReadUserFlash, + DeviceReset = 0xE3, +} + +enum FuCcgxPdRespReg { + DeviceModeAddr, + BootModeReason, + SiliconId, + BlLastRow = 0x04, + IntrRegAddr = 0x06, + JumpToBootRegAddr, + ResetAddr, + EnterFlashModeAddr = 0x0a, + ValidateFwAddr, + FlashReadWriteAddr, + GetVersion = 0x10, + DbgPdInit = 0x12, + UVdmCtrlAddr = 0x20, + ReadPdProfile = 0x22, + EffectiveSourcePdoMask = 0x24, + EffectiveSinkPdoMask, + SelectSourcePdo, + SelectSinkPdo, + PdControl, + PdStatus = 0x2c, + TypeCStatus = 0x30, + CurrentPdo = 0x34, + CurrentRdo = 0x38, + CurrentCableVdo = 0x3c, + DisplayPortStatus = 0x40, + DisplayPortConfig = 0x44, + AlternateModeMuxSelection = 0x45, + EventMask = 0x48, + ResponseAddr = 0x7e, + BootdataMemoryAddr = 0x80, + FwdataMemoryAddr = 0xc0, +} + +enum FuCcgxHpiRegPart { + Reg = 0, // register region + Data = 1, // data memory + Flash = 2, // flash memory + PddataRead = 4, // read data memory + PddataWrite = 8, // write data memory +} + +enum FuCcgxI2cDataConfig { + None = 0, + Stop = 1 << 0, + Nak = 1 << 1, // only for read +} + +enum FuCcgxHpiDevReg { + DeviceMode = 0, + BootModeReason, + SiId, + SiIdLsb, + BlLastRow, + BlLastRowLsb, + IntrAddr, + JumpToBoot, + ResetAddr, + ResetCmd, + EnterFlashMode, + ValidateFwAddr, + FlashReadWrite, + FlashReadWriteCmd, + FlashRow, + FlashRowLsb, + AllVersion, + AllVersionByte1, + AllVersionByte2, + AllVersionByte3, + AllVersionByte4, + AllVersionByte5, + AllVersionByte6, + AllVersionByte7, + AllVersionByte8, + AllVersionByte9, + AllVersionByte10, + AllVersionByte11, + AllVersionByte12, + AllVersionByte13, + AllVersionByte14, + AllVersionByte15, + Fw2Version, + Fw2VersionByte1, + Fw2VersionByte2, + Fw2VersionByte3, + Fw2VersionByte4, + Fw2VersionByte5, + Fw2VersionByte6, + Fw2VersionByte7, + FwbinLoc, + Fw1BinLocLsb, + Fw2BinLocMsb, + Fw2BinLocLsb, + PortEnable, + Len, + Response = 0x007e, + FlashMem = 0x0200, +} + +enum FuCcgxHpiRegSection { + Dev, // device information + Port0, // USB-PD Port 0 + Port1, // USB-PD Port 1 + All, +} diff -Nru fwupd-2.0.8/plugins/ccgx/fu-self-test.c fwupd-2.0.20/plugins/ccgx/fu-self-test.c --- fwupd-2.0.8/plugins/ccgx/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -31,7 +31,7 @@ g_assert_true(ret); csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); g_assert_no_error(error); - g_assert_cmpstr(csum1, ==, "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"); + g_assert_cmpstr(csum1, ==, "ccc06acf112b7e3baf0b4ff6574d759110c7bd5d"); /* ensure we can round-trip */ xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); diff -Nru fwupd-2.0.8/plugins/ccgx/meson.build fwupd-2.0.20/plugins/ccgx/meson.build --- fwupd-2.0.8/plugins/ccgx/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -48,6 +48,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', diff -Nru fwupd-2.0.8/plugins/ccgx-dmc/README.md fwupd-2.0.20/plugins/ccgx-dmc/README.md --- fwupd-2.0.8/plugins/ccgx-dmc/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx-dmc/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -101,10 +101,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.5.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Brent Wu: @IfxBrent diff -Nru fwupd-2.0.8/plugins/ccgx-dmc/ccgx-dmc.quirk fwupd-2.0.20/plugins/ccgx-dmc/ccgx-dmc.quirk --- fwupd-2.0.8/plugins/ccgx-dmc/ccgx-dmc.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx-dmc/ccgx-dmc.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,12 @@ +# Lenovo ThinkPad Thunderbolt 5 Smart Dock +[USB\VID_17EF&PID_310D] +Plugin = ccgx_dmc +Summary = Dock Management Controller Device +Name = ThinkPad Thunderbolt 5 Smart Dock +CcgxDmcTriggerCode = 0x01 +InstallDuration = 1200 +RemoveDelay = 1200000 + # Lenovo ThinkPad Universal USB-C Dock [USB\VID_17EF&PID_30A9] Plugin = ccgx_dmc @@ -22,7 +31,6 @@ Plugin = ccgx_dmc Summary = Dock Management Controller Device ParentGuid = USB\VID_03F0&PID_0363 -Vendor = HP Name = USB-C Dock G5 InstallDuration = 233 RemoveDelay = 203000 @@ -32,7 +40,6 @@ Plugin = ccgx_dmc Summary = Dock Management Controller Device ParentGuid = USB\VID_03F0&PID_096B -Vendor = HP Name = USB-C/A Universal Dock G2 InstallDuration = 180 RemoveDelay = 162000 @@ -53,7 +60,6 @@ Plugin = ccgx_dmc Summary = Dock Management Controller Device ParentGuid = USB\VID_03F0&PID_2488 -Vendor = HP Name = Thunderbolt Dock G4 InstallDuration = 898 RemoveDelay = 732000 @@ -90,7 +96,6 @@ Plugin = ccgx_dmc Summary = Dock Management Controller Device ParentGuid = USB\VID_03F0&PID_0281 -Vendor = HP Name = HP Engage One Pro Aio System CcgxImageKind = dmc-composite InstallDuration = 180 diff -Nru fwupd-2.0.8/plugins/ccgx-dmc/fu-ccgx-dmc-device.c fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc-device.c --- fwupd-2.0.8/plugins/ccgx-dmc/fu-ccgx-dmc-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -42,7 +42,7 @@ static gboolean fu_ccgx_dmc_device_ensure_dock_id(FuCcgxDmcDevice *self, GError **error) { - g_autoptr(GByteArray) st_id = fu_struct_ccgx_dmc_dock_identity_new(); + g_autoptr(FuStructCcgxDmcDockIdentity) st_id = fu_struct_ccgx_dmc_dock_identity_new(); if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, @@ -50,13 +50,13 @@ FU_CCGX_DMC_RQT_CODE_DOCK_IDENTITY, /* request */ 0, /* value */ 0, /* index */ - st_id->data, - st_id->len, + st_id->buf->data, + st_id->buf->len, NULL, /* actual length */ DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "get_dock_id error: "); + g_prefix_error_literal(error, "get_dock_id error: "); return FALSE; } self->custom_meta_flag = fu_struct_ccgx_dmc_dock_identity_get_custom_meta_data_flag(st_id); @@ -70,10 +70,10 @@ gsize bufsz; gsize offset = FU_STRUCT_CCGX_DMC_DOCK_STATUS_SIZE; g_autofree guint8 *buf = NULL; - g_autoptr(GByteArray) st = fu_struct_ccgx_dmc_dock_status_new(); + g_autoptr(FuStructCcgxDmcDockStatus) st = fu_struct_ccgx_dmc_dock_status_new(); /* read minimum status length */ - fu_byte_array_set_size(st, 32, 0x0); + fu_byte_array_set_size(st->buf, 32, 0x0); if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, @@ -81,13 +81,13 @@ FU_CCGX_DMC_RQT_CODE_DOCK_STATUS, /* request */ 0, /* value */ 0, /* index */ - st->data, - st->len, + st->buf->data, + st->buf->len, NULL, /* actual length */ DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "get_dock_status min size error: "); + g_prefix_error_literal(error, "failed to get_dock_status min size: "); return FALSE; } @@ -97,7 +97,14 @@ buf = g_malloc0(bufsz); if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) { /* copying the old buffer preserves compatibility with old emulation files */ - if (!fu_memcpy_safe(buf, bufsz, 0x0, st->data, st->len, 0x0, st->len, error)) + if (!fu_memcpy_safe(buf, + bufsz, + 0x0, + st->buf->data, + st->buf->len, + 0x0, + st->buf->len, + error)) return FALSE; } if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), @@ -113,7 +120,7 @@ DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "get_dock_status actual size error: "); + g_prefix_error_literal(error, "failed to get_dock_status actual size: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, "DmcDockStatus", buf, bufsz); @@ -125,7 +132,7 @@ fu_ccgx_dmc_devx_device_new(FU_DEVICE(self), buf, bufsz, offset, error); if (devx == NULL) return FALSE; - locker = fu_device_locker_new(devx, error); + locker = fu_device_locker_new(FU_DEVICE(devx), error); if (locker == NULL) return FALSE; remove_delay += fu_ccgx_dmc_devx_device_get_remove_delay(devx); @@ -162,7 +169,7 @@ DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "send reset state machine error: "); + g_prefix_error_literal(error, "failed to reset state machine: "); return FALSE; } return TRUE; @@ -184,7 +191,7 @@ DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "send reset error: "); + g_prefix_error_literal(error, "failed to send reset: "); return FALSE; } return TRUE; @@ -217,7 +224,7 @@ DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "send reset error: "); + g_prefix_error_literal(error, "failed to send reset: "); return FALSE; } return TRUE; @@ -239,7 +246,7 @@ DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "send download trigger error: "); + g_prefix_error_literal(error, "failed to send download trigger: "); return FALSE; } return TRUE; @@ -271,14 +278,16 @@ DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "send fwct error: "); + g_prefix_error_literal(error, "failed to send fwct: "); return FALSE; } return TRUE; } static gboolean -fu_ccgx_dmc_device_read_intr_req(FuCcgxDmcDevice *self, GByteArray *intr_rqt, GError **error) +fu_ccgx_dmc_device_read_intr_req(FuCcgxDmcDevice *self, + FuStructCcgxDmcIntRqt *intr_rqt, + GError **error) { guint8 rqt_opcode; g_autofree gchar *title = NULL; @@ -287,13 +296,13 @@ if (!fu_usb_device_interrupt_transfer(FU_USB_DEVICE(self), self->ep_intr_in, - intr_rqt->data, - intr_rqt->len, + intr_rqt->buf->data, + intr_rqt->buf->len, NULL, DMC_GET_REQUEST_TIMEOUT, NULL, error)) { - g_prefix_error(error, "read intr rqt error: "); + g_prefix_error_literal(error, "failed to read intr rqt: "); return FALSE; } @@ -329,7 +338,7 @@ DMC_CONTROL_TRANSFER_DEFAULT_TIMEOUT, NULL, error)) { - g_prefix_error(error, "send fwct error: "); + g_prefix_error_literal(error, "failed to send fwct: "); return FALSE; } return TRUE; @@ -351,7 +360,7 @@ DMC_BULK_OUT_PIPE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "write row data error: "); + g_prefix_error_literal(error, "failed to write row data: "); return FALSE; } return TRUE; @@ -380,16 +389,16 @@ FuCcgxDmcDevice *self = FU_CCGX_DMC_DEVICE(device); const guint8 *req_data; guint8 req_opcode; - g_autoptr(GByteArray) dmc_int_req = fu_struct_ccgx_dmc_int_rqt_new(); + g_autoptr(FuStructCcgxDmcIntRqt) st_rqt = fu_struct_ccgx_dmc_int_rqt_new(); /* get interrupt request */ - if (!fu_ccgx_dmc_device_read_intr_req(self, dmc_int_req, error)) { - g_prefix_error(error, "failed to read intr req in image write status: "); + if (!fu_ccgx_dmc_device_read_intr_req(self, st_rqt, error)) { + g_prefix_error_literal(error, "failed to read intr req in image write status: "); return FALSE; } /* check opcode for fw write */ - req_opcode = fu_struct_ccgx_dmc_int_rqt_get_opcode(dmc_int_req); + req_opcode = fu_struct_ccgx_dmc_int_rqt_get_opcode(st_rqt); if (req_opcode != FU_CCGX_DMC_INT_OPCODE_IMG_WRITE_STATUS) { g_set_error(error, FWUPD_ERROR, @@ -401,7 +410,7 @@ } /* retry if data[0] is 1 otherwise error */ - req_data = fu_struct_ccgx_dmc_int_rqt_get_data(dmc_int_req, NULL); + req_data = fu_struct_ccgx_dmc_int_rqt_get_data(st_rqt, NULL); if (req_data[0] != 0) { g_set_error(error, FWUPD_ERROR, @@ -471,14 +480,13 @@ } static gboolean -fu_ccgx_dmc_device_write_firmware_image(FuDevice *device, +fu_ccgx_dmc_device_write_firmware_image(FuCcgxDmcDevice *self, FuCcgxDmcFirmwareRecord *img_rcd, gsize *fw_data_written, const gsize fw_data_size, FuProgress *progress, GError **error) { - FuCcgxDmcDevice *self = FU_CCGX_DMC_DEVICE(device); GPtrArray *seg_records; g_return_val_if_fail(img_rcd != NULL, FALSE); @@ -520,7 +528,7 @@ gsize fw_data_written = 0; guint8 img_index = 0; guint8 rqt_opcode; - g_autoptr(GByteArray) dmc_int_rqt = fu_struct_ccgx_dmc_int_rqt_new(); + g_autoptr(FuStructCcgxDmcIntRqt) st_rqt = fu_struct_ccgx_dmc_int_rqt_new(); /* progress */ fu_progress_set_id(progress, G_STRLOC); @@ -563,12 +571,12 @@ fw_data_size = fu_ccgx_dmc_firmware_get_fw_data_size(FU_CCGX_DMC_FIRMWARE(firmware)); while (1) { /* get interrupt request */ - if (!fu_ccgx_dmc_device_read_intr_req(self, dmc_int_rqt, error)) + if (!fu_ccgx_dmc_device_read_intr_req(self, st_rqt, error)) return FALSE; - rqt_data = fu_struct_ccgx_dmc_int_rqt_get_data(dmc_int_rqt, NULL); + rqt_data = fu_struct_ccgx_dmc_int_rqt_get_data(st_rqt, NULL); /* fw upgrade request */ - rqt_opcode = fu_struct_ccgx_dmc_int_rqt_get_opcode(dmc_int_rqt); + rqt_opcode = fu_struct_ccgx_dmc_int_rqt_get_opcode(st_rqt); if (rqt_opcode != FU_CCGX_DMC_INT_OPCODE_FW_UPGRADE_RQT) break; img_index = rqt_data[0]; @@ -585,7 +593,7 @@ /* write image */ g_debug("writing image index %u/%u", img_index, image_records->len - 1); img_rcd = g_ptr_array_index(image_records, img_index); - if (!fu_ccgx_dmc_device_write_firmware_image(device, + if (!fu_ccgx_dmc_device_write_firmware_image(self, img_rcd, &fw_data_written, fw_data_size, @@ -642,7 +650,7 @@ fu_ccgx_dmc_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) firmware = fu_ccgx_dmc_firmware_new(); @@ -687,13 +695,13 @@ if (!fu_ccgx_dmc_device_send_download_trigger(self, self->trigger_code, error)) { - g_prefix_error(error, "download trigger error: "); + g_prefix_error_literal(error, "download trigger error: "); return FALSE; } } } else if (self->update_model == FU_CCGX_DMC_UPDATE_MODEL_PENDING_RESET) { if (!fu_ccgx_dmc_device_send_soft_reset(self, manual_replug, error)) { - g_prefix_error(error, "soft reset error: "); + g_prefix_error_literal(error, "soft reset error: "); return FALSE; } } @@ -800,7 +808,7 @@ } static void -fu_ccgx_dmc_device_set_progress(FuDevice *self, FuProgress *progress) +fu_ccgx_dmc_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); /* actually 0, 20, 0, 80! */ @@ -826,6 +834,7 @@ fu_device_add_protocol(FU_DEVICE(self), "com.cypress.ccgx.dmc"); fu_device_add_protocol(FU_DEVICE(self), "com.infineon.ccgx.dmc"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); diff -Nru fwupd-2.0.8/plugins/ccgx-dmc/fu-ccgx-dmc-devx-device.c fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc-devx-device.c --- fwupd-2.0.8/plugins/ccgx-dmc/fu-ccgx-dmc-devx-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc-devx-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -7,6 +7,7 @@ #include "config.h" +#include "fu-ccgx-dmc-device.h" #include "fu-ccgx-dmc-devx-device.h" #define DMC_FW_WRITE_STATUS_RETRY_COUNT 3 @@ -14,7 +15,7 @@ struct _FuCcgxDmcDevxDevice { FuDevice parent_instance; - GByteArray *status; /* DmcDevxStatus */ + FuStructCcgxDmcDevxStatus *status; }; G_DEFINE_TYPE(FuCcgxDmcDevxDevice, fu_ccgx_dmc_devx_device, FU_TYPE_DEVICE) @@ -198,9 +199,12 @@ { if (g_strcmp0(key, "CcgxDmcCompositeVersion") == 0) { guint64 tmp = 0; - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy; if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) return FALSE; + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; if (fu_device_get_version_raw(proxy) != tmp) { g_debug("overriding composite version from %u to %u from %s", (guint)fu_device_get_version_raw(proxy), @@ -268,7 +272,7 @@ fu_ccgx_dmc_devx_device_probe(FuDevice *device, GError **error) { FuCcgxDmcDevxDevice *self = FU_CCGX_DMC_DEVX_DEVICE(device); - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy; FuCcgxDmcDevxDeviceType device_version_type = fu_ccgx_dmc_devx_device_version_type(self); gsize offset = 0; guint8 device_type; @@ -311,6 +315,9 @@ } /* add GUIDs */ + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; fu_device_add_instance_strup(device, "TYPE", fu_ccgx_dmc_devx_device_type_to_string(device_type)); @@ -352,6 +359,8 @@ static void fu_ccgx_dmc_devx_device_init(FuCcgxDmcDevxDevice *self) { + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_CCGX_DMC_DEVICE); } static void diff -Nru fwupd-2.0.8/plugins/ccgx-dmc/fu-ccgx-dmc-firmware.c fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc-firmware.c --- fwupd-2.0.8/plugins/ccgx-dmc/fu-ccgx-dmc-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,7 +13,7 @@ #include "fu-ccgx-dmc-struct.h" struct _FuCcgxDmcFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; GPtrArray *image_records; GBytes *fwct_blob; GBytes *custom_meta_blob; @@ -86,14 +86,13 @@ } static gboolean -fu_ccgx_dmc_firmware_parse_segment(FuFirmware *firmware, +fu_ccgx_dmc_firmware_parse_segment(FuCcgxDmcFirmware *self, GInputStream *stream, FuCcgxDmcFirmwareRecord *img_rcd, gsize *seg_off, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { - FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(firmware); gsize row_off; g_autoptr(GChecksum) csum = g_checksum_new(G_CHECKSUM_SHA256); @@ -106,7 +105,7 @@ for (guint32 i = 0; i < img_rcd->num_img_segments; i++) { guint16 row_size_bytes = 0; g_autoptr(FuCcgxDmcFirmwareSegmentRecord) seg_rcd = NULL; - g_autoptr(GByteArray) st_info = NULL; + g_autoptr(FuStructCcgxDmcFwctSegmentationInfo) st_info = NULL; /* read segment info */ seg_rcd = g_new0(FuCcgxDmcFirmwareSegmentRecord, 1); @@ -153,11 +152,11 @@ g_ptr_array_add(img_rcd->seg_records, g_steal_pointer(&seg_rcd)); /* increment segment info offset */ - *seg_off += st_info->len; + *seg_off += st_info->buf->len; } /* check checksum */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint8 csumbuf[DMC_HASH_SIZE] = {0x0}; gsize csumbufsz = sizeof(csumbuf); g_checksum_get_digest(csum, csumbuf, &csumbufsz); @@ -175,13 +174,12 @@ } static gboolean -fu_ccgx_dmc_firmware_parse_image(FuFirmware *firmware, +fu_ccgx_dmc_firmware_parse_image(FuCcgxDmcFirmware *self, guint8 image_count, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { - FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(firmware); gsize img_off = FU_STRUCT_CCGX_DMC_FWCT_INFO_SIZE; gsize seg_off = FU_STRUCT_CCGX_DMC_FWCT_INFO_SIZE + image_count * FU_STRUCT_CCGX_DMC_FWCT_IMAGE_INFO_SIZE; @@ -189,7 +187,7 @@ /* set initial segment info offset */ for (guint32 i = 0; i < image_count; i++) { g_autoptr(FuCcgxDmcFirmwareRecord) img_rcd = NULL; - g_autoptr(GByteArray) st_img = NULL; + g_autoptr(FuStructCcgxDmcFwctImageInfo) st_img = NULL; /* read image info */ img_rcd = g_new0(FuCcgxDmcFirmwareRecord, 1); @@ -228,7 +226,7 @@ return FALSE; /* parse segment */ - if (!fu_ccgx_dmc_firmware_parse_segment(firmware, + if (!fu_ccgx_dmc_firmware_parse_segment(self, stream, img_rcd, &seg_off, @@ -259,7 +257,7 @@ static gboolean fu_ccgx_dmc_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuCcgxDmcFirmware *self = FU_CCGX_DMC_FIRMWARE(firmware); @@ -268,7 +266,7 @@ guint16 mdbufsz = 0; guint32 hdr_composite_version = 0; guint8 hdr_image_count = 0; - g_autoptr(GByteArray) st_hdr = NULL; + g_autoptr(FuStructCcgxDmcFwctInfo) st_hdr = NULL; /* parse */ st_hdr = fu_struct_ccgx_dmc_fwct_info_parse_stream(stream, 0x0, error); @@ -299,7 +297,7 @@ /* create custom meta binary */ if (!fu_input_stream_read_u16(stream, hdr_size, &mdbufsz, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to read metadata size: "); + g_prefix_error_literal(error, "failed to read metadata size: "); return FALSE; } if (mdbufsz > 0) { @@ -317,7 +315,7 @@ /* parse image */ hdr_image_count = fu_struct_ccgx_dmc_fwct_info_get_image_count(st_hdr); - if (!fu_ccgx_dmc_firmware_parse_image(firmware, hdr_image_count, stream, flags, error)) + if (!fu_ccgx_dmc_firmware_parse_image(self, hdr_image_count, stream, flags, error)) return FALSE; /* success */ @@ -328,7 +326,7 @@ fu_ccgx_dmc_firmware_write(FuFirmware *firmware, GError **error) { g_autoptr(GByteArray) buf = g_byte_array_new(); - g_autoptr(GByteArray) st_hdr = fu_struct_ccgx_dmc_fwct_info_new(); + g_autoptr(FuStructCcgxDmcFwctInfo) st_hdr = fu_struct_ccgx_dmc_fwct_info_new(); g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); /* add header */ @@ -344,24 +342,26 @@ fu_struct_ccgx_dmc_fwct_info_set_composite_version(st_hdr, fu_firmware_get_version_raw(firmware)); fu_struct_ccgx_dmc_fwct_info_set_image_count(st_hdr, images->len); - g_byte_array_append(buf, st_hdr->data, st_hdr->len); + g_byte_array_append(buf, st_hdr->buf->data, st_hdr->buf->len); /* add image headers */ for (guint i = 0; i < images->len; i++) { - g_autoptr(GByteArray) st_img = fu_struct_ccgx_dmc_fwct_image_info_new(); + g_autoptr(FuStructCcgxDmcFwctImageInfo) st_img = + fu_struct_ccgx_dmc_fwct_image_info_new(); fu_struct_ccgx_dmc_fwct_image_info_set_device_type(st_img, 0x2); fu_struct_ccgx_dmc_fwct_image_info_set_img_type(st_img, 0x1); fu_struct_ccgx_dmc_fwct_image_info_set_row_size(st_img, 0x1); fu_struct_ccgx_dmc_fwct_image_info_set_fw_version(st_img, 0x330006d2); fu_struct_ccgx_dmc_fwct_image_info_set_app_version(st_img, 0x14136161); fu_struct_ccgx_dmc_fwct_image_info_set_num_img_segments(st_img, 0x1); - g_byte_array_append(buf, st_img->data, st_img->len); + fu_byte_array_append_array(buf, st_img->buf); } /* add segments */ for (guint i = 0; i < images->len; i++) { FuFirmware *img = g_ptr_array_index(images, i); - g_autoptr(GByteArray) st_info = fu_struct_ccgx_dmc_fwct_segmentation_info_new(); + g_autoptr(FuStructCcgxDmcFwctSegmentationInfo) st_info = + fu_struct_ccgx_dmc_fwct_segmentation_info_new(); g_autoptr(FuChunkArray) chunks = NULL; g_autoptr(GBytes) img_bytes = fu_firmware_get_bytes(img, error); if (img_bytes == NULL) @@ -373,7 +373,7 @@ fu_struct_ccgx_dmc_fwct_segmentation_info_set_num_rows( st_info, MAX(fu_chunk_array_length(chunks), 1)); - g_byte_array_append(buf, st_info->data, st_info->len); + fu_byte_array_append_array(buf, st_info->buf); } /* metadata */ @@ -430,6 +430,15 @@ } static void +fu_ccgx_dmc_firmware_add_magic(FuFirmware *firmware) +{ + fu_firmware_add_magic(firmware, + (const guint8 *)FU_STRUCT_CCGX_DMC_FWCT_INFO_DEFAULT_SIGNATURE, + strlen(FU_STRUCT_CCGX_DMC_FWCT_INFO_DEFAULT_SIGNATURE), + 0x0); +} + +static void fu_ccgx_dmc_firmware_init(FuCcgxDmcFirmware *self) { self->image_records = @@ -464,6 +473,7 @@ firmware_class->parse = fu_ccgx_dmc_firmware_parse; firmware_class->write = fu_ccgx_dmc_firmware_write; firmware_class->export = fu_ccgx_dmc_firmware_export; + firmware_class->add_magic = fu_ccgx_dmc_firmware_add_magic; } FuFirmware * diff -Nru fwupd-2.0.8/plugins/ccgx-dmc/fu-ccgx-dmc-plugin.c fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc-plugin.c --- fwupd-2.0.8/plugins/ccgx-dmc/fu-ccgx-dmc-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -29,6 +29,7 @@ FuPlugin *plugin = FU_PLUGIN(obj); FuContext *ctx = fu_plugin_get_context(plugin); fu_context_add_quirk_key(ctx, "CcgxDmcTriggerCode"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_CCGX_DMC_FIRMWARE); fu_plugin_set_device_gtype_default(plugin, FU_TYPE_CCGX_DMC_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_CCGX_DMC_DEVX_DEVICE); /* coverage */ diff -Nru fwupd-2.0.8/plugins/ccgx-dmc/fu-ccgx-dmc.rs fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc.rs --- fwupd-2.0.8/plugins/ccgx-dmc/fu-ccgx-dmc.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx-dmc/fu-ccgx-dmc.rs 2026-02-26 11:36:18.000000000 +0000 @@ -214,7 +214,7 @@ #[derive(New, ParseStream, ValidateStream, Default)] #[repr(C, packed)] struct FuStructCcgxDmcFwctInfo { - signature: u32le == 0x54435746, // 'F' 'W' 'C' 'T' + signature: [char; 4] == "FWCT", size: u16le, checksum: u8, version: u8, diff -Nru fwupd-2.0.8/plugins/ccgx-dmc/fu-self-test.c fwupd-2.0.20/plugins/ccgx-dmc/fu-self-test.c --- fwupd-2.0.8/plugins/ccgx-dmc/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx-dmc/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -31,7 +31,7 @@ g_assert_true(ret); csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); g_assert_no_error(error); - g_assert_cmpstr(csum1, ==, "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"); + g_assert_cmpstr(csum1, ==, "8bfcf543805d54280164813d792581764bd4b48b"); /* ensure we can round-trip */ xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); diff -Nru fwupd-2.0.8/plugins/ccgx-dmc/meson.build fwupd-2.0.20/plugins/ccgx-dmc/meson.build --- fwupd-2.0.8/plugins/ccgx-dmc/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx-dmc/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -43,6 +43,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', diff -Nru fwupd-2.0.8/plugins/ccgx-dmc/tests/hp-dock-g5.json fwupd-2.0.20/plugins/ccgx-dmc/tests/hp-dock-g5.json --- fwupd-2.0.8/plugins/ccgx-dmc/tests/hp-dock-g5.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ccgx-dmc/tests/hp-dock-g5.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/eb866447bb755c00e748cce14918dcbfaec0ec123237daefce5876c769c2bf92-HP-USBC_DOCK_G5-V1.0.11.0.cab", - "emulation-url": "https://fwupd.org/downloads/1cd36afb2da2ec2c43c346076d494180ee8f8f236671985cccd3b82601349bc7-HP-USBC_DOCK_G5-V1.0.11.0.zip", + "url": "eb866447bb755c00e748cce14918dcbfaec0ec123237daefce5876c769c2bf92-HP-USBC_DOCK_G5-V1.0.11.0.cab", + "emulation-url": "1cd36afb2da2ec2c43c346076d494180ee8f8f236671985cccd3b82601349bc7-HP-USBC_DOCK_G5-V1.0.11.0.zip", "components": [ { "version": "1.0.11.0", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/c15a0df7386812781d1f376fe54729e64f69b2a8a6c4b580914d4f6740e4fcc3-HP-USBC_DOCK_G5-V1.0.13.0.cab", - "emulation-url": "https://fwupd.org/downloads/5d35b4edc89fd01b7e6f7e7101cbf076fe4f712bdd817540351573c9fcafe01a-HP-USBC_DOCK_G5-V1.0.13.0.zip", + "url": "c15a0df7386812781d1f376fe54729e64f69b2a8a6c4b580914d4f6740e4fcc3-HP-USBC_DOCK_G5-V1.0.13.0.cab", + "emulation-url": "5d35b4edc89fd01b7e6f7e7101cbf076fe4f712bdd817540351573c9fcafe01a-HP-USBC_DOCK_G5-V1.0.13.0.zip", "components": [ { "version": "1.0.13.0", diff -Nru fwupd-2.0.8/plugins/cfu/cfu.quirk fwupd-2.0.20/plugins/cfu/cfu.quirk --- fwupd-2.0.8/plugins/cfu/cfu.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cfu/cfu.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -4,7 +4,6 @@ # Microsoft USB-C Travel Hub [USB\VID_045E&PID_09BC] Plugin = cfu -Vendor = Microsoft CfuVersionGetReport = 0xC8 CfuOfferSetReport = 0xDC CfuOfferGetReport = 0xD8 diff -Nru fwupd-2.0.8/plugins/cfu/fu-cfu-device.c fwupd-2.0.20/plugins/cfu/fu-cfu-device.c --- fwupd-2.0.8/plugins/cfu/fu-cfu-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cfu/fu-cfu-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -60,8 +60,8 @@ { g_autoptr(GByteArray) buf_in = g_byte_array_new(); g_autoptr(GByteArray) buf_out = g_byte_array_new(); - g_autoptr(GByteArray) st_req = fu_struct_cfu_offer_info_req_new(); - g_autoptr(GByteArray) st_res = NULL; + g_autoptr(FuStructCfuOfferInfoReq) st_req = fu_struct_cfu_offer_info_req_new(); + g_autoptr(FuStructCfuOfferRsp) st_res = NULL; /* not all devices handle this */ if (!fu_device_has_private_flag(FU_DEVICE(self), FU_CFU_DEVICE_FLAG_SEND_OFFER_INFO)) @@ -70,7 +70,7 @@ /* SetReport */ fu_struct_cfu_offer_info_req_set_code(st_req, info_code); fu_byte_array_append_uint8(buf_out, self->offer_set_report.id); - g_byte_array_append(buf_out, st_req->data, st_req->len); + fu_byte_array_append_array(buf_out, st_req->buf); fu_byte_array_set_size(buf_out, self->offer_set_report.ct, 0x0); if (!fu_hid_device_set_report(FU_HID_DEVICE(self), self->offer_set_report.id, @@ -79,7 +79,7 @@ FU_CFU_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to send offer info: "); + g_prefix_error_literal(error, "failed to send offer info: "); return FALSE; } @@ -93,7 +93,7 @@ FU_CFU_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, error)) { - g_prefix_error(error, "failed to send offer info: "); + g_prefix_error_literal(error, "failed to send offer info: "); return FALSE; } st_res = fu_struct_cfu_offer_rsp_parse(buf_in->data, buf_in->len, 0x1, error); @@ -133,7 +133,7 @@ { g_autoptr(GByteArray) buf_in = g_byte_array_new(); g_autoptr(GByteArray) buf_out = g_byte_array_new(); - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructCfuOfferRsp) st = NULL; g_autoptr(GBytes) blob = NULL; /* generate a offer blob */ @@ -154,7 +154,7 @@ FU_CFU_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to send offer: "); + g_prefix_error_literal(error, "failed to send offer: "); return FALSE; } @@ -168,7 +168,7 @@ FU_CFU_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, error)) { - g_prefix_error(error, "failed to get offer response: "); + g_prefix_error_literal(error, "failed to get offer response: "); return FALSE; } st = fu_struct_cfu_offer_rsp_parse(buf_in->data, buf_in->len, 0x1, error); @@ -216,8 +216,8 @@ FuChunk *chk = g_ptr_array_index(chunks, i); g_autoptr(GByteArray) buf_in = g_byte_array_new(); g_autoptr(GByteArray) buf_out = g_byte_array_new(); - g_autoptr(GByteArray) st_req = fu_struct_cfu_content_req_new(); - g_autoptr(GByteArray) st_rsp = NULL; + g_autoptr(FuStructCfuContentReq) st_req = fu_struct_cfu_content_req_new(); + g_autoptr(FuStructCfuContentRsp) st_rsp = NULL; /* build */ if (i == 0) { @@ -231,7 +231,7 @@ fu_struct_cfu_content_req_set_address(st_req, fu_chunk_get_address(chk)); fu_byte_array_append_uint8(buf_out, self->content_set_report.id); - g_byte_array_append(buf_out, st_req->data, st_req->len); + fu_byte_array_append_array(buf_out, st_req->buf); g_byte_array_append(buf_out, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); fu_byte_array_set_size(buf_out, self->content_set_report.ct + 1, 0x0); @@ -243,7 +243,7 @@ FU_CFU_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to send payload: "); + g_prefix_error_literal(error, "failed to send payload: "); return FALSE; } @@ -257,7 +257,7 @@ FU_CFU_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, error)) { - g_prefix_error(error, "failed to get payload response: "); + g_prefix_error_literal(error, "failed to get payload response: "); return FALSE; } st_rsp = fu_struct_cfu_content_rsp_parse(buf_in->data, buf_in->len, 0x1, error); @@ -383,23 +383,23 @@ fu_cfu_device_verify_descriptor(FuCfuDevice *self, FuHidDescriptor *descriptor, GError **error) { if (!fu_cfu_device_ensure_map_item(descriptor, &self->version_get_report, error)) { - g_prefix_error(error, "invalid version-get-report: "); + g_prefix_error_literal(error, "invalid version-get-report: "); return FALSE; } if (!fu_cfu_device_ensure_map_item(descriptor, &self->offer_set_report, error)) { - g_prefix_error(error, "invalid offer-set-report: "); + g_prefix_error_literal(error, "invalid offer-set-report: "); return FALSE; } if (!fu_cfu_device_ensure_map_item(descriptor, &self->offer_get_report, error)) { - g_prefix_error(error, "invalid offer-get-report: "); + g_prefix_error_literal(error, "invalid offer-get-report: "); return FALSE; } if (!fu_cfu_device_ensure_map_item(descriptor, &self->content_set_report, error)) { - g_prefix_error(error, "invalid content-set-report: "); + g_prefix_error_literal(error, "invalid content-set-report: "); return FALSE; } if (!fu_cfu_device_ensure_map_item(descriptor, &self->content_get_report, error)) { - g_prefix_error(error, "invalid content-get-report: "); + g_prefix_error_literal(error, "invalid content-get-report: "); return FALSE; } return TRUE; @@ -412,7 +412,7 @@ guint8 component_cnt = 0; gsize offset = 0; g_autoptr(GHashTable) modules_by_cid = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructCfuGetVersionRsp) st = NULL; g_autoptr(GByteArray) buf = g_byte_array_new(); g_autoptr(GPtrArray) descriptors = NULL; g_autoptr(GString) descriptors_error = g_string_new(NULL); @@ -435,9 +435,8 @@ FuHidDescriptor *descriptor = g_ptr_array_index(descriptors, i); g_autoptr(GError) error_local = NULL; if (!fu_cfu_device_verify_descriptor(self, descriptor, &error_local)) { - if (descriptors_error->len > 0) { + if (descriptors_error->len > 0) g_string_append(descriptors_error, ", "); - } g_string_append_printf(descriptors_error, "descriptor 0x%x: %s", i, @@ -474,7 +473,7 @@ modules_by_cid = g_hash_table_new(g_direct_hash, g_direct_equal); /* read each component module version */ - offset += 0x1 + st->len; + offset += 0x1 + st->buf->len; component_cnt = fu_struct_cfu_get_version_rsp_get_component_cnt(st); for (guint i = 0; i < component_cnt; i++) { g_autoptr(FuCfuModule) module = fu_cfu_module_new(device); diff -Nru fwupd-2.0.8/plugins/cfu/fu-cfu-module.c fwupd-2.0.20/plugins/cfu/fu-cfu-module.c --- fwupd-2.0.8/plugins/cfu/fu-cfu-module.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cfu/fu-cfu-module.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,6 +6,7 @@ #include "config.h" +#include "fu-cfu-device.h" #include "fu-cfu-module.h" #include "fu-cfu-struct.h" @@ -35,9 +36,8 @@ fu_cfu_module_setup(FuCfuModule *self, const guint8 *buf, gsize bufsz, gsize offset, GError **error) { FuDevice *device = FU_DEVICE(self); - FuDevice *parent = fu_device_get_proxy(device); g_autofree gchar *logical_id = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructCfuGetVersionRspComponent) st = NULL; /* parse */ st = fu_struct_cfu_get_version_rsp_component_parse(buf, bufsz, offset, error); @@ -60,11 +60,8 @@ /* set name, if not already set using a quirk */ if (fu_device_get_name(device) == NULL) { - g_autofree gchar *name = NULL; - name = g_strdup_printf("%s (0x%02X:0x%02x)", - fu_device_get_name(parent), - self->component_id, - self->bank); + g_autofree gchar *name = + g_strdup_printf("0x%02X:0x%02x", self->component_id, self->bank); fu_device_set_name(device, name); } @@ -84,7 +81,7 @@ fu_cfu_module_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) firmware = fu_firmware_new(); @@ -112,7 +109,8 @@ if (!fu_firmware_parse_bytes(offer, blob_offer, 0x0, flags, error)) return NULL; fu_firmware_set_id(offer, FU_FIRMWARE_ID_HEADER); - fu_firmware_add_image(firmware, offer); + if (!fu_firmware_add_image(firmware, offer, error)) + return NULL; /* payload */ fw_payload = fu_archive_firmware_get_image_fnmatch(FU_ARCHIVE_FIRMWARE(firmware_archive), @@ -126,7 +124,8 @@ if (!fu_firmware_parse_bytes(payload, blob_payload, 0x0, flags, error)) return NULL; fu_firmware_set_id(payload, FU_FIRMWARE_ID_PAYLOAD); - fu_firmware_add_image(firmware, payload); + if (!fu_firmware_add_image(firmware, payload, error)) + return NULL; /* success */ return g_steal_pointer(&firmware); @@ -143,20 +142,15 @@ FuDeviceClass *device_class; /* process by the parent */ - proxy = fu_device_get_proxy(device); - if (proxy == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no proxy device assigned"); + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) return FALSE; - } device_class = FU_DEVICE_GET_CLASS(proxy); return device_class->write_firmware(proxy, firmware, progress, flags, error); } static void -fu_cfu_module_set_progress(FuDevice *self, FuProgress *progress) +fu_cfu_module_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -179,10 +173,12 @@ fu_device_add_protocol(FU_DEVICE(self), "com.microsoft.cfu"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_SURFACE); fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_ARCHIVE_FIRMWARE); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_CFU_DEVICE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_SIGNED); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); } static void diff -Nru fwupd-2.0.8/plugins/cfu/fu-cfu-plugin.c fwupd-2.0.20/plugins/cfu/fu-cfu-plugin.c --- fwupd-2.0.8/plugins/cfu/fu-cfu-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cfu/fu-cfu-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,7 @@ static void fu_cfu_plugin_init(FuCfuPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void @@ -30,6 +31,7 @@ fu_context_add_quirk_key(ctx, "CfuOfferGetReport"); fu_context_add_quirk_key(ctx, "CfuContentSetReport"); fu_context_add_quirk_key(ctx, "CfuContentGetReport"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_CFU_DEVICE); } diff -Nru fwupd-2.0.8/plugins/cfu/fu-cfu.rs fwupd-2.0.20/plugins/cfu/fu-cfu.rs --- fwupd-2.0.8/plugins/cfu/fu-cfu.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cfu/fu-cfu.rs 2026-02-26 11:36:18.000000000 +0000 @@ -74,7 +74,7 @@ } #[repr(u8)] -enum FuCfuContentFlag { +enum FuCfuContentFlags { Verify = 0x08, TestReplaceFilesystem = 0x20, LastBlock = 0x40, @@ -101,7 +101,7 @@ #[derive(New, Getters)] #[repr(C, packed)] struct FuStructCfuContentReq { - flags: FuCfuContentFlag, + flags: FuCfuContentFlags, data_length: u8, seq_number: u16le, address: u32le, diff -Nru fwupd-2.0.8/plugins/cfu/meson.build fwupd-2.0.20/plugins/cfu/meson.build --- fwupd-2.0.8/plugins/cfu/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cfu/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -8,7 +8,11 @@ command: [ python3, join_paths(meson.project_source_root(), 'libfwupdplugin', 'rustgen.py'), - '@INPUT@', '@OUTPUT0@', '@OUTPUT1@', + '@INPUT@', + '@OUTPUT0@', + '@OUTPUT1@', + '--prefix', + 'Fu', ], ) diff -Nru fwupd-2.0.8/plugins/ch341a/README.md fwupd-2.0.20/plugins/ch341a/README.md --- fwupd-2.0.8/plugins/ch341a/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch341a/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,62 +0,0 @@ ---- -title: Plugin: CH341A ---- - -## Introduction - -The CH341A is an affordable SPI programmer. - -The assumed map between UIO command bits, pins on CH341A chip and pins on SPI chip: - - UIO CH341A SPI CH341A - 0 D0/15 CS/1 CS0 - 1 D1/16 unused CS1 - 2 D2/17 unused CS2 - 3 D3/18 SCK/6 DCK - 4 D4/19 unused DOUT2 - 5 D5/20 SI/5 DOUBT - 6 D6/21 unused DIN2 - 7 D7/22 SO/2 DIN - -IMPORTANT NOTE: - -You must perform the 3.3V signal output modification if you are using the CH341A with 3.3V SPI -chips. The CH341A has a design flaw that outputs 5V on the MISO and MOSI pins even when VCC is 3V -which will almost certainly be out-of-specification for the device you are trying to program. - -![CH341A Signal Output Modification](ch341a-vmod.png) - -See [this guide](https://www.chucknemeth.com/usb-devices/ch341a/3v-ch341a-mod) for more details. - -## Firmware Format - -The daemon will decompress the cabinet archive and extract a firmware blob of unspecified format. - -This plugin supports the following protocol ID: - -- `org.jedec.cfi` - -## GUID Generation - -These devices use the standard USB DeviceInstanceId values, e.g. - -- `USB\VID_1A86&PID_5512` - -## Update Behavior - -The device programs devices in raw mode, and can best be used with `fwupdtool`. - -To write an image, use `sudo fwupdtool --plugins ch341a install-blob firmware.bin` and to backup -the contents of a SPI device use `sudo fwupdtool --plugins ch341a firmware-dump backup.bin` - -## Vendor ID Security - -The vendor ID is set from the USB vendor, in this instance set to `USB:0x1A86` - -## External Interface Access - -This plugin requires read/write access to `/dev/bus/usb`. - -## Version Considerations - -This plugin has been available since fwupd version `1.8.0`. Binary files /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/plugins/ch341a/ch341a-vmod.png and /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/plugins/ch341a/ch341a-vmod.png differ diff -Nru fwupd-2.0.8/plugins/ch341a/ch341a.quirk fwupd-2.0.20/plugins/ch341a/ch341a.quirk --- fwupd-2.0.8/plugins/ch341a/ch341a.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch341a/ch341a.quirk 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -[USB\VID_1A86&PID_5512] -Plugin = ch341a diff -Nru fwupd-2.0.8/plugins/ch341a/fu-ch341a-cfi-device.c fwupd-2.0.20/plugins/ch341a/fu-ch341a-cfi-device.c --- fwupd-2.0.8/plugins/ch341a/fu-ch341a-cfi-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch341a/fu-ch341a-cfi-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,429 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-ch341a-cfi-device.h" -#include "fu-ch341a-device.h" - -struct _FuCh341aCfiDevice { - FuCfiDevice parent_instance; -}; - -G_DEFINE_TYPE(FuCh341aCfiDevice, fu_ch341a_cfi_device, FU_TYPE_CFI_DEVICE) - -#define CH341A_PAYLOAD_SIZE 0x1A - -static gboolean -fu_ch341a_cfi_device_chip_select(FuCfiDevice *self, gboolean value, GError **error) -{ - FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); - return fu_ch341a_device_chip_select(proxy, value, error); -} - -typedef struct { - guint8 mask; - guint8 value; -} FuCh341aCfiDeviceHelper; - -static gboolean -fu_ch341a_cfi_device_wait_for_status_cb(FuDevice *device, gpointer user_data, GError **error) -{ - FuCh341aCfiDeviceHelper *helper = (FuCh341aCfiDeviceHelper *)user_data; - FuCh341aCfiDevice *self = FU_CH341A_CFI_DEVICE(device); - FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(device)); - guint8 buf[2] = {0x0}; - g_autoptr(FuDeviceLocker) cslocker = NULL; - - /* enable chip */ - cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); - if (cslocker == NULL) - return FALSE; - if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), - FU_CFI_DEVICE_CMD_READ_STATUS, - &buf[0], - error)) - return FALSE; - if (!fu_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to want to status: "); - return FALSE; - } - if ((buf[0x1] & helper->mask) != helper->value) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "wanted 0x%x, got 0x%x", - helper->value, - (guint)(buf[0x1] & helper->mask)); - return FALSE; - } - - /* success */ - return TRUE; -} - -static gboolean -fu_ch341a_cfi_device_wait_for_status(FuCh341aCfiDevice *self, - guint8 mask, - guint8 value, - guint count, - guint delay, - GError **error) -{ - FuCh341aCfiDeviceHelper helper = {.mask = mask, .value = value}; - return fu_device_retry_full(FU_DEVICE(self), - fu_ch341a_cfi_device_wait_for_status_cb, - count, - delay, - &helper, - error); -} - -static gboolean -fu_ch341a_cfi_device_read_jedec(FuCfiDevice *self, GError **error) -{ - FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); - guint8 buf[CH341A_PAYLOAD_SIZE] = {0x9F}; - g_autoptr(FuDeviceLocker) cslocker = NULL; - g_autoptr(GString) flash_id = g_string_new(NULL); - - /* enable chip */ - cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); - if (cslocker == NULL) - return FALSE; - - /* read JEDEC ID */ - if (!fu_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to request JEDEC ID: "); - return FALSE; - } - if (buf[1] == 0x0 && buf[2] == 0x0 && buf[3] == 0x0) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "flash ID non-valid, got 0x000000"); - return FALSE; - } - if (buf[1] == 0xFF && buf[2] == 0xFF && buf[3] == 0xFF) { - fu_cfi_device_set_flash_id(FU_CFI_DEVICE(self), NULL); - return TRUE; - } - g_string_append_printf(flash_id, "%02X", buf[1]); - g_string_append_printf(flash_id, "%02X", buf[2]); - g_string_append_printf(flash_id, "%02X", buf[3]); - fu_cfi_device_set_flash_id(FU_CFI_DEVICE(self), flash_id->str); - - /* success */ - return TRUE; -} - -static gboolean -fu_ch341a_cfi_device_write_enable(FuCh341aCfiDevice *self, GError **error) -{ - FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); - guint8 buf[1] = {0x0}; - g_autoptr(FuDeviceLocker) cslocker = NULL; - - /* write enable */ - if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), FU_CFI_DEVICE_CMD_WRITE_EN, &buf[0], error)) - return FALSE; - cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); - if (cslocker == NULL) - return FALSE; - if (!fu_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) - return FALSE; - if (!fu_device_locker_close(cslocker, error)) - return FALSE; - - /* check that WEL is now set */ - return fu_ch341a_cfi_device_wait_for_status(self, 0b10, 0b10, 10, 5, error); -} - -static gboolean -fu_ch341a_cfi_device_chip_erase(FuCh341aCfiDevice *self, GError **error) -{ - FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); - guint8 buf[] = {0x0}; - g_autoptr(FuDeviceLocker) cslocker = NULL; - - /* enable chip */ - cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); - if (cslocker == NULL) - return FALSE; - - /* erase */ - if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), - FU_CFI_DEVICE_CMD_CHIP_ERASE, - &buf[0], - error)) - return FALSE; - if (!fu_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) - return FALSE; - if (!fu_device_locker_close(cslocker, error)) - return FALSE; - - /* poll Read Status register BUSY */ - return fu_ch341a_cfi_device_wait_for_status(self, 0b1, 0b0, 100, 500, error); -} - -static gboolean -fu_ch341a_cfi_device_write_page(FuCh341aCfiDevice *self, FuChunk *page, GError **error) -{ - FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); - guint8 buf[4] = {0x0}; - g_autoptr(FuChunkArray) chunks = NULL; - g_autoptr(FuDeviceLocker) cslocker = NULL; - g_autoptr(GBytes) page_blob = fu_chunk_get_bytes(page); - - if (!fu_ch341a_cfi_device_write_enable(self, error)) - return FALSE; - - cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); - if (cslocker == NULL) - return FALSE; - - /* cmd, then 24 bit starting address */ - fu_memwrite_uint32(buf, fu_chunk_get_address(page), G_BIG_ENDIAN); - if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), - FU_CFI_DEVICE_CMD_PAGE_PROG, - &buf[0], - error)) - return FALSE; - if (!fu_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) - return FALSE; - - /* send data */ - chunks = fu_chunk_array_new_from_bytes(page_blob, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - CH341A_PAYLOAD_SIZE); - for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - g_autoptr(FuChunk) chk = NULL; - guint8 buf2[CH341A_PAYLOAD_SIZE] = {0x0}; - - /* prepare chunk */ - chk = fu_chunk_array_index(chunks, i, error); - if (chk == NULL) - return FALSE; - if (!fu_memcpy_safe(buf2, - sizeof(buf2), - 0x0, /* dst */ - fu_chunk_get_data(chk), - fu_chunk_get_data_sz(chk), - 0x0, /* src */ - fu_chunk_get_data_sz(chk), - error)) - return FALSE; - if (!fu_ch341a_device_spi_transfer(proxy, buf2, fu_chunk_get_data_sz(chk), error)) - return FALSE; - } - if (!fu_device_locker_close(cslocker, error)) - return FALSE; - - /* poll Read Status register BUSY */ - return fu_ch341a_cfi_device_wait_for_status(self, 0b1, 0b0, 100, 50, error); -} - -static gboolean -fu_ch341a_cfi_device_write_pages(FuCh341aCfiDevice *self, - FuChunkArray *pages, - FuProgress *progress, - GError **error) -{ - /* progress */ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_set_steps(progress, fu_chunk_array_length(pages)); - for (guint i = 0; i < fu_chunk_array_length(pages); i++) { - g_autoptr(FuChunk) page = fu_chunk_array_index(pages, i, error); - if (page == NULL) - return FALSE; - if (!fu_ch341a_cfi_device_write_page(self, page, error)) - return FALSE; - fu_progress_step_done(progress); - } - /* success */ - return TRUE; -} - -static GBytes * -fu_ch341a_cfi_device_read_firmware(FuCh341aCfiDevice *self, - gsize bufsz, - FuProgress *progress, - GError **error) -{ - FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); - guint8 buf[CH341A_PAYLOAD_SIZE] = {0x0}; - g_autoptr(FuDeviceLocker) cslocker = NULL; - g_autoptr(GByteArray) blob = g_byte_array_new(); - g_autoptr(GPtrArray) chunks = NULL; - - /* enable chip */ - cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); - if (cslocker == NULL) - return NULL; - - /* read each block */ - chunks = fu_chunk_array_new(NULL, bufsz + 0x4, 0x0, 0x0, CH341A_PAYLOAD_SIZE); - fu_progress_set_id(progress, G_STRLOC); - fu_progress_set_steps(progress, chunks->len); - fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); - - /* cmd, then 24 bit starting address */ - fu_memwrite_uint32(buf, 0x0, G_BIG_ENDIAN); - if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), - FU_CFI_DEVICE_CMD_READ_DATA, - &buf[0], - error)) - return NULL; - for (guint i = 0; i < chunks->len; i++) { - FuChunk *chk = g_ptr_array_index(chunks, i); - - /* the first package has cmd and address info */ - if (!fu_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) - return NULL; - if (i == 0) { - g_byte_array_append(blob, buf + 0x4, fu_chunk_get_data_sz(chk) - 0x4); - } else { - g_byte_array_append(blob, buf + 0x0, fu_chunk_get_data_sz(chk)); - } - - /* done */ - fu_progress_step_done(progress); - } - - /* success */ - return g_bytes_new(blob->data, blob->len); -} - -static gboolean -fu_ch341a_cfi_device_write_firmware(FuDevice *device, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - FuCh341aCfiDevice *self = FU_CH341A_CFI_DEVICE(device); - FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); - g_autoptr(GBytes) fw = NULL; - g_autoptr(GBytes) fw_verify = NULL; - g_autoptr(FuChunkArray) pages = NULL; - g_autoptr(FuDeviceLocker) locker = NULL; - - /* open programmer */ - locker = fu_device_locker_new(proxy, error); - if (locker == NULL) - return FALSE; - - /* progress */ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 33, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 44, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 35, NULL); - - /* get default image */ - fw = fu_firmware_get_bytes(firmware, error); - if (fw == NULL) - return FALSE; - - /* erase */ - if (!fu_ch341a_cfi_device_write_enable(self, error)) { - g_prefix_error(error, "failed to enable writes: "); - return FALSE; - } - if (!fu_ch341a_cfi_device_chip_erase(self, error)) { - g_prefix_error(error, "failed to erase: "); - return FALSE; - } - fu_progress_step_done(progress); - - /* write each block */ - pages = fu_chunk_array_new_from_bytes(fw, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - fu_cfi_device_get_page_size(FU_CFI_DEVICE(self))); - if (!fu_ch341a_cfi_device_write_pages(self, - pages, - fu_progress_get_child(progress), - error)) { - g_prefix_error(error, "failed to write pages: "); - return FALSE; - } - fu_progress_step_done(progress); - - /* verify each block */ - fw_verify = fu_ch341a_cfi_device_read_firmware(self, - g_bytes_get_size(fw), - fu_progress_get_child(progress), - error); - if (fw_verify == NULL) { - g_prefix_error(error, "failed to verify blocks: "); - return FALSE; - } - if (!fu_bytes_compare(fw, fw_verify, error)) - return FALSE; - fu_progress_step_done(progress); - - /* success! */ - return TRUE; -} - -static GBytes * -fu_ch341a_cfi_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) -{ - FuCh341aCfiDevice *self = FU_CH341A_CFI_DEVICE(device); - FuCh341aDevice *proxy = FU_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); - gsize bufsz = fu_device_get_firmware_size_max(device); - g_autoptr(FuDeviceLocker) locker = NULL; - - /* open programmer */ - locker = fu_device_locker_new(proxy, error); - if (locker == NULL) - return NULL; - - /* sanity check */ - if (bufsz == 0x0) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "device firmware size not set"); - return NULL; - } - return fu_ch341a_cfi_device_read_firmware(self, bufsz, progress, error); -} - -static void -fu_ch341a_cfi_device_set_progress(FuDevice *self, FuProgress *progress) -{ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); -} - -static void -fu_ch341a_cfi_device_init(FuCh341aCfiDevice *self) -{ - fu_device_add_protocol(FU_DEVICE(self), "org.jedec.cfi"); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); -} - -static void -fu_ch341a_cfi_device_class_init(FuCh341aCfiDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - FuCfiDeviceClass *cfi_class = FU_CFI_DEVICE_CLASS(klass); - - cfi_class->chip_select = fu_ch341a_cfi_device_chip_select; - cfi_class->read_jedec = fu_ch341a_cfi_device_read_jedec; - - device_class->write_firmware = fu_ch341a_cfi_device_write_firmware; - device_class->dump_firmware = fu_ch341a_cfi_device_dump_firmware; - device_class->set_progress = fu_ch341a_cfi_device_set_progress; -} diff -Nru fwupd-2.0.8/plugins/ch341a/fu-ch341a-cfi-device.h fwupd-2.0.20/plugins/ch341a/fu-ch341a-cfi-device.h --- fwupd-2.0.8/plugins/ch341a/fu-ch341a-cfi-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch341a/fu-ch341a-cfi-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_CH341A_CFI_DEVICE (fu_ch341a_cfi_device_get_type()) -G_DECLARE_FINAL_TYPE(FuCh341aCfiDevice, fu_ch341a_cfi_device, FU, CH341A_CFI_DEVICE, FuCfiDevice) diff -Nru fwupd-2.0.8/plugins/ch341a/fu-ch341a-device.c fwupd-2.0.20/plugins/ch341a/fu-ch341a-device.c --- fwupd-2.0.8/plugins/ch341a/fu-ch341a-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch341a/fu-ch341a-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,290 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-ch341a-cfi-device.h" -#include "fu-ch341a-device.h" - -struct _FuCh341aDevice { - FuUsbDevice parent_instance; - guint8 speed; -}; - -G_DEFINE_TYPE(FuCh341aDevice, fu_ch341a_device, FU_TYPE_USB_DEVICE) - -#define CH341A_USB_TIMEOUT 1000 -#define CH341A_EP_OUT 0x02 /* host to device (write) */ -#define CH341A_EP_IN 0x82 /* device to host (read) */ -#define CH341A_EP_SIZE 0x20 - -#define CH341A_CMD_SET_OUTPUT 0xA1 -#define CH341A_CMD_IO_ADDR 0xA2 -#define CH341A_CMD_PRINT_OUT 0xA3 -#define CH341A_CMD_SPI_STREAM 0xA8 -#define CH341A_CMD_SIO_STREAM 0xA9 -#define CH341A_CMD_I2C_STREAM 0xAA -#define CH341A_CMD_UIO_STREAM 0xAB - -#define CH341A_CMD_I2C_STM_START 0x74 -#define CH341A_CMD_I2C_STM_STOP 0x75 -#define CH341A_CMD_I2C_STM_OUT 0x80 -#define CH341A_CMD_I2C_STM_IN 0xC0 -#define CH341A_CMD_I2C_STM_SET 0x60 -#define CH341A_CMD_I2C_STM_US 0x40 -#define CH341A_CMD_I2C_STM_MS 0x50 -#define CH341A_CMD_I2C_STM_DLY 0x0F -#define CH341A_CMD_I2C_STM_END 0x00 - -#define CH341A_CMD_UIO_STM_IN 0x00 -#define CH341A_CMD_UIO_STM_DIR 0x40 -#define CH341A_CMD_UIO_STM_OUT 0x80 -#define CH341A_CMD_UIO_STM_US 0xC0 -#define CH341A_CMD_UIO_STM_END 0x20 - -#define CH341A_STM_I2C_SPEED_LOW 0x00 -#define CH341A_STM_I2C_SPEED_STANDARD 0x01 -#define CH341A_STM_I2C_SPEED_FAST 0x02 -#define CH341A_STM_I2C_SPEED_HIGH 0x03 - -#define CH341A_STM_SPI_MODUS_STANDARD 0x00 -#define CH341A_STM_SPI_MODUS_DOUBLE 0x04 - -#define CH341A_STM_SPI_ENDIAN_BIG 0x0 -#define CH341A_STM_SPI_ENDIAN_LITTLE 0x80 - -static const gchar * -fu_ch341a_device_speed_to_string(guint8 speed) -{ - if (speed == CH341A_STM_I2C_SPEED_LOW) - return "20kHz"; - if (speed == CH341A_STM_I2C_SPEED_STANDARD) - return "100kHz"; - if (speed == CH341A_STM_I2C_SPEED_FAST) - return "400kHz"; - if (speed == CH341A_STM_I2C_SPEED_HIGH) - return "750kHz"; - if (speed == (CH341A_STM_I2C_SPEED_LOW | CH341A_STM_SPI_MODUS_DOUBLE)) - return "2*20kHz"; - if (speed == (CH341A_STM_I2C_SPEED_STANDARD | CH341A_STM_SPI_MODUS_DOUBLE)) - return "2*100kHz"; - if (speed == (CH341A_STM_I2C_SPEED_FAST | CH341A_STM_SPI_MODUS_DOUBLE)) - return "2*400kHz"; - if (speed == (CH341A_STM_I2C_SPEED_HIGH | CH341A_STM_SPI_MODUS_DOUBLE)) - return "2*750kHz"; - return NULL; -} - -static void -fu_ch341a_device_to_string(FuDevice *device, guint idt, GString *str) -{ - FuCh341aDevice *self = FU_CH341A_DEVICE(device); - fwupd_codec_string_append(str, idt, "Speed", fu_ch341a_device_speed_to_string(self->speed)); -} - -static gboolean -fu_ch341a_device_write(FuCh341aDevice *self, guint8 *buf, gsize bufsz, GError **error) -{ - gsize actual_length = 0; - - /* debug */ - fu_dump_raw(G_LOG_DOMAIN, "write", buf, bufsz); - if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), - CH341A_EP_OUT, - buf, - bufsz, - &actual_length, - CH341A_USB_TIMEOUT, - NULL, - error)) { - g_prefix_error(error, "failed to write 0x%x bytes: ", (guint)bufsz); - return FALSE; - } - if (bufsz != actual_length) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "only wrote 0x%x of 0x%x", - (guint)actual_length, - (guint)bufsz); - return FALSE; - } - - /* success */ - return TRUE; -} - -static gboolean -fu_ch341a_device_read(FuCh341aDevice *self, guint8 *buf, gsize bufsz, GError **error) -{ - gsize actual_length = 0; - - if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), - CH341A_EP_IN, - buf, - bufsz, - &actual_length, - CH341A_USB_TIMEOUT, - NULL, - error)) { - g_prefix_error(error, "failed to read 0x%x bytes: ", (guint)bufsz); - return FALSE; - } - if (bufsz != actual_length) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "only read 0x%x of 0x%x", - (guint)actual_length, - (guint)bufsz); - return FALSE; - } - - /* debug */ - fu_dump_raw(G_LOG_DOMAIN, "read", buf, bufsz); - - /* success */ - return TRUE; -} - -/** - * fu_ch341a_device_reverse_uint8: - * @value: integer - * - * Calculates the reverse bit order for a single byte. - * - * Returns: the @value, reversed - **/ -static guint8 -fu_ch341a_device_reverse_uint8(guint8 value) -{ - guint8 tmp = 0; - if (value & 0x01) - tmp = 0x80; - if (value & 0x02) - tmp |= 0x40; - if (value & 0x04) - tmp |= 0x20; - if (value & 0x08) - tmp |= 0x10; - if (value & 0x10) - tmp |= 0x08; - if (value & 0x20) - tmp |= 0x04; - if (value & 0x40) - tmp |= 0x02; - if (value & 0x80) - tmp |= 0x01; - return tmp; -} - -gboolean -fu_ch341a_device_spi_transfer(FuCh341aDevice *self, guint8 *buf, gsize bufsz, GError **error) -{ - gsize buf2sz = bufsz + 1; - g_autofree guint8 *buf2 = g_malloc0(buf2sz); - - /* requires LSB first */ - buf2[0] = CH341A_CMD_SPI_STREAM; - for (gsize i = 0; i < bufsz; i++) - buf2[i + 1] = fu_ch341a_device_reverse_uint8(buf[i]); - - /* debug */ - fu_dump_raw(G_LOG_DOMAIN, "SPIwrite", buf, bufsz); - if (!fu_ch341a_device_write(self, buf2, buf2sz, error)) - return FALSE; - - if (!fu_ch341a_device_read(self, buf, bufsz, error)) - return FALSE; - - /* requires LSB first */ - for (gsize i = 0; i < bufsz; i++) - buf[i] = fu_ch341a_device_reverse_uint8(buf[i]); - - /* debug */ - fu_dump_raw(G_LOG_DOMAIN, "SPIread", buf, bufsz); - - /* success */ - return TRUE; -} - -static gboolean -fu_ch341a_device_configure_stream(FuCh341aDevice *self, GError **error) -{ - guint8 buf[] = {CH341A_CMD_I2C_STREAM, - CH341A_CMD_I2C_STM_SET | self->speed, - CH341A_CMD_I2C_STM_END}; - if (!fu_ch341a_device_write(self, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to configure stream: "); - return FALSE; - } - - /* success */ - return TRUE; -} - -gboolean -fu_ch341a_device_chip_select(FuCh341aDevice *self, gboolean val, GError **error) -{ - guint8 buf[] = { - CH341A_CMD_UIO_STREAM, - CH341A_CMD_UIO_STM_OUT | (val ? 0x36 : 0x37), /* CS* high, SCK=0, DOUBT*=1 */ - CH341A_CMD_UIO_STM_DIR | (val ? 0x3F : 0x00), /* pin direction */ - CH341A_CMD_UIO_STM_END, - }; - return fu_ch341a_device_write(self, buf, sizeof(buf), error); -} - -static gboolean -fu_ch341a_device_probe(FuDevice *device, GError **error) -{ - g_autoptr(FuCh341aCfiDevice) cfi_device = NULL; - cfi_device = g_object_new(FU_TYPE_CH341A_CFI_DEVICE, - "context", - fu_device_get_context(device), - "proxy", - device, - "logical-id", - "SPI", - NULL); - fu_device_add_child(device, FU_DEVICE(cfi_device)); - return TRUE; -} - -static gboolean -fu_ch341a_device_setup(FuDevice *device, GError **error) -{ - FuCh341aDevice *self = FU_CH341A_DEVICE(device); - - /* FuUsbDevice->setup */ - if (!FU_DEVICE_CLASS(fu_ch341a_device_parent_class)->setup(device, error)) - return FALSE; - - /* set speed */ - if (!fu_ch341a_device_configure_stream(self, error)) - return FALSE; - - /* success */ - return TRUE; -} - -static void -fu_ch341a_device_init(FuCh341aDevice *self) -{ - self->speed = CH341A_STM_I2C_SPEED_STANDARD; - fu_usb_device_add_interface(FU_USB_DEVICE(self), 0x0); - fu_device_set_name(FU_DEVICE(self), "CH341A"); - fu_device_set_vendor(FU_DEVICE(self), "WinChipHead"); -} - -static void -fu_ch341a_device_class_init(FuCh341aDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->probe = fu_ch341a_device_probe; - device_class->setup = fu_ch341a_device_setup; - device_class->to_string = fu_ch341a_device_to_string; -} diff -Nru fwupd-2.0.8/plugins/ch341a/fu-ch341a-device.h fwupd-2.0.20/plugins/ch341a/fu-ch341a-device.h --- fwupd-2.0.8/plugins/ch341a/fu-ch341a-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch341a/fu-ch341a-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_CH341A_DEVICE (fu_ch341a_device_get_type()) -G_DECLARE_FINAL_TYPE(FuCh341aDevice, fu_ch341a_device, FU, CH341A_DEVICE, FuUsbDevice) - -gboolean -fu_ch341a_device_chip_select(FuCh341aDevice *self, gboolean val, GError **error); -gboolean -fu_ch341a_device_spi_transfer(FuCh341aDevice *self, guint8 *buf, gsize bufsz, GError **error); diff -Nru fwupd-2.0.8/plugins/ch341a/fu-ch341a-plugin.c fwupd-2.0.20/plugins/ch341a/fu-ch341a-plugin.c --- fwupd-2.0.8/plugins/ch341a/fu-ch341a-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch341a/fu-ch341a-plugin.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,44 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-ch341a-device.h" -#include "fu-ch341a-plugin.h" - -struct _FuCh341APlugin { - FuPlugin parent_instance; -}; - -G_DEFINE_TYPE(FuCh341APlugin, fu_ch341a_plugin, FU_TYPE_PLUGIN) - -static void -fu_ch341a_plugin_init(FuCh341APlugin *self) -{ -} - -static void -fu_ch341a_plugin_object_constructed(GObject *obj) -{ - FuPlugin *plugin = FU_PLUGIN(obj); - fu_plugin_set_name(plugin, "ch341a"); -} - -static void -fu_ch341a_plugin_constructed(GObject *obj) -{ - FuPlugin *plugin = FU_PLUGIN(obj); - fu_plugin_add_device_gtype(plugin, FU_TYPE_CH341A_DEVICE); -} - -static void -fu_ch341a_plugin_class_init(FuCh341APluginClass *klass) -{ - FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); - GObjectClass *object_class = G_OBJECT_CLASS(klass); - object_class->constructed = fu_ch341a_plugin_object_constructed; - plugin_class->constructed = fu_ch341a_plugin_constructed; -} diff -Nru fwupd-2.0.8/plugins/ch341a/fu-ch341a-plugin.h fwupd-2.0.20/plugins/ch341a/fu-ch341a-plugin.h --- fwupd-2.0.8/plugins/ch341a/fu-ch341a-plugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch341a/fu-ch341a-plugin.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -G_DECLARE_FINAL_TYPE(FuCh341APlugin, fu_ch341a_plugin, FU, CH341A_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/ch341a/lsusb.txt fwupd-2.0.20/plugins/ch341a/lsusb.txt --- fwupd-2.0.8/plugins/ch341a/lsusb.txt 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch341a/lsusb.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -Bus 001 Device 124: ID 1a86:5512 QinHeng Electronics CH341 in EPP/MEM/I2C mode, EPP/I2C adapter -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.10 - bDeviceClass 255 Vendor Specific Class - bDeviceSubClass 0 - bDeviceProtocol 2 - bMaxPacketSize0 8 - idVendor 0x1a86 QinHeng Electronics - idProduct 0x5512 CH341 in EPP/MEM/I2C mode, EPP/I2C adapter - bcdDevice 3.04 - iManufacturer 0 - iProduct 0 - iSerial 0 - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 0x0027 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0x80 - (Bus Powered) - MaxPower 96mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 3 - bInterfaceClass 255 Vendor Specific Class - bInterfaceSubClass 1 - bInterfaceProtocol 2 - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x82 EP 2 IN - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0020 1x 32 bytes - bInterval 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x02 EP 2 OUT - bmAttributes 2 - Transfer Type Bulk - Synch Type None - Usage Type Data - wMaxPacketSize 0x0020 1x 32 bytes - bInterval 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0008 1x 8 bytes - bInterval 1 -Device Status: 0x0000 - (Bus Powered) diff -Nru fwupd-2.0.8/plugins/ch341a/meson.build fwupd-2.0.20/plugins/ch341a/meson.build --- fwupd-2.0.8/plugins/ch341a/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch341a/meson.build 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -cargs = ['-DG_LOG_DOMAIN="FuPluginCh341a"'] -plugins += {meson.current_source_dir().split('/')[-1]: true} - -plugin_quirks += files('ch341a.quirk') -plugin_builtins += static_library('fu_plugin_ch341a', - sources: [ - 'fu-ch341a-cfi-device.c', - 'fu-ch341a-device.c', - 'fu-ch341a-plugin.c', - ], - include_directories: plugin_incdirs, - link_with: plugin_libs, - c_args: cargs, - dependencies: plugin_deps, -) - -enumeration_data += files('tests/ch341a-setup.json') -device_tests += files('tests/ch341a.json') diff -Nru fwupd-2.0.8/plugins/ch341a/tests/ch341a-setup.json fwupd-2.0.20/plugins/ch341a/tests/ch341a-setup.json --- fwupd-2.0.8/plugins/ch341a/tests/ch341a-setup.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch341a/tests/ch341a-setup.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,81 +0,0 @@ -{ - "UsbDevices": [ - { - "GType": "FuUsbDevice", - "PlatformId": "1-6", - "Created": "2024-10-24T17:46:38.660081Z", - "IdVendor": 6790, - "IdProduct": 21778, - "Device": 772, - "USB": 272, - "DeviceClass": 255, - "DeviceProtocol": 2, - "UsbConfigDescriptors": [ - { - "ConfigurationValue": 1 - } - ], - "UsbInterfaces": [ - { - "Length": 9, - "DescriptorType": 4, - "InterfaceClass": 255, - "InterfaceSubClass": 1, - "InterfaceProtocol": 2, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 130, - "MaxPacketSize": 32 - }, - { - "DescriptorType": 5, - "EndpointAddress": 2, - "MaxPacketSize": 32 - }, - { - "DescriptorType": 5, - "EndpointAddress": 129, - "Interval": 1, - "MaxPacketSize": 8 - } - ] - } - ], - "UsbEvents": [ - { - "Id": "#4693935e", - "Data": "001" - }, - { - "Id": "#bddbca22", - "Data": "MAJOR=189\nMINOR=76\nDEVNAME=bus/usb/001/077\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=1a86/5512/304\nTYPE=255/0/2\nBUSNUM=001\nDEVNUM=077" - }, - { - "Id": "#1ab3ae0a", - "Data": "077" - }, - { - "Id": "#cb0c7049", - "Data": "qmEA" - }, - { - "Id": "#8fcc316a", - "Data": "q7Z/IA==" - }, - { - "Id": "#21f01298", - "Data": "qPkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" - }, - { - "Id": "#469f419f", - "Data": "//////////////////////////////////8=" - }, - { - "Id": "#9952189d", - "Data": "q7dAIA==" - } - ] - } - ] -} diff -Nru fwupd-2.0.8/plugins/ch341a/tests/ch341a.json fwupd-2.0.20/plugins/ch341a/tests/ch341a.json --- fwupd-2.0.8/plugins/ch341a/tests/ch341a.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch341a/tests/ch341a.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -{ - "name": "WinChipHead CH341a", - "interactive": false, - "steps": [ - { - "emulation-file": "@enumeration_datadir@/ch341a-setup.json", - "components": [ - { - "version": "3.4", - "guids": [ - "e06cabdb-2908-5d74-a63f-0daa8a1be58a" - ] - } - ] - } - ] -} diff -Nru fwupd-2.0.8/plugins/ch347/README.md fwupd-2.0.20/plugins/ch347/README.md --- fwupd-2.0.8/plugins/ch347/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch347/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ ---- -title: Plugin: CH347 ---- - -## Introduction - -The CH347 is an affordable SPI programmer. - -## Firmware Format - -The daemon will decompress the cabinet archive and extract a firmware blob of unspecified format. - -This plugin supports the following protocol ID: - -- `org.jedec.cfi` - -## GUID Generation - -These devices use the standard USB DeviceInstanceId values, e.g. - -- `USB\VID_1A86&PID_55DB` - -## Update Behavior - -The device programs devices in raw mode, and can best be used with `fwupdtool`. - -To write an image, use `sudo fwupdtool --plugins ch347 install-blob firmware.bin` and to backup -the contents of a SPI device use `sudo fwupdtool --plugins ch347 firmware-dump backup.bin` - -## Vendor ID Security - -The vendor ID is set from the USB vendor, in this instance set to `USB:0x1A86` - -## External Interface Access - -This plugin requires read/write access to `/dev/bus/usb`. - -## Version Considerations - -This plugin has been available since fwupd version `1.8.14`. diff -Nru fwupd-2.0.8/plugins/ch347/ch347.quirk fwupd-2.0.20/plugins/ch347/ch347.quirk --- fwupd-2.0.8/plugins/ch347/ch347.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch347/ch347.quirk 1970-01-01 00:00:00.000000000 +0000 @@ -1,2 +0,0 @@ -[USB\VID_1A86&PID_55DB] -Plugin = ch347 diff -Nru fwupd-2.0.8/plugins/ch347/fu-ch347-cfi-device.c fwupd-2.0.20/plugins/ch347/fu-ch347-cfi-device.c --- fwupd-2.0.8/plugins/ch347/fu-ch347-cfi-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch347/fu-ch347-cfi-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,49 +0,0 @@ -/* - * Copyright 2023 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-ch347-cfi-device.h" -#include "fu-ch347-device.h" - -struct _FuCh347CfiDevice { - FuCfiDevice parent_instance; -}; - -G_DEFINE_TYPE(FuCh347CfiDevice, fu_ch347_cfi_device, FU_TYPE_CFI_DEVICE) - -static gboolean -fu_ch347_cfi_device_chip_select(FuCfiDevice *self, gboolean value, GError **error) -{ - FuCh347Device *proxy = FU_CH347_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); - return fu_ch347_device_chip_select(proxy, value, error); -} - -static gboolean -fu_ch347_cfi_device_send_command(FuCfiDevice *self, - const guint8 *wbuf, - gsize wbufsz, - guint8 *rbuf, - gsize rbufsz, - FuProgress *progress, - GError **error) -{ - FuCh347Device *proxy = FU_CH347_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); - return fu_ch347_device_send_command(proxy, wbuf, wbufsz, rbuf, rbufsz, progress, error); -} - -static void -fu_ch347_cfi_device_init(FuCh347CfiDevice *self) -{ -} - -static void -fu_ch347_cfi_device_class_init(FuCh347CfiDeviceClass *klass) -{ - FuCfiDeviceClass *cfi_class = FU_CFI_DEVICE_CLASS(klass); - cfi_class->chip_select = fu_ch347_cfi_device_chip_select; - cfi_class->send_command = fu_ch347_cfi_device_send_command; -} diff -Nru fwupd-2.0.8/plugins/ch347/fu-ch347-cfi-device.h fwupd-2.0.20/plugins/ch347/fu-ch347-cfi-device.h --- fwupd-2.0.8/plugins/ch347/fu-ch347-cfi-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch347/fu-ch347-cfi-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -/* - * Copyright 2023 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_CH347_CFI_DEVICE (fu_ch347_cfi_device_get_type()) -G_DECLARE_FINAL_TYPE(FuCh347CfiDevice, fu_ch347_cfi_device, FU, CH347_CFI_DEVICE, FuCfiDevice) diff -Nru fwupd-2.0.8/plugins/ch347/fu-ch347-device.c fwupd-2.0.20/plugins/ch347/fu-ch347-device.c --- fwupd-2.0.8/plugins/ch347/fu-ch347-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch347/fu-ch347-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,310 +0,0 @@ -/* - * Copyright 2023 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-ch347-cfi-device.h" -#include "fu-ch347-device.h" -#include "fu-ch347-struct.h" - -struct _FuCh347Device { - FuUsbDevice parent_instance; - guint8 divisor; -}; - -G_DEFINE_TYPE(FuCh347Device, fu_ch347_device, FU_TYPE_USB_DEVICE) - -#define FU_CH347_USB_TIMEOUT 1000 - -#define FU_CH347_CS_ASSERT 0x00 -#define FU_CH347_CS_DEASSERT 0x40 -#define FU_CH347_CS_CHANGE 0x80 -#define FU_CH347_CS_IGNORE 0x00 - -#define FU_CH347_EP_OUT 0x06 -#define FU_CH347_EP_IN 0x86 - -#define FU_CH347_MODE1_IFACE 0x2 -#define FU_CH347_MODE2_IFACE 0x1 - -#define FU_CH347_PACKET_SIZE 510 -#define FU_CH347_PAYLOAD_SIZE (FU_CH347_PACKET_SIZE - 3) - -static void -fu_ch347_device_to_string(FuDevice *device, guint idt, GString *str) -{ - FuCh347Device *self = FU_CH347_DEVICE(device); - fwupd_codec_string_append_hex(str, idt, "Divisor", self->divisor); -} - -static gboolean -fu_ch347_device_write(FuCh347Device *self, - FuCh347CmdSpi cmd, - const guint8 *buf, - gsize bufsz, - GError **error) -{ - gsize actual_length = 0; - g_autoptr(FuStructCh347Req) st = fu_struct_ch347_req_new(); - - /* pack */ - fu_struct_ch347_req_set_cmd(st, cmd); - fu_struct_ch347_req_set_payloadsz(st, bufsz); - if (bufsz > 0) - g_byte_array_append(st, buf, bufsz); - - /* debug */ - fu_dump_raw(G_LOG_DOMAIN, "write", st->data, st->len); - if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), - FU_CH347_EP_OUT, - st->data, - st->len, - &actual_length, - FU_CH347_USB_TIMEOUT, - NULL, - error)) { - g_prefix_error(error, "failed to write 0x%x bytes: ", (guint)bufsz); - return FALSE; - } - if (st->len != actual_length) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "only wrote 0x%x of 0x%x", - (guint)actual_length, - (guint)st->len); - return FALSE; - } - - /* success */ - return TRUE; -} - -static gboolean -fu_ch347_device_read(FuCh347Device *self, - FuCh347CmdSpi cmd, - guint8 *buf, - gsize bufsz, - GError **error) -{ - gsize actual_length = 0; - guint16 size_rsp; - g_autoptr(GByteArray) st = fu_struct_ch347_req_new(); - - /* pack */ - fu_struct_ch347_req_set_cmd(st, cmd); - fu_struct_ch347_req_set_payloadsz(st, sizeof(guint32)); - fu_byte_array_append_uint32(st, bufsz, G_LITTLE_ENDIAN); - fu_byte_array_set_size(st, FU_CH347_PACKET_SIZE, 0x0); - - if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), - FU_CH347_EP_IN, - st->data, - st->len, - &actual_length, - FU_CH347_USB_TIMEOUT, - NULL, - error)) { - g_prefix_error(error, "failed to read 0x%x bytes: ", (guint)bufsz); - return FALSE; - } - fu_dump_raw(G_LOG_DOMAIN, "read", st->data, actual_length); - if (actual_length == 0) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "returned 0 bytes"); - return FALSE; - } - - /* debug */ - if (fu_struct_ch347_req_get_cmd(st) != cmd) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid cmd, got 0x%02x, expected 0x%02x", - fu_struct_ch347_req_get_cmd(st), - cmd); - return FALSE; - } - size_rsp = fu_struct_ch347_req_get_payloadsz(st); - if (size_rsp != bufsz) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "size invalid, got 0x%04x, expected 0x04%x", - size_rsp, - (guint)bufsz); - return FALSE; - } - - /* success */ - return fu_memcpy_safe(buf, - bufsz, - 0x0, - st->data, - st->len, - FU_STRUCT_CH347_REQ_SIZE, - size_rsp, - error); -} - -gboolean -fu_ch347_device_send_command(FuCh347Device *self, - const guint8 *wbuf, - gsize wbufsz, - guint8 *rbuf, - gsize rbufsz, - FuProgress *progress, - GError **error) -{ - /* write */ - if (wbufsz > 0) { - g_autoptr(GBytes) wblob = g_bytes_new_static(wbuf, wbufsz); - g_autoptr(FuChunkArray) chunks = - fu_chunk_array_new_from_bytes(wblob, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - FU_CH347_PAYLOAD_SIZE); - for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - guint8 buf[1] = {0x0}; - g_autoptr(FuChunk) chk = NULL; - - /* prepare chunk */ - chk = fu_chunk_array_index(chunks, i, error); - if (chk == NULL) - return FALSE; - if (!fu_ch347_device_write(self, - FU_CH347_CMD_SPI_OUT, - fu_chunk_get_data(chk), - fu_chunk_get_data_sz(chk), - error)) - return FALSE; - if (!fu_ch347_device_read(self, - FU_CH347_CMD_SPI_OUT, - buf, - sizeof(buf), - error)) - return FALSE; - } - } - - /* read */ - if (rbufsz > 0) { - g_autoptr(GPtrArray) chunks = - fu_chunk_array_mutable_new(rbuf, rbufsz, 0x0, 0x0, FU_CH347_PAYLOAD_SIZE); - g_autoptr(GByteArray) cmdbuf = g_byte_array_new(); - fu_byte_array_append_uint32(cmdbuf, rbufsz, G_LITTLE_ENDIAN); - if (!fu_ch347_device_write(self, - FU_CH347_CMD_SPI_IN, - cmdbuf->data, - cmdbuf->len, - error)) - return FALSE; - fu_progress_set_id(progress, G_STRLOC); - fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); - fu_progress_set_steps(progress, chunks->len); - for (guint i = 0; i < chunks->len; i++) { - FuChunk *chk = g_ptr_array_index(chunks, i); - if (!fu_ch347_device_read(self, - FU_CH347_CMD_SPI_IN, - fu_chunk_get_data_out(chk), - fu_chunk_get_data_sz(chk), - error)) - return FALSE; - fu_progress_step_done(progress); - } - } - - /* success */ - return TRUE; -} - -static gboolean -fu_ch347_device_configure_stream(FuCh347Device *self, GError **error) -{ - guint8 data[26] = {[2] = 4, /* ?? */ - [3] = 1, /* ?? */ - [6] = 0, /* clock polarity: bit 1 */ - [8] = 0, /* clock phase: bit 0 */ - [11] = 2, /* ?? */ - [12] = (self->divisor & 0x7) << 3, /* clock divisor: bits 5:3 */ - [14] = 0, /* bit order: bit 7, 0=MSB */ - [16] = 7, /* ?? */ - [21] = 0}; /* CS polarity: bit 7 CS2, bit 6 CS1. 0 = active low */ - if (!fu_ch347_device_write(self, FU_CH347_CMD_SPI_SET_CFG, data, sizeof(data), error)) { - g_prefix_error(error, "failed to configure stream: "); - return FALSE; - } - if (!fu_ch347_device_read(self, FU_CH347_CMD_SPI_SET_CFG, data, 1, error)) { - g_prefix_error(error, "failed to confirm configure stream: "); - return FALSE; - } - - /* success */ - return TRUE; -} - -gboolean -fu_ch347_device_chip_select(FuCh347Device *self, gboolean val, GError **error) -{ - guint8 buf[10] = { - [0] = val ? FU_CH347_CS_ASSERT | FU_CH347_CS_CHANGE - : FU_CH347_CS_DEASSERT | FU_CH347_CS_CHANGE, - [5] = FU_CH347_CS_IGNORE /* CS2 */ - }; - return fu_ch347_device_write(self, FU_CH347_CMD_SPI_CS_CTRL, buf, sizeof(buf), error); -} - -static gboolean -fu_ch347_device_probe(FuDevice *device, GError **error) -{ - g_autoptr(FuCh347CfiDevice) cfi_device = NULL; - cfi_device = g_object_new(FU_TYPE_CH347_CFI_DEVICE, - "context", - fu_device_get_context(device), - "proxy", - device, - "parent", - device, - "logical-id", - "SPI", - NULL); - fu_device_add_child(device, FU_DEVICE(cfi_device)); - return TRUE; -} - -static gboolean -fu_ch347_device_setup(FuDevice *device, GError **error) -{ - FuCh347Device *self = FU_CH347_DEVICE(device); - - /* FuUsbDevice->setup */ - if (!FU_DEVICE_CLASS(fu_ch347_device_parent_class)->setup(device, error)) - return FALSE; - - /* set divisor */ - if (!fu_ch347_device_configure_stream(self, error)) - return FALSE; - - /* success */ - return TRUE; -} - -static void -fu_ch347_device_init(FuCh347Device *self) -{ - self->divisor = 0b10; - fu_usb_device_add_interface(FU_USB_DEVICE(self), FU_CH347_MODE1_IFACE); - fu_device_set_name(FU_DEVICE(self), "CH347"); - fu_device_set_vendor(FU_DEVICE(self), "WinChipHead"); -} - -static void -fu_ch347_device_class_init(FuCh347DeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->probe = fu_ch347_device_probe; - device_class->setup = fu_ch347_device_setup; - device_class->to_string = fu_ch347_device_to_string; -} diff -Nru fwupd-2.0.8/plugins/ch347/fu-ch347-device.h fwupd-2.0.20/plugins/ch347/fu-ch347-device.h --- fwupd-2.0.8/plugins/ch347/fu-ch347-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch347/fu-ch347-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -/* - * Copyright 2023 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_CH347_DEVICE (fu_ch347_device_get_type()) -G_DECLARE_FINAL_TYPE(FuCh347Device, fu_ch347_device, FU, CH347_DEVICE, FuUsbDevice) - -gboolean -fu_ch347_device_chip_select(FuCh347Device *self, gboolean val, GError **error); -gboolean -fu_ch347_device_send_command(FuCh347Device *self, - const guint8 *wbuf, - gsize wbufsz, - guint8 *rbuf, - gsize rbufsz, - FuProgress *progress, - GError **error); diff -Nru fwupd-2.0.8/plugins/ch347/fu-ch347-plugin.c fwupd-2.0.20/plugins/ch347/fu-ch347-plugin.c --- fwupd-2.0.8/plugins/ch347/fu-ch347-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch347/fu-ch347-plugin.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,35 +0,0 @@ -/* - * Copyright 2023 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-ch347-device.h" -#include "fu-ch347-plugin.h" - -struct _FuCh347Plugin { - FuPlugin parent_instance; -}; - -G_DEFINE_TYPE(FuCh347Plugin, fu_ch347_plugin, FU_TYPE_PLUGIN) - -static void -fu_ch347_plugin_init(FuCh347Plugin *self) -{ -} - -static void -fu_ch347_plugin_constructed(GObject *obj) -{ - FuPlugin *plugin = FU_PLUGIN(obj); - fu_plugin_add_device_gtype(plugin, FU_TYPE_CH347_DEVICE); -} - -static void -fu_ch347_plugin_class_init(FuCh347PluginClass *klass) -{ - FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); - plugin_class->constructed = fu_ch347_plugin_constructed; -} diff -Nru fwupd-2.0.8/plugins/ch347/fu-ch347-plugin.h fwupd-2.0.20/plugins/ch347/fu-ch347-plugin.h --- fwupd-2.0.8/plugins/ch347/fu-ch347-plugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch347/fu-ch347-plugin.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -/* - * Copyright 2023 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -G_DECLARE_FINAL_TYPE(FuCh347Plugin, fu_ch347_plugin, FU, CH347_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/ch347/fu-ch347.rs fwupd-2.0.20/plugins/ch347/fu-ch347.rs --- fwupd-2.0.8/plugins/ch347/fu-ch347.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch347/fu-ch347.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -// Copyright 2024 Richard Hughes -// SPDX-License-Identifier: LGPL-2.1-or-later - -#[repr(u8)] -enum FuCh347CmdSpi { - SetCfg = 0xC0, - CsCtrl = 0xC1, - OutIn = 0xC2, - In = 0xC3, - Out = 0xC4, - GetCfg = 0xCA, -} - -#[derive(New, Getters)] -#[repr(C, packed)] -struct FuStructCh347Req { - cmd: FuCh347CmdSpi, - payloadsz: u16le, -} diff -Nru fwupd-2.0.8/plugins/ch347/meson.build fwupd-2.0.20/plugins/ch347/meson.build --- fwupd-2.0.8/plugins/ch347/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch347/meson.build 1970-01-01 00:00:00.000000000 +0000 @@ -1,19 +0,0 @@ -cargs = ['-DG_LOG_DOMAIN="FuPluginCh347"'] -plugins += {meson.current_source_dir().split('/')[-1]: true} - -plugin_quirks += files('ch347.quirk') -plugin_builtins += static_library('fu_plugin_ch347', - rustgen.process('fu-ch347.rs'), - sources: [ - 'fu-ch347-cfi-device.c', - 'fu-ch347-device.c', - 'fu-ch347-plugin.c', - ], - include_directories: plugin_incdirs, - link_with: plugin_libs, - c_args: cargs, - dependencies: plugin_deps, -) - -enumeration_data += files('tests/ch347-setup.json') -device_tests += files('tests/ch347.json') diff -Nru fwupd-2.0.8/plugins/ch347/tests/ch347-setup.json fwupd-2.0.20/plugins/ch347/tests/ch347-setup.json --- fwupd-2.0.8/plugins/ch347/tests/ch347-setup.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch347/tests/ch347-setup.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,124 +0,0 @@ -{ - "UsbDevices": [ - { - "GType": "FuUsbDevice", - "PlatformId": "1-1", - "Created": "2024-10-24T17:45:42.490939Z", - "IdVendor": 6790, - "IdProduct": 21979, - "Device": 320, - "USB": 512, - "Manufacturer": 1, - "Product": 2, - "SerialNumber": 3, - "UsbConfigDescriptors": [ - { - "ConfigurationValue": 1 - } - ], - "UsbInterfaces": [ - { - "Length": 9, - "DescriptorType": 4, - "InterfaceClass": 2, - "InterfaceSubClass": 2, - "InterfaceProtocol": 1, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 131, - "Interval": 1, - "MaxPacketSize": 64 - } - ] - }, - { - "Length": 9, - "DescriptorType": 4, - "InterfaceNumber": 1, - "InterfaceClass": 10, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 4, - "MaxPacketSize": 512 - }, - { - "DescriptorType": 5, - "EndpointAddress": 132, - "MaxPacketSize": 512 - } - ] - }, - { - "Length": 9, - "DescriptorType": 4, - "InterfaceNumber": 2, - "InterfaceClass": 255, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 6, - "MaxPacketSize": 512 - }, - { - "DescriptorType": 5, - "EndpointAddress": 134, - "MaxPacketSize": 512 - } - ] - } - ], - "UsbEvents": [ - { - "Id": "#4693935e", - "Data": "001" - }, - { - "Id": "#bddbca22", - "Data": "MAJOR=189\nMINOR=75\nDEVNAME=bus/usb/001/076\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=1a86/55db/140\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=076" - }, - { - "Id": "#1ab3ae0a", - "Data": "076" - }, - { - "Id": "#028c3a0e", - "Data": "MDEyMzQ1Njc4OQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" - }, - { - "Id": "#4f7f767b", - "Data": "wBoAAAAEAQAAAAAAAAACEAAAAAcAAAAAAAAAAAA=" - }, - { - "Id": "#e8e979a2", - "Data": "wAEAAA==" - }, - { - "Id": "#e62e60a8", - "Data": "wQoAgAAAAAAAAAAAAA==" - }, - { - "Id": "#e8f9929d", - "Data": "xAEAnw==" - }, - { - "Id": "#9074dd4e", - "Data": "xAEAAA==" - }, - { - "Id": "#c6abc2c8", - "Data": "wwQAAwAAAA==" - }, - { - "Id": "#43321f40", - "Data": "wwMAAAAA" - }, - { - "Id": "#279f56ea", - "Data": "wQoAwAAAAAAAAAAAAA==" - } - ] - } - ] -} diff -Nru fwupd-2.0.8/plugins/ch347/tests/ch347.json fwupd-2.0.20/plugins/ch347/tests/ch347.json --- fwupd-2.0.8/plugins/ch347/tests/ch347.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ch347/tests/ch347.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -{ - "name": "WinChipHead CH347", - "interactive": false, - "steps": [ - { - "emulation-file": "@enumeration_datadir@/ch347-setup.json", - "components": [ - { - "version": "1.40", - "guids": [ - "114d0d9f-3a6a-55f1-9f67-4ba7ec653071" - ] - } - ] - } - ] -} diff -Nru fwupd-2.0.8/plugins/corsair/README.md fwupd-2.0.20/plugins/corsair/README.md --- fwupd-2.0.8/plugins/corsair/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/corsair/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -64,10 +64,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.8.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Andrii Dushko: @dushko-devx diff -Nru fwupd-2.0.8/plugins/corsair/fu-corsair-bp.c fwupd-2.0.20/plugins/corsair/fu-corsair-bp.c --- fwupd-2.0.8/plugins/corsair/fu-corsair-bp.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/corsair/fu-corsair-bp.c 2026-02-26 11:36:18.000000000 +0000 @@ -71,7 +71,7 @@ NULL, error); if (!ret) { - g_prefix_error(error, "failed to write command: "); + g_prefix_error_literal(error, "failed to write command: "); return FALSE; } if (actual_len != self->cmd_write_size) { @@ -97,7 +97,7 @@ NULL, error); if (!ret) { - g_prefix_error(error, "failed to get command response: "); + g_prefix_error_literal(error, "failed to get command response: "); return FALSE; } if (actual_len != self->cmd_read_size) { @@ -159,7 +159,7 @@ guint8 init_cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x0d, 0x00, 0x03}; guint8 write_cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x06, 0x00}; if (!fu_corsair_bp_command(self, init_cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) { - g_prefix_error(error, "firmware init fail: "); + g_prefix_error_literal(error, "firmware init fail: "); return FALSE; } @@ -169,7 +169,7 @@ firmware_size, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "cannot serialize firmware size: "); + g_prefix_error_literal(error, "cannot serialize firmware size: "); return FALSE; } if (!fu_memcpy_safe(write_cmd, @@ -180,11 +180,11 @@ 0, fu_chunk_get_data_sz(chunk), error)) { - g_prefix_error(error, "cannot set data: "); + g_prefix_error_literal(error, "cannot set data: "); return FALSE; } if (!fu_corsair_bp_command(self, write_cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) { - g_prefix_error(error, "write command fail: "); + g_prefix_error_literal(error, "write command fail: "); return FALSE; } return TRUE; @@ -202,23 +202,23 @@ 0, fu_chunk_get_data_sz(chunk), error)) { - g_prefix_error(error, "cannot set data: "); + g_prefix_error_literal(error, "cannot set data: "); return FALSE; } if (!fu_corsair_bp_command(self, cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) { - g_prefix_error(error, "write command fail: "); + g_prefix_error_literal(error, "write command fail: "); return FALSE; } return TRUE; } static void -fu_corsair_bp_incorporate(FuDevice *self, FuDevice *donor) +fu_corsair_bp_incorporate(FuDevice *device, FuDevice *donor) { - FuCorsairBp *bp_self = FU_CORSAIR_BP(self); + FuCorsairBp *bp_self = FU_CORSAIR_BP(device); FuCorsairBp *bp_donor = FU_CORSAIR_BP(donor); - g_return_if_fail(FU_IS_CORSAIR_BP(self)); + g_return_if_fail(FU_IS_CORSAIR_BP(device)); g_return_if_fail(FU_IS_CORSAIR_BP(donor)); bp_self->epin = bp_donor->epin; @@ -263,7 +263,7 @@ cmd[CORSAIR_OFFSET_CMD_SET_MODE] = mode; if (!fu_corsair_bp_command(self, cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) { - g_prefix_error(error, "set mode command fail: "); + g_prefix_error_literal(error, "set mode command fail: "); return FALSE; } @@ -282,7 +282,7 @@ fu_progress_set_steps(progress, fu_chunk_array_length(chunks) + 1); if (!fu_corsair_bp_write_first_chunk(self, first_chunk, firmware_size, error)) { - g_prefix_error(error, "cannot write first chunk: "); + g_prefix_error_literal(error, "cannot write first chunk: "); return FALSE; } fu_progress_step_done(progress); @@ -309,7 +309,7 @@ { guint8 commit_cmd[FU_CORSAIR_MAX_CMD_SIZE] = {0x08, 0x05, 0x01, 0x00}; if (!fu_corsair_bp_command(self, commit_cmd, CORSAIR_TRANSACTION_TIMEOUT, TRUE, error)) { - g_prefix_error(error, "firmware commit fail: "); + g_prefix_error_literal(error, "firmware commit fail: "); return FALSE; } return TRUE; @@ -333,21 +333,21 @@ blob = fu_firmware_get_bytes(firmware, error); if (blob == NULL) { - g_prefix_error(error, "cannot get firmware data: "); + g_prefix_error_literal(error, "cannot get firmware data: "); return FALSE; } firmware_raw = fu_bytes_get_data_safe(blob, &firmware_size, error); if (firmware_raw == NULL) { - g_prefix_error(error, "cannot get firmware data: "); + g_prefix_error_literal(error, "cannot get firmware data: "); return FALSE; } /* the firmware size should be greater than 1 chunk */ if (firmware_size <= first_chunk_size) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "update file should be bigger"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "update file should be bigger"); return FALSE; } @@ -355,7 +355,7 @@ rest_of_firmware = fu_bytes_new_offset(blob, first_chunk_size, firmware_size - first_chunk_size, error); if (rest_of_firmware == NULL) { - g_prefix_error(error, "cannot get firmware past first chunk: "); + g_prefix_error_literal(error, "cannot get firmware past first chunk: "); return FALSE; } chunks = @@ -386,7 +386,7 @@ blob = fu_firmware_get_bytes(firmware, error); if (blob == NULL) { - g_prefix_error(error, "cannot get firmware bytes: "); + g_prefix_error_literal(error, "cannot get firmware bytes: "); return FALSE; } fu_memwrite_uint32(&cmd[CORSAIR_OFFSET_CMD_CRC], diff -Nru fwupd-2.0.8/plugins/corsair/fu-corsair-device.c fwupd-2.0.20/plugins/corsair/fu-corsair-device.c --- fwupd-2.0.8/plugins/corsair/fu-corsair-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/corsair/fu-corsair-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -111,9 +111,8 @@ } static gboolean -fu_corsair_device_poll_subdevice(FuDevice *device, gboolean *subdevice_added, GError **error) +fu_corsair_device_poll_subdevice(FuCorsairDevice *self, gboolean *subdevice_added, GError **error) { - FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); guint32 subdevices; g_autoptr(FuCorsairDevice) child = NULL; g_autoptr(FuCorsairBp) child_bp = NULL; @@ -122,7 +121,7 @@ FU_CORSAIR_BP_PROPERTY_SUBDEVICES, &subdevices, error)) { - g_prefix_error(error, "cannot get subdevices: "); + g_prefix_error_literal(error, "cannot get subdevices: "); return FALSE; } @@ -131,7 +130,7 @@ return TRUE; } - child_bp = fu_corsair_bp_new(FU_USB_DEVICE(device), TRUE); + child_bp = fu_corsair_bp_new(FU_USB_DEVICE(self), TRUE); fu_device_incorporate(FU_DEVICE(child_bp), FU_DEVICE(self->bp), FU_DEVICE_INCORPORATE_FLAG_ALL); @@ -146,16 +145,15 @@ if (!fu_device_setup(FU_DEVICE(child), error)) return FALSE; - fu_device_add_child(device, FU_DEVICE(child)); + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(child)); *subdevice_added = TRUE; return TRUE; } static gchar * -fu_corsair_device_get_version(FuDevice *device, GError **error) +fu_corsair_device_get_version(FuCorsairDevice *self, GError **error) { - FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); guint32 version_raw; if (!fu_corsair_bp_get_property(self->bp, @@ -164,9 +162,9 @@ error)) return NULL; - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { gboolean broken_by_flag = - fu_device_has_private_flag(device, + fu_device_has_private_flag(FU_DEVICE(self), FU_CORSAIR_DEVICE_FLAG_NO_VERSION_IN_BOOTLOADER); /* Version 0xffffffff means that previous update was interrupted. @@ -174,9 +172,8 @@ firmware will not be rejected because of older version. It is safe to always pass firmware because setup in bootloader mode can only happen during emergency update */ - if (broken_by_flag || version_raw == G_MAXUINT32) { + if (broken_by_flag || version_raw == G_MAXUINT32) version_raw = 0; - } } return fu_corsair_version_from_uint32(version_raw); @@ -205,6 +202,12 @@ g_autofree gchar *version = NULL; FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + /* sanity check */ + if (self->bp == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no bp"); + return FALSE; + } + fu_corsair_bp_flush_input_reports(self->bp); if (!fu_corsair_bp_get_property(self->bp, FU_CORSAIR_BP_PROPERTY_MODE, &mode, error)) @@ -212,16 +215,16 @@ if (mode == FU_CORSAIR_DEVICE_MODE_BOOTLOADER) fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); - version = fu_corsair_device_get_version(device, error); + version = fu_corsair_device_get_version(self, error); if (version == NULL) { - g_prefix_error(error, "cannot get version: "); + g_prefix_error_literal(error, "cannot get version: "); return FALSE; } fu_device_set_version(device, version); bootloader_version = fu_corsair_device_get_bootloader_version(self->bp, error); if (bootloader_version == NULL) { - g_prefix_error(error, "cannot get bootloader version: "); + g_prefix_error_literal(error, "cannot get bootloader version: "); return FALSE; } fu_device_set_version_bootloader(device, bootloader_version); @@ -232,7 +235,7 @@ FU_CORSAIR_BP_PROPERTY_BATTERY_LEVEL, &battery_level, error)) { - g_prefix_error(error, "cannot get battery level: "); + g_prefix_error_literal(error, "cannot get battery level: "); return FALSE; } fu_device_set_battery_level(device, battery_level / 10); @@ -247,14 +250,14 @@ if (self->subdevice_id != NULL && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { gboolean subdevice_added = FALSE; - g_autoptr(GError) local_error = NULL; + g_autoptr(GError) error_local = NULL; /* Give some time to a subdevice to get connected to the receiver. * Without this delay a subdevice may be not present even if it is * turned on. */ fu_device_sleep(device, CORSAIR_SUBDEVICE_FIRST_POLL_DELAY); - if (!fu_corsair_device_poll_subdevice(device, &subdevice_added, &local_error)) { - g_warning("error polling subdevice: %s", local_error->message); + if (!fu_corsair_device_poll_subdevice(self, &subdevice_added, &error_local)) { + g_warning("error polling subdevice: %s", error_local->message); } else { /* start polling if a subdevice was not added */ if (!subdevice_added) @@ -268,9 +271,8 @@ static gboolean fu_corsair_device_reload(FuDevice *device, GError **error) { - if (fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE)) { + if (fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE)) return fu_corsair_device_setup(device, error); - } /* USB devices will be reloaded by FWUPD after reenumeration */ return TRUE; @@ -286,37 +288,38 @@ FU_CORSAIR_BP_PROPERTY_SUBDEVICES, &subdevices, error)) { - g_prefix_error(error, "cannot get subdevices: "); + g_prefix_error_literal(error, "cannot get subdevices: "); return FALSE; } if (subdevices == 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "subdevice is not connected"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "subdevice is not connected"); return FALSE; } return TRUE; } static gboolean -fu_corsair_device_reconnect_subdevice(FuDevice *device, GError **error) +fu_corsair_device_reconnect_subdevice(FuCorsairDevice *self, GError **error) { - FuDevice *parent = fu_device_get_parent(device); - - if (parent == NULL) { - g_prefix_error(error, "cannot get parent: "); - return FALSE; - } + FuDevice *parent; - /* Wait some time to make sure that a subdevice was disconnected. */ - fu_device_sleep(device, CORSAIR_SUBDEVICE_REBOOT_DELAY); + /* wait some time to make sure that a subdevice was disconnected */ + fu_device_sleep(FU_DEVICE(self), CORSAIR_SUBDEVICE_REBOOT_DELAY); + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; if (!fu_device_retry_full(parent, fu_corsair_device_is_subdevice_connected_cb, CORSAIR_SUBDEVICE_RECONNECT_RETRIES, CORSAIR_SUBDEVICE_RECONNECT_PERIOD, NULL, error)) { - g_prefix_error(error, "a subdevice did not reconnect after attach: "); + g_prefix_error_literal(error, "a subdevice did not reconnect after attach: "); return FALSE; } @@ -324,12 +327,17 @@ } static gboolean -fu_corsair_device_ensure_mode(FuDevice *device, FuCorsairDeviceMode mode, GError **error) +fu_corsair_device_ensure_mode(FuCorsairDevice *self, FuCorsairDeviceMode mode, GError **error) { - FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); FuCorsairDeviceMode current_mode; - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + /* sanity check */ + if (self->bp == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no bp"); + return FALSE; + } + + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { current_mode = FU_CORSAIR_DEVICE_MODE_BOOTLOADER; } else { current_mode = FU_CORSAIR_DEVICE_MODE_APPLICATION; @@ -340,28 +348,28 @@ if (mode == FU_CORSAIR_DEVICE_MODE_APPLICATION) { if (!fu_device_attach(FU_DEVICE(self->bp), error)) { - g_prefix_error(error, "attach failed: "); + g_prefix_error_literal(error, "attach failed: "); return FALSE; } } else { if (!fu_device_detach(FU_DEVICE(self->bp), error)) { - g_prefix_error(error, "detach failed: "); + g_prefix_error_literal(error, "detach failed: "); return FALSE; } } - if (fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE)) { - if (!fu_corsair_device_reconnect_subdevice(device, error)) { - g_prefix_error(error, "subdevice did not reconnect: "); + if (fu_device_has_private_flag(FU_DEVICE(self), FU_CORSAIR_DEVICE_FLAG_IS_SUBDEVICE)) { + if (!fu_corsair_device_reconnect_subdevice(self, error)) { + g_prefix_error_literal(error, "subdevice did not reconnect: "); return FALSE; } if (mode == FU_CORSAIR_DEVICE_MODE_BOOTLOADER) { - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); } else { - fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); } } else { - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); } return TRUE; @@ -370,13 +378,15 @@ static gboolean fu_corsair_device_attach(FuDevice *device, FuProgress *progress, GError **error) { - return fu_corsair_device_ensure_mode(device, FU_CORSAIR_DEVICE_MODE_APPLICATION, error); + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + return fu_corsair_device_ensure_mode(self, FU_CORSAIR_DEVICE_MODE_APPLICATION, error); } static gboolean fu_corsair_device_detach(FuDevice *device, FuProgress *progress, GError **error) { - return fu_corsair_device_ensure_mode(device, FU_CORSAIR_DEVICE_MODE_BOOTLOADER, error); + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); + return fu_corsair_device_ensure_mode(self, FU_CORSAIR_DEVICE_MODE_BOOTLOADER, error); } static gboolean @@ -397,7 +407,7 @@ fu_progress_get_child(progress), flags, error)) { - g_prefix_error(error, "cannot write firmware: "); + g_prefix_error_literal(error, "cannot write firmware: "); return FALSE; } @@ -405,7 +415,7 @@ if (!fu_device_has_private_flag(device, FU_CORSAIR_DEVICE_FLAG_LEGACY_ATTACH)) { if (!fu_corsair_bp_activate_firmware(self->bp, firmware, error)) { - g_prefix_error(error, "firmware activation fail: "); + g_prefix_error_literal(error, "firmware activation fail: "); return FALSE; } fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); @@ -429,7 +439,7 @@ } static void -fu_corsair_device_set_progress(FuDevice *self, FuProgress *progress) +fu_corsair_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -467,7 +477,7 @@ G_MAXUINT8, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "cannot parse CorsairVendorInterface: "); + g_prefix_error_literal(error, "cannot parse CorsairVendorInterface: "); return FALSE; } self->vendor_interface = vendor_interface; @@ -488,25 +498,25 @@ static gboolean fu_corsair_device_poll(FuDevice *device, GError **error) { + FuCorsairDevice *self = FU_CORSAIR_DEVICE(device); gboolean subdevice_added = FALSE; g_autoptr(FuDeviceLocker) locker = NULL; locker = fu_device_locker_new(device, error); if (locker == NULL) { - g_prefix_error(error, "cannot open device: "); + g_prefix_error_literal(error, "cannot open device: "); return FALSE; } - if (!fu_corsair_device_poll_subdevice(device, &subdevice_added, error)) { + if (!fu_corsair_device_poll_subdevice(self, &subdevice_added, error)) return FALSE; - } /* stop polling if a subdevice was added */ if (subdevice_added) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "subdevice added successfully"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "subdevice added successfully"); return FALSE; } diff -Nru fwupd-2.0.8/plugins/corsair/fu-corsair-plugin.c fwupd-2.0.20/plugins/corsair/fu-corsair-plugin.c --- fwupd-2.0.8/plugins/corsair/fu-corsair-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/corsair/fu-corsair-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -30,6 +30,7 @@ fu_context_add_quirk_key(ctx, "CorsairDeviceKind"); fu_context_add_quirk_key(ctx, "CorsairVendorInterfaceId"); fu_context_add_quirk_key(ctx, "CorsairSubdeviceId"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_set_device_gtype_default(plugin, FU_TYPE_CORSAIR_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_CORSAIR_BP); /* coverage */ } diff -Nru fwupd-2.0.8/plugins/corsair/tests/corsair-katar-pro-xt.json fwupd-2.0.20/plugins/corsair/tests/corsair-katar-pro-xt.json --- fwupd-2.0.8/plugins/corsair/tests/corsair-katar-pro-xt.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/corsair/tests/corsair-katar-pro-xt.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/7598356dbb3c40f16bc4cd314497ad0b983303e52311f95afc1d9aa8661a154d-corsair-bora-1.6.26.cab", + "url": "7598356dbb3c40f16bc4cd314497ad0b983303e52311f95afc1d9aa8661a154d-corsair-bora-1.6.26.cab", "components": [ { "version": "1.6.26", diff -Nru fwupd-2.0.8/plugins/corsair/tests/corsair-sabre-pro.json fwupd-2.0.20/plugins/corsair/tests/corsair-sabre-pro.json --- fwupd-2.0.8/plugins/corsair/tests/corsair-sabre-pro.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/corsair/tests/corsair-sabre-pro.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/5adc54e39596b5b941339d247ac01f6a40377bc58e6b1b87c49d60c8a7509f4e-corsair-tongs-1.15.25.cab", + "url": "5adc54e39596b5b941339d247ac01f6a40377bc58e6b1b87c49d60c8a7509f4e-corsair-tongs-1.15.25.cab", "components": [ { "version": "1.15.25", diff -Nru fwupd-2.0.8/plugins/corsair/tests/corsair-sabre-rgb-pro.json fwupd-2.0.20/plugins/corsair/tests/corsair-sabre-rgb-pro.json --- fwupd-2.0.8/plugins/corsair/tests/corsair-sabre-rgb-pro.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/corsair/tests/corsair-sabre-rgb-pro.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/a7540311d6b8da599cc24cd6225c34d4c27a4afb9bbbe73397fa23ccf71723f8-corsair-vise-1.15.30.cab", + "url": "a7540311d6b8da599cc24cd6225c34d4c27a4afb9bbbe73397fa23ccf71723f8-corsair-vise-1.15.30.cab", "components": [ { "version": "1.15.30", diff -Nru fwupd-2.0.8/plugins/cpu/cpu.quirk fwupd-2.0.20/plugins/cpu/cpu.quirk --- fwupd-2.0.8/plugins/cpu/cpu.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cpu/cpu.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -70,7 +70,7 @@ [CPUID\PRO_0&FAM_06&MOD_A7] CpuMitigationsRequired = gds -#Affected by Sinkclose +# Affected by Sinkclose [CPUID\PRO_0&FAM_17&MOD_01&STP_2] CpuMitigationsRequired = sinkclose CpuSinkcloseMicrocodeVersion = 0x0800126f diff -Nru fwupd-2.0.8/plugins/cpu/fu-cpu-device.c fwupd-2.0.20/plugins/cpu/fu-cpu-device.c --- fwupd-2.0.8/plugins/cpu/fu-cpu-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cpu/fu-cpu-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,14 +11,7 @@ #endif #include "fu-cpu-device.h" - -typedef enum { - FU_CPU_DEVICE_FLAG_NONE = 0, - FU_CPU_DEVICE_FLAG_SHSTK = 1 << 0, - FU_CPU_DEVICE_FLAG_IBT = 1 << 1, - FU_CPU_DEVICE_FLAG_TME = 1 << 2, - FU_CPU_DEVICE_FLAG_SMAP = 1 << 3, -} FuCpuDeviceFlag; +#include "fu-cpu-struct.h" struct _FuCpuDevice { FuDevice parent_instance; @@ -37,22 +30,10 @@ fu_cpu_device_to_string(FuDevice *device, guint idt, GString *str) { FuCpuDevice *self = FU_CPU_DEVICE(device); - fwupd_codec_string_append_bool(str, - idt, - "HasSHSTK", - fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_SHSTK)); - fwupd_codec_string_append_bool(str, - idt, - "HasIBT", - fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_IBT)); - fwupd_codec_string_append_bool(str, - idt, - "HasTME", - fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_TME)); - fwupd_codec_string_append_bool(str, - idt, - "HasSMAP", - fu_cpu_device_has_flag(self, FU_CPU_DEVICE_FLAG_SMAP)); + if (self->flags != FU_CPU_DEVICE_FLAG_NONE) { + g_autofree gchar *flags_str = fu_cpu_device_flag_to_string(self->flags); + fwupd_codec_string_append(str, idt, "Flags", flags_str); + } } static const gchar * @@ -61,7 +42,7 @@ if (g_strcmp0(vendor, "GenuineIntel") == 0) return "Intel"; if (g_strcmp0(vendor, "AuthenticAMD") == 0 || g_strcmp0(vendor, "AMDisbetter!") == 0) - return "Advanced Micro Devices, Inc."; + return "AMD"; if (g_strcmp0(vendor, "CentaurHauls") == 0) return "IDT"; if (g_strcmp0(vendor, "CyrixInstead") == 0) @@ -116,13 +97,13 @@ { fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_CPU); - fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); fu_device_set_physical_id(FU_DEVICE(self), "cpu:0"); } static gboolean -fu_cpu_device_add_instance_ids(FuDevice *device, GError **error) +fu_cpu_device_add_instance_ids(FuCpuDevice *self, GError **error) { guint32 eax = 0; guint32 family_id; @@ -149,26 +130,33 @@ family_id += family_id_ext; /* add GUIDs */ - fu_device_add_instance_u4(device, "PRO", processor_id); - fu_device_add_instance_u8(device, "FAM", family_id); - fu_device_add_instance_u8(device, "MOD", model_id); - fu_device_add_instance_u4(device, "STP", stepping_id); - fu_device_build_instance_id_full(device, + fu_device_add_instance_u4(FU_DEVICE(self), "PRO", processor_id); + fu_device_add_instance_u8(FU_DEVICE(self), "FAM", family_id); + fu_device_add_instance_u8(FU_DEVICE(self), "MOD", model_id); + fu_device_add_instance_u4(FU_DEVICE(self), "STP", stepping_id); + fu_device_build_instance_id_full(FU_DEVICE(self), FU_DEVICE_INSTANCE_FLAG_QUIRKS, NULL, "CPUID", "PRO", "FAM", NULL); - fu_device_build_instance_id(device, NULL, "CPUID", "PRO", "FAM", "MOD", NULL); - fu_device_build_instance_id(device, NULL, "CPUID", "PRO", "FAM", "MOD", "STP", NULL); + fu_device_build_instance_id(FU_DEVICE(self), NULL, "CPUID", "PRO", "FAM", "MOD", NULL); + fu_device_build_instance_id(FU_DEVICE(self), + NULL, + "CPUID", + "PRO", + "FAM", + "MOD", + "STP", + NULL); /* success */ return TRUE; } static gboolean -fu_cpu_device_probe_manufacturer_id(FuDevice *device, GError **error) +fu_cpu_device_probe_manufacturer_id(FuCpuDevice *self, GError **error) { guint32 ebx = 0; guint32 ecx = 0; @@ -203,12 +191,12 @@ sizeof(guint32), error)) return FALSE; - fu_device_set_vendor(device, fu_cpu_device_convert_vendor(str)); + fu_device_set_vendor(FU_DEVICE(self), fu_cpu_device_convert_vendor(str)); return TRUE; } static gboolean -fu_cpu_device_probe_model(FuDevice *device, GError **error) +fu_cpu_device_probe_model(FuCpuDevice *self, GError **error) { guint32 eax = 0; guint32 ebx = 0; @@ -256,14 +244,13 @@ error)) return FALSE; } - fu_device_set_name(device, str); + fu_device_set_name(FU_DEVICE(self), str); return TRUE; } static gboolean -fu_cpu_device_probe_extended_features(FuDevice *device, GError **error) +fu_cpu_device_probe_extended_features(FuCpuDevice *self, GError **error) { - FuCpuDevice *self = FU_CPU_DEVICE(device); guint32 ebx = 0; guint32 ecx = 0; guint32 edx = 0; @@ -288,13 +275,14 @@ static gboolean fu_cpu_device_probe(FuDevice *device, GError **error) { - if (!fu_cpu_device_probe_manufacturer_id(device, error)) + FuCpuDevice *self = FU_CPU_DEVICE(device); + if (!fu_cpu_device_probe_manufacturer_id(self, error)) return FALSE; - if (!fu_cpu_device_probe_model(device, error)) + if (!fu_cpu_device_probe_model(self, error)) return FALSE; - if (!fu_cpu_device_probe_extended_features(device, error)) + if (!fu_cpu_device_probe_extended_features(self, error)) return FALSE; - if (!fu_cpu_device_add_instance_ids(device, error)) + if (!fu_cpu_device_add_instance_ids(self, error)) return FALSE; return TRUE; } @@ -362,7 +350,6 @@ { gint exit_status = 0xff; g_autofree gchar *toolfn = NULL; - g_autofree gchar *dir = NULL; g_autoptr(FwupdSecurityAttr) attr = NULL; g_autoptr(FwupdSecurityAttr) cet_plat_attr = NULL; g_autoptr(GError) error_local = NULL; @@ -382,8 +369,7 @@ fu_security_attrs_append(attrs, attr); /* check that userspace has been compiled for CET support */ - dir = fu_path_from_kind(FU_PATH_KIND_LIBEXECDIR_PKG); - toolfn = g_build_filename(dir, "fwupd-detect-cet", NULL); + toolfn = fu_path_build(FU_PATH_KIND_LIBEXECDIR_PKG, "fwupd-detect-cet", NULL); if (!g_spawn_command_line_sync(toolfn, NULL, NULL, &exit_status, &error_local)) { g_warning("failed to test CET: %s", error_local->message); return; @@ -440,10 +426,8 @@ #ifdef HAVE_UTSNAME_H static void -fu_cpu_device_add_x86_64_security_attrs(FuDevice *device, FuSecurityAttrs *attrs) +fu_cpu_device_add_x86_64_security_attrs(FuCpuDevice *self, FuSecurityAttrs *attrs) { - FuCpuDevice *self = FU_CPU_DEVICE(device); - /* only Intel */ if (fu_cpu_get_vendor() == FU_CPU_VENDOR_INTEL) fu_cpu_device_add_security_attrs_intel_tme(self, attrs); @@ -457,16 +441,16 @@ fu_cpu_device_add_security_attrs(FuDevice *device, FuSecurityAttrs *attrs) { #ifdef HAVE_UTSNAME_H - struct utsname name_tmp; + FuCpuDevice *self = FU_CPU_DEVICE(device); + struct utsname name_tmp = {0}; - memset(&name_tmp, 0, sizeof(struct utsname)); if (uname(&name_tmp) < 0) { g_warning("failed to read CPU architecture"); return; } if (g_strcmp0(name_tmp.machine, "x86_64") == 0) - fu_cpu_device_add_x86_64_security_attrs(device, attrs); + fu_cpu_device_add_x86_64_security_attrs(self, attrs); #endif } diff -Nru fwupd-2.0.8/plugins/cpu/fu-cpu.rs fwupd-2.0.20/plugins/cpu/fu-cpu.rs --- fwupd-2.0.8/plugins/cpu/fu-cpu.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/cpu/fu-cpu.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +// Copyright 2024 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[derive(Bitfield, ToString)] +enum FuCpuDeviceFlag { + None = 0, + Shstk = 1 << 0, + Ibt = 1 << 1, + Tme = 1 << 2, + Smap = 1 << 3, +} diff -Nru fwupd-2.0.8/plugins/cpu/meson.build fwupd-2.0.20/plugins/cpu/meson.build --- fwupd-2.0.8/plugins/cpu/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cpu/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,9 +1,11 @@ -if hsi +hsi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginCpu"'] plugins += {meson.current_source_dir().split('/')[-1]: true} plugin_quirks += files('cpu.quirk') plugin_builtins += static_library('fu_plugin_cpu', + rustgen.process('fu-cpu.rs'), sources: [ 'fu-cpu-plugin.c', 'fu-cpu-device.c', @@ -58,4 +60,3 @@ install_dir: join_paths(libexecdir, 'fwupd') ) endif -endif diff -Nru fwupd-2.0.8/plugins/cros-ec/README.md fwupd-2.0.20/plugins/cros-ec/README.md --- fwupd-2.0.8/plugins/cros-ec/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cros-ec/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -57,10 +57,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.5.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Benson Leung: @bleungatchromium diff -Nru fwupd-2.0.8/plugins/cros-ec/fu-cros-ec-common.h fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-common.h --- fwupd-2.0.8/plugins/cros-ec/fu-cros-ec-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,6 +8,8 @@ #include +#define SHA256_DIGEST_LENGTH 32 + typedef struct { gchar *boardname; gchar *triplet; diff -Nru fwupd-2.0.8/plugins/cros-ec/fu-cros-ec-firmware.c fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-firmware.c --- fwupd-2.0.8/plugins/cros-ec/fu-cros-ec-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -38,7 +38,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "no writeable section found with offset: 0x%x", + "no writable section found with offset: 0x%x", writeable_offset); return FALSE; } diff -Nru fwupd-2.0.8/plugins/cros-ec/fu-cros-ec-plugin.c fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-plugin.c --- fwupd-2.0.8/plugins/cros-ec/fu-cros-ec-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,6 +25,7 @@ fu_cros_ec_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_CROS_EC_USB_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_CROS_EC_FIRMWARE); } diff -Nru fwupd-2.0.8/plugins/cros-ec/fu-cros-ec-usb-device.c fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-usb-device.c --- fwupd-2.0.8/plugins/cros-ec/fu-cros-ec-usb-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-usb-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -16,8 +16,6 @@ #define FU_CROS_EC_USB_SUBCLASS_GOOGLE_UPDATE 0x53 #define FU_CROS_EC_USB_PROTOCOL_GOOGLE_UPDATE 0xff -#define FU_CROS_EC_SETUP_RETRY_CNT 5 -#define FU_CROS_EC_MAX_BLOCK_XFER_RETRIES 10 #define FU_CROS_EC_FLUSH_TIMEOUT_MS 10 #define FU_CROS_EC_BULK_SEND_TIMEOUT 2000 /* ms */ #define FU_CROS_EC_BULK_RECV_TIMEOUT 5000 /* ms */ @@ -26,8 +24,7 @@ #define FU_CROS_EC_REQUEST_UPDATE_DONE 0xB007AB1E #define FU_CROS_EC_REQUEST_UPDATE_EXTRA_CMD 0xB007AB1F -struct _FuCrosEcUsbDevice { - FuUsbDevice parent_instance; +typedef struct { guint8 iface_idx; /* bInterfaceNumber */ guint8 ep_num; /* bEndpointAddress */ guint16 chunk_len; /* wMaxPacketSize */ @@ -38,25 +35,23 @@ guint16 protocol_version; gchar configuration[FU_STRUCT_CROS_EC_FIRST_RESPONSE_PDU_SIZE_VERSION]; gboolean in_bootloader; -}; +} FuCrosEcUsbDevicePrivate; -G_DEFINE_TYPE(FuCrosEcUsbDevice, fu_cros_ec_usb_device, FU_TYPE_USB_DEVICE) +G_DEFINE_TYPE_WITH_PRIVATE(FuCrosEcUsbDevice, fu_cros_ec_usb_device, FU_TYPE_USB_DEVICE) +#define GET_PRIVATE(o) (fu_cros_ec_usb_device_get_instance_private(o)) typedef struct { FuChunk *block; FuProgress *progress; } FuCrosEcUsbBlockHelper; -#define FU_CROS_EC_USB_DEVICE_FLAG_RO_WRITTEN "ro-written" -#define FU_CROS_EC_USB_DEVICE_FLAG_RW_WRITTEN "rw-written" -#define FU_CROS_EC_USB_DEVICE_FLAG_REBOOTING_TO_RO "rebooting-to-ro" -#define FU_CROS_EC_USB_DEVICE_FLAG_SPECIAL "special" - static gboolean fu_cros_ec_usb_device_get_configuration(FuCrosEcUsbDevice *self, GError **error) { + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); guint8 index; g_autofree gchar *configuration = NULL; + g_autofree gchar *id_display = fu_device_get_id_display(FU_DEVICE(self)); index = fu_usb_device_get_configuration_index(FU_USB_DEVICE(self), error); if (index == 0x0) @@ -64,12 +59,9 @@ configuration = fu_usb_device_get_string_descriptor(FU_USB_DEVICE(self), index, error); if (configuration == NULL) return FALSE; - g_debug("%s(%s): raw configuration read: %s", - fu_device_get_id(FU_DEVICE(self)), - fu_device_get_name(FU_DEVICE(self)), - configuration); + g_debug("%s raw configuration read: %s", id_display, configuration); - if (g_strlcpy(self->configuration, + if (g_strlcpy(priv->configuration, configuration, FU_STRUCT_CROS_EC_FIRST_RESPONSE_PDU_SIZE_VERSION) == 0) { g_set_error_literal(error, @@ -87,6 +79,7 @@ fu_cros_ec_usb_device_find_interface(FuUsbDevice *device, GError **error) { FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); g_autoptr(GPtrArray) intfs = NULL; /* based on usb_updater2's find_interfacei() and find_endpoint() */ @@ -103,9 +96,9 @@ if (NULL == endpoints || endpoints->len == 0) continue; ep = g_ptr_array_index(endpoints, 0); - self->iface_idx = fu_usb_interface_get_number(intf); - self->ep_num = fu_usb_endpoint_get_address(ep) & 0x7f; - self->chunk_len = fu_usb_endpoint_get_maximum_packet_size(ep); + priv->iface_idx = fu_usb_interface_get_number(intf); + priv->ep_num = fu_usb_endpoint_get_address(ep) & 0x7f; + priv->chunk_len = fu_usb_endpoint_get_maximum_packet_size(ep); return TRUE; } } @@ -117,20 +110,21 @@ fu_cros_ec_usb_device_probe(FuDevice *device, GError **error) { FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); /* very much like usb_updater2's usb_findit() */ if (!fu_cros_ec_usb_device_find_interface(FU_USB_DEVICE(device), error)) { - g_prefix_error(error, "failed to find update interface: "); + g_prefix_error_literal(error, "failed to find update interface: "); return FALSE; } - fu_usb_device_add_interface(FU_USB_DEVICE(self), self->iface_idx); + fu_usb_device_add_interface(FU_USB_DEVICE(self), priv->iface_idx); - if (self->chunk_len == 0) { + if (priv->chunk_len == 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "wMaxPacketSize isn't valid: %" G_GUINT16_FORMAT, - self->chunk_len); + priv->chunk_len); return FALSE; } @@ -148,6 +142,7 @@ gsize *rxed_count, GError **error) { + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); gsize actual = 0; /* send data out */ @@ -160,7 +155,7 @@ return FALSE; if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), - self->ep_num, + priv->ep_num, outbuf_tmp, outlen, &actual, @@ -183,14 +178,14 @@ if (inbuf != NULL && inlen > 0) { actual = 0; if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), - self->ep_num | 0x80, + priv->ep_num | 0x80, inbuf, inlen, &actual, FU_CROS_EC_BULK_RECV_TIMEOUT, NULL, error)) { - fu_error_convert(error); + fwupd_error_convert(error); return FALSE; } if (actual != inlen && !allow_less) { @@ -211,19 +206,20 @@ } static gboolean -fu_cros_ec_usb_device_flush(FuDevice *device, gpointer user_data, GError **error) +fu_cros_ec_usb_device_flush_cb(FuDevice *device, gpointer user_data, GError **error) { FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); gsize actual = 0; - g_autofree guint8 *inbuf = g_malloc0(self->chunk_len); + g_autofree guint8 *inbuf = g_malloc0(priv->chunk_len); /* bulk transfer expected to fail normally (ie, no stale data) * but if bulk transfer succeeds, indicates stale bytes on the device * so this will retry until they're emptied */ if (fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), - self->ep_num | 0x80, + priv->ep_num | 0x80, inbuf, - self->chunk_len, + priv->chunk_len, &actual, FU_CROS_EC_FLUSH_TIMEOUT_MS, NULL, @@ -246,11 +242,11 @@ { /* flush all data from endpoint to recover in case of error */ if (!fu_device_retry(FU_DEVICE(self), - fu_cros_ec_usb_device_flush, + fu_cros_ec_usb_device_flush_cb, FU_CROS_EC_SETUP_RETRY_CNT, NULL, error)) { - g_prefix_error(error, "failed to flush device to idle state: "); + g_prefix_error_literal(error, "failed to flush device to idle state: "); return FALSE; } @@ -276,18 +272,18 @@ { gsize usb_msg_size = FU_STRUCT_CROS_EC_UPDATE_FRAME_HEADER_SIZE + sizeof(subcommand) + body_size; - g_autoptr(FuStructCrosEcUpdateFrameHeader) ufh = + g_autoptr(FuStructCrosEcUpdateFrameHeader) st_ufh = fu_struct_cros_ec_update_frame_header_new(); - fu_struct_cros_ec_update_frame_header_set_block_size(ufh, usb_msg_size); + fu_struct_cros_ec_update_frame_header_set_block_size(st_ufh, usb_msg_size); fu_struct_cros_ec_update_frame_header_set_cmd_block_base( - ufh, + st_ufh, FU_CROS_EC_REQUEST_UPDATE_EXTRA_CMD); - fu_byte_array_append_uint16(ufh, subcommand, G_BIG_ENDIAN); + fu_byte_array_append_uint16(st_ufh->buf, subcommand, G_BIG_ENDIAN); if (body_size > 0) - g_byte_array_append(ufh, cmd_body, body_size); + g_byte_array_append(st_ufh->buf, cmd_body, body_size); return fu_cros_ec_usb_device_do_xfer(self, - ufh->data, - ufh->len, + st_ufh->buf->data, + st_ufh->buf->len, (guint8 *)resp, resp_size != NULL ? *resp_size : 0, TRUE, @@ -301,15 +297,15 @@ FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); FuStructCrosEcFirstResponsePdu *st_rpdu = (FuStructCrosEcFirstResponsePdu *)user_data; gsize rxed_size = 0; - g_autoptr(FuStructCrosEcUpdateFrameHeader) ufh = + g_autoptr(FuStructCrosEcUpdateFrameHeader) st_ufh = fu_struct_cros_ec_update_frame_header_new(); - fu_struct_cros_ec_update_frame_header_set_block_size(ufh, ufh->len); + fu_struct_cros_ec_update_frame_header_set_block_size(st_ufh, st_ufh->buf->len); if (!fu_cros_ec_usb_device_do_xfer(self, - ufh->data, - ufh->len, - st_rpdu->data, - st_rpdu->len, + st_ufh->buf->data, + st_ufh->buf->len, + st_rpdu->buf->data, + st_rpdu->buf->len, TRUE, &rxed_size, error)) @@ -333,6 +329,7 @@ fu_cros_ec_usb_device_setup(FuDevice *device, GError **error) { FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); guint32 error_code; g_auto(GStrv) config_split = NULL; g_autoptr(FuStructCrosEcFirstResponsePdu) st_rpdu = @@ -354,17 +351,17 @@ FU_CROS_EC_SETUP_RETRY_CNT, st_rpdu, error)) { - g_prefix_error(error, "failed to send start request: "); + g_prefix_error_literal(error, "failed to send start request: "); return FALSE; } - self->protocol_version = fu_struct_cros_ec_first_response_pdu_get_protocol_version(st_rpdu); - if (self->protocol_version < 5 || self->protocol_version > 6) { + priv->protocol_version = fu_struct_cros_ec_first_response_pdu_get_protocol_version(st_rpdu); + if (priv->protocol_version < 5 || priv->protocol_version > 6) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "unsupported protocol version %d", - self->protocol_version); + priv->protocol_version); return FALSE; } @@ -378,28 +375,28 @@ return FALSE; } - self->writeable_offset = fu_struct_cros_ec_first_response_pdu_get_offset(st_rpdu); - g_free(self->raw_version); - self->raw_version = fu_struct_cros_ec_first_response_pdu_get_version(st_rpdu); - self->maximum_pdu_size = fu_struct_cros_ec_first_response_pdu_get_maximum_pdu_size(st_rpdu); - self->flash_protection = fu_struct_cros_ec_first_response_pdu_get_flash_protection(st_rpdu); + priv->writeable_offset = fu_struct_cros_ec_first_response_pdu_get_offset(st_rpdu); + g_free(priv->raw_version); + priv->raw_version = fu_struct_cros_ec_first_response_pdu_get_version(st_rpdu); + priv->maximum_pdu_size = fu_struct_cros_ec_first_response_pdu_get_maximum_pdu_size(st_rpdu); + priv->flash_protection = fu_struct_cros_ec_first_response_pdu_get_flash_protection(st_rpdu); /* get active version string and running region from iConfiguration */ if (!fu_cros_ec_usb_device_get_configuration(self, error)) return FALSE; - config_split = g_strsplit(self->configuration, ":", 2); + config_split = g_strsplit(priv->configuration, ":", 2); if (g_strv_length(config_split) < 2) { /* no prefix found so fall back to offset */ - self->in_bootloader = self->writeable_offset != 0x0; - active_version = fu_cros_ec_version_parse(self->configuration, error); + priv->in_bootloader = priv->writeable_offset != 0x0; + active_version = fu_cros_ec_version_parse(priv->configuration, error); if (active_version == NULL) { g_prefix_error(error, "failed parsing device's version: %32s: ", - self->configuration); + priv->configuration); return FALSE; } } else { - self->in_bootloader = g_strcmp0("RO", config_split[0]) == 0; + priv->in_bootloader = g_strcmp0("RO", config_split[0]) == 0; active_version = fu_cros_ec_version_parse(config_split[1], error); if (active_version == NULL) { g_prefix_error(error, @@ -410,13 +407,13 @@ } /* get the other region's version string from targ */ - version = fu_cros_ec_version_parse(self->raw_version, &error_local); + version = fu_cros_ec_version_parse(priv->raw_version, &error_local); if (version == NULL) { - if (!self->in_bootloader) { + if (!priv->in_bootloader) { g_propagate_prefixed_error(error, g_steal_pointer(&error_local), "failed parsing device's version: %32s: ", - self->raw_version); + priv->raw_version); return FALSE; } /* if unable to parse version, copy from the active_version. @@ -431,7 +428,7 @@ version->dirty = active_version->dirty; } - if (self->in_bootloader) { + if (priv->in_bootloader) { fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); fu_device_set_version(FU_DEVICE(device), version->triplet); fu_device_set_version_bootloader(FU_DEVICE(device), active_version->triplet); @@ -471,23 +468,58 @@ fu_cros_ec_usb_device_transfer_block_cb(FuDevice *device, gpointer user_data, GError **error) { FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); FuCrosEcUsbBlockHelper *helper = (FuCrosEcUsbBlockHelper *)user_data; gsize transfer_size = 0; guint32 reply = 0; - g_autoptr(FuStructCrosEcUpdateFrameHeader) ufh = + g_autoptr(FuStructCrosEcUpdateFrameHeader) st_ufh = fu_struct_cros_ec_update_frame_header_new(); g_autoptr(GPtrArray) chunks = NULL; /* first send the header */ fu_struct_cros_ec_update_frame_header_set_block_size( - ufh, - ufh->len + fu_chunk_get_data_sz(helper->block)); + st_ufh, + st_ufh->buf->len + fu_chunk_get_data_sz(helper->block)); fu_struct_cros_ec_update_frame_header_set_cmd_block_base( - ufh, + st_ufh, fu_chunk_get_address(helper->block)); + + if (fu_device_has_private_flag(device, + FU_CROS_EC_USB_DEVICE_FLAG_CMD_BLOCK_DIGEST_REQUIRED)) { + /* sets the the first 32 bits of the SHA256 digest */ + guint32 digest_val = 0; + guint8 digest[SHA256_DIGEST_LENGTH] = {0}; + gsize out_len = SHA256_DIGEST_LENGTH; + g_autoptr(GChecksum) checksum = g_checksum_new(G_CHECKSUM_SHA256); + + g_checksum_update(checksum, + fu_chunk_get_data(helper->block), + fu_chunk_get_data_sz(helper->block)); + g_checksum_get_digest(checksum, digest, &out_len); + + /* copy the first 4 bytes of the SHA256 digest into digest_val + * + * NOTE: We use fu_memcpy_safe() instead of fu_memread_uint32_safe() because + * we want to preserve the raw byte order here. The endianness conversion + * will be handled later when setting the value in the struct, + * replicating the behavior of hammerd. + */ + if (!fu_memcpy_safe((guint8 *)&digest_val, + sizeof(digest_val), + 0x0, + (const guint8 *)digest, + sizeof(digest), + 0x0, + sizeof(digest_val), + error)) + return FALSE; + + fu_struct_cros_ec_update_frame_header_set_cmd_block_digest(st_ufh, digest_val); + } + if (!fu_cros_ec_usb_device_do_xfer(self, - ufh->data, - ufh->len, + st_ufh->buf->data, + st_ufh->buf->len, NULL, 0, FALSE, @@ -497,7 +529,7 @@ /* flush all data from endpoint to recover in case of error */ if (!fu_cros_ec_usb_device_recovery(self, &error_flush)) g_debug("failed to flush to idle: %s", error_flush->message); - g_prefix_error(error, "failed at sending header: "); + g_prefix_error_literal(error, "failed at sending header: "); return FALSE; } @@ -509,7 +541,7 @@ fu_chunk_get_data_sz(helper->block), 0x00, 0x00, - self->chunk_len); + priv->chunk_len); fu_progress_set_id(helper->progress, G_STRLOC); fu_progress_set_steps(helper->progress, chunks->len); for (guint i = 0; i < chunks->len; i++) { @@ -544,7 +576,7 @@ &transfer_size, error)) { g_autoptr(GError) error_flush = NULL; - g_prefix_error(error, "failed at reply: "); + g_prefix_error_literal(error, "failed at reply: "); /* flush all data from endpoint to recover in case of error */ if (!fu_cros_ec_usb_device_recovery(self, &error_flush)) g_debug("failed to flush to idle: %s", error_flush->message); @@ -573,6 +605,7 @@ FuProgress *progress, GError **error) { + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); const guint8 *data_ptr = NULL; gsize data_len = 0; g_autoptr(GBytes) img_bytes = NULL; @@ -582,7 +615,7 @@ img_bytes = fu_firmware_get_image_by_idx_bytes(firmware, section->image_idx, error); if (img_bytes == NULL) { - g_prefix_error(error, "failed to find section image: "); + g_prefix_error_literal(error, "failed to find section image: "); return FALSE; } @@ -591,10 +624,9 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "image and section sizes do not match: image = %" G_GSIZE_FORMAT - " bytes vs section size = %" G_GSIZE_FORMAT " bytes", - data_len, - section->size); + "image (0x%x bytes) and section (0x%x bytes) sizes do not match", + (guint)data_len, + (guint)section->size); return FALSE; } @@ -606,7 +638,7 @@ /* send in chunks of PDU size */ blocks = - fu_chunk_array_new(data_ptr, data_len, section->offset, 0x0, self->maximum_pdu_size); + fu_chunk_array_new(data_ptr, data_len, section->offset, 0x0, priv->maximum_pdu_size); fu_progress_set_id(progress, G_STRLOC); fu_progress_set_steps(progress, blocks->len); for (guint i = 0; i < blocks->len; i++) { @@ -638,8 +670,8 @@ /* send stop request, ignoring reply */ if (!fu_cros_ec_usb_device_do_xfer(self, - st->data, - st->len, + st->buf->data, + st->buf->len, buf, sizeof(buf), FALSE, @@ -725,7 +757,7 @@ return TRUE; } - /* Jump to rw may not work, so if we've reached here, initiate a + /* jump to rw may not work, so if we've reached here, initiate a * full reset using immediate reset */ fu_cros_ec_usb_device_reset_to_ro(self); @@ -764,6 +796,7 @@ GError **error) { FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); g_autoptr(GPtrArray) sections = NULL; FuCrosEcFirmware *cros_ec_firmware = FU_CROS_EC_FIRMWARE(firmware); @@ -775,13 +808,13 @@ fu_device_remove_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_REBOOTING_TO_RO); if (!fu_cros_ec_usb_device_stay_in_ro(self, error)) { - g_prefix_error(error, "failed to send stay-in-ro subcommand: "); + g_prefix_error_literal(error, "failed to send stay-in-ro subcommand: "); return FALSE; } /* flush all data from endpoint to recover in case of error */ if (!fu_cros_ec_usb_device_recovery(self, error)) { - g_prefix_error(error, "failed to flush device to idle state: "); + g_prefix_error_literal(error, "failed to flush device to idle state: "); return FALSE; } @@ -791,13 +824,13 @@ FU_CROS_EC_SETUP_RETRY_CNT, st_rpdu, error)) { - g_prefix_error(error, "failed to send start request: "); + g_prefix_error_literal(error, "failed to send start request: "); return FALSE; } } if (fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RW_WRITTEN) && - self->in_bootloader) { + priv->in_bootloader) { /* * We had previously written to the rw region (while we were * booted from ro region), but somehow landed in ro again after @@ -847,7 +880,8 @@ return FALSE; } - if (self->in_bootloader) { + /* TODO: fix me, section->version.triplet has no data... */ + if (priv->in_bootloader) { fu_device_set_version(device, section->version.triplet); } else { fu_device_set_version_bootloader(device, section->version.triplet); @@ -859,7 +893,7 @@ /* send done */ fu_cros_ec_usb_device_send_done(self); - if (self->in_bootloader) + if (priv->in_bootloader) fu_device_add_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RW_WRITTEN); else fu_device_add_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RO_WRITTEN); @@ -877,10 +911,11 @@ fu_cros_ec_usb_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); g_autoptr(FuFirmware) firmware = fu_cros_ec_firmware_new(); if (!fu_firmware_parse_stream(firmware, stream, 0x0, flags, error)) @@ -890,11 +925,12 @@ /* pick sections */ if (!fu_cros_ec_firmware_pick_sections(FU_CROS_EC_FIRMWARE(firmware), - self->writeable_offset, + priv->writeable_offset, error)) { - g_prefix_error(error, "failed to pick sections: "); + g_prefix_error_literal(error, "failed to pick sections: "); return NULL; } + return g_steal_pointer(&firmware); } @@ -902,8 +938,9 @@ fu_cros_ec_usb_device_attach(FuDevice *device, FuProgress *progress, GError **error) { FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); - if (self->in_bootloader && + if (priv->in_bootloader && fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_SPECIAL)) { /* * attach after the SPECIAL flag was set. The EC will auto-jump @@ -935,12 +972,13 @@ fu_cros_ec_usb_device_detach(FuDevice *device, FuProgress *progress, GError **error) { FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); if (fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RW_WRITTEN) && !fu_device_has_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RO_WRITTEN)) return TRUE; - if (self->in_bootloader) { + if (priv->in_bootloader) { /* If EC just rebooted - prevent jumping to RW during the update */ fu_device_add_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_REBOOTING_TO_RO); g_debug("skipping immediate reboot in case of already in bootloader"); @@ -948,7 +986,7 @@ return TRUE; } - if (self->flash_protection != 0x0) { + if (priv->flash_protection != 0x0) { /* in RW, and RO region is write protected, so jump to RO */ fu_device_add_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_RO_WRITTEN); fu_device_add_private_flag(device, FU_CROS_EC_USB_DEVICE_FLAG_REBOOTING_TO_RO); @@ -1006,21 +1044,24 @@ fu_device_register_private_flag(FU_DEVICE(self), FU_CROS_EC_USB_DEVICE_FLAG_REBOOTING_TO_RO); fu_device_register_private_flag(FU_DEVICE(self), FU_CROS_EC_USB_DEVICE_FLAG_SPECIAL); + fu_device_register_private_flag(FU_DEVICE(self), + FU_CROS_EC_USB_DEVICE_FLAG_CMD_BLOCK_DIGEST_REQUIRED); } static void fu_cros_ec_usb_device_to_string(FuDevice *device, guint idt, GString *str) { FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(device); - fwupd_codec_string_append_int(str, idt, "ProtocolVersion", self->protocol_version); - fwupd_codec_string_append_int(str, idt, "MaxPduSize", self->maximum_pdu_size); - fwupd_codec_string_append_hex(str, idt, "FlashProtection", self->flash_protection); - fwupd_codec_string_append(str, idt, "RawVersion", self->raw_version); - fwupd_codec_string_append_hex(str, idt, "WriteableOffset", self->writeable_offset); + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); + fwupd_codec_string_append_int(str, idt, "ProtocolVersion", priv->protocol_version); + fwupd_codec_string_append_int(str, idt, "MaxPduSize", priv->maximum_pdu_size); + fwupd_codec_string_append_hex(str, idt, "FlashProtection", priv->flash_protection); + fwupd_codec_string_append(str, idt, "RawVersion", priv->raw_version); + fwupd_codec_string_append_hex(str, idt, "WriteableOffset", priv->writeable_offset); } static void -fu_cros_ec_usb_device_set_progress(FuDevice *self, FuProgress *progress) +fu_cros_ec_usb_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -1034,7 +1075,8 @@ fu_cros_ec_usb_device_finalize(GObject *object) { FuCrosEcUsbDevice *self = FU_CROS_EC_USB_DEVICE(object); - g_free(self->raw_version); + FuCrosEcUsbDevicePrivate *priv = GET_PRIVATE(self); + g_free(priv->raw_version); G_OBJECT_CLASS(fu_cros_ec_usb_device_parent_class)->finalize(object); } diff -Nru fwupd-2.0.8/plugins/cros-ec/fu-cros-ec-usb-device.h fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-usb-device.h --- fwupd-2.0.8/plugins/cros-ec/fu-cros-ec-usb-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cros-ec/fu-cros-ec-usb-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,5 +8,22 @@ #include +#define FU_CROS_EC_SETUP_RETRY_CNT 5 +#define FU_CROS_EC_MAX_BLOCK_XFER_RETRIES 10 + #define FU_TYPE_CROS_EC_USB_DEVICE (fu_cros_ec_usb_device_get_type()) -G_DECLARE_FINAL_TYPE(FuCrosEcUsbDevice, fu_cros_ec_usb_device, FU, CROS_EC_USB_DEVICE, FuUsbDevice) +G_DECLARE_DERIVABLE_TYPE(FuCrosEcUsbDevice, + fu_cros_ec_usb_device, + FU, + CROS_EC_USB_DEVICE, + FuUsbDevice) + +struct _FuCrosEcUsbDeviceClass { + FuUsbDeviceClass parent_class; +}; + +#define FU_CROS_EC_USB_DEVICE_FLAG_RO_WRITTEN "ro-written" +#define FU_CROS_EC_USB_DEVICE_FLAG_RW_WRITTEN "rw-written" +#define FU_CROS_EC_USB_DEVICE_FLAG_REBOOTING_TO_RO "rebooting-to-ro" +#define FU_CROS_EC_USB_DEVICE_FLAG_SPECIAL "special" +#define FU_CROS_EC_USB_DEVICE_FLAG_CMD_BLOCK_DIGEST_REQUIRED "cmd-block-digest-required" diff -Nru fwupd-2.0.8/plugins/cros-ec/fu-cros-ec.rs fwupd-2.0.20/plugins/cros-ec/fu-cros-ec.rs --- fwupd-2.0.8/plugins/cros-ec/fu-cros-ec.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cros-ec/fu-cros-ec.rs 2026-02-26 11:36:18.000000000 +0000 @@ -37,7 +37,7 @@ #[repr(C, packed)] struct FuStructCrosEcUpdateFrameHeader { block_size: u32be, // total frame size, including this field - _cmd_block_digest: u32be, // four bytes of the structure sha1 digest (or 0 where ignored) + cmd_block_digest: u32be, // first four bytes of the block's SHA256 digest (or 0 where ignored) cmd_block_base: u32be, // offset of this PDU into the flash SPI // payload goes here } diff -Nru fwupd-2.0.8/plugins/cros-ec/tests/acer-d501.json fwupd-2.0.20/plugins/cros-ec/tests/acer-d501.json --- fwupd-2.0.8/plugins/cros-ec/tests/acer-d501.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cros-ec/tests/acer-d501.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/1aca09b306293eab77af46f02c4ef913538ac99d33e53ff8cc70f71850ef405a-Baklava_11022021_sign.cab", + "url": "1aca09b306293eab77af46f02c4ef913538ac99d33e53ff8cc70f71850ef405a-Baklava_11022021_sign.cab", "components": [ { "version": "2.0.10832", diff -Nru fwupd-2.0.8/plugins/cros-ec/tests/google-servo-micro.json fwupd-2.0.20/plugins/cros-ec/tests/google-servo-micro.json --- fwupd-2.0.8/plugins/cros-ec/tests/google-servo-micro.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/cros-ec/tests/google-servo-micro.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/3c3123b6eaa89d5469553b301210a9e4cb06efa98a1cb7c6847a1d10bb0a0c4b-servo_micro_v2.4.0.cab", - "emulation-url": "https://fwupd.org/downloads/86fd0f1ae48eec0aad0ec97eaebc5bb8aa67fe03485f7b5824e6e1e9dfab42a6-servo_micro_v2.4.0.zip", + "url": "3c3123b6eaa89d5469553b301210a9e4cb06efa98a1cb7c6847a1d10bb0a0c4b-servo_micro_v2.4.0.cab", + "emulation-url": "86fd0f1ae48eec0aad0ec97eaebc5bb8aa67fe03485f7b5824e6e1e9dfab42a6-servo_micro_v2.4.0.zip", "components": [ { "version": "2.4.0", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/1dc362734f138e71fa838ac5503491d297715d7e9bccc0424a62c4a5c68526cc-servo_micro_v2.4.17.cab", - "emulation-url": "https://fwupd.org/downloads/4e85ebbd498b25b1528151745f86b167363629665f4a2580a472773ea176dc14-servo_micro_v2.4.17.zip", + "url": "1dc362734f138e71fa838ac5503491d297715d7e9bccc0424a62c4a5c68526cc-servo_micro_v2.4.17.cab", + "emulation-url": "4e85ebbd498b25b1528151745f86b167363629665f4a2580a472773ea176dc14-servo_micro_v2.4.17.zip", "components": [ { "version": "2.4.17", diff -Nru fwupd-2.0.8/plugins/dell/README.md fwupd-2.0.20/plugins/dell/README.md --- fwupd-2.0.8/plugins/dell/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -51,10 +51,3 @@ ## Version Considerations This plugin has been available since fwupd version `0.8.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Mario Limonciello: @superm1 diff -Nru fwupd-2.0.8/plugins/dell/dell.quirk fwupd-2.0.20/plugins/dell/dell.quirk --- fwupd-2.0.8/plugins/dell/dell.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/dell.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -21,7 +21,7 @@ Plugin = synaptics_mst ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 -#Dell TB18 dock +# Dell TB18 dock [MST-tb18-vmm3320-274] Plugin = synaptics_mst ParentGuid = e7ca1f36-bf73-4574-afe6-a4ccacabf479 diff -Nru fwupd-2.0.8/plugins/dell/fu-dell-plugin.c fwupd-2.0.20/plugins/dell/fu-dell-plugin.c --- fwupd-2.0.8/plugins/dell/fu-dell-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/fu-dell-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,6 +12,7 @@ #include #include "fu-dell-plugin.h" +#include "fu-dell-struct.h" #define DACI_FLASH_INTERFACE_CLASS 7 #define DACI_FLASH_INTERFACE_SELECT 3 @@ -23,32 +24,22 @@ G_DEFINE_TYPE(FuDellPlugin, fu_dell_plugin, FU_TYPE_PLUGIN) -struct da_structure { - guint8 type; - guint8 length; - guint16 handle; - guint16 cmd_address; - guint8 cmd_code; - guint32 supported_cmds; - guint8 *tokens; -} __attribute__((packed)); /* nocheck:blocked */ - /** * Dell device types to run */ -static guint8 enclosure_allowlist[] = {0x03, /* desktop */ - 0x04, /* low profile desktop */ - 0x06, /* mini tower */ - 0x07, /* tower */ - 0x08, /* portable */ - 0x09, /* laptop */ - 0x0A, /* notebook */ - 0x0D, /* AIO */ - 0x1E, /* tablet */ - 0x1F, /* convertible */ - 0x21, /* IoT gateway */ - 0x22, - /* embedded PC */}; +const guint8 enclosure_allowlist[] = {0x03, /* desktop */ + 0x04, /* low profile desktop */ + 0x06, /* mini tower */ + 0x07, /* tower */ + 0x08, /* portable */ + 0x09, /* laptop */ + 0x0A, /* notebook */ + 0x0D, /* AIO */ + 0x1E, /* tablet */ + 0x1F, /* convertible */ + 0x21, /* IoT gateway */ + 0x22, + /* embedded PC */}; static guint16 fu_dell_plugin_get_system_id(FuPlugin *plugin) @@ -83,7 +74,7 @@ g_autoptr(GPtrArray) de_tables = NULL; g_autoptr(GPtrArray) da_tables = NULL; guint8 value = 0; - struct da_structure da_values = {0x0}; + g_autoptr(FuStructDellSmbiosDa) st_smbios_da = NULL; /* make sure that Dell SMBIOS methods are available */ de_tables = fu_context_get_smbios_data(ctx, 0xDE, FU_SMBIOS_STRUCTURE_LENGTH_ANY, error); @@ -95,7 +86,7 @@ 0x0, &value, error)) { - g_prefix_error(error, "invalid DE data: "); + g_prefix_error_literal(error, "invalid DE data: "); return FALSE; } if (value != 0xDE) { @@ -110,23 +101,19 @@ if (da_tables == NULL) return FALSE; da_blob = g_ptr_array_index(da_tables, 0); - if (!fu_memcpy_safe((guint8 *)&da_values, - sizeof(da_values), - 0x0, /* dst */ - g_bytes_get_data(da_blob, NULL), - g_bytes_get_size(da_blob), - 0x0, /* src */ - sizeof(da_values), - error)) { - g_prefix_error(error, "unable to access flash interface: "); + + st_smbios_da = fu_struct_dell_smbios_da_parse_bytes(da_blob, 0x0, error); + if (st_smbios_da == NULL) { + g_prefix_error_literal(error, "unable to parse flash interface: "); return FALSE; } - if (!(da_values.supported_cmds & (1 << DACI_FLASH_INTERFACE_CLASS))) { + if ((fu_struct_dell_smbios_da_get_supported_cmds(st_smbios_da) & + (1 << DACI_FLASH_INTERFACE_CLASS)) == 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "unable to access flash interface. supported commands: 0x%x", - da_values.supported_cmds); + fu_struct_dell_smbios_da_get_supported_cmds(st_smbios_da)); return FALSE; } @@ -181,11 +168,10 @@ static gboolean fu_dell_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) { - g_autofree gchar *sysfsfwdir = NULL; g_autofree gchar *esrtdir = NULL; if (!fu_dell_plugin_supported(plugin, error)) { - g_prefix_error(error, "firmware updating not supported: "); + g_prefix_error_literal(error, "firmware updating not supported: "); return FALSE; } @@ -194,13 +180,12 @@ * * Once unlocked, that will enable flashing capsules here too. */ - sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - esrtdir = g_build_filename(sysfsfwdir, "efi", "esrt", NULL); + esrtdir = fu_path_build(FU_PATH_KIND_SYSFSDIR_FW, "efi", "esrt", NULL); if (!g_file_test(esrtdir, G_FILE_TEST_EXISTS)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "capsule support disabled in BIOS"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "capsule support disabled in BIOS"); return FALSE; } @@ -244,6 +229,8 @@ { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); + /* make sure that UEFI plugin is ready to receive devices */ fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_RUN_AFTER, "uefi_capsule"); } diff -Nru fwupd-2.0.8/plugins/dell/fu-dell.rs fwupd-2.0.20/plugins/dell/fu-dell.rs --- fwupd-2.0.8/plugins/dell/fu-dell.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/fu-dell.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[derive(ParseBytes)] +struct FuStructDellSmbiosDa { + type: u8, + length: u8, + handle: u16le, + cmd_address: u16le, + cmd_code: u8, + supported_cmds: u32le, + // tokens here +} diff -Nru fwupd-2.0.8/plugins/dell/meson.build fwupd-2.0.20/plugins/dell/meson.build --- fwupd-2.0.8/plugins/dell/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,10 +1,12 @@ -if allow_uefi_capsule +allow_uefi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginDell"'] plugins += {meson.current_source_dir().split('/')[-1]: true} plugin_quirks += files('dell.quirk') plugin_builtin_dell = static_library('fu_plugin_dell', + rustgen.process('fu-dell.rs'), sources: [ 'fu-dell-plugin.c', ], @@ -18,5 +20,3 @@ ], ) plugin_builtins += plugin_builtin_dell - -endif diff -Nru fwupd-2.0.8/plugins/dell/tests/build-fwupdx64-efi-signed.py fwupd-2.0.20/plugins/dell/tests/build-fwupdx64-efi-signed.py --- fwupd-2.0.8/plugins/dell/tests/build-fwupdx64-efi-signed.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/tests/build-fwupdx64-efi-signed.py 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +# Copyright 2025 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# pylint: disable=invalid-name,missing-module-docstring + +import sys + +with open(sys.argv[1], "wb") as f: + f.write("fwupd-efi version 1.2\0".encode("utf-16") + b"PADDING" * 10) diff -Nru fwupd-2.0.8/plugins/dell/tests/build-uefi-insyde.py fwupd-2.0.20/plugins/dell/tests/build-uefi-insyde.py --- fwupd-2.0.8/plugins/dell/tests/build-uefi-insyde.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/tests/build-uefi-insyde.py 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# Copyright 2025 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# pylint: disable=invalid-name,missing-module-docstring + +import sys +import struct +import uuid + +buf = b"" +buf += struct.pack("<4s", b"UEFI") # signature +buf += struct.pack(" firmware.bin +echo -n "hello world" >firmware.bin fwupdtool build-cabinet firmware.cab firmware.bin firmware.metainfo.xml diff -Nru fwupd-2.0.8/plugins/dell/tests/grub2/grub.cfg fwupd-2.0.20/plugins/dell/tests/grub2/grub.cfg --- fwupd-2.0.8/plugins/dell/tests/grub2/grub.cfg 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/tests/grub2/grub.cfg 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1 @@ +# hello world diff -Nru fwupd-2.0.8/plugins/dell/tests/grub2-mkconfig fwupd-2.0.20/plugins/dell/tests/grub2-mkconfig --- fwupd-2.0.8/plugins/dell/tests/grub2-mkconfig 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/tests/grub2-mkconfig 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1 @@ +#!/usr/bin/env python3 diff -Nru fwupd-2.0.8/plugins/dell/tests/grub2-reboot fwupd-2.0.20/plugins/dell/tests/grub2-reboot --- fwupd-2.0.8/plugins/dell/tests/grub2-reboot 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/tests/grub2-reboot 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1 @@ +#!/usr/bin/env python3 diff -Nru fwupd-2.0.8/plugins/dell/tests/meson.build fwupd-2.0.20/plugins/dell/tests/meson.build --- fwupd-2.0.8/plugins/dell/tests/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/tests/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,29 @@ +if get_option('tests') + uefi_insyde_blob = custom_target('UEFI-insyde', + output: 'UEFI', + command: [ + python3.full_path(), + files('build-uefi-insyde.py'), + '@OUTPUT@', + ], + install: true, + install_dir: installed_test_datadir, + ) + fwupdx64_efi_signed = custom_target('fwupdx64.efi.signed', + output: 'fwupdx64.efi.signed', + command: [ + python3.full_path(), + files('build-fwupdx64-efi-signed.py'), + '@OUTPUT@', + ], + install: true, + install_dir: installed_test_datadir, + ) + install_data([ + 'grub2/grub.cfg', + 'test.quirk', + 'uefi-update-info.builder.xml', + ], + install_dir: installed_test_datadir, + ) +endif diff -Nru fwupd-2.0.8/plugins/dell/tests/test.quirk fwupd-2.0.20/plugins/dell/tests/test.quirk --- fwupd-2.0.8/plugins/dell/tests/test.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/tests/test.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,2 @@ +[d09738c8-2210-5611-9f88-c6ef8f8055be] +Flags = no-coalesce,no-capsule-on-disk diff -Nru fwupd-2.0.8/plugins/dell/tests/uefi-update-info.builder.xml fwupd-2.0.20/plugins/dell/tests/uefi-update-info.builder.xml --- fwupd-2.0.8/plugins/dell/tests/uefi-update-info.builder.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/dell/tests/uefi-update-info.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,7 @@ + + 697bd920-12cf-4da9-8385-996909bc6559 + foo.cap + 0x1 + 0x2 + attempt-update + diff -Nru fwupd-2.0.8/plugins/dell-dock/README.md fwupd-2.0.20/plugins/dell-dock/README.md --- fwupd-2.0.8/plugins/dell-dock/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -192,11 +192,3 @@ I2C~~~USB1 I2C~~~USB2 ``` - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Crag Wang: @CragW -* Mario Limonciello: @superm1 diff -Nru fwupd-2.0.8/plugins/dell-dock/dell-dock.quirk fwupd-2.0.20/plugins/dell-dock/dell-dock.quirk --- fwupd-2.0.8/plugins/dell-dock/dell-dock.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/dell-dock.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -28,7 +28,7 @@ Summary = USB 3.1 Generation 1 Hub ParentGuid = USB\VID_413C&PID_B06E&hub&embedded Plugin = dell_dock -Vendor = Dell Inc. +Vendor = Dell Icon = dock-usb FirmwareSize = 0x10000 Flags = updatable,dual-image,usable-during-update @@ -42,7 +42,7 @@ Name = RTS5487 in Dell dock Summary = USB 3.1 Generation 2 Hub ParentGuid = USB\VID_413C&PID_B06E&hub&embedded -Vendor = Dell Inc. +Vendor = Dell Plugin = dell_dock Icon = dock-usb FirmwareSize = 0x10000 @@ -58,7 +58,7 @@ Summary = USB 3.1 Generation 1 Hub ParentGuid = USB\VID_413C&PID_B06E&hub&atomic_embedded Plugin = dell_dock -Vendor = Dell Inc. +Vendor = Dell Icon = dock-usb FirmwareSize = 0x10000 Flags = updatable,dual-image,usable-during-update @@ -72,7 +72,7 @@ Name = RTS5487 in Dell dock Summary = USB 3.1 Generation 2 Hub ParentGuid = USB\VID_413C&PID_B06E&hub&atomic_embedded -Vendor = Dell Inc. +Vendor = Dell Plugin = dell_dock Icon = dock-usb FirmwareSize = 0x10000 @@ -88,7 +88,7 @@ Name = Dell dock Summary = High performance dock Plugin = dell_dock -Vendor = Dell Inc. +Vendor = Dell VendorId = USB:0x413C Icon = dock-usb FirmwareSizeMin = 0x1FFC0 @@ -100,13 +100,13 @@ DellDockBlobVersionOffset = 0x1AFC0 InstallDuration = 60 -#Atomic Embedded Controller +# Atomic Embedded Controller # Name is intentionally not set (it's queried by dock) [USB\VID_413C&PID_B06E&hub&atomic_embedded] Name = Dell dock Summary = High performance dock Plugin = dell_dock -Vendor = Dell Inc. +Vendor = Dell VendorId = USB:0x413C Icon = dock-usb FirmwareSizeMin = 0x1FFC0 @@ -121,7 +121,7 @@ Name = Package level of Dell dock Summary = A representation of dock update status Plugin = dell_dock -Vendor = Dell Inc. +Vendor = Dell Flags = self-recovery,usable-during-update FirmwareSize = 24 InstallDuration = 5 @@ -132,7 +132,7 @@ Name = Package level of Dell dock Summary = A representation of dock update status Plugin = dell_dock -Vendor = Dell Inc. +Vendor = Dell Flags = self-recovery,usable-during-update FirmwareSize = 24 InstallDuration = 5 @@ -143,7 +143,7 @@ Name = Package level of Dell dock Summary = A representation of dock update status Plugin = dell_dock -Vendor = Dell Inc. +Vendor = Dell Flags = self-recovery,usable-during-update FirmwareSize = 24 InstallDuration = 5 @@ -153,7 +153,7 @@ [MST-panamera-vmm5331-259] Name = VMM5331 in Dell dock Summary = Multi Stream Transport controller -Vendor = Dell Inc. +Vendor = Dell Plugin = dell_dock ParentGuid = USB\VID_413C&PID_B06E&hub&embedded Flags = skips-restart,dual-image,usable-during-update @@ -166,11 +166,11 @@ DellDockBlobBuildOffset = 0x18402 Icon = video-display -#Atomic MST Hub +# Atomic MST Hub [MST-cayenne-vmm6210-257] Name = VMM6210 in Dell dock Summary = Multi Stream Transport controller -Vendor = Dell Inc. +Vendor = Dell Plugin = dell_dock ParentGuid = USB\VID_413C&PID_B06E&hub&atomic_embedded Flags = skips-restart,dual-image,usable-during-update @@ -187,7 +187,7 @@ [TBT-00d4b070] Name = Thunderbolt controller in Dell dock Summary = Thunderbolt controller -Vendor = Dell Inc. +Vendor = Dell VendorId = TBT:0x00D4 ParentGuid = USB\VID_413C&PID_B06E&hub&embedded FirmwareSizeMin = 0x40000 @@ -205,7 +205,7 @@ [TBT-00d4b071] Name = USB4 controller in Dell dock Summary = USB4 controller -Vendor = Dell Inc. +Vendor = Dell VendorId = TBT:0x00D4 ParentGuid = USB\VID_413C&PID_B06E&hub&embedded FirmwareSizeMin = 0x40000 diff -Nru fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-common.c fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-common.c --- fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,7 @@ #include "fu-dell-dock-common.h" #include "fu-dell-dock-ec.h" +/* nocheck:name */ gboolean fu_dell_dock_set_power(FuDevice *device, guint8 target, gboolean enabled, GError **error) { @@ -26,8 +27,7 @@ g_return_val_if_fail(device != NULL, FALSE); - parent = FU_IS_DELL_DOCK_EC(device) ? device : fu_device_get_parent(device); - + parent = FU_IS_DELL_DOCK_EC(device) ? device : fu_device_get_parent(device, NULL); if (parent == NULL) { g_set_error(error, FWUPD_ERROR, @@ -41,5 +41,5 @@ if (locker == NULL) return FALSE; - return fu_dell_dock_ec_modify_lock(parent, target, enabled, error); + return fu_dell_dock_ec_modify_lock(FU_DELL_DOCK_EC(parent), target, enabled, error); } diff -Nru fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-ec.c fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-ec.c --- fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-ec.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-ec.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,7 @@ #include #include "fu-dell-dock-common.h" +#include "fu-dell-dock-struct.h" #define I2C_EC_ADDRESS 0xec @@ -47,7 +48,7 @@ const FuHIDI2CParameters ec_base_settings = { .i2ctargetaddr = I2C_EC_ADDRESS, .regaddrlen = 1, - .i2cspeed = I2C_SPEED_250K, + .i2cspeed = FU_DELL_DOCK_I2C_SPEED_250K, }; typedef enum { @@ -142,7 +143,7 @@ }; static gboolean -fu_dell_dock_ec_get_status(FuDevice *device, +fu_dell_dock_ec_get_status(FuDellDockEc *self, FuDellDockECFWUpdateStatus *status_out, GError **error); @@ -158,36 +159,32 @@ } static void -fu_dell_dock_ec_set_board(FuDevice *device) +fu_dell_dock_ec_set_board(FuDellDockEc *self) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); const gchar *summary = NULL; g_autofree gchar *board_type_str = NULL; board_type_str = g_strdup_printf("DellDockBoard%u", self->data->board_id); - summary = fu_device_get_metadata(device, board_type_str); + summary = fu_device_get_metadata(FU_DEVICE(self), board_type_str); if (summary != NULL) - fu_device_set_summary(device, summary); + fu_device_set_summary(FU_DEVICE(self), summary); } gboolean -fu_dell_dock_ec_module_is_usb4(FuDevice *device) +fu_dell_dock_ec_module_is_usb4(FuDellDockEc *self) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); return self->data->module_type == MODULE_TYPE_130_USB4; } guint8 -fu_dell_dock_ec_get_dock_type(FuDevice *device) +fu_dell_dock_ec_get_dock_type(FuDellDockEc *self) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); return self->base_type; } const gchar * -fu_dell_dock_ec_get_module_type(FuDevice *device) +fu_dell_dock_ec_get_module_type(FuDellDockEc *self) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); switch (self->data->module_type) { case MODULE_TYPE_45_TBT: return "45 (TBT)"; @@ -211,9 +208,8 @@ } gboolean -fu_dell_dock_ec_needs_tbt(FuDevice *device) +fu_dell_dock_ec_needs_tbt(FuDellDockEc *self) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); gboolean port0_tbt_mode = self->data->port0_dock_status & TBT_MODE_MASK; /* check for TBT module type */ @@ -226,10 +222,8 @@ } gboolean -fu_dell_dock_ec_tbt_passive(FuDevice *device) +fu_dell_dock_ec_tbt_passive(FuDellDockEc *self) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); - if (self->passive_flow > 0) { self->passive_flow |= PASSIVE_TBT_MASK; return TRUE; @@ -261,25 +255,28 @@ } static gboolean -fu_dell_dock_ec_read(FuDevice *device, guint32 cmd, gsize length, GBytes **bytes, GError **error) +fu_dell_dock_ec_read(FuDellDockEc *self, guint32 cmd, gsize length, GBytes **bytes, GError **error) { + FuDevice *proxy; /* The first byte of result data will be the size of the return, hide this from callers */ guint8 result_length = length + 1; g_autoptr(GBytes) bytes_local = NULL; const guint8 *result; - g_return_val_if_fail(device != NULL, FALSE); - g_return_val_if_fail(fu_device_get_proxy(device) != NULL, FALSE); + g_return_val_if_fail(self != NULL, FALSE); g_return_val_if_fail(bytes != NULL, FALSE); - if (!fu_dell_dock_hid_i2c_read(fu_device_get_proxy(device), + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (!fu_dell_dock_hid_i2c_read(proxy, cmd, result_length, &bytes_local, &ec_base_settings, error)) { - g_prefix_error(error, "read over HID-I2C failed: "); + g_prefix_error_literal(error, "read over HID-I2C failed: "); return FALSE; } result = g_bytes_get_data(bytes_local, NULL); @@ -299,18 +296,18 @@ } static gboolean -fu_dell_dock_ec_write(FuDevice *device, gsize length, guint8 *data, GError **error) +fu_dell_dock_ec_write(FuDellDockEc *self, gsize length, guint8 *data, GError **error) { - g_return_val_if_fail(device != NULL, FALSE); - g_return_val_if_fail(fu_device_get_proxy(device) != NULL, FALSE); + FuDevice *proxy; + + g_return_val_if_fail(self != NULL, FALSE); g_return_val_if_fail(length > 1, FALSE); - if (!fu_dell_dock_hid_i2c_write(fu_device_get_proxy(device), - data, - length, - &ec_base_settings, - error)) { - g_prefix_error(error, "write over HID-I2C failed: "); + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (!fu_dell_dock_hid_i2c_write(proxy, data, length, &ec_base_settings, error)) { + g_prefix_error_literal(error, "write over HID-I2C failed: "); return FALSE; } @@ -318,17 +315,16 @@ } static gboolean -fu_dell_dock_ec_is_valid_dock(FuDevice *device, GError **error) +fu_dell_dock_ec_is_valid_dock(FuDellDockEc *self, GError **error) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); const guint8 *result = NULL; gsize sz = 0; g_autoptr(GBytes) data = NULL; - g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(self != NULL, FALSE); - if (!fu_dell_dock_ec_read(device, EC_CMD_GET_DOCK_TYPE, 1, &data, error)) { - g_prefix_error(error, "Failed to query dock type: "); + if (!fu_dell_dock_ec_read(self, EC_CMD_GET_DOCK_TYPE, 1, &data, error)) { + g_prefix_error_literal(error, "Failed to query dock type: "); return FALSE; } result = g_bytes_get_data(data, &sz); @@ -343,11 +339,11 @@ /* this will trigger setting up all the quirks */ if (self->base_type == DOCK_BASE_TYPE_SALOMON) { - fu_device_add_instance_id(device, DELL_DOCK_EC_INSTANCE_ID); + fu_device_add_instance_id(FU_DEVICE(self), DELL_DOCK_EC_INSTANCE_ID); return TRUE; } if (self->base_type == DOCK_BASE_TYPE_ATOMIC) { - fu_device_add_instance_id(device, DELL_DOCK_ATOMIC_EC_INSTANCE_ID); + fu_device_add_instance_id(FU_DEVICE(self), DELL_DOCK_ATOMIC_EC_INSTANCE_ID); return TRUE; } g_set_error(error, @@ -359,23 +355,22 @@ } static gboolean -fu_dell_dock_ec_get_dock_info(FuDevice *device, GError **error) +fu_dell_dock_ec_get_dock_info(FuDellDockEc *self, GError **error) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); const FuDellDockDockInfoHeader *header = NULL; const FuDellDockEcQueryEntry *device_entry = NULL; const FuDellDockEcAddrMap *map = NULL; guint32 oldest_base_pd = 0; g_autoptr(GBytes) data = NULL; - g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(self != NULL, FALSE); - if (!fu_dell_dock_ec_read(device, + if (!fu_dell_dock_ec_read(self, EC_CMD_GET_DOCK_INFO, EXPECTED_DOCK_INFO_SIZE, &data, error)) { - g_prefix_error(error, "Failed to query dock info: "); + g_prefix_error_literal(error, "Failed to query dock info: "); return FALSE; } if (!g_bytes_get_data(data, NULL)) { @@ -397,10 +392,10 @@ /* guard against EC not yet ready and fail init */ if (header->total_devices == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_SIGNATURE_INVALID, - "No bridge devices detected, dock may be booting up"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_SIGNATURE_INVALID, + "No bridge devices detected, dock may be booting up"); return FALSE; } g_info("%u devices [%u->%u]", @@ -495,20 +490,19 @@ if (self->data->module_type == MODULE_TYPE_130_TBT || self->data->module_type == MODULE_TYPE_45_TBT || self->data->module_type == MODULE_TYPE_130_USB4) { - guint64 tmp = fu_device_get_install_duration(device); - fu_device_set_install_duration(device, tmp + 20); + guint64 tmp = fu_device_get_install_duration(FU_DEVICE(self)); + fu_device_set_install_duration(FU_DEVICE(self), tmp + 20); } /* passive flow is default enabled for production docks */ self->passive_flow = PASSIVE_REBOOT_MASK; - fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART); return TRUE; } static gboolean -fu_dell_dock_ec_get_dock_data(FuDevice *device, GError **error) +fu_dell_dock_ec_get_dock_data(FuDellDockEc *self, GError **error) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); g_autoptr(GBytes) data = NULL; g_autoptr(GString) name = NULL; gchar service_tag[8] = {0x00}; @@ -517,10 +511,10 @@ g_autofree gchar *bundled_serial = NULL; FuDellDockECFWUpdateStatus status; - g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(self != NULL, FALSE); - if (!fu_dell_dock_ec_read(device, EC_CMD_GET_DOCK_DATA, length, &data, error)) { - g_prefix_error(error, "Failed to query dock info: "); + if (!fu_dell_dock_ec_read(self, EC_CMD_GET_DOCK_DATA, length, &data, error)) { + g_prefix_error_literal(error, "Failed to query dock info: "); return FALSE; } result = g_bytes_get_data(data, NULL); @@ -544,7 +538,7 @@ /* guard against EC not yet ready and fail init */ name = g_string_new(self->data->marketing_name); if (name->len > 0) - fu_device_set_name(device, name->str); + fu_device_set_name(FU_DEVICE(self), name->str); else g_warning("[EC bug] Invalid dock name detected"); @@ -555,26 +549,25 @@ memcpy(service_tag, self->data->service_tag, 7); /* nocheck:blocked */ bundled_serial = g_strdup_printf("%s/%08" G_GUINT64_FORMAT, service_tag, self->data->module_serial); - fu_device_set_serial(device, bundled_serial); + fu_device_set_serial(FU_DEVICE(self), bundled_serial); /* copy this for being able to send in next commit transaction */ self->raw_versions->pkg_version = self->data->dock_firmware_pkg_ver; /* read if passive update pending */ - if (!fu_dell_dock_ec_get_status(device, &status, error)) + if (!fu_dell_dock_ec_get_status(self, &status, error)) return FALSE; - /* make sure this hardware spin matches our expectations */ - if (self->data->board_id >= self->board_min) { - if (status != FW_UPDATE_IN_PROGRESS) { - fu_dell_dock_ec_set_board(device); - fu_device_uninhibit(device, "update-pending"); - } else { - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); - fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_UPDATE_PENDING); - } + /* record board id */ + fu_dell_dock_ec_set_board(self); + + /* check whether a firmware update is currently staged */ + if (status != FW_UPDATE_IN_PROGRESS) { + fu_device_remove_problem(FU_DEVICE(self), FWUPD_DEVICE_PROBLEM_UPDATE_PENDING); } else { - fu_device_inhibit(device, "not-supported", "Utility does not support this board"); + fu_device_add_problem(FU_DEVICE(self), FWUPD_DEVICE_PROBLEM_UPDATE_PENDING); + g_debug("found a staged firmware update for %s", + fu_device_get_name(FU_DEVICE(self))); } return TRUE; @@ -610,12 +603,11 @@ } gboolean -fu_dell_dock_ec_modify_lock(FuDevice *device, guint8 target, gboolean unlocked, GError **error) +fu_dell_dock_ec_modify_lock(FuDellDockEc *self, guint8 target, gboolean unlocked, GError **error) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); guint32 cmd; - g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(FU_IS_DELL_DOCK_EC(self), FALSE); g_return_val_if_fail(target != 0, FALSE); cmd = EC_CMD_MODIFY_LOCK | /* cmd */ @@ -623,15 +615,15 @@ target << 16 | /* device to operate on */ unlocked << 24; /* unlock/lock */ - if (!fu_dell_dock_ec_write(device, 4, (guint8 *)&cmd, error)) { - g_prefix_error(error, "Failed to unlock device %d: ", target); + if (!fu_dell_dock_ec_write(self, 4, (guint8 *)&cmd, error)) { + g_prefix_error(error, "failed to unlock device %d: ", target); return FALSE; } - g_debug("Modified lock for %d to %d through %s (%s)", + g_debug("modified lock for %d to %d through %s (%s)", target, unlocked, - fu_device_get_name(device), - fu_device_get_id(device)); + fu_device_get_name(FU_DEVICE(self)), + fu_device_get_id(FU_DEVICE(self))); if (unlocked) FU_BIT_SET(self->dock_unlock_status, target); @@ -643,22 +635,23 @@ } static gboolean -fu_dell_dock_ec_reset(FuDevice *device, GError **error) +fu_dell_dock_ec_reset(FuDellDockEc *self, GError **error) { guint16 cmd = EC_CMD_RESET; - g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(self != NULL, FALSE); - return fu_dell_dock_ec_write(device, 2, (guint8 *)&cmd, error); + return fu_dell_dock_ec_write(self, 2, (guint8 *)&cmd, error); } static gboolean fu_dell_dock_ec_activate(FuDevice *device, FuProgress *progress, GError **error) { + FuDellDockEc *self = FU_DELL_DOCK_EC(device); FuDellDockECFWUpdateStatus status; /* read if passive update pending */ - if (!fu_dell_dock_ec_get_status(device, &status, error)) + if (!fu_dell_dock_ec_get_status(self, &status, error)) return FALSE; if (status != FW_UPDATE_IN_PROGRESS) { @@ -666,40 +659,41 @@ FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "No firmware update pending for %s", - fu_device_get_name(device)); + fu_device_get_name(FU_DEVICE(self))); return FALSE; } - return fu_dell_dock_ec_reset(device, error); + return fu_dell_dock_ec_reset(self, error); } gboolean -fu_dell_dock_ec_reboot_dock(FuDevice *device, GError **error) +fu_dell_dock_ec_reboot_dock(FuDellDockEc *self, GError **error) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); guint32 cmd = EC_CMD_PASSIVE | /* cmd */ 1 << 8 | /* length of data arguments */ self->passive_flow << 16; - g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(self != NULL, FALSE); g_info("activating passive flow (%x) for %s", self->passive_flow, - fu_device_get_name(device)); - return fu_dell_dock_ec_write(device, 3, (guint8 *)&cmd, error); + fu_device_get_name(FU_DEVICE(self))); + return fu_dell_dock_ec_write(self, 3, (guint8 *)&cmd, error); } static gboolean -fu_dell_dock_ec_get_status(FuDevice *device, FuDellDockECFWUpdateStatus *status_out, GError **error) +fu_dell_dock_ec_get_status(FuDellDockEc *self, + FuDellDockECFWUpdateStatus *status_out, + GError **error) { g_autoptr(GBytes) data = NULL; const guint8 *result = NULL; - g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(self != NULL, FALSE); g_return_val_if_fail(status_out != NULL, FALSE); - if (!fu_dell_dock_ec_read(device, EC_GET_FW_UPDATE_STATUS, 1, &data, error)) { - g_prefix_error(error, "Failed to read FW update status: "); + if (!fu_dell_dock_ec_read(self, EC_GET_FW_UPDATE_STATUS, 1, &data, error)) { + g_prefix_error_literal(error, "failed to read FW update status: "); return FALSE; } result = g_bytes_get_data(data, NULL); @@ -708,7 +702,7 @@ g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, - "Failed to read FW update status"); + "failed to read FW update status"); return FALSE; } *status_out = *result; @@ -716,35 +710,31 @@ } const gchar * -fu_dell_dock_ec_get_tbt_version(FuDevice *device) +fu_dell_dock_ec_get_tbt_version(FuDellDockEc *self) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); return self->tbt_version; } const gchar * -fu_dell_dock_ec_get_mst_version(FuDevice *device) +fu_dell_dock_ec_get_mst_version(FuDellDockEc *self) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); return self->mst_version; } guint32 -fu_dell_dock_ec_get_status_version(FuDevice *device) +fu_dell_dock_ec_get_status_version(FuDellDockEc *self) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); return self->raw_versions->pkg_version; } gboolean -fu_dell_dock_ec_commit_package(FuDevice *device, GBytes *blob_fw, GError **error) +fu_dell_dock_ec_commit_package(FuDellDockEc *self, GBytes *blob_fw, GError **error) { - FuDellDockEc *self = FU_DELL_DOCK_EC(device); gsize length = 0; const guint8 *data = g_bytes_get_data(blob_fw, &length); g_autofree guint8 *payload = g_malloc0(length + 2); - g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(self != NULL, FALSE); g_return_val_if_fail(blob_fw != NULL, FALSE); if (length != sizeof(FuDellDockDockPackageFWVersion)) { @@ -757,7 +747,7 @@ } memcpy(self->raw_versions, data, length); /* nocheck:blocked */ - g_debug("Committing (%zu) bytes ", sizeof(FuDellDockDockPackageFWVersion)); + g_debug("committing (%zu) bytes ", sizeof(FuDellDockDockPackageFWVersion)); g_debug("\tec_version: %x", self->raw_versions->ec_version); g_debug("\tmst_version: %x", self->raw_versions->mst_version); g_debug("\thub1_version: %x", self->raw_versions->hub1_version); @@ -769,8 +759,8 @@ payload[1] = length; memcpy(payload + 2, data, length); /* nocheck:blocked */ - if (!fu_dell_dock_ec_write(device, length + 2, payload, error)) { - g_prefix_error(error, "Failed to query dock info: "); + if (!fu_dell_dock_ec_write(self, length + 2, payload, error)) { + g_prefix_error_literal(error, "failed to query dock info: "); return FALSE; } @@ -778,13 +768,14 @@ } static gboolean -fu_dell_dock_ec_write_fw(FuDevice *device, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) +fu_dell_dock_ec_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) { FuDellDockEc *self = FU_DELL_DOCK_EC(device); + FuDevice *proxy; gsize fw_size = 0; const guint8 *data; gsize write_size = 0; @@ -793,7 +784,7 @@ g_autofree gchar *dynamic_version = NULL; g_autoptr(GBytes) fw = NULL; - g_return_val_if_fail(device != NULL, FALSE); + g_return_val_if_fail(self != NULL, FALSE); g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE); /* progress */ @@ -825,14 +816,17 @@ } g_info("writing EC firmware version %s", dynamic_version); - if (!fu_dell_dock_ec_modify_lock(device, self->unlock_target, TRUE, error)) + if (!fu_dell_dock_ec_modify_lock(self, self->unlock_target, TRUE, error)) return FALSE; - if (!fu_dell_dock_hid_raise_mcu_clock(fu_device_get_proxy(device), TRUE, error)) + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + if (!fu_dell_dock_hid_raise_mcu_clock(proxy, TRUE, error)) return FALSE; /* erase */ - if (!fu_dell_dock_hid_erase_bank(fu_device_get_proxy(device), 0xff, error)) + if (!fu_dell_dock_hid_erase_bank(proxy, 0xff, error)) return FALSE; fu_progress_step_done(progress); @@ -842,12 +836,8 @@ if (fw_size - nwritten < write_size) write_size = fw_size - nwritten; - if (!fu_dell_dock_hid_write_flash(fu_device_get_proxy(device), - address, - data, - write_size, - error)) { - g_prefix_error(error, "write over HID failed: "); + if (!fu_dell_dock_hid_write_flash(proxy, address, data, write_size, error)) { + g_prefix_error_literal(error, "write over HID failed: "); return FALSE; } fu_progress_set_percentage_full(fu_progress_get_child(progress), nwritten, fw_size); @@ -857,16 +847,16 @@ } while (nwritten < fw_size); fu_progress_step_done(progress); - if (!fu_dell_dock_hid_raise_mcu_clock(fu_device_get_proxy(device), FALSE, error)) + if (!fu_dell_dock_hid_raise_mcu_clock(proxy, FALSE, error)) return FALSE; /* dock will reboot to re-read; this is to appease the daemon */ - fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_QUAD); - fu_device_set_version(device, dynamic_version); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_version(FU_DEVICE(self), dynamic_version); /* activate passive behavior */ self->passive_flow |= PASSIVE_RESET_MASK; - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); return TRUE; } @@ -912,25 +902,26 @@ } static gboolean -fu_dell_dock_ec_query(FuDevice *device, GError **error) +fu_dell_dock_ec_query(FuDellDockEc *self, GError **error) { - if (!fu_dell_dock_ec_get_dock_data(device, error)) + if (!fu_dell_dock_ec_get_dock_data(self, error)) return FALSE; - return fu_dell_dock_ec_get_dock_info(device, error); + return fu_dell_dock_ec_get_dock_info(self, error); } static gboolean fu_dell_dock_ec_setup(FuDevice *device, GError **error) { + FuDellDockEc *self = FU_DELL_DOCK_EC(device); g_autoptr(GError) error_local = NULL; /* if query looks bad, wait a few seconds and retry */ - if (!fu_dell_dock_ec_query(device, &error_local)) { + if (!fu_dell_dock_ec_query(self, &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_SIGNATURE_INVALID)) { g_warning("%s", error_local->message); fu_device_sleep(device, 2000); /* ms */ - if (!fu_dell_dock_ec_query(device, error)) + if (!fu_dell_dock_ec_query(self, error)) return FALSE; } else { g_propagate_error(error, g_steal_pointer(&error_local)); @@ -944,29 +935,26 @@ fu_dell_dock_ec_open(FuDevice *device, GError **error) { FuDellDockEc *self = FU_DELL_DOCK_EC(device); + FuDevice *proxy; /* sanity check */ - if (fu_device_get_proxy(device) == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) return FALSE; - } - - if (!fu_device_open(fu_device_get_proxy(device), error)) + if (!fu_device_open(proxy, error)) return FALSE; if (!self->data->dock_type) - return fu_dell_dock_ec_is_valid_dock(device, error); + return fu_dell_dock_ec_is_valid_dock(self, error); return TRUE; } static gboolean fu_dell_dock_ec_close(FuDevice *device, GError **error) { - /* sanity check */ - if (fu_device_get_proxy(device) == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) return FALSE; - } - return fu_device_close(fu_device_get_proxy(device), error); + return fu_device_close(proxy, error); } static void @@ -983,7 +971,7 @@ } static void -fu_dell_dock_ec_set_progress(FuDevice *self, FuProgress *progress) +fu_dell_dock_ec_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -1001,6 +989,7 @@ fu_device_add_protocol(FU_DEVICE(self), "com.dell.dock"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_HID_DEVICE); } static void @@ -1014,7 +1003,7 @@ device_class->setup = fu_dell_dock_ec_setup; device_class->open = fu_dell_dock_ec_open; device_class->close = fu_dell_dock_ec_close; - device_class->write_firmware = fu_dell_dock_ec_write_fw; + device_class->write_firmware = fu_dell_dock_ec_write_firmware; device_class->set_quirk_kv = fu_dell_dock_ec_set_quirk_kv; device_class->set_progress = fu_dell_dock_ec_set_progress; } diff -Nru fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-ec.h fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-ec.h --- fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-ec.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-ec.h 2026-02-26 11:36:18.000000000 +0000 @@ -24,26 +24,26 @@ fu_dell_dock_ec_new(FuDevice *proxy); const gchar * -fu_dell_dock_ec_get_module_type(FuDevice *device); +fu_dell_dock_ec_get_module_type(FuDellDockEc *self); gboolean -fu_dell_dock_ec_needs_tbt(FuDevice *device); +fu_dell_dock_ec_needs_tbt(FuDellDockEc *self); gboolean -fu_dell_dock_ec_tbt_passive(FuDevice *device); +fu_dell_dock_ec_tbt_passive(FuDellDockEc *self); gboolean -fu_dell_dock_ec_modify_lock(FuDevice *device, guint8 target, gboolean unlocked, GError **error); +fu_dell_dock_ec_modify_lock(FuDellDockEc *self, guint8 target, gboolean unlocked, GError **error); gboolean -fu_dell_dock_ec_reboot_dock(FuDevice *device, GError **error); +fu_dell_dock_ec_reboot_dock(FuDellDockEc *self, GError **error); const gchar * -fu_dell_dock_ec_get_mst_version(FuDevice *device); +fu_dell_dock_ec_get_mst_version(FuDellDockEc *self); const gchar * -fu_dell_dock_ec_get_tbt_version(FuDevice *device); +fu_dell_dock_ec_get_tbt_version(FuDellDockEc *self); guint32 -fu_dell_dock_ec_get_status_version(FuDevice *device); +fu_dell_dock_ec_get_status_version(FuDellDockEc *self); gboolean -fu_dell_dock_ec_commit_package(FuDevice *device, GBytes *blob_fw, GError **error); +fu_dell_dock_ec_commit_package(FuDellDockEc *self, GBytes *blob_fw, GError **error); gboolean -fu_dell_dock_ec_module_is_usb4(FuDevice *device); +fu_dell_dock_ec_module_is_usb4(FuDellDockEc *self); guint8 -fu_dell_dock_ec_get_dock_type(FuDevice *device); +fu_dell_dock_ec_get_dock_type(FuDellDockEc *self); diff -Nru fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-hid.c fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-hid.c --- fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-hid.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-hid.c 2026-02-26 11:36:18.000000000 +0000 @@ -24,7 +24,7 @@ #define HIDI2C_MAX_REGISTER 4 #define HID_MAX_RETRIES 5 #define TBT_MAX_RETRIES 2 -#define HIDI2C_TRANSACTION_TIMEOUT 300 +#define HIDI2C_TRANSACTION_TIMEOUT 2000 #define HUB_CMD_READ_DATA 0xC0 #define HUB_CMD_WRITE_DATA 0x40 @@ -74,10 +74,10 @@ } FuTbtCmdBuffer; static gboolean -fu_dell_dock_hid_set_report_cb(FuDevice *self, gpointer user_data, GError **error) +fu_dell_dock_hid_set_report_cb(FuDevice *device, gpointer user_data, GError **error) { guint8 *outbuffer = (guint8 *)user_data; - return fu_hid_device_set_report(FU_HID_DEVICE(self), + return fu_hid_device_set_report(FU_HID_DEVICE(device), 0x0, outbuffer, 192, @@ -86,10 +86,11 @@ error); } +/* nocheck:name -- this should probably be implemented using an interface */ static gboolean -fu_dell_dock_hid_set_report(FuDevice *self, guint8 *outbuffer, GError **error) +fu_dell_dock_hid_set_report(FuDevice *device, guint8 *outbuffer, GError **error) { - return fu_device_retry(self, + return fu_device_retry(device, fu_dell_dock_hid_set_report_cb, HID_MAX_RETRIES, outbuffer, @@ -97,10 +98,10 @@ } static gboolean -fu_dell_dock_hid_get_report_cb(FuDevice *self, gpointer user_data, GError **error) +fu_dell_dock_hid_get_report_cb(FuDevice *device, gpointer user_data, GError **error) { guint8 *inbuffer = (guint8 *)user_data; - return fu_hid_device_get_report(FU_HID_DEVICE(self), + return fu_hid_device_get_report(FU_HID_DEVICE(device), 0x0, inbuffer, 192, @@ -109,18 +110,20 @@ error); } +/* nocheck:name -- this should probably be implemented using an interface */ static gboolean -fu_dell_dock_hid_get_report(FuDevice *self, guint8 *inbuffer, GError **error) +fu_dell_dock_hid_get_report(FuDevice *device, guint8 *inbuffer, GError **error) { - return fu_device_retry(self, + return fu_device_retry(device, fu_dell_dock_hid_get_report_cb, HID_MAX_RETRIES, inbuffer, error); } +/* nocheck:name -- this should probably be implemented using an interface */ gboolean -fu_dell_dock_hid_get_hub_version(FuDevice *self, GError **error) +fu_dell_dock_hid_get_hub_version(FuDevice *device, GError **error) { g_autofree gchar *version = NULL; FuHIDCmdBuffer cmd_buffer = { @@ -135,23 +138,24 @@ .extended_cmdarea[0 ... 52] = 0, }; - if (!fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error)) { - g_prefix_error(error, "failed to query hub version: "); + if (!fu_dell_dock_hid_set_report(device, (guint8 *)&cmd_buffer, error)) { + g_prefix_error_literal(error, "failed to query hub version: "); return FALSE; } - if (!fu_dell_dock_hid_get_report(self, cmd_buffer.data, error)) { - g_prefix_error(error, "failed to query hub version: "); + if (!fu_dell_dock_hid_get_report(device, cmd_buffer.data, error)) { + g_prefix_error_literal(error, "failed to query hub version: "); return FALSE; } version = g_strdup_printf("%02x.%02x", cmd_buffer.data[10], cmd_buffer.data[11]); - fu_device_set_version_format(self, FWUPD_VERSION_FORMAT_PAIR); - fu_device_set_version(self, version); + fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_version(device, version); return TRUE; } +/* nocheck:name -- this should probably be implemented using an interface */ gboolean -fu_dell_dock_hid_raise_mcu_clock(FuDevice *self, gboolean enable, GError **error) +fu_dell_dock_hid_raise_mcu_clock(FuDevice *device, gboolean enable, GError **error) { FuHIDCmdBuffer cmd_buffer = { .cmd = HUB_CMD_WRITE_DATA, @@ -165,7 +169,7 @@ .extended_cmdarea[0 ... 52] = 0, }; - if (!fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error)) { + if (!fu_dell_dock_hid_set_report(device, (guint8 *)&cmd_buffer, error)) { g_prefix_error(error, "failed to set mcu clock to %d: ", enable); return FALSE; } @@ -173,8 +177,9 @@ return TRUE; } +/* nocheck:name -- this should probably be implemented using an interface */ gboolean -fu_dell_dock_hid_erase_bank(FuDevice *self, guint8 idx, GError **error) +fu_dell_dock_hid_erase_bank(FuDevice *device, guint8 idx, GError **error) { FuHIDCmdBuffer cmd_buffer = { .cmd = HUB_CMD_WRITE_DATA, @@ -188,16 +193,17 @@ .extended_cmdarea[0 ... 52] = 0, }; - if (!fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error)) { - g_prefix_error(error, "failed to erase bank: "); + if (!fu_dell_dock_hid_set_report(device, (guint8 *)&cmd_buffer, error)) { + g_prefix_error_literal(error, "failed to erase bank: "); return FALSE; } return TRUE; } +/* nocheck:name -- this should probably be implemented using an interface */ gboolean -fu_dell_dock_hid_write_flash(FuDevice *self, +fu_dell_dock_hid_write_flash(FuDevice *device, guint32 dwAddr, const guint8 *input, gsize write_size, @@ -215,7 +221,7 @@ g_return_val_if_fail(write_size <= HIDI2C_MAX_WRITE, FALSE); memcpy(cmd_buffer.data, input, write_size); /* nocheck:blocked */ - if (!fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error)) { + if (!fu_dell_dock_hid_set_report(device, (guint8 *)&cmd_buffer, error)) { g_prefix_error(error, "failed to write %" G_GSIZE_FORMAT " flash to %x: ", write_size, @@ -226,8 +232,9 @@ return TRUE; } +/* nocheck:name -- this should probably be implemented using an interface */ gboolean -fu_dell_dock_hid_verify_update(FuDevice *self, gboolean *result, GError **error) +fu_dell_dock_hid_verify_update(FuDevice *device, gboolean *result, GError **error) { FuHIDCmdBuffer cmd_buffer = { .cmd = HUB_CMD_WRITE_DATA, @@ -241,12 +248,12 @@ .extended_cmdarea[0 ... 52] = 0, }; - if (!fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error)) { - g_prefix_error(error, "failed to verify update: "); + if (!fu_dell_dock_hid_set_report(device, (guint8 *)&cmd_buffer, error)) { + g_prefix_error_literal(error, "failed to verify update: "); return FALSE; } - if (!fu_dell_dock_hid_get_report(self, cmd_buffer.data, error)) { - g_prefix_error(error, "failed to verify update: "); + if (!fu_dell_dock_hid_get_report(device, cmd_buffer.data, error)) { + g_prefix_error_literal(error, "failed to verify update: "); return FALSE; } *result = cmd_buffer.data[0]; @@ -254,8 +261,9 @@ return TRUE; } +/* nocheck:name -- this should probably be implemented using an interface */ gboolean -fu_dell_dock_hid_i2c_write(FuDevice *self, +fu_dell_dock_hid_i2c_write(FuDevice *device, const guint8 *input, gsize write_size, const FuHIDI2CParameters *parameters, @@ -276,11 +284,12 @@ memcpy(cmd_buffer.data, input, write_size); /* nocheck:blocked */ - return fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error); + return fu_dell_dock_hid_set_report(device, (guint8 *)&cmd_buffer, error); } +/* nocheck:name -- this should probably be implemented using an interface */ gboolean -fu_dell_dock_hid_i2c_read(FuDevice *self, +fu_dell_dock_hid_i2c_read(FuDevice *device, guint32 cmd, gsize read_size, GBytes **bytes, @@ -303,9 +312,9 @@ g_return_val_if_fail(bytes != NULL, FALSE); g_return_val_if_fail(parameters->regaddrlen < HIDI2C_MAX_REGISTER, FALSE); - if (!fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error)) + if (!fu_dell_dock_hid_set_report(device, (guint8 *)&cmd_buffer, error)) return FALSE; - if (!fu_dell_dock_hid_get_report(self, cmd_buffer.data, error)) + if (!fu_dell_dock_hid_get_report(device, cmd_buffer.data, error)) return FALSE; *bytes = g_bytes_new(cmd_buffer.data, read_size); @@ -313,8 +322,9 @@ return TRUE; } +/* nocheck:name -- this should probably be implemented using an interface */ gboolean -fu_dell_dock_hid_tbt_wake(FuDevice *self, const FuHIDI2CParameters *parameters, GError **error) +fu_dell_dock_hid_tbt_wake(FuDevice *device, const FuHIDI2CParameters *parameters, GError **error) { FuTbtCmdBuffer cmd_buffer = { .cmd = HUB_CMD_READ_DATA, /* special write command that reads status result */ @@ -327,12 +337,12 @@ .data[0 ... 191] = 0, }; - if (!fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error)) { - g_prefix_error(error, "failed to set wake thunderbolt: "); + if (!fu_dell_dock_hid_set_report(device, (guint8 *)&cmd_buffer, error)) { + g_prefix_error_literal(error, "failed to set wake thunderbolt: "); return FALSE; } - if (!fu_dell_dock_hid_get_report(self, cmd_buffer.data, error)) { - g_prefix_error(error, "failed to get wake thunderbolt status: "); + if (!fu_dell_dock_hid_get_report(device, cmd_buffer.data, error)) { + g_prefix_error_literal(error, "failed to get wake thunderbolt status: "); return FALSE; } g_debug("thunderbolt wake result: 0x%x", cmd_buffer.data[1]); @@ -344,15 +354,16 @@ fu_dell_dock_hid_tbt_map_error(guint32 code) { if (code == 1) - return g_strerror(EINVAL); + return fwupd_strerror(EINVAL); if (code == 2) - return g_strerror(EPERM); + return fwupd_strerror(EPERM); - return g_strerror(EIO); + return fwupd_strerror(EIO); } +/* nocheck:name -- this should probably be implemented using an interface */ gboolean -fu_dell_dock_hid_tbt_write(FuDevice *self, +fu_dell_dock_hid_tbt_write(FuDevice *device, guint32 start_addr, const guint8 *input, gsize write_size, @@ -360,7 +371,7 @@ GError **error) { FuTbtCmdBuffer cmd_buffer = { - .cmd = HUB_CMD_READ_DATA, /* It's a special write command that reads status result */ + .cmd = HUB_CMD_READ_DATA, /* a special write command that reads status result */ .ext = HUB_EXT_WRITE_TBT_FLASH, .i2ctargetaddr = parameters->i2ctargetaddr, .i2cspeed = parameters->i2cspeed, /* unlike other commands doesn't need | 0x80 */ @@ -376,12 +387,12 @@ memcpy(cmd_buffer.data, input, write_size); /* nocheck:blocked */ for (gint i = 1; i <= TBT_MAX_RETRIES; i++) { - if (!fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error)) { - g_prefix_error(error, "failed to run TBT update: "); + if (!fu_dell_dock_hid_set_report(device, (guint8 *)&cmd_buffer, error)) { + g_prefix_error_literal(error, "failed to run TBT update: "); return FALSE; } - if (!fu_dell_dock_hid_get_report(self, cmd_buffer.data, error)) { - g_prefix_error(error, "failed to get TBT flash status: "); + if (!fu_dell_dock_hid_get_report(device, cmd_buffer.data, error)) { + g_prefix_error_literal(error, "failed to get TBT flash status: "); return FALSE; } result = cmd_buffer.data[1] & 0xf; @@ -402,13 +413,14 @@ return TRUE; } +/* nocheck:name -- this should probably be implemented using an interface */ gboolean -fu_dell_dock_hid_tbt_authenticate(FuDevice *self, +fu_dell_dock_hid_tbt_authenticate(FuDevice *device, const FuHIDI2CParameters *parameters, GError **error) { FuTbtCmdBuffer cmd_buffer = { - .cmd = HUB_CMD_READ_DATA, /* It's a special write command that reads status result */ + .cmd = HUB_CMD_READ_DATA, /* a special write command that reads status result */ .ext = HUB_EXT_WRITE_TBT_FLASH, .i2ctargetaddr = parameters->i2ctargetaddr, .i2cspeed = parameters->i2cspeed, /* unlike other commands doesn't need | 0x80 */ @@ -418,22 +430,22 @@ }; guint8 result; - if (!fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error)) { - g_prefix_error(error, "failed to send authentication: "); + if (!fu_dell_dock_hid_set_report(device, (guint8 *)&cmd_buffer, error)) { + g_prefix_error_literal(error, "failed to send authentication: "); return FALSE; } cmd_buffer.tbt_command = GUINT32_TO_LE(TBT_COMMAND_AUTHENTICATE_STATUS); /* nocheck:blocked */ /* needs at least 2 seconds */ - fu_device_sleep(self, 2000); + fu_device_sleep(device, 2000); for (gint i = 1; i <= TBT_MAX_RETRIES; i++) { - if (!fu_dell_dock_hid_set_report(self, (guint8 *)&cmd_buffer, error)) { - g_prefix_error(error, "failed to set check authentication: "); + if (!fu_dell_dock_hid_set_report(device, (guint8 *)&cmd_buffer, error)) { + g_prefix_error_literal(error, "failed to set check authentication: "); return FALSE; } - if (!fu_dell_dock_hid_get_report(self, cmd_buffer.data, error)) { - g_prefix_error(error, "failed to get check authentication: "); + if (!fu_dell_dock_hid_get_report(device, cmd_buffer.data, error)) { + g_prefix_error_literal(error, "failed to get check authentication: "); return FALSE; } result = cmd_buffer.data[1] & 0xf; @@ -443,7 +455,7 @@ i, TBT_MAX_RETRIES, result); - fu_device_sleep(self, 500); /* ms */ + fu_device_sleep(device, 500); /* ms */ } if (result != 0) { g_set_error(error, diff -Nru fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-hid.h fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-hid.h --- fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-hid.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-hid.h 2026-02-26 11:36:18.000000000 +0000 @@ -24,14 +24,6 @@ guint8 i2cspeed; } FuHIDI2CParameters; -typedef enum { - I2C_SPEED_250K, - I2C_SPEED_400K, - I2C_SPEED_800K, - /* */ - I2C_SPEED_LAST, -} BridgedI2CSpeed; - #define HIDI2C_MAX_READ 192 #define HIDI2C_MAX_WRITE 128 diff -Nru fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-hub.c fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-hub.c --- fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-hub.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-hub.c 2026-02-26 11:36:18.000000000 +0000 @@ -27,20 +27,20 @@ G_DEFINE_TYPE(FuDellDockHub, fu_dell_dock_hub, FU_TYPE_HID_DEVICE) void -fu_dell_dock_hub_add_instance(FuDevice *device, guint8 dock_type) +fu_dell_dock_hub_add_instance(FuDellDockHub *self, guint8 dock_type) { g_autofree gchar *devid = NULL; if (dock_type == DOCK_BASE_TYPE_ATOMIC) { devid = g_strdup_printf("USB\\VID_%04X&PID_%04X&atomic_hub", - (guint)fu_device_get_vid(device), - (guint)fu_device_get_pid(device)); + (guint)fu_device_get_vid(FU_DEVICE(self)), + (guint)fu_device_get_pid(FU_DEVICE(self))); } else { devid = g_strdup_printf("USB\\VID_%04X&PID_%04X&hub", - (guint)fu_device_get_vid(device), - (guint)fu_device_get_pid(device)); + (guint)fu_device_get_vid(FU_DEVICE(self)), + (guint)fu_device_get_pid(FU_DEVICE(self))); } - fu_device_add_instance_id(device, devid); + fu_device_add_instance_id(FU_DEVICE(self), devid); } static gboolean @@ -185,7 +185,7 @@ } static void -fu_dell_dock_hub_set_progress(FuDevice *self, FuProgress *progress) +fu_dell_dock_hub_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-hub.h fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-hub.h --- fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-hub.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-hub.h 2026-02-26 11:36:18.000000000 +0000 @@ -30,4 +30,4 @@ FuDellDockHub * fu_dell_dock_hub_new(FuUsbDevice *device); void -fu_dell_dock_hub_add_instance(FuDevice *device, guint8 dock_type); +fu_dell_dock_hub_add_instance(FuDellDockHub *self, guint8 dock_type); diff -Nru fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-mst.c fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-mst.c --- fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-mst.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-mst.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,6 +19,13 @@ #include #include "fu-dell-dock-common.h" +#include "fu-dell-dock-struct.h" + +/* + * NOTE: DO NOT ALLOW ANY MORE MAGIC CONSTANTS IN THIS FILE + * nocheck:magic-defines=38 + * nocheck:magic-inlines=600 + */ #define I2C_MST_ADDRESS 0x72 @@ -41,20 +48,6 @@ #define CAYENNE_MST_RC_LENGTH_ADDR 0x20200288 #define CAYENNE_MST_RC_DATA_ADDR 0x20200290 -/* MST remote control commands */ -#define MST_CMD_ENABLE_REMOTE_CONTROL 0x1 -#define MST_CMD_DISABLE_REMOTE_CONTROL 0x2 -#define MST_CMD_CHECKSUM 0x11 -#define MST_CMD_ERASE_FLASH 0x14 -#define MST_CMD_WRITE_FLASH 0x20 -#define MST_CMD_READ_FLASH 0x30 -#define MST_CMD_WRITE_MEMORY 0x21 -#define MST_CMD_READ_MEMORY 0x31 - -/* Cayenne specific remote control commands */ -#define MST_CMD_CRC16_CHECKSUM 0x17 -#define MST_CMD_ACTIVATE_FW 0x18 - /* Arguments related to flashing */ #define FLASH_SECTOR_ERASE_4K 0x1000 #define FLASH_SECTOR_ERASE_32K 0x2000 @@ -79,53 +72,40 @@ /* firmware file offsets */ #define MST_BLOB_VERSION_OFFSET 0x06F0 -typedef enum { - Panamera_mst, - Cayenne_mst, - Unknown, -} MSTType; - -typedef enum { - Bank0, - Bank1, - ESM, - Cayenne, -} MSTBank; - typedef struct { guint start; guint length; guint checksum_cmd; -} MSTBankAttributes; +} FuDellDockMstBankAttributes; -const MSTBankAttributes bank0_attributes = { +const FuDellDockMstBankAttributes bank0_attributes = { .start = 0, .length = EEPROM_BANK_OFFSET, - .checksum_cmd = MST_CMD_CHECKSUM, + .checksum_cmd = FU_DELL_DOCK_MST_CMD_CHECKSUM, }; -const MSTBankAttributes bank1_attributes = { +const FuDellDockMstBankAttributes bank1_attributes = { .start = EEPROM_BANK_OFFSET, .length = EEPROM_BANK_OFFSET, - .checksum_cmd = MST_CMD_CHECKSUM, + .checksum_cmd = FU_DELL_DOCK_MST_CMD_CHECKSUM, }; -const MSTBankAttributes esm_attributes = { +const FuDellDockMstBankAttributes esm_attributes = { .start = EEPROM_ESM_OFFSET, .length = 0x3ffff, - .checksum_cmd = MST_CMD_CHECKSUM, + .checksum_cmd = FU_DELL_DOCK_MST_CMD_CHECKSUM, }; -const MSTBankAttributes cayenne_attributes = { +const FuDellDockMstBankAttributes cayenne_attributes = { .start = 0, .length = 0x50000, - .checksum_cmd = MST_CMD_CRC16_CHECKSUM, + .checksum_cmd = FU_DELL_DOCK_MST_CMD_CRC16_CHECKSUM, }; FuHIDI2CParameters mst_base_settings = { .i2ctargetaddr = I2C_MST_ADDRESS, .regaddrlen = 0, - .i2cspeed = I2C_SPEED_400K, + .i2cspeed = FU_DELL_DOCK_I2C_SPEED_400K, }; struct _FuDellDockMst { @@ -139,15 +119,15 @@ guint32 mst_rc_data_addr; guint32 mst_core_mcu_bootloader_addr; guint8 dock_type; - MSTType mst_type; + FuDellDockMstType mst_type; }; G_DEFINE_TYPE(FuDellDockMst, fu_dell_dock_mst, FU_TYPE_DEVICE) /** * fu_dell_dock_mst_get_bank_attribs: - * @bank: the MSTBank - * @out (out): the MSTBankAttributes attribute that matches + * @bank: the FuDellDockMstBank + * @out (out): the FuDellDockMstBankAttributes attribute that matches * @error: (nullable): optional return location for an error * * Returns a structure that corresponds to the attributes for a bank @@ -155,26 +135,28 @@ * Returns: %TRUE for success **/ static gboolean -fu_dell_dock_mst_get_bank_attribs(MSTBank bank, const MSTBankAttributes **out, GError **error) +fu_dell_dock_mst_get_bank_attribs(FuDellDockMstBank bank, + const FuDellDockMstBankAttributes **out, + GError **error) { switch (bank) { - case Bank0: + case FU_DELL_DOCK_MST_BANK_0: *out = &bank0_attributes; break; - case Bank1: + case FU_DELL_DOCK_MST_BANK_1: *out = &bank1_attributes; break; - case ESM: + case FU_DELL_DOCK_MST_BANK_ESM: *out = &esm_attributes; break; - case Cayenne: + case FU_DELL_DOCK_MST_BANK_CAYENNE: *out = &cayenne_attributes; break; default: g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "Invalid bank specified %u", + "invalid bank specified %u", bank); return FALSE; } @@ -182,7 +164,7 @@ } static gboolean -fu_dell_dock_mst_rc_command(FuDevice *device, +fu_dell_dock_mst_rc_command(FuDellDockMst *self, guint8 cmd, guint32 length, guint32 offset, @@ -231,7 +213,7 @@ } static gboolean -fu_dell_dock_mst_query_active_bank(FuDevice *proxy, MSTBank *active, GError **error) +fu_dell_dock_mst_query_active_bank(FuDevice *proxy, FuDellDockMstBank *active, GError **error) { g_autoptr(GBytes) bytes = NULL; const guint32 *data = NULL; @@ -242,26 +224,26 @@ length, &bytes, error)) { - g_prefix_error(error, "Failed to query active bank: "); + g_prefix_error_literal(error, "failed to query active bank: "); return FALSE; } data = g_bytes_get_data(bytes, &length); if ((data[0] & (1 << 7)) || (data[0] & (1 << 30))) - *active = Bank1; + *active = FU_DELL_DOCK_MST_BANK_1; else - *active = Bank0; + *active = FU_DELL_DOCK_MST_BANK_0; g_debug("MST: active bank is: %u", *active); return TRUE; } static gboolean -fu_dell_dock_mst_disable_remote_control(FuDevice *device, GError **error) +fu_dell_dock_mst_disable_remote_control(FuDellDockMst *self, GError **error) { g_debug("MST: Disabling remote control"); - return fu_dell_dock_mst_rc_command(device, - MST_CMD_DISABLE_REMOTE_CONTROL, + return fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_DISABLE_REMOTE_CONTROL, 0, 0, NULL, @@ -269,43 +251,45 @@ } static gboolean -fu_dell_dock_mst_enable_remote_control(FuDevice *device, GError **error) +fu_dell_dock_mst_enable_remote_control(FuDellDockMst *self, GError **error) { g_autoptr(GError) error_local = NULL; const gchar *data = "PRIUS"; g_debug("MST: Enabling remote control"); - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_ENABLE_REMOTE_CONTROL, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_ENABLE_REMOTE_CONTROL, 5, 0, (guint8 *)data, &error_local)) { g_debug("Failed to enable remote control: %s", error_local->message); /* try to disable / re-enable */ - if (!fu_dell_dock_mst_disable_remote_control(device, error)) + if (!fu_dell_dock_mst_disable_remote_control(self, error)) return FALSE; - return fu_dell_dock_mst_enable_remote_control(device, error); + return fu_dell_dock_mst_enable_remote_control(self, error); } return TRUE; } static gboolean -fu_dell_dock_mst_trigger_rc_command(FuDevice *device, GError **error) +fu_dell_dock_mst_trigger_rc_command(FuDellDockMst *self, GError **error) { const guint8 *result = NULL; - FuDevice *proxy = fu_device_get_proxy(device); - FuDellDockMst *self = FU_DELL_DOCK_MST(device); + FuDevice *proxy; guint32 tmp; - /* Trigger the write */ + /* trigger the write */ tmp = MST_TRIGGER_WRITE; + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; if (!fu_dell_dock_mst_write_register(proxy, self->mst_rc_trigger_addr, (guint8 *)&tmp, sizeof(guint32), error)) { - g_prefix_error(error, "Failed to write MST_RC_TRIGGER_ADDR: "); + g_prefix_error_literal(error, "failed to write MST_RC_TRIGGER_ADDR: "); return FALSE; } /* poll for completion */ @@ -317,7 +301,7 @@ sizeof(guint32), &bytes, error)) { - g_prefix_error(error, "Failed to poll MST_RC_COMMAND_ADDR: "); + g_prefix_error_literal(error, "failed to poll MST_RC_COMMAND_ADDR: "); return FALSE; } result = g_bytes_get_data(bytes, NULL); @@ -331,7 +315,7 @@ switch (tmp) { /* need to enable remote control */ case 4: - return fu_dell_dock_mst_enable_remote_control(device, error); + return fu_dell_dock_mst_enable_remote_control(self, error); /* error scenarios */ case 3: g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Unknown error"); @@ -363,7 +347,7 @@ } static gboolean -fu_dell_dock_mst_rc_command(FuDevice *device, +fu_dell_dock_mst_rc_command(FuDellDockMst *self, guint8 cmd, guint32 length, guint32 offset, @@ -371,14 +355,11 @@ GError **error) { /* 4 for cmd, 4 for offset, 4 for length, 4 for garbage */ - FuDellDockMst *self = FU_DELL_DOCK_MST(device); - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy; gint buffer_len = (data == NULL) ? 12 : length + 16; g_autofree guint8 *buffer = g_malloc0(buffer_len); guint32 tmp; - g_return_val_if_fail(proxy != NULL, FALSE); - /* command */ tmp = (cmd | 0x80) << 16; memcpy(buffer, &tmp, 4); /* nocheck:blocked */ @@ -391,6 +372,9 @@ memcpy(buffer + 16, data, length); /* nocheck:blocked */ /* write the combined register stream */ + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; if (!fu_dell_dock_mst_write_register(proxy, self->mst_rc_command_addr, buffer, @@ -398,13 +382,12 @@ error)) return FALSE; - return fu_dell_dock_mst_trigger_rc_command(device, error); + return fu_dell_dock_mst_trigger_rc_command(self, error); } -static MSTType -fu_dell_dock_mst_get_module_type(FuDevice *device) +static FuDellDockMstType +fu_dell_dock_mst_get_module_type(FuDellDockMst *self) { - FuDellDockMst *self = FU_DELL_DOCK_MST(device); return self->mst_type; } @@ -417,14 +400,17 @@ } static gboolean -fu_dell_dock_mst_d19_check_fw(FuDevice *device, GError **error) +fu_dell_dock_mst_d19_check_fw(FuDellDockMst *self, GError **error) { - FuDellDockMst *self = FU_DELL_DOCK_MST(device); g_autoptr(GBytes) bytes = NULL; const guint8 *data; gsize length = 4; + FuDevice *proxy; - if (!fu_dell_dock_mst_read_register(fu_device_get_proxy(device), + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (!fu_dell_dock_mst_read_register(proxy, self->mst_core_mcu_bootloader_addr, length, &bytes, @@ -449,7 +435,7 @@ } static guint16 -fu_dell_dock_mst_get_crc(guint8 type, guint32 length, const guint8 *payload_data) +fu_dell_dock_mst_get_crc(guint32 length, const guint8 *payload_data) { static const guint16 CRC16_table[] = { 0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011, 0x8033, 0x0036, 0x003c, @@ -476,55 +462,27 @@ 0x0252, 0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261, 0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231, 0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202}; - static const guint16 CRC8_table[] = { - 0x00, 0xd5, 0x7f, 0xaa, 0xfe, 0x2b, 0x81, 0x54, 0x29, 0xfc, 0x56, 0x83, 0xd7, 0x02, - 0xa8, 0x7d, 0x52, 0x87, 0x2d, 0xf8, 0xac, 0x79, 0xd3, 0x06, 0x7b, 0xae, 0x04, 0xd1, - 0x85, 0x50, 0xfa, 0x2f, 0xa4, 0x71, 0xdb, 0x0e, 0x5a, 0x8f, 0x25, 0xf0, 0x8d, 0x58, - 0xf2, 0x27, 0x73, 0xa6, 0x0c, 0xd9, 0xf6, 0x23, 0x89, 0x5c, 0x08, 0xdd, 0x77, 0xa2, - 0xdf, 0x0a, 0xa0, 0x75, 0x21, 0xf4, 0x5e, 0x8b, 0x9d, 0x48, 0xe2, 0x37, 0x63, 0xb6, - 0x1c, 0xc9, 0xb4, 0x61, 0xcb, 0x1e, 0x4a, 0x9f, 0x35, 0xe0, 0xcf, 0x1a, 0xb0, 0x65, - 0x31, 0xe4, 0x4e, 0x9b, 0xe6, 0x33, 0x99, 0x4c, 0x18, 0xcd, 0x67, 0xb2, 0x39, 0xec, - 0x46, 0x93, 0xc7, 0x12, 0xb8, 0x6d, 0x10, 0xc5, 0x6f, 0xba, 0xee, 0x3b, 0x91, 0x44, - 0x6b, 0xbe, 0x14, 0xc1, 0x95, 0x40, 0xea, 0x3f, 0x42, 0x97, 0x3d, 0xe8, 0xbc, 0x69, - 0xc3, 0x16, 0xef, 0x3a, 0x90, 0x45, 0x11, 0xc4, 0x6e, 0xbb, 0xc6, 0x13, 0xb9, 0x6c, - 0x38, 0xed, 0x47, 0x92, 0xbd, 0x68, 0xc2, 0x17, 0x43, 0x96, 0x3c, 0xe9, 0x94, 0x41, - 0xeb, 0x3e, 0x6a, 0xbf, 0x15, 0xc0, 0x4b, 0x9e, 0x34, 0xe1, 0xb5, 0x60, 0xca, 0x1f, - 0x62, 0xb7, 0x1d, 0xc8, 0x9c, 0x49, 0xe3, 0x36, 0x19, 0xcc, 0x66, 0xb3, 0xe7, 0x32, - 0x98, 0x4d, 0x30, 0xe5, 0x4f, 0x9a, 0xce, 0x1b, 0xb1, 0x64, 0x72, 0xa7, 0x0d, 0xd8, - 0x8c, 0x59, 0xf3, 0x26, 0x5b, 0x8e, 0x24, 0xf1, 0xa5, 0x70, 0xda, 0x0f, 0x20, 0xf5, - 0x5f, 0x8a, 0xde, 0x0b, 0xa1, 0x74, 0x09, 0xdc, 0x76, 0xa3, 0xf7, 0x22, 0x88, 0x5d, - 0xd6, 0x03, 0xa9, 0x7c, 0x28, 0xfd, 0x57, 0x82, 0xff, 0x2a, 0x80, 0x55, 0x01, 0xd4, - 0x7e, 0xab, 0x84, 0x51, 0xfb, 0x2e, 0x7a, 0xaf, 0x05, 0xd0, 0xad, 0x78, 0xd2, 0x07, - 0x53, 0x86, 0x2c, 0xf9}; guint8 val; guint16 crc = 0; - const guint8 *message = payload_data; - if (type == 8) { - for (guint32 byte = 0; byte < length; ++byte) { - val = (guint8)(message[byte] ^ crc); - crc = CRC8_table[val]; - } - } else { - for (guint32 byte = 0; byte < length; ++byte) { - val = (guint8)(message[byte] ^ (crc >> 8)); - crc = CRC16_table[val] ^ (crc << 8); - } + for (guint32 byte = 0; byte < length; ++byte) { + val = (guint8)(payload_data[byte] ^ (crc >> 8)); + crc = CRC16_table[val] ^ (crc << 8); } return crc; } static gboolean -fu_dell_dock_mst_checksum_bank(FuDevice *device, +fu_dell_dock_mst_checksum_bank(FuDellDockMst *self, GBytes *blob_fw, - MSTBank bank, + FuDellDockMstBank bank, gboolean *checksum, GError **error) { - FuDellDockMst *self = FU_DELL_DOCK_MST(device); + FuDevice *proxy; g_autoptr(GBytes) csum_bytes = NULL; - const MSTBankAttributes *attribs = NULL; + const FuDellDockMstBankAttributes *attribs = NULL; gsize length = 0; const guint8 *data = g_bytes_get_data(blob_fw, &length); guint32 payload_sum = 0; @@ -548,9 +506,8 @@ } /* checksum the file */ - if (attribs->checksum_cmd == MST_CMD_CRC16_CHECKSUM) - payload_sum = - fu_dell_dock_mst_get_crc(16, (attribs->length + attribs->start), data); + if (attribs->checksum_cmd == FU_DELL_DOCK_MST_CMD_CRC16_CHECKSUM) + payload_sum = fu_dell_dock_mst_get_crc(attribs->length + attribs->start, data); else { for (guint i = attribs->start; i < attribs->length + attribs->start; i++) { payload_sum += data[i]; @@ -559,25 +516,24 @@ g_debug("MST: Payload checksum: 0x%x", payload_sum); /* checksum the bank */ - if (!fu_dell_dock_mst_rc_command(device, + if (!fu_dell_dock_mst_rc_command(self, attribs->checksum_cmd, attribs->length, attribs->start, NULL, error)) { - g_prefix_error(error, "Failed to checksum bank %u: ", bank); + g_prefix_error(error, "failed to checksum bank %u: ", bank); return FALSE; } /* read result from data register */ - if (!fu_dell_dock_mst_read_register(fu_device_get_proxy(device), - self->mst_rc_data_addr, - 4, - &csum_bytes, - error)) + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (!fu_dell_dock_mst_read_register(proxy, self->mst_rc_data_addr, 4, &csum_bytes, error)) return FALSE; data = g_bytes_get_data(csum_bytes, NULL); - bank_sum = GUINT32_FROM_LE(data[0] | data[1] << 8 | data[2] << 16 | /* nocheck:blocked */ - data[3] << 24); + bank_sum = GUINT32_FROM_LE(data[0] | data[1] << 8 | /* nocheck:blocked nocheck:endian */ + data[2] << 16 | data[3] << 24); g_debug("MST: Bank %u checksum: 0x%x", bank, bank_sum); *checksum = (bank_sum == payload_sum); @@ -586,9 +542,9 @@ } static gboolean -fu_dell_dock_mst_erase_panamera_bank(FuDevice *device, MSTBank bank, GError **error) +fu_dell_dock_mst_erase_panamera_bank(FuDellDockMst *self, FuDellDockMstBank bank, GError **error) { - const MSTBankAttributes *attribs = NULL; + const FuDellDockMstBankAttributes *attribs = NULL; guint32 sector; if (!fu_dell_dock_mst_get_bank_attribs(bank, &attribs, error)) @@ -597,53 +553,53 @@ for (guint32 i = attribs->start; i < attribs->start + attribs->length; i += 0x10000) { sector = FLASH_SECTOR_ERASE_64K | (i / 0x10000); g_debug("MST: Erasing sector 0x%x", sector); - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_ERASE_FLASH, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_ERASE_FLASH, 4, 0, (guint8 *)§or, error)) { - g_prefix_error(error, "Failed to erase sector 0x%x: ", sector); + g_prefix_error(error, "failed to erase sector 0x%x: ", sector); return FALSE; } } g_debug("MST: Waiting for flash clear to settle"); - fu_device_sleep(device, 5000); /* ms */ + fu_device_sleep(FU_DEVICE(self), 5000); /* ms */ return TRUE; } static gboolean -fu_dell_dock_mst_erase_cayenne(FuDevice *device, GError **error) +fu_dell_dock_mst_erase_cayenne(FuDellDockMst *self, GError **error) { guint8 data[4] = {0, 0x30, 0, 0}; for (guint8 i = 0; i < 5; i++) { data[0] = i; - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_ERASE_FLASH, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_ERASE_FLASH, 4, 0, (guint8 *)&data, error)) { - g_prefix_error(error, "Failed to erase sector: %d", i); + g_prefix_error(error, "failed to erase sector %d: ", i); return FALSE; } } g_debug("MST: Waiting for flash clear to settle"); - fu_device_sleep(device, 5000); + fu_device_sleep(FU_DEVICE(self), 5000); return TRUE; } static gboolean -fu_dell_dock_mst_write_flash_bank(FuDevice *device, +fu_dell_dock_mst_write_flash_bank(FuDellDockMst *self, GBytes *blob_fw, - MSTBank bank, + FuDellDockMstBank bank, FuProgress *progress, GError **error) { - const MSTBankAttributes *attribs = NULL; + const FuDellDockMstBankAttributes *attribs = NULL; gsize write_size = 32; guint end; const guint8 *data = g_bytes_get_data(blob_fw, NULL); @@ -656,14 +612,14 @@ g_debug("MST: Writing payload to bank %u", bank); for (guint i = attribs->start; i < end; i += write_size) { - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_WRITE_FLASH, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_WRITE_FLASH, write_size, i, data + i, error)) { g_prefix_error(error, - "Failed to write bank %u payload offset 0x%x: ", + "failed to write bank %u payload offset 0x%x: ", bank, i); return FALSE; @@ -675,18 +631,19 @@ } static gboolean -fu_dell_dock_mst_stop_esm(FuDevice *device, GError **error) +fu_dell_dock_mst_stop_esm(FuDellDockMst *self, GError **error) { + FuDevice *proxy; g_autoptr(GBytes) quad_bytes = NULL; g_autoptr(GBytes) hdcp_bytes = NULL; guint32 payload = 0x21; gsize length = sizeof(guint32); const guint8 *data; - guint8 data_out[sizeof(guint32)]; + guint8 data_out[4] = {0}; /* disable ESM first */ - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_WRITE_MEMORY, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_WRITE_MEMORY, length, PANAMERA_MST_RC_TRIGGER_ADDR, (guint8 *)&payload, @@ -694,18 +651,21 @@ return FALSE; /* waiting for ESM exit */ - fu_device_sleep(device, 1); + fu_device_sleep(FU_DEVICE(self), 1); /* disable QUAD mode */ - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_READ_MEMORY, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_READ_MEMORY, length, PANAMERA_MST_REG_QUAD_DISABLE, NULL, error)) return FALSE; - if (!fu_dell_dock_mst_read_register(fu_device_get_proxy(device), + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (!fu_dell_dock_mst_read_register(proxy, PANAMERA_MST_RC_DATA_ADDR, length, &quad_bytes, @@ -715,8 +675,8 @@ data = g_bytes_get_data(quad_bytes, &length); memcpy(data_out, data, length); /* nocheck:blocked */ data_out[0] = 0x00; - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_WRITE_MEMORY, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_WRITE_MEMORY, length, PANAMERA_MST_REG_QUAD_DISABLE, data_out, @@ -724,15 +684,15 @@ return FALSE; /* disable HDCP2.2 */ - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_READ_MEMORY, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_READ_MEMORY, length, PANAMERA_MST_REG_HDCP22_DISABLE, NULL, error)) return FALSE; - if (!fu_dell_dock_mst_read_register(fu_device_get_proxy(device), + if (!fu_dell_dock_mst_read_register(proxy, PANAMERA_MST_RC_DATA_ADDR, length, &hdcp_bytes, @@ -742,8 +702,8 @@ data = g_bytes_get_data(hdcp_bytes, &length); memcpy(data_out, data, length); /* nocheck:blocked */ data_out[0] = data[0] & (1 << 2); - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_WRITE_MEMORY, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_WRITE_MEMORY, length, PANAMERA_MST_REG_HDCP22_DISABLE, data_out, @@ -754,9 +714,10 @@ } static gboolean -fu_dell_dock_mst_invalidate_bank(FuDevice *device, MSTBank bank_in_use, GError **error) +fu_dell_dock_mst_invalidate_bank(FuDellDockMst *self, FuDellDockMstBank bank_in_use, GError **error) { - const MSTBankAttributes *attribs; + FuDevice *proxy; + const FuDellDockMstBankAttributes *attribs; g_autoptr(GBytes) bytes = NULL; const guint8 *crc_tag; const guint8 *new_tag; @@ -764,22 +725,26 @@ guint retries = 2; if (!fu_dell_dock_mst_get_bank_attribs(bank_in_use, &attribs, error)) { - g_prefix_error(error, "unable to invalidate bank: "); + g_prefix_error_literal(error, "unable to invalidate bank: "); return FALSE; } /* we need to write 4 byte increments over I2C so this differs from DP aux */ crc_offset = attribs->start + EEPROM_TAG_OFFSET + 12; - /* Read CRC byte to flip */ - if (!fu_dell_dock_mst_rc_command(device, MST_CMD_READ_FLASH, 4, crc_offset, NULL, error)) { - g_prefix_error(error, "failed to read tag from flash: "); + /* read CRC byte to flip */ + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_READ_FLASH, + 4, + crc_offset, + NULL, + error)) { + g_prefix_error_literal(error, "failed to read tag from flash: "); return FALSE; } - if (!fu_dell_dock_mst_read_register(fu_device_get_proxy(device), - PANAMERA_MST_RC_DATA_ADDR, - 1, - &bytes, - error)) { + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (!fu_dell_dock_mst_read_register(proxy, PANAMERA_MST_RC_DATA_ADDR, 1, &bytes, error)) { return FALSE; } crc_tag = g_bytes_get_data(bytes, NULL); @@ -791,12 +756,12 @@ if (crc_tag[3] != 0xff) { guint32 sector = FLASH_SECTOR_ERASE_4K + (attribs->start + attribs->length - 0x1000) / 0x1000; - g_debug("Erasing 4k from sector 0x%x invalidate bank %u", + g_debug("erasing 4k from sector 0x%x invalidate bank %u", sector, bank_in_use); /* offset for last 4k of bank# */ - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_ERASE_FLASH, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_ERASE_FLASH, 4, 0, (guint8 *)§or, @@ -807,30 +772,30 @@ /* CRC8 is 0xff, set it to 0x00 */ } else { guint32 write = 0x00; - g_debug("Writing 0x00 byte to 0x%x to invalidate bank %u", + g_debug("writing 0x00 byte to 0x%x to invalidate bank %u", crc_offset, bank_in_use); - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_WRITE_FLASH, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_WRITE_FLASH, 4, crc_offset, (guint8 *)&write, error)) { - g_prefix_error(error, "failed to clear CRC byte: "); + g_prefix_error_literal(error, "failed to clear CRC byte: "); return FALSE; } } /* re-read for comparison */ - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_READ_FLASH, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_READ_FLASH, 4, crc_offset, NULL, error)) { - g_prefix_error(error, "failed to read tag from flash: "); + g_prefix_error_literal(error, "failed to read tag from flash: "); return FALSE; } - if (!fu_dell_dock_mst_read_register(fu_device_get_proxy(device), + if (!fu_dell_dock_mst_read_register(proxy, PANAMERA_MST_RC_DATA_ADDR, 4, &bytes_new, @@ -860,7 +825,7 @@ } static gboolean -fu_dell_dock_mst_write_bank(FuDevice *device, +fu_dell_dock_mst_write_bank(FuDellDockMst *self, GBytes *fw, guint8 bank, FuProgress *progress, @@ -876,11 +841,11 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 84, NULL); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 1, NULL); - if (!fu_dell_dock_mst_erase_panamera_bank(device, bank, error)) + if (!fu_dell_dock_mst_erase_panamera_bank(self, bank, error)) return FALSE; fu_progress_step_done(progress); - if (!fu_dell_dock_mst_write_flash_bank(device, + if (!fu_dell_dock_mst_write_flash_bank(self, fw, bank, fu_progress_get_child(progress), @@ -888,7 +853,7 @@ return FALSE; fu_progress_step_done(progress); - if (!fu_dell_dock_mst_checksum_bank(device, fw, bank, &checksum, error)) + if (!fu_dell_dock_mst_checksum_bank(self, fw, bank, &checksum, error)) return FALSE; if (!checksum) { g_debug("MST: Failed to verify checksum on bank %u", bank); @@ -918,36 +883,41 @@ } static gboolean -fu_dell_dock_mst_write_panamera(FuDevice *device, +fu_dell_dock_mst_write_panamera(FuDellDockMst *self, GBytes *fw, FwupdInstallFlags flags, FuProgress *progress, GError **error) { + FuDevice *proxy; gboolean checksum = FALSE; - MSTBank bank_in_use = 0; - guint8 order[2] = {ESM, Bank0}; + FuDellDockMstBank bank_in_use = 0; + guint8 order[2] = {FU_DELL_DOCK_MST_BANK_ESM, FU_DELL_DOCK_MST_BANK_0}; FuProgress *progress_local; fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "stop-esm"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, NULL); + /* determine the flash order */ - if (!fu_dell_dock_mst_query_active_bank(fu_device_get_proxy(device), &bank_in_use, error)) + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (!fu_dell_dock_mst_query_active_bank(proxy, &bank_in_use, error)) return FALSE; - if (bank_in_use == Bank0) - order[1] = Bank1; + if (bank_in_use == FU_DELL_DOCK_MST_BANK_0) + order[1] = FU_DELL_DOCK_MST_BANK_1; /* ESM needs special handling during flash process*/ - if (!fu_dell_dock_mst_stop_esm(device, error)) + if (!fu_dell_dock_mst_stop_esm(self, error)) return FALSE; fu_progress_step_done(progress); progress_local = fu_dell_dock_mst_set_local_progress(progress, 2); - /* Write each bank in order */ + /* write each bank in order */ for (guint phase = 0; phase < 2; phase++) { g_debug("MST: Checking bank %u", order[phase]); - if (!fu_dell_dock_mst_checksum_bank(device, fw, order[phase], &checksum, error)) + if (!fu_dell_dock_mst_checksum_bank(self, fw, order[phase], &checksum, error)) return FALSE; if (checksum) { g_debug("MST: bank %u is already up to date", order[phase]); @@ -955,7 +925,7 @@ continue; } g_debug("MST: bank %u needs to be updated", order[phase]); - if (!fu_dell_dock_mst_write_bank(device, + if (!fu_dell_dock_mst_write_bank(self, fw, order[phase], fu_progress_get_child(progress_local), @@ -964,7 +934,7 @@ fu_progress_step_done(progress_local); } /* invalidate the previous bank */ - if (!fu_dell_dock_mst_invalidate_bank(device, bank_in_use, error)) { + if (!fu_dell_dock_mst_invalidate_bank(self, bank_in_use, error)) { g_prefix_error(error, "failed to invalidate bank %u: ", bank_in_use); return FALSE; } @@ -974,7 +944,7 @@ } static gboolean -fu_dell_dock_mst_write_cayenne(FuDevice *device, +fu_dell_dock_mst_write_cayenne(FuDellDockMst *self, GBytes *fw, FwupdInstallFlags flags, FuProgress *progress, @@ -988,16 +958,20 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, NULL); for (guint i = 0; i < retries; i++) { - if (!fu_dell_dock_mst_erase_cayenne(device, error)) + if (!fu_dell_dock_mst_erase_cayenne(self, error)) return FALSE; fu_progress_step_done(progress); - if (!fu_dell_dock_mst_write_flash_bank(device, + if (!fu_dell_dock_mst_write_flash_bank(self, fw, - Cayenne, + FU_DELL_DOCK_MST_BANK_CAYENNE, fu_progress_get_child(progress), error)) return FALSE; - if (!fu_dell_dock_mst_checksum_bank(device, fw, Cayenne, &checksum, error)) + if (!fu_dell_dock_mst_checksum_bank(self, + fw, + FU_DELL_DOCK_MST_BANK_CAYENNE, + &checksum, + error)) return FALSE; fu_progress_step_done(progress); if (!checksum) { @@ -1009,41 +983,47 @@ } /* failed after all our retries */ if (!checksum) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed to write to bank"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to write to bank"); return FALSE; } /* activate the FW */ - if (!fu_dell_dock_mst_rc_command(device, - MST_CMD_ACTIVATE_FW, + if (!fu_dell_dock_mst_rc_command(self, + FU_DELL_DOCK_MST_CMD_ACTIVATE_FW, g_bytes_get_size(fw), 0x0, NULL, error)) { - g_prefix_error(error, "Failed to activate FW: "); + g_prefix_error_literal(error, "failed to activate FW: "); return FALSE; } return TRUE; } static gboolean -fu_dell_dock_mst_write_fw(FuDevice *device, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) +fu_dell_dock_mst_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) { FuDellDockMst *self = FU_DELL_DOCK_MST(device); + FuDevice *proxy; const guint8 *data; g_autofree gchar *dynamic_version = NULL; g_autoptr(GBytes) fw = NULL; - MSTType type; + FuDellDockMstType type; g_return_val_if_fail(device != NULL, FALSE); g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE); - g_return_val_if_fail(fu_device_get_proxy(device) != NULL, FALSE); /* open the hub*/ - if (!fu_device_open(fu_device_get_proxy(device), error)) + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (!fu_device_open(proxy, error)) return FALSE; /* open up access to controller bus */ @@ -1063,18 +1043,21 @@ g_info("writing MST firmware version %s", dynamic_version); /* enable remote control */ - if (!fu_dell_dock_mst_enable_remote_control(device, error)) + if (!fu_dell_dock_mst_enable_remote_control(self, error)) return FALSE; - type = fu_dell_dock_mst_get_module_type(device); - if (type == Panamera_mst) { - if (!fu_dell_dock_mst_write_panamera(device, fw, flags, progress, error)) + type = fu_dell_dock_mst_get_module_type(self); + if (type == FU_DELL_DOCK_MST_TYPE_PANAMERA) { + if (!fu_dell_dock_mst_write_panamera(self, fw, flags, progress, error)) return FALSE; - } else if (type == Cayenne_mst) { - if (!fu_dell_dock_mst_write_cayenne(device, fw, flags, progress, error)) + } else if (type == FU_DELL_DOCK_MST_TYPE_CAYENNE) { + if (!fu_dell_dock_mst_write_cayenne(self, fw, flags, progress, error)) return FALSE; } else { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "Unknown mst found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Unknown mst found"); return FALSE; } @@ -1083,7 +1066,7 @@ fu_device_set_version(device, dynamic_version); /* disable remote control now */ - return fu_dell_dock_mst_disable_remote_control(device, error); + return fu_dell_dock_mst_disable_remote_control(self, error); } static gboolean @@ -1137,16 +1120,19 @@ static gboolean fu_dell_dock_mst_setup(FuDevice *device, GError **error) { + FuDellDockMst *self = FU_DELL_DOCK_MST(device); FuDevice *parent; const gchar *version; /* sanity check that we can talk to MST */ - if (!fu_dell_dock_mst_d19_check_fw(device, error)) + if (!fu_dell_dock_mst_d19_check_fw(self, error)) return FALSE; /* set version from EC if we know it */ - parent = fu_device_get_parent(device); - version = fu_dell_dock_ec_get_mst_version(parent); + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + version = fu_dell_dock_ec_get_mst_version(FU_DELL_DOCK_EC(parent)); if (version != NULL) { fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_version(device, version); @@ -1165,7 +1151,7 @@ /* instance id */ if (self->dock_type == DOCK_BASE_TYPE_ATOMIC) { - self->mst_type = Cayenne_mst; + self->mst_type = FU_DELL_DOCK_MST_TYPE_CAYENNE; self->mst_rc_trigger_addr = CAYENNE_MST_RC_TRIGGER_ADDR; self->mst_rc_command_addr = CAYENNE_MST_RC_COMMAND_ADDR; self->mst_rc_data_addr = CAYENNE_MST_RC_DATA_ADDR; @@ -1173,7 +1159,7 @@ fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_add_instance_id(device, DELL_DOCK_VMM6210_INSTANCE_ID); } else if (self->dock_type == DOCK_BASE_TYPE_SALOMON) { - self->mst_type = Panamera_mst; + self->mst_type = FU_DELL_DOCK_MST_TYPE_PANAMERA; self->mst_rc_trigger_addr = PANAMERA_MST_RC_TRIGGER_ADDR; self->mst_rc_command_addr = PANAMERA_MST_RC_COMMAND_ADDR; self->mst_rc_data_addr = PANAMERA_MST_RC_DATA_ADDR; @@ -1196,15 +1182,19 @@ fu_dell_dock_mst_open(FuDevice *device, GError **error) { FuDellDockMst *self = FU_DELL_DOCK_MST(device); - FuDevice *parent = fu_device_get_parent(device); + FuDevice *proxy; + FuDevice *parent = fu_device_get_parent(device, NULL); g_return_val_if_fail(self->unlock_target != 0, FALSE); g_return_val_if_fail(parent != NULL, FALSE); - if (fu_device_get_proxy(device) == NULL) - fu_device_set_proxy(device, fu_device_get_proxy(parent)); + if (fu_device_get_proxy(device, NULL) == NULL) + fu_device_set_proxy(device, fu_device_get_proxy(parent, NULL)); - if (!fu_device_open(fu_device_get_proxy(device), error)) + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + if (!fu_device_open(proxy, error)) return FALSE; /* open up access to controller bus */ @@ -1218,16 +1208,20 @@ fu_dell_dock_mst_close(FuDevice *device, GError **error) { FuDellDockMst *self = FU_DELL_DOCK_MST(device); + FuDevice *proxy; /* close access to controller bus */ if (!fu_dell_dock_set_power(device, self->unlock_target, FALSE, error)) return FALSE; - return fu_device_close(fu_device_get_proxy(device), error); + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + return fu_device_close(proxy, error); } static void -fu_dell_dock_mst_set_progress(FuDevice *self, FuProgress *progress) +fu_dell_dock_mst_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -1242,6 +1236,7 @@ { fu_device_add_protocol(FU_DEVICE(self), "com.synaptics.mst"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_HID_DEVICE); } static void @@ -1252,7 +1247,7 @@ device_class->open = fu_dell_dock_mst_open; device_class->close = fu_dell_dock_mst_close; device_class->setup = fu_dell_dock_mst_setup; - device_class->write_firmware = fu_dell_dock_mst_write_fw; + device_class->write_firmware = fu_dell_dock_mst_write_firmware; device_class->set_quirk_kv = fu_dell_dock_mst_set_quirk_kv; device_class->set_progress = fu_dell_dock_mst_set_progress; } diff -Nru fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-plugin.c fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-plugin.c --- fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -62,7 +62,7 @@ return FALSE; /* determine the dock type */ - dock_type = fu_dell_dock_ec_get_dock_type(FU_DEVICE(ec_device)); + dock_type = fu_dell_dock_ec_get_dock_type(ec_device); /* create mst endpoint */ mst_device = fu_dell_dock_mst_new(ctx, dock_type); @@ -73,7 +73,7 @@ return FALSE; /* create package version endpoint */ - dock_usb4_present = fu_dell_dock_ec_module_is_usb4(FU_DEVICE(ec_device)); + dock_usb4_present = fu_dell_dock_ec_module_is_usb4(ec_device); status_device = fu_dell_dock_status_new(ctx, dock_type, dock_usb4_present); if (!fu_device_probe(FU_DEVICE(status_device), error)) return FALSE; @@ -82,7 +82,7 @@ return FALSE; /* create TBT endpoint if Thunderbolt SKU and Thunderbolt link inactive */ - if (fu_dell_dock_ec_needs_tbt(FU_DEVICE(ec_device))) { + if (fu_dell_dock_ec_needs_tbt(ec_device)) { g_autoptr(FuDellDockTbt) tbt_device = fu_dell_dock_tbt_new(proxy); fu_device_add_instance_id(FU_DEVICE(tbt_device), DELL_DOCK_TBT_INSTANCE_ID); fu_device_add_child(FU_DEVICE(ec_device), FU_DEVICE(tbt_device)); @@ -94,18 +94,18 @@ } /* prefer to use EC if in the transaction and parent if it is not */ -static FuDevice * +static FuDellDockEc * fu_dell_dock_plugin_get_ec(GPtrArray *devices) { - FuDevice *ec_parent = NULL; + FuDellDockEc *ec_parent = NULL; for (gint i = devices->len - 1; i >= 0; i--) { FuDevice *dev = g_ptr_array_index(devices, i); FuDevice *parent; if (FU_IS_DELL_DOCK_EC(dev)) - return dev; - parent = fu_device_get_parent(dev); + return FU_DELL_DOCK_EC(dev); + parent = fu_device_get_parent(dev, NULL); if (parent != NULL && FU_IS_DELL_DOCK_EC(parent)) - ec_parent = parent; + ec_parent = FU_DELL_DOCK_EC(parent); } return ec_parent; @@ -121,7 +121,7 @@ g_autoptr(FuDellDockHub) hub = NULL; const gchar *hub_cache_key = "hub-usb3-gen1"; GPtrArray *devices; - FuDevice *ec_device; + FuDellDockEc *ec_device; FuDevice *hub_dev; guint8 dock_type; @@ -151,19 +151,19 @@ /* determine dock type by ec */ dock_type = fu_dell_dock_ec_get_dock_type(ec_device); if (dock_type == DOCK_BASE_TYPE_UNKNOWN) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "can't read base dock type from EC"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "can't read base dock type from EC"); return FALSE; } - fu_dell_dock_hub_add_instance(FU_DEVICE(hub), dock_type); + fu_dell_dock_hub_add_instance(hub, dock_type); fu_plugin_device_add(plugin, FU_DEVICE(hub)); /* add hub instance id for the cached device */ hub_dev = fu_plugin_cache_lookup(plugin, hub_cache_key); if (hub_dev != NULL) { - fu_dell_dock_hub_add_instance(FU_DEVICE(hub_dev), dock_type); + fu_dell_dock_hub_add_instance(FU_DELL_DOCK_HUB(hub_dev), dock_type); fu_plugin_device_add(plugin, FU_DEVICE(hub_dev)); fu_plugin_cache_remove(plugin, hub_cache_key); } @@ -226,10 +226,28 @@ if (!FU_IS_USB_DEVICE(device)) return TRUE; - parent = fu_device_get_parent(device); - if (parent != NULL && FU_IS_DELL_DOCK_EC(parent)) { - g_debug("Removing %s (%s)", fu_device_get_name(parent), fu_device_get_id(parent)); + /* device will be activated on disconnected */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + g_debug("dropped needs-activation flag: %s", fu_device_get_name(device)); + } + + /* remove the dock device tree */ + parent = fu_device_get_parent(device, NULL); + if (parent == NULL) + return TRUE; + if (FU_IS_DELL_DOCK_EC(parent)) { + g_autofree gchar *id_display = fu_device_get_id_display(parent); + + /* device will be activated on disconnected */ + if (fu_device_has_flag(parent, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { + fu_device_remove_flag(parent, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + g_debug("dropped needs-activation flag: %s", id_display); + } + + /* remove parent */ fu_plugin_device_remove(plugin, parent); + g_debug("removed device: %s", id_display); } return TRUE; @@ -238,7 +256,7 @@ static gboolean fu_dell_dock_plugin_composite_prepare(FuPlugin *plugin, GPtrArray *devices, GError **error) { - FuDevice *parent = fu_dell_dock_plugin_get_ec(devices); + FuDellDockEc *parent = fu_dell_dock_plugin_get_ec(devices); const gchar *sku; if (parent == NULL) return TRUE; @@ -252,7 +270,7 @@ static gboolean fu_dell_dock_plugin_composite_cleanup(FuPlugin *plugin, GPtrArray *devices, GError **error) { - FuDevice *parent = fu_dell_dock_plugin_get_ec(devices); + FuDellDockEc *parent = fu_dell_dock_plugin_get_ec(devices); FuDevice *dev = NULL; g_autoptr(FuDeviceLocker) locker = NULL; gboolean needs_activation = FALSE; @@ -281,7 +299,7 @@ /* separate activation flag between usb4 and ec device */ fu_dell_dock_plugin_separate_activation(plugin); - locker = fu_device_locker_new(parent, error); + locker = fu_device_locker_new(FU_DEVICE(parent), error); if (locker == NULL) return FALSE; @@ -304,6 +322,7 @@ static void fu_dell_dock_plugin_init(FuDellDockPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void @@ -321,6 +340,8 @@ fu_context_add_quirk_key(ctx, "DellDockUnlockTarget"); fu_context_add_quirk_key(ctx, "DellDockVersionLowest"); + fu_plugin_add_udev_subsystem(plugin, "usb"); + /* allow these to be built by quirks */ fu_plugin_add_device_gtype(plugin, FU_TYPE_DELL_DOCK_STATUS); fu_plugin_add_device_gtype(plugin, FU_TYPE_DELL_DOCK_MST); diff -Nru fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-status.c fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-status.c --- fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-status.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-status.c 2026-02-26 11:36:18.000000000 +0000 @@ -46,8 +46,10 @@ guint32 status_version; g_autofree gchar *dynamic_version = NULL; - parent = fu_device_get_parent(device); - status_version = fu_dell_dock_ec_get_status_version(parent); + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + status_version = fu_dell_dock_ec_get_status_version(FU_DELL_DOCK_EC(parent)); dynamic_version = fu_dell_dock_status_ver_string(status_version); fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_QUAD); @@ -64,6 +66,7 @@ GError **error) { FuDellDockStatus *self = FU_DELL_DOCK_STATUS(device); + FuDellDockEc *proxy; gsize length = 0; guint32 status_version = 0; const guint8 *data; @@ -90,7 +93,10 @@ dynamic_version = fu_dell_dock_status_ver_string(status_version); g_info("writing status firmware version %s", dynamic_version); - if (!fu_dell_dock_ec_commit_package(fu_device_get_proxy(device), fw, error)) + proxy = FU_DELL_DOCK_EC(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + if (!fu_dell_dock_ec_commit_package(proxy, fw, error)) return FALSE; /* dock will reboot to re-read; this is to appease the daemon */ @@ -126,24 +132,26 @@ static gboolean fu_dell_dock_status_open(FuDevice *device, GError **error) { - if (fu_device_get_proxy(device) == NULL) { - if (fu_device_get_parent(device) == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no parent"); + FuDevice *proxy; + + proxy = fu_device_get_proxy(device, NULL); + if (proxy == NULL) { + proxy = fu_device_get_parent(device, error); + if (proxy == NULL) return FALSE; - } - fu_device_set_proxy(device, fu_device_get_parent(device)); + fu_device_set_proxy(device, proxy); } - return fu_device_open(fu_device_get_proxy(device), error); + return fu_device_open(proxy, error); } static gboolean fu_dell_dock_status_close(FuDevice *device, GError **error) { - if (fu_device_get_proxy(device) == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no proxy"); + FuDevice *proxy; + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) return FALSE; - } - return fu_device_close(fu_device_get_proxy(device), error); + return fu_device_close(proxy, error); } static gboolean @@ -170,7 +178,7 @@ } static void -fu_dell_dock_status_set_progress(FuDevice *self, FuProgress *progress) +fu_dell_dock_status_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -186,6 +194,7 @@ fu_device_add_protocol(FU_DEVICE(self), "com.dell.dock"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_DELL_DOCK_EC); } static void diff -Nru fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-tbt.c fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-tbt.c --- fwupd-2.0.8/plugins/dell-dock/fu-dell-dock-tbt.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/fu-dell-dock-tbt.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,13 +19,14 @@ #include #include "fu-dell-dock-common.h" +#include "fu-dell-dock-struct.h" #define I2C_TBT_ADDRESS 0xa2 const FuHIDI2CParameters tbt_base_settings = { .i2ctargetaddr = I2C_TBT_ADDRESS, .regaddrlen = 1, - .i2cspeed = I2C_SPEED_400K, + .i2cspeed = FU_DELL_DOCK_I2C_SPEED_400K, }; /* TR Device ID */ @@ -53,6 +54,8 @@ GError **error) { FuDellDockTbt *self = FU_DELL_DOCK_TBT(device); + FuDevice *proxy; + FuDevice *parent; guint32 start_offset = 0; gsize image_size = 0; const guint8 *buffer; @@ -74,10 +77,10 @@ buffer[self->blob_major_offset], buffer[self->blob_minor_offset]); g_info("writing Thunderbolt firmware version %s", dynamic_version); - g_debug("Total Image size: %" G_GSIZE_FORMAT, image_size); + g_debug("total image size: %" G_GSIZE_FORMAT, image_size); memcpy(&start_offset, buffer, sizeof(guint32)); /* nocheck:blocked */ - g_debug("Header size 0x%x", start_offset); + g_debug("header size 0x%x", start_offset); if (start_offset > image_size) { g_set_error(error, FWUPD_ERROR, @@ -103,7 +106,10 @@ image_size -= start_offset; g_debug("waking Thunderbolt controller"); - if (!fu_dell_dock_hid_tbt_wake(fu_device_get_proxy(device), &tbt_base_settings, error)) + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (!fu_dell_dock_hid_tbt_wake(proxy, &tbt_base_settings, error)) return FALSE; fu_device_sleep(device, 2000); @@ -112,7 +118,7 @@ guint8 write_size = (image_size - i) > HIDI2C_MAX_WRITE ? HIDI2C_MAX_WRITE : (image_size - i); - if (!fu_dell_dock_hid_tbt_write(fu_device_get_proxy(device), + if (!fu_dell_dock_hid_tbt_write(proxy, i, buffer, write_size, @@ -126,12 +132,13 @@ fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_BUSY); - if (fu_dell_dock_ec_tbt_passive(fu_device_get_parent(device))) { + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + if (fu_dell_dock_ec_tbt_passive(FU_DELL_DOCK_EC(parent))) { g_info("using passive flow for Thunderbolt"); - } else if (!fu_dell_dock_hid_tbt_authenticate(fu_device_get_proxy(device), - &tbt_base_settings, - error)) { - g_prefix_error(error, "failed to authenticate: "); + } else if (!fu_dell_dock_hid_tbt_authenticate(proxy, &tbt_base_settings, error)) { + g_prefix_error_literal(error, "failed to authenticate: "); return FALSE; } @@ -192,13 +199,15 @@ fu_dell_dock_tbt_setup(FuDevice *device, GError **error) { FuDellDockTbt *self = FU_DELL_DOCK_TBT(device); + FuDevice *proxy; FuDevice *parent; const gchar *version; - const gchar *hub_version; /* set version from EC if we know it */ - parent = fu_device_get_parent(device); - version = fu_dell_dock_ec_get_tbt_version(parent); + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + version = fu_dell_dock_ec_get_tbt_version(FU_DELL_DOCK_EC(parent)); if (version != NULL) { fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_PAIR); fu_device_set_version(device, version); @@ -213,9 +222,12 @@ return TRUE; } /* minimum Hub2 version that supports this feature */ - hub_version = fu_device_get_version(fu_device_get_proxy(device)); - if (fu_version_compare(hub_version, self->hub_minimum_version, FWUPD_VERSION_FORMAT_PAIR) < - 0) { + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (fu_version_compare(fu_device_get_version(proxy), + self->hub_minimum_version, + FWUPD_VERSION_FORMAT_PAIR) < 0) { fu_device_set_update_error( device, "Updates over I2C are disabled due to insufficient USB 3.1 G2 hub version"); @@ -228,13 +240,12 @@ static gboolean fu_dell_dock_tbt_probe(FuDevice *device, GError **error) { - FuDevice *parent = fu_device_get_parent(device); + FuDevice *parent; /* sanity check */ - if (parent == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no parent"); + parent = fu_device_get_parent(device, error); + if (parent == NULL) return FALSE; - } fu_device_incorporate(device, parent, FU_DEVICE_INCORPORATE_FLAG_PHYSICAL_ID); fu_device_set_logical_id(FU_DEVICE(device), "tbt"); @@ -249,10 +260,14 @@ fu_dell_dock_tbt_open(FuDevice *device, GError **error) { FuDellDockTbt *self = FU_DELL_DOCK_TBT(device); + FuDevice *proxy; g_return_val_if_fail(self->unlock_target != 0, FALSE); - if (!fu_device_open(fu_device_get_proxy(device), error)) + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (!fu_device_open(proxy, error)) return FALSE; /* adjust to access controller */ @@ -266,12 +281,16 @@ fu_dell_dock_tbt_close(FuDevice *device, GError **error) { FuDellDockTbt *self = FU_DELL_DOCK_TBT(device); + FuDevice *proxy; /* adjust to access controller */ if (!fu_dell_dock_set_power(device, self->unlock_target, FALSE, error)) return FALSE; - return fu_device_close(fu_device_get_proxy(device), error); + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + return fu_device_close(proxy, error); } static void @@ -289,6 +308,7 @@ fu_device_add_protocol(FU_DEVICE(self), "com.intel.thunderbolt"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_HID_DEVICE); } static void diff -Nru fwupd-2.0.8/plugins/dell-dock/fu-dell-dock.rs fwupd-2.0.20/plugins/dell-dock/fu-dell-dock.rs --- fwupd-2.0.8/plugins/dell-dock/fu-dell-dock.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/fu-dell-dock.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,34 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +enum FuDellDockI2cSpeed { + 250K, + 400K, + 800K, +} + +enum FuDellDockMstCmd { + EnableRemoteControl = 0x1, + DisableRemoteControl = 0x2, + Checksum = 0x11, + EraseFlash = 0x14, + Crc16Checksum = 0x17, // Cayenne specific + ActivateFw = 0x18, // Cayenne specific + WriteFlash = 0x20, + WriteMemory = 0x21, + ReadFlash = 0x30, + ReadMemory = 0x31, +} + +enum FuDellDockMstType { + Unknown, + Panamera, + Cayenne, +} + +enum FuDellDockMstBank { + 0, + 1, + Esm, + Cayenne, +} diff -Nru fwupd-2.0.8/plugins/dell-dock/meson.build fwupd-2.0.20/plugins/dell-dock/meson.build --- fwupd-2.0.8/plugins/dell-dock/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-dock/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -3,6 +3,7 @@ plugin_quirks += files('dell-dock.quirk') plugin_builtins += static_library('fu_plugin_dell_dock', + rustgen.process('fu-dell-dock.rs'), sources: [ 'fu-dell-dock-plugin.c', 'fu-dell-dock-common.c', diff -Nru fwupd-2.0.8/plugins/dell-kestrel/README.md fwupd-2.0.20/plugins/dell-kestrel/README.md --- fwupd-2.0.8/plugins/dell-kestrel/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -77,11 +77,31 @@ ## Update Behavior -This dock is a composite device with inclusion of various components, the -update takes particular order to completion. +This dock contains multiple components and updates in a specific sequence. -All updates will be staged on the device until the user manually disconnects -the dock's Type-C cable from the host, at which point they will take effect. +Components don't reboot during firmware writing; updates are staged and managed +activation according to the plugin-defined process during the `composite_cleanup` +phase. The Power Button LED of the dock will flash three times to indicate the +update is completed. + +There are two ways to trigger the staged firmware, and the update behavior can +be adjusted via the plugin configuration `UpdateOnDisconnect`. + +### Update on Connected (UOC) + +When all the component updates are installed and staged to the dock, the dock +reboots the devices to activate the new version immediately. The connected +peripherals on the dock side will be disconnected from the host, and take a few +minutes to finish. + +Starting from fwupd `2.0.17`, or `1.9.33`, this is the default update behavior. + +### Update on Disconnected (UOD) + +Staged updates are activated when the dock's Type-C cable is disconnected from +the host, while the power cable must remain connected to support the update process. +This design ensures devices remain usable during firmware installation in the +user's OS runtime session. ## Plugin Configuration @@ -91,15 +111,8 @@ ### UpdateOnDisconnect The firmware updates are staged to the devices in the dell dock and activated -when the user manually unplugs the dock cable. Default: true. +when the user manually unplugs the dock cable. Default: false. ## Vendor ID Security The vendor ID is set from the USB vendor, in this instance set to `USB:0x413C` - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Crag Wang: @CragW diff -Nru fwupd-2.0.8/plugins/dell-kestrel/dell-kestrel.quirk fwupd-2.0.20/plugins/dell-kestrel/dell-kestrel.quirk --- fwupd-2.0.8/plugins/dell-kestrel/dell-kestrel.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/dell-kestrel.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -45,12 +45,12 @@ Name = USB4v2 controller Summary = Dell Dock USB4v2 Controller Icon = thunderbolt -Vendor = Dell Computer Corp. +Vendor = Dell VendorId = TBT:0x00D4 [TBT-00d4b0a1] Name = USB4 controller Summary = Dell Dock USB4 Controller Icon = thunderbolt -Vendor = Dell Computer Corp. +Vendor = Dell VendorId = TBT:0x00D4 diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-common.h fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-common.h --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -29,3 +29,6 @@ /* device IDs: tbt */ #define DELL_KESTREL_T5_DEVID "TBT-00d4b0a2" #define DELL_KESTREL_T4_DEVID "TBT-00d4b0a1" + +/* max retries */ +#define DELL_KESTREL_MAX_RETRIES 5 diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-dpmux.c fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-dpmux.c --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-dpmux.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-dpmux.c 2026-02-26 11:36:18.000000000 +0000 @@ -23,13 +23,19 @@ static gboolean fu_dell_kestrel_dpmux_setup(FuDevice *device, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); - FuDellDockBaseType dock_type = fu_dell_kestrel_ec_get_dock_type(FU_DELL_KESTREL_EC(proxy)); - FuDellKestrelDockSku dock_sku = fu_dell_kestrel_ec_get_dock_sku(FU_DELL_KESTREL_EC(proxy)); + FuDevice *proxy; + FuDellDockBaseType dock_type; + FuDellKestrelDockSku dock_sku; FuDellKestrelEcDevType dev_type = FU_DELL_KESTREL_EC_DEV_TYPE_DP_MUX; guint32 dpmux_version; g_autofree gchar *devname = NULL; + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + dock_type = fu_dell_kestrel_ec_get_dock_type(FU_DELL_KESTREL_EC(proxy)); + dock_sku = fu_dell_kestrel_ec_get_dock_sku(FU_DELL_KESTREL_EC(proxy)); + /* name */ devname = g_strdup_printf("%s", fu_dell_kestrel_ec_devicetype_to_str(dev_type, 0, 0)); fu_device_set_name(device, devname); @@ -55,7 +61,9 @@ FwupdInstallFlags flags, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; return fu_dell_kestrel_hid_device_write_firmware(FU_DELL_KESTREL_HID_DEVICE(proxy), firmware, progress, @@ -65,7 +73,7 @@ } static void -fu_dell_kestrel_dpmux_set_progress(FuDevice *self, FuProgress *progress) +fu_dell_kestrel_dpmux_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -80,13 +88,15 @@ { fu_device_add_protocol(FU_DEVICE(self), "com.dell.kestrel"); fu_device_add_vendor_id(FU_DEVICE(self), "USB:0x413C"); - fu_device_add_icon(FU_DEVICE(self), "thunderbolt"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_THUNDERBOLT); fu_device_set_summary(FU_DEVICE(self), "Dell Dock Retimer"); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_DELL_KESTREL_EC); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INSTALL_SKIP_VERSION_CHECK); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_EXPLICIT_ORDER); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN); } diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-ec.c fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-ec.c --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-ec.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-ec.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,39 +25,37 @@ FuDellKestrelEcDevSubtype subtype, FuDellKestrelEcDevInstance instance) { - g_autoptr(FuStructDellKestrelDockInfoHeader) hdr = NULL; + g_autoptr(FuStructDellKestrelDockInfoHeader) st = NULL; guint num = 0; - hdr = fu_struct_dell_kestrel_dock_info_get_header(self->dock_info); - num = fu_struct_dell_kestrel_dock_info_header_get_total_devices(hdr); + st = fu_struct_dell_kestrel_dock_info_get_header(self->dock_info); + num = fu_struct_dell_kestrel_dock_info_header_get_total_devices(st); if (num < 1) { - g_debug("no device found in dock info hdr"); + g_debug("no device found in dock info st"); return NULL; } for (guint i = 0; i < num; i++) { - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) comp_dev = NULL; - g_autoptr(FuStructDellKestrelDockInfoEcAddrMap) comp_info = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st_dev = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcAddrMap) st_info = NULL; - comp_dev = fu_struct_dell_kestrel_dock_info_get_devices(self->dock_info, i); - comp_info = - fu_struct_dell_kestrel_dock_info_ec_query_entry_get_ec_addr_map(comp_dev); + st_dev = fu_struct_dell_kestrel_dock_info_get_devices(self->dock_info, i); + st_info = fu_struct_dell_kestrel_dock_info_ec_query_entry_get_ec_addr_map(st_dev); if (dev_type != - fu_struct_dell_kestrel_dock_info_ec_addr_map_get_device_type(comp_info)) + fu_struct_dell_kestrel_dock_info_ec_addr_map_get_device_type(st_info)) continue; if (subtype != 0 && - subtype != fu_struct_dell_kestrel_dock_info_ec_addr_map_get_subtype(comp_info)) + subtype != fu_struct_dell_kestrel_dock_info_ec_addr_map_get_subtype(st_info)) continue; /* vary by instance index */ if (dev_type == FU_DELL_KESTREL_EC_DEV_TYPE_PD && - instance != - fu_struct_dell_kestrel_dock_info_ec_addr_map_get_instance(comp_info)) + instance != fu_struct_dell_kestrel_dock_info_ec_addr_map_get_instance(st_info)) continue; - return g_steal_pointer(&comp_dev); + return g_steal_pointer(&st_dev); } return NULL; } @@ -68,9 +66,21 @@ FuDellKestrelEcDevSubtype subtype, FuDellKestrelEcDevInstance instance) { - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) dev_entry = NULL; - dev_entry = fu_dell_kestrel_ec_dev_entry(self, dev_type, subtype, instance); - return dev_entry != NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st = NULL; + st = fu_dell_kestrel_ec_dev_entry(self, dev_type, subtype, instance); + return st != NULL; +} + +gboolean +fu_dell_kestrel_ec_is_chunk_supported(FuDellKestrelEc *self, FuDellKestrelEcDevType dev_type) +{ + if (dev_type == FU_DELL_KESTREL_EC_DEV_TYPE_PD) { + guint8 chunk_support = 0; + + chunk_support = fu_struct_dell_kestrel_dock_data_get_chunk_support(self->dock_data); + return (chunk_support & FU_DELL_KESTREL_DOCK_DATA_CHUNK_SUPPORT_BITMAP_PD); + } + return TRUE; } const gchar * @@ -149,9 +159,9 @@ if (!fu_dell_kestrel_hid_device_i2c_read(FU_DELL_KESTREL_HID_DEVICE(self), cmd, res, - 800, + 100, error)) { - g_prefix_error(error, "read over HID-I2C failed: "); + g_prefix_error_literal(error, "read over HID-I2C failed: "); return FALSE; } return TRUE; @@ -163,7 +173,7 @@ g_return_val_if_fail(buf->len > 1, FALSE); if (!fu_dell_kestrel_hid_device_i2c_write(FU_DELL_KESTREL_HID_DEVICE(self), buf, error)) { - g_prefix_error(error, "write over HID-I2C failed: "); + g_prefix_error_literal(error, "write over HID-I2C failed: "); return FALSE; } @@ -201,10 +211,10 @@ GError **error) { g_autoptr(FuDellKestrelPd) pd_dev = NULL; - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) dev_entry = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st = NULL; - dev_entry = fu_dell_kestrel_ec_dev_entry(self, dev_type, subtype, instance); - if (dev_entry == NULL) + st = fu_dell_kestrel_ec_dev_entry(self, dev_type, subtype, instance); + if (st == NULL) return TRUE; pd_dev = fu_dell_kestrel_pd_new(FU_DEVICE(self), subtype, instance); @@ -214,11 +224,11 @@ static gboolean fu_dell_kestrel_ec_probe_subcomponents(FuDellKestrelEc *self, GError **error) { - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) dev_entry_ilan = NULL; - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) dev_entry_dpmux = NULL; - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) dev_entry_wt = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st_ilan = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st_dpmux = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st_wt = NULL; - /* Package */ + /* package */ if (!fu_dell_kestrel_ec_probe_package(self, error)) return FALSE; @@ -247,9 +257,8 @@ return FALSE; /* DP MUX | Retimer */ - dev_entry_dpmux = - fu_dell_kestrel_ec_dev_entry(self, FU_DELL_KESTREL_EC_DEV_TYPE_DP_MUX, 0, 0); - if (dev_entry_dpmux != NULL) { + st_dpmux = fu_dell_kestrel_ec_dev_entry(self, FU_DELL_KESTREL_EC_DEV_TYPE_DP_MUX, 0, 0); + if (st_dpmux != NULL) { g_autoptr(FuDellKestrelDpmux) dpmux_device = NULL; dpmux_device = fu_dell_kestrel_dpmux_new(FU_DEVICE(self)); @@ -258,8 +267,8 @@ } /* WT PD */ - dev_entry_wt = fu_dell_kestrel_ec_dev_entry(self, FU_DELL_KESTREL_EC_DEV_TYPE_WTPD, 0, 0); - if (dev_entry_wt != NULL) { + st_wt = fu_dell_kestrel_ec_dev_entry(self, FU_DELL_KESTREL_EC_DEV_TYPE_WTPD, 0, 0); + if (st_wt != NULL) { g_autoptr(FuDellKestrelWtpd) wt_dev = NULL; wt_dev = fu_dell_kestrel_wtpd_new(FU_DEVICE(self)); @@ -268,8 +277,8 @@ } /* LAN */ - dev_entry_ilan = fu_dell_kestrel_ec_dev_entry(self, FU_DELL_KESTREL_EC_DEV_TYPE_LAN, 0, 0); - if (dev_entry_ilan != NULL) { + st_ilan = fu_dell_kestrel_ec_dev_entry(self, FU_DELL_KESTREL_EC_DEV_TYPE_LAN, 0, 0); + if (st_ilan != NULL) { g_autoptr(FuDellKestrelIlan) ilan_device = NULL; ilan_device = fu_dell_kestrel_ilan_new(FU_DEVICE(self)); @@ -294,7 +303,10 @@ /* don't change error type, the plugin ignores it */ if (dock_type != FU_DELL_DOCK_BASE_TYPE_KESTREL) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "No valid dock was found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no valid dock was found"); return FALSE; } @@ -321,7 +333,7 @@ /* expect response 1 byte */ if (!fu_dell_kestrel_ec_read(self, cmd, res, error)) { - g_prefix_error(error, "Failed to query dock type: "); + g_prefix_error_literal(error, "failed to query dock type: "); return FALSE; } @@ -335,14 +347,15 @@ fu_dell_kestrel_ec_dock_info_cmd(FuDellKestrelEc *self, GError **error) { FuDellKestrelEcCmd cmd = FU_DELL_KESTREL_EC_CMD_GET_DOCK_INFO; - g_autoptr(GByteArray) res = fu_struct_dell_kestrel_dock_info_new(); + g_autoptr(FuStructDellKestrelDockInfo) st_res = fu_struct_dell_kestrel_dock_info_new(); /* get dock info over HID */ - if (!fu_dell_kestrel_ec_read(self, cmd, res, error)) { - g_prefix_error(error, "Failed to query dock info: "); + if (!fu_dell_kestrel_ec_read(self, cmd, st_res->buf, error)) { + g_prefix_error_literal(error, "failed to query dock info: "); return FALSE; } - self->dock_info = fu_struct_dell_kestrel_dock_info_parse(res->data, res->len, 0, error); + self->dock_info = + fu_struct_dell_kestrel_dock_info_parse(st_res->buf->data, st_res->buf->len, 0, error); return TRUE; } @@ -372,17 +385,18 @@ fu_dell_kestrel_ec_dock_data_cmd(FuDellKestrelEc *self, GError **error) { FuDellKestrelEcCmd cmd = FU_DELL_KESTREL_EC_CMD_GET_DOCK_DATA; - g_autoptr(GByteArray) res = fu_struct_dell_kestrel_dock_data_new(); + g_autoptr(FuStructDellKestrelDockData) st = fu_struct_dell_kestrel_dock_data_new(); /* get dock data over HID */ - if (!fu_dell_kestrel_ec_read(self, cmd, res, error)) { - g_prefix_error(error, "Failed to query dock data: "); + if (!fu_dell_kestrel_ec_read(self, cmd, st->buf, error)) { + g_prefix_error_literal(error, "failed to query dock data: "); return FALSE; } if (self->dock_data != NULL) fu_struct_dell_kestrel_dock_data_unref(self->dock_data); - self->dock_data = fu_struct_dell_kestrel_dock_data_parse(res->data, res->len, 0, error); + self->dock_data = + fu_struct_dell_kestrel_dock_data_parse(st->buf->data, st->buf->len, 0, error); if (self->dock_data == NULL) return FALSE; if (!fu_dell_kestrel_ec_dock_data_extract(self, error)) @@ -391,9 +405,8 @@ } gboolean -fu_dell_kestrel_ec_is_dock_ready4update(FuDevice *device, GError **error) +fu_dell_kestrel_ec_is_dock_ready_for_update(FuDellKestrelEc *self, GError **error) { - FuDellKestrelEc *self = FU_DELL_KESTREL_EC(device); guint16 bitmask_fw_update_pending = 1 << 8; guint32 dock_status = 0; @@ -413,23 +426,40 @@ return TRUE; } +static gboolean +fu_dell_kestrel_ec_is_new_ownership_cmd(FuDellKestrelEc *self) +{ + FuDevice *device = FU_DEVICE(self); + const gchar *version = fu_device_get_version(device); + FwupdVersionFormat fmt = fu_device_get_version_format(device); + + if (fu_version_compare(version, "01.00.00.00", fmt) >= 0) { + if (fu_version_compare(version, "01.00.05.02", fmt) >= 0) + return TRUE; + + return FALSE; + } + return fu_version_compare(version, "00.00.34.00", fmt) >= 0; +} + gboolean fu_dell_kestrel_ec_own_dock(FuDellKestrelEc *self, gboolean lock, GError **error) { - g_autoptr(GByteArray) st_req = fu_struct_dell_kestrel_ec_databytes_new(); + guint16 bitmask = 0x0; + g_autoptr(FuStructDellKestrelEcDatabytes) st_req = + fu_struct_dell_kestrel_ec_databytes_new(); g_autoptr(GError) error_local = NULL; g_autofree gchar *msg = NULL; - guint16 bitmask = 0x0; fu_struct_dell_kestrel_ec_databytes_set_cmd(st_req, FU_DELL_KESTREL_EC_CMD_SET_MODIFY_LOCK); fu_struct_dell_kestrel_ec_databytes_set_data_sz(st_req, 2); if (lock) { msg = g_strdup("own the dock"); - bitmask = 0xFFFF; + bitmask = fu_dell_kestrel_ec_is_new_ownership_cmd(self) ? 0x10CC : 0xFFFF; } else { - msg = g_strdup("relesae the dock"); - bitmask = 0x0000; + msg = g_strdup("release the dock"); + bitmask = fu_dell_kestrel_ec_is_new_ownership_cmd(self) ? 0xC001 : 0x0000; } if (!fu_struct_dell_kestrel_ec_databytes_set_data(st_req, (const guint8 *)&bitmask, @@ -438,12 +468,14 @@ return FALSE; fu_device_sleep(FU_DEVICE(self), 1000); - if (!fu_dell_kestrel_ec_write(self, st_req, &error_local)) { + if (!fu_dell_kestrel_ec_write(self, st_req->buf, &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) g_debug("ignoring: %s", error_local->message); else { - g_propagate_error(error, g_steal_pointer(&error_local)); - g_prefix_error(error, "failed to %s", msg); + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to %s: ", + msg); return FALSE; } } @@ -456,7 +488,8 @@ fu_dell_kestrel_ec_run_passive_update(FuDellKestrelEc *self, GError **error) { guint max_tries = 2; - g_autoptr(GByteArray) st_req = fu_struct_dell_kestrel_ec_databytes_new(); + g_autoptr(FuStructDellKestrelEcDatabytes) st_req = + fu_struct_dell_kestrel_ec_databytes_new(); const guint8 bitmap = 0x07; /* ec included in cmd, set bit2 in data for tbt */ @@ -470,8 +503,8 @@ for (guint i = 1; i <= max_tries; i++) { g_debug("register passive update (uod) flow (%u/%u)", i, max_tries); - if (!fu_dell_kestrel_ec_write(self, st_req, error)) { - g_prefix_error(error, "failed to register uod flow: "); + if (!fu_dell_kestrel_ec_write(self, st_req->buf, error)) { + g_prefix_error_literal(error, "failed to register uod flow: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), 100); @@ -480,25 +513,25 @@ } static gboolean -fu_dell_kestrel_ec_set_dock_sku(FuDellKestrelEc *self, GError **error) +fu_dell_kestrel_ec_ensure_dock_sku(FuDellKestrelEc *self, GError **error) { if (self->base_type == FU_DELL_DOCK_BASE_TYPE_KESTREL) { - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) dev_entry = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st = NULL; /* TBT type yet available, do workaround */ - dev_entry = fu_dell_kestrel_ec_dev_entry(self, - FU_DELL_KESTREL_EC_DEV_TYPE_TBT, - FU_DELL_KESTREL_EC_DEV_SUBTYPE_BR, - 0); - if (dev_entry != NULL) { + st = fu_dell_kestrel_ec_dev_entry(self, + FU_DELL_KESTREL_EC_DEV_TYPE_TBT, + FU_DELL_KESTREL_EC_DEV_SUBTYPE_BR, + 0); + if (st != NULL) { self->base_sku = FU_DELL_KESTREL_DOCK_SKU_T5; return TRUE; } - dev_entry = fu_dell_kestrel_ec_dev_entry(self, - FU_DELL_KESTREL_EC_DEV_TYPE_TBT, - FU_DELL_KESTREL_EC_DEV_SUBTYPE_GR, - 0); - if (dev_entry != NULL) { + st = fu_dell_kestrel_ec_dev_entry(self, + FU_DELL_KESTREL_EC_DEV_TYPE_TBT, + FU_DELL_KESTREL_EC_DEV_SUBTYPE_GR, + 0); + if (st != NULL) { self->base_sku = FU_DELL_KESTREL_DOCK_SKU_T4; return TRUE; } @@ -519,72 +552,66 @@ FuDellKestrelEcDevInstance instance) { FuDellKestrelEcDevType dev_type = FU_DELL_KESTREL_EC_DEV_TYPE_PD; - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) dev_entry = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st = NULL; - dev_entry = fu_dell_kestrel_ec_dev_entry(self, dev_type, subtype, instance); - return (dev_entry == NULL) - ? 0 - : fu_struct_dell_kestrel_dock_info_ec_query_entry_get_version_32(dev_entry); + st = fu_dell_kestrel_ec_dev_entry(self, dev_type, subtype, instance); + return (st == NULL) ? 0 + : fu_struct_dell_kestrel_dock_info_ec_query_entry_get_version_32(st); } guint32 fu_dell_kestrel_ec_get_ilan_version(FuDellKestrelEc *self) { FuDellKestrelEcDevType dev_type = FU_DELL_KESTREL_EC_DEV_TYPE_LAN; - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) dev_entry = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st = NULL; - dev_entry = fu_dell_kestrel_ec_dev_entry(self, dev_type, 0, 0); - return (dev_entry == NULL) - ? 0 - : fu_struct_dell_kestrel_dock_info_ec_query_entry_get_version_32(dev_entry); + st = fu_dell_kestrel_ec_dev_entry(self, dev_type, 0, 0); + return (st == NULL) ? 0 + : fu_struct_dell_kestrel_dock_info_ec_query_entry_get_version_32(st); } guint32 fu_dell_kestrel_ec_get_wtpd_version(FuDellKestrelEc *self) { FuDellKestrelEcDevType dev_type = FU_DELL_KESTREL_EC_DEV_TYPE_WTPD; - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) dev_entry = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st = NULL; - dev_entry = fu_dell_kestrel_ec_dev_entry(self, dev_type, 0, 0); - return (dev_entry == NULL) - ? 0 - : fu_struct_dell_kestrel_dock_info_ec_query_entry_get_version_32(dev_entry); + st = fu_dell_kestrel_ec_dev_entry(self, dev_type, 0, 0); + return (st == NULL) ? 0 + : fu_struct_dell_kestrel_dock_info_ec_query_entry_get_version_32(st); } guint32 fu_dell_kestrel_ec_get_dpmux_version(FuDellKestrelEc *self) { FuDellKestrelEcDevType dev_type = FU_DELL_KESTREL_EC_DEV_TYPE_DP_MUX; - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) dev_entry = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st = NULL; - dev_entry = fu_dell_kestrel_ec_dev_entry(self, dev_type, 0, 0); - return (dev_entry == NULL) - ? 0 - : fu_struct_dell_kestrel_dock_info_ec_query_entry_get_version_32(dev_entry); + st = fu_dell_kestrel_ec_dev_entry(self, dev_type, 0, 0); + return (st == NULL) ? 0 + : fu_struct_dell_kestrel_dock_info_ec_query_entry_get_version_32(st); } guint32 fu_dell_kestrel_ec_get_rmm_version(FuDellKestrelEc *self) { FuDellKestrelEcDevType dev_type = FU_DELL_KESTREL_EC_DEV_TYPE_RMM; - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) dev_entry = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st = NULL; - dev_entry = fu_dell_kestrel_ec_dev_entry(self, dev_type, 0, 0); - return (dev_entry == NULL) - ? 0 - : fu_struct_dell_kestrel_dock_info_ec_query_entry_get_version_32(dev_entry); + st = fu_dell_kestrel_ec_dev_entry(self, dev_type, 0, 0); + return (st == NULL) ? 0 + : fu_struct_dell_kestrel_dock_info_ec_query_entry_get_version_32(st); } static guint32 fu_dell_kestrel_ec_get_ec_version(FuDellKestrelEc *self) { FuDellKestrelEcDevType dev_type = FU_DELL_KESTREL_EC_DEV_TYPE_MAIN_EC; - g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) dev_entry = NULL; + g_autoptr(FuStructDellKestrelDockInfoEcQueryEntry) st = NULL; - dev_entry = fu_dell_kestrel_ec_dev_entry(self, dev_type, 0, 0); - return (dev_entry == NULL) - ? 0 - : fu_struct_dell_kestrel_dock_info_ec_query_entry_get_version_32(dev_entry); + st = fu_dell_kestrel_ec_dev_entry(self, dev_type, 0, 0); + return (st == NULL) ? 0 + : fu_struct_dell_kestrel_dock_info_ec_query_entry_get_version_32(st); } guint32 @@ -596,7 +623,8 @@ gboolean fu_dell_kestrel_ec_commit_package(FuDellKestrelEc *self, GInputStream *stream, GError **error) { - g_autoptr(GByteArray) st_req = fu_struct_dell_kestrel_ec_databytes_new(); + g_autoptr(FuStructDellKestrelEcDatabytes) st_req = + fu_struct_dell_kestrel_ec_databytes_new(); g_autoptr(GByteArray) buf = NULL; gsize streamsz = 0; @@ -608,7 +636,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "Invalid package size %" G_GSIZE_FORMAT, + "invalid package size %" G_GSIZE_FORMAT, streamsz); return FALSE; } @@ -625,10 +653,10 @@ if (!fu_struct_dell_kestrel_ec_databytes_set_data(st_req, buf->data, buf->len, error)) return FALSE; - fu_dump_raw(G_LOG_DOMAIN, "->PACKAGE", st_req->data, st_req->len); + fu_dump_raw(G_LOG_DOMAIN, "->PACKAGE", st_req->buf->data, st_req->buf->len); - if (!fu_dell_kestrel_ec_write(self, st_req, error)) { - g_prefix_error(error, "Failed to commit package: "); + if (!fu_dell_kestrel_ec_write(self, st_req->buf, error)) { + g_prefix_error_literal(error, "failed to commit package: "); return FALSE; } return TRUE; @@ -664,7 +692,7 @@ return FALSE; /* set internal dock sku, must after dock info */ - if (!fu_dell_kestrel_ec_set_dock_sku(self, error)) + if (!fu_dell_kestrel_ec_ensure_dock_sku(self, error)) return FALSE; return TRUE; @@ -678,11 +706,11 @@ /* if query looks bad, wait a few seconds and retry */ if (!fu_device_retry_full(FU_DEVICE(self), fu_dell_kestrel_ec_query_cb, - 10, - 2000, + DELL_KESTREL_MAX_RETRIES, + 500, NULL, error)) { - g_prefix_error(error, "failed to query dock ec: "); + g_prefix_error_literal(error, "failed to query dock ec: "); return FALSE; } return TRUE; @@ -703,8 +731,13 @@ return FALSE; /* if query looks bad, wait a few seconds and retry */ - if (!fu_device_retry_full(device, fu_dell_kestrel_ec_query_cb, 10, 2000, NULL, error)) { - g_prefix_error(error, "failed to query dock ec: "); + if (!fu_device_retry_full(device, + fu_dell_kestrel_ec_query_cb, + DELL_KESTREL_MAX_RETRIES, + 500, + NULL, + error)) { + g_prefix_error_literal(error, "failed to query dock ec: "); return FALSE; } @@ -745,7 +778,7 @@ } static void -fu_dell_kestrel_ec_set_progress(FuDevice *self, FuProgress *progress) +fu_dell_kestrel_ec_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -760,7 +793,7 @@ { fu_device_add_protocol(FU_DEVICE(self), "com.dell.kestrel"); fu_device_add_vendor_id(FU_DEVICE(self), "USB:0x413C"); - fu_device_add_icon(FU_DEVICE(self), "dock-usb"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_DOCK_USB); fu_device_set_summary(FU_DEVICE(self), "Dell Dock EC"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); @@ -789,13 +822,15 @@ } FuDellKestrelEc * -fu_dell_kestrel_ec_new(FuDevice *device, gboolean uod) +fu_dell_kestrel_ec_new(FuUsbDevice *usb_device, gboolean uod) { FuDellKestrelEc *self = NULL; - FuContext *ctx = fu_device_get_context(device); + FuContext *ctx = fu_device_get_context(FU_DEVICE(usb_device)); self = g_object_new(FU_TYPE_DELL_KESTREL_EC, "context", ctx, NULL); - fu_device_incorporate(FU_DEVICE(self), device, FU_DEVICE_INCORPORATE_FLAG_ALL); + fu_device_incorporate(FU_DEVICE(self), + FU_DEVICE(usb_device), + FU_DEVICE_INCORPORATE_FLAG_ALL); fu_device_set_logical_id(FU_DEVICE(self), "ec"); if (uod) fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-ec.h fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-ec.h --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-ec.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-ec.h 2026-02-26 11:36:18.000000000 +0000 @@ -18,7 +18,7 @@ FuDellKestrelHidDevice) FuDellKestrelEc * -fu_dell_kestrel_ec_new(FuDevice *device, gboolean uod); +fu_dell_kestrel_ec_new(FuUsbDevice *usb_device, gboolean uod); void fu_dell_kestrel_ec_enable_tbt_passive(FuDellKestrelEc *self); gboolean @@ -50,9 +50,11 @@ FuDellKestrelEcDevSubtype subtype, FuDellKestrelEcDevInstance instance); gboolean -fu_dell_kestrel_ec_is_dock_ready4update(FuDevice *device, GError **error); +fu_dell_kestrel_ec_is_dock_ready_for_update(FuDellKestrelEc *self, GError **error); gboolean fu_dell_kestrel_ec_is_dev_present(FuDellKestrelEc *self, FuDellKestrelEcDevType dev_type, FuDellKestrelEcDevSubtype subtype, FuDellKestrelEcDevInstance instance); +gboolean +fu_dell_kestrel_ec_is_chunk_supported(FuDellKestrelEc *self, FuDellKestrelEcDevType dev_type); diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-ec.rs fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-ec.rs --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-ec.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-ec.rs 2026-02-26 11:36:18.000000000 +0000 @@ -76,6 +76,11 @@ T5, } +enum FuDellKestrelDockDataChunkSupportBitmap { + Ec = 1 << 0, + Pd = 1 << 1, +} + #[repr(C, packed)] #[derive(New, Getters, Parse)] struct FuStructDellKestrelDockData { @@ -94,7 +99,7 @@ reserved: u32le, reserved: u32le, reserved: u32le, - reserved: u8, + chunk_support: u8, dock_status: u32le, reserved: u16le, reserved: u16le, diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-hid-device.c fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-hid-device.c --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -7,24 +7,22 @@ #include "config.h" +#include "fu-dell-kestrel-ec.h" #include "fu-dell-kestrel-hid-device.h" #include "fu-dell-kestrel-hid-struct.h" G_DEFINE_TYPE(FuDellKestrelHidDevice, fu_dell_kestrel_hid_device, FU_TYPE_HID_DEVICE) /* Used for EC HID communication */ -#define FU_DELL_KESTREL_HID_TIMEOUT 300 +#define FU_DELL_KESTREL_HID_TIMEOUT 3000 #define FU_DELL_KESTREL_HID_CMD_FWUPDATE 0xAB #define FU_DELL_KESTREL_HID_EXT_FWUPDATE 0x80 #define FU_DELL_KESTREL_HID_SUBCMD_FWUPDATE 0x00 -#define FU_DELL_KESTREL_HID_DEV_EC_CHUNK_SZ 160000 -#define FU_DELL_KESTREL_HID_DEV_PD_CHUNK_SZ 190000 -#define FU_DELL_KESTREL_HID_DEV_ANY_CHUNK_SZ 180000 +#define FU_DELL_KESTREL_HID_DEV_ANY_CHUNK_SZ 120000 #define FU_DELL_KESTREL_HID_DEV_NO_CHUNK_SZ G_MAXSIZE #define FU_DELL_KESTREL_HID_DATA_PAGE_SZ 192 #define FU_DELL_KESTREL_HID_RESPONSE_LENGTH 0x03 #define FU_DELL_KESTREL_HID_I2C_ADDRESS 0xec -#define FU_DELL_KESTREL_HID_MAX_RETRIES 8 #define FU_DELL_KESTREL_HID_I2C_MAX_READ 192 #define FU_DELL_KESTREL_HID_I2C_MAX_WRITE 128 @@ -50,60 +48,62 @@ FuDellKestrelEcDevType dev_type, guint8 dev_identifier) { - g_autoptr(FuStructDellKestrelHidFwUpdatePkg) fwbuf = + g_autoptr(FuStructDellKestrelHidFwUpdatePkg) st_fwbuf = fu_struct_dell_kestrel_hid_fw_update_pkg_new(); gsize chk_datasz = fu_chunk_get_data_sz(chk); /* header */ - fu_struct_dell_kestrel_hid_fw_update_pkg_set_cmd(fwbuf, FU_DELL_KESTREL_HID_CMD_FWUPDATE); - fu_struct_dell_kestrel_hid_fw_update_pkg_set_ext(fwbuf, FU_DELL_KESTREL_HID_EXT_FWUPDATE); + fu_struct_dell_kestrel_hid_fw_update_pkg_set_cmd(st_fwbuf, + FU_DELL_KESTREL_HID_CMD_FWUPDATE); + fu_struct_dell_kestrel_hid_fw_update_pkg_set_ext(st_fwbuf, + FU_DELL_KESTREL_HID_EXT_FWUPDATE); fu_struct_dell_kestrel_hid_fw_update_pkg_set_chunk_sz( - fwbuf, - 7 + chk_datasz); // 7 = sizeof(command) + st_fwbuf, + 7 + chk_datasz); /* 7 = sizeof(command) */ /* command */ - fu_struct_dell_kestrel_hid_fw_update_pkg_set_sub_cmd(fwbuf, + fu_struct_dell_kestrel_hid_fw_update_pkg_set_sub_cmd(st_fwbuf, FU_DELL_KESTREL_HID_SUBCMD_FWUPDATE); - fu_struct_dell_kestrel_hid_fw_update_pkg_set_dev_type(fwbuf, dev_type); - fu_struct_dell_kestrel_hid_fw_update_pkg_set_dev_identifier(fwbuf, dev_identifier); - fu_struct_dell_kestrel_hid_fw_update_pkg_set_fw_sz(fwbuf, fw_size); + fu_struct_dell_kestrel_hid_fw_update_pkg_set_dev_type(st_fwbuf, dev_type); + fu_struct_dell_kestrel_hid_fw_update_pkg_set_dev_identifier(st_fwbuf, dev_identifier); + fu_struct_dell_kestrel_hid_fw_update_pkg_set_fw_sz(st_fwbuf, fw_size); /* data */ - fu_byte_array_append_bytes(fwbuf, fu_chunk_get_bytes(chk)); + fu_byte_array_append_bytes(st_fwbuf->buf, fu_chunk_get_bytes(chk)); - return g_bytes_new(fwbuf->data, fwbuf->len); + return fu_struct_dell_kestrel_hid_fw_update_pkg_to_bytes(st_fwbuf); } static gboolean -fu_dell_kestrel_hid_device_hid_set_report_cb(FuDevice *self, gpointer user_data, GError **error) +fu_dell_kestrel_hid_device_hid_set_report_cb(FuDevice *device, gpointer user_data, GError **error) { - guint8 *outbuffer = (guint8 *)user_data; - return fu_hid_device_set_report(FU_HID_DEVICE(self), + GByteArray *buf = (GByteArray *)user_data; + return fu_hid_device_set_report(FU_HID_DEVICE(device), 0x0, - outbuffer, - 192, - FU_DELL_KESTREL_HID_TIMEOUT * 3, + buf->data, + buf->len, + FU_DELL_KESTREL_HID_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error); } static gboolean fu_dell_kestrel_hid_device_hid_set_report(FuDellKestrelHidDevice *self, - guint8 *outbuffer, + GByteArray *buf, GError **error) { return fu_device_retry(FU_DEVICE(self), fu_dell_kestrel_hid_device_hid_set_report_cb, - FU_DELL_KESTREL_HID_MAX_RETRIES, - outbuffer, + DELL_KESTREL_MAX_RETRIES, + buf, error); } static gboolean -fu_dell_kestrel_hid_device_get_report_cb(FuDevice *self, gpointer user_data, GError **error) +fu_dell_kestrel_hid_device_get_report_cb(FuDevice *device, gpointer user_data, GError **error) { guint8 *inbuffer = (guint8 *)user_data; - return fu_hid_device_get_report(FU_HID_DEVICE(self), + return fu_hid_device_get_report(FU_HID_DEVICE(device), 0x0, inbuffer, 192, @@ -119,7 +119,7 @@ { return fu_device_retry_full(FU_DEVICE(self), fu_dell_kestrel_hid_device_get_report_cb, - FU_DELL_KESTREL_HID_MAX_RETRIES, + DELL_KESTREL_MAX_RETRIES, 2000, inbuffer, error); @@ -130,21 +130,20 @@ GByteArray *cmd_buf, GError **error) { - g_autoptr(FuStructDellKestrelHidCmdBuffer) buf = - fu_struct_dell_kestrel_hid_cmd_buffer_new(); + g_autoptr(FuStructDellKestrelHidCmdBuffer) st = fu_struct_dell_kestrel_hid_cmd_buffer_new(); g_return_val_if_fail(cmd_buf->len <= FU_DELL_KESTREL_HID_I2C_MAX_WRITE, FALSE); - fu_struct_dell_kestrel_hid_cmd_buffer_set_cmd(buf, FU_DELL_KESTREL_HID_CMD_WRITE_DATA); - fu_struct_dell_kestrel_hid_cmd_buffer_set_ext(buf, FU_DELL_KESTREL_HID_CMD_EXT_I2C_WRITE); - fu_struct_dell_kestrel_hid_cmd_buffer_set_dwregaddr(buf, 0x00); - fu_struct_dell_kestrel_hid_cmd_buffer_set_bufferlen(buf, cmd_buf->len); - if (!fu_struct_dell_kestrel_hid_cmd_buffer_set_databytes(buf, + fu_struct_dell_kestrel_hid_cmd_buffer_set_cmd(st, FU_DELL_KESTREL_HID_CMD_WRITE_DATA); + fu_struct_dell_kestrel_hid_cmd_buffer_set_ext(st, FU_DELL_KESTREL_HID_CMD_EXT_I2C_WRITE); + fu_struct_dell_kestrel_hid_cmd_buffer_set_dwregaddr(st, 0x00); + fu_struct_dell_kestrel_hid_cmd_buffer_set_bufferlen(st, cmd_buf->len); + if (!fu_struct_dell_kestrel_hid_cmd_buffer_set_databytes(st, cmd_buf->data, cmd_buf->len, error)) return FALSE; - return fu_dell_kestrel_hid_device_hid_set_report(self, buf->data, error); + return fu_dell_kestrel_hid_device_hid_set_report(self, st->buf, error); } gboolean @@ -154,15 +153,14 @@ guint delayms, GError **error) { - g_autoptr(FuStructDellKestrelHidCmdBuffer) buf = - fu_struct_dell_kestrel_hid_cmd_buffer_new(); + g_autoptr(FuStructDellKestrelHidCmdBuffer) st = fu_struct_dell_kestrel_hid_cmd_buffer_new(); guint8 inbuf[FU_DELL_KESTREL_HID_I2C_MAX_READ] = {0xff}; - fu_struct_dell_kestrel_hid_cmd_buffer_set_cmd(buf, FU_DELL_KESTREL_HID_CMD_WRITE_DATA); - fu_struct_dell_kestrel_hid_cmd_buffer_set_ext(buf, FU_DELL_KESTREL_HID_CMD_EXT_I2C_READ); - fu_struct_dell_kestrel_hid_cmd_buffer_set_dwregaddr(buf, cmd); - fu_struct_dell_kestrel_hid_cmd_buffer_set_bufferlen(buf, res->len + 1); - if (!fu_dell_kestrel_hid_device_hid_set_report(self, buf->data, error)) + fu_struct_dell_kestrel_hid_cmd_buffer_set_cmd(st, FU_DELL_KESTREL_HID_CMD_WRITE_DATA); + fu_struct_dell_kestrel_hid_cmd_buffer_set_ext(st, FU_DELL_KESTREL_HID_CMD_EXT_I2C_READ); + fu_struct_dell_kestrel_hid_cmd_buffer_set_dwregaddr(st, cmd); + fu_struct_dell_kestrel_hid_cmd_buffer_set_bufferlen(st, res->len + 1); + if (!fu_dell_kestrel_hid_device_hid_set_report(self, st->buf, error)) return FALSE; if (delayms > 0) @@ -192,19 +190,17 @@ } static gsize -fu_dell_kestrel_hid_device_get_chunk_size(FuDellKestrelEcDevType dev_type) +fu_dell_kestrel_hid_device_get_chunk_size(FuDellKestrelHidDevice *self, + FuDellKestrelEcDevType dev_type) { - /* return the max chunk size in bytes */ - switch (dev_type) { - case FU_DELL_KESTREL_EC_DEV_TYPE_MAIN_EC: - return FU_DELL_KESTREL_HID_DEV_EC_CHUNK_SZ; - case FU_DELL_KESTREL_EC_DEV_TYPE_PD: - return FU_DELL_KESTREL_HID_DEV_PD_CHUNK_SZ; - case FU_DELL_KESTREL_EC_DEV_TYPE_RMM: + if (dev_type == FU_DELL_KESTREL_EC_DEV_TYPE_RMM) return FU_DELL_KESTREL_HID_DEV_NO_CHUNK_SZ; - default: + + if (FU_IS_DELL_KESTREL_EC(self) && + fu_dell_kestrel_ec_is_chunk_supported(FU_DELL_KESTREL_EC(self), dev_type)) return FU_DELL_KESTREL_HID_DEV_ANY_CHUNK_SZ; - } + + return FU_DELL_KESTREL_HID_DEV_NO_CHUNK_SZ; } static gboolean @@ -213,6 +209,7 @@ FuProgress *progress, FuDellKestrelEcDevType dev_type, guint chunk_idx, + guint chunks_num, GError **error) { /* progress */ @@ -229,8 +226,9 @@ if (page == NULL) return FALSE; - g_debug("sending chunk: %u, page: %u/%u.", + g_debug("sending chunk: %u/%u, page: %u/%u", chunk_idx, + chunks_num - 1, j, fu_chunk_array_length(pages) - 1); @@ -250,17 +248,23 @@ page_ack_time, &error_local)) { /* A buggy device may fail to send an acknowledgment receipt - after the last page write, resulting in a timeout error. - - This is a known issue so waive it for now. + after the last page write, resulting in a timeout error or a pipe + error on reading. This is a known issue so waive it for now. */ if (dev_type == FU_DELL_KESTREL_EC_DEV_TYPE_LAN && - j == fu_chunk_array_length(pages) - 1 && - g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT)) { - g_debug("ignored error: %s", error_local->message); - fu_progress_step_done(progress); - continue; + j == fu_chunk_array_length(pages) - 1) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_READ) || + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_BUSY) || + g_error_matches(error_local, + FWUPD_ERROR, + FWUPD_ERROR_TIMED_OUT)) { + g_debug("ignored error: %s", error_local->message); + fu_progress_step_done(progress); + continue; + } } + + /* propagate error as usual */ g_propagate_prefixed_error(error, g_steal_pointer(&error_local), "%s failed to write page: ", @@ -312,7 +316,7 @@ GError **error) { gsize fw_sz = 0; - gsize chunk_sz = fu_dell_kestrel_hid_device_get_chunk_size(dev_type); + gsize chunk_sz = fu_dell_kestrel_hid_device_get_chunk_size(self, dev_type); guint chunk_delay = fu_dell_kestrel_hid_device_get_chunk_delaytime(dev_type); g_autoptr(GBytes) fw = NULL; g_autoptr(FuChunkArray) chunks = NULL; @@ -370,6 +374,7 @@ fu_progress_get_child(progress), dev_type, i, + fu_chunk_array_length(chunks), error)) return FALSE; diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-hid.rs fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-hid.rs --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-hid.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-hid.rs 2026-02-26 11:36:18.000000000 +0000 @@ -19,11 +19,11 @@ dwregaddr: u32le, bufferlen: u16le, parameters: [u8; 3] = 0xEC0180, // addr, length, speed - extended_cmdarea: [u8; 53] = 0x00, - databytes: [u8; 192] = 0x00, + extended_cmdarea: [u8; 53], + databytes: [u8; 128], } -#[derive(New, Setters)] +#[derive(New, Setters, ToBytes)] struct FuStructDellKestrelHidFwUpdatePkg { cmd: u8, ext: u8, diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-ilan.c fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-ilan.c --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-ilan.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-ilan.c 2026-02-26 11:36:18.000000000 +0000 @@ -23,12 +23,17 @@ static gboolean fu_dell_kestrel_ilan_setup(FuDevice *device, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); - FuDellDockBaseType dock_type = fu_dell_kestrel_ec_get_dock_type(FU_DELL_KESTREL_EC(proxy)); + FuDevice *proxy; + FuDellDockBaseType dock_type; FuDellKestrelEcDevType dev_type = FU_DELL_KESTREL_EC_DEV_TYPE_LAN; guint32 version_raw; g_autofree gchar *devname = NULL; + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + dock_type = fu_dell_kestrel_ec_get_dock_type(FU_DELL_KESTREL_EC(proxy)); + /* name */ devname = g_strdup_printf("%s", fu_dell_kestrel_ec_devicetype_to_str(dev_type, 0, 0)); fu_device_set_name(device, devname); @@ -52,7 +57,9 @@ FwupdInstallFlags flags, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; return fu_dell_kestrel_hid_device_write_firmware(FU_DELL_KESTREL_HID_DEVICE(proxy), firmware, progress, @@ -62,7 +69,7 @@ } static void -fu_dell_kestrel_ilan_set_progress(FuDevice *self, FuProgress *progress) +fu_dell_kestrel_ilan_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -77,11 +84,13 @@ { fu_device_add_protocol(FU_DEVICE(self), "com.dell.kestrel"); fu_device_add_vendor_id(FU_DEVICE(self), "USB:0x413C"); - fu_device_add_icon(FU_DEVICE(self), "network-wired"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_NETWORK_WIRED); fu_device_set_summary(FU_DEVICE(self), "Dell Dock LAN"); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_DELL_KESTREL_EC); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INSTALL_SKIP_VERSION_CHECK); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_EXPLICIT_ORDER); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-package.c fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-package.c --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-package.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-package.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,11 +25,17 @@ static gboolean fu_dell_kestrel_package_setup(FuDevice *device, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); - FuDellKestrelDockSku dock_sku = fu_dell_kestrel_ec_get_dock_sku(FU_DELL_KESTREL_EC(proxy)); - FuDellDockBaseType dock_type = fu_dell_kestrel_ec_get_dock_type(FU_DELL_KESTREL_EC(proxy)); + FuDevice *proxy; + FuDellKestrelDockSku dock_sku; + FuDellDockBaseType dock_type; guint32 pkg_version_raw; + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + dock_sku = fu_dell_kestrel_ec_get_dock_sku(FU_DELL_KESTREL_EC(proxy)); + dock_type = fu_dell_kestrel_ec_get_dock_type(FU_DELL_KESTREL_EC(proxy)); + /* instance ID */ fu_device_add_instance_u8(device, "DOCKTYPE", dock_type); fu_device_add_instance_u8(device, "DOCKSKU", dock_sku); @@ -50,7 +56,7 @@ FwupdInstallFlags flags, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy; guint32 pkg_version = 0; g_autoptr(GInputStream) stream = NULL; g_autofree gchar *dynamic_version = NULL; @@ -76,6 +82,9 @@ dynamic_version); /* write to device */ + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; if (!fu_dell_kestrel_ec_commit_package(FU_DELL_KESTREL_EC(proxy), stream, error)) return FALSE; @@ -88,7 +97,9 @@ static gboolean fu_dell_kestrel_package_attach(FuDevice *device, FuProgress *progress, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; /* register post message */ if (fu_device_has_flag(proxy, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE)) { @@ -103,7 +114,7 @@ } static void -fu_dell_kestrel_package_set_progress(FuDevice *self, FuProgress *progress) +fu_dell_kestrel_package_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -121,9 +132,11 @@ fu_device_set_name(FU_DEVICE(self), "Package Version of Dell dock"); fu_device_set_summary(FU_DEVICE(self), "Dell Dock Package"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_DELL_KESTREL_EC); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_request_flag(FU_DEVICE(self), FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_EXPLICIT_ORDER); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN); } diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-pd.c fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-pd.c --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-pd.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-pd.c 2026-02-26 11:36:18.000000000 +0000 @@ -28,13 +28,19 @@ fu_dell_kestrel_pd_setup(FuDevice *device, GError **error) { FuDellKestrelPd *self = FU_DELL_KESTREL_PD(device); - FuDevice *proxy = fu_device_get_proxy(device); - FuDellDockBaseType dock_type = fu_dell_kestrel_ec_get_dock_type(FU_DELL_KESTREL_EC(proxy)); - FuDellKestrelDockSku dock_sku = fu_dell_kestrel_ec_get_dock_sku(FU_DELL_KESTREL_EC(proxy)); + FuDevice *proxy; + FuDellDockBaseType dock_type; + FuDellKestrelDockSku dock_sku; FuDellKestrelEcDevType dev_type = FU_DELL_KESTREL_EC_DEV_TYPE_PD; guint32 raw_version; g_autofree gchar *devname = NULL; + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + dock_type = fu_dell_kestrel_ec_get_dock_type(FU_DELL_KESTREL_EC(proxy)); + dock_sku = fu_dell_kestrel_ec_get_dock_sku(FU_DELL_KESTREL_EC(proxy)); + /* name */ devname = g_strdup( fu_dell_kestrel_ec_devicetype_to_str(dev_type, self->pd_subtype, self->pd_instance)); @@ -72,7 +78,9 @@ GError **error) { FuDellKestrelPd *self = FU_DELL_KESTREL_PD(device); - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; return fu_dell_kestrel_hid_device_write_firmware(FU_DELL_KESTREL_HID_DEVICE(proxy), firmware, progress, @@ -82,7 +90,7 @@ } static void -fu_dell_kestrel_pd_set_progress(FuDevice *self, FuProgress *progress) +fu_dell_kestrel_pd_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -104,6 +112,7 @@ fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_EXPLICIT_ORDER); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_DELL_KESTREL_EC); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); } diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-plugin.c fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-plugin.c --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -52,10 +52,10 @@ /* dock type according to ec */ dock_type = fu_dell_kestrel_ec_get_dock_type(FU_DELL_KESTREL_EC(ec_device)); if (dock_type == FU_DELL_DOCK_BASE_TYPE_UNKNOWN) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "can't read base dock type from EC"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "can't read base dock type from EC"); return FALSE; } @@ -69,7 +69,7 @@ return FALSE; } - /* Remote Management */ + /* remote management */ if (pid == DELL_KESTREL_USB_RMM_PID) { g_autoptr(FuDellKestrelRmm) rmm_device = NULL; g_autoptr(FuDeviceLocker) locker = NULL; @@ -88,7 +88,11 @@ return FALSE; fu_device_add_child(ec_device, FU_DEVICE(rmm_device)); - fu_dell_kestrel_rmm_fix_version(rmm_device); + + if (!fu_dell_kestrel_rmm_fix_version(rmm_device, error)) { + g_prefix_error_literal(error, "failed to fix rmm version: "); + return FALSE; + } return TRUE; } @@ -96,10 +100,14 @@ /* RTS usb hub devices */ if (pid == DELL_KESTREL_USB_RTS0_G1_PID || pid == DELL_KESTREL_USB_RTS0_G2_PID || pid == DELL_KESTREL_USB_RTS5_G2_PID) { - g_autoptr(FuDellKestrelRtsHub) hub_device = NULL; + g_autoptr(FuDellKestrelRtshub) hub_device = NULL; g_autoptr(FuDeviceLocker) locker = NULL; + gboolean uod = FALSE; + + uod = fu_plugin_get_config_value_boolean(plugin, + FWUPD_DELL_KESTREL_PLUGIN_CONFIG_UOD); - hub_device = fu_dell_kestrel_rtshub_new(FU_USB_DEVICE(device), dock_type); + hub_device = fu_dell_kestrel_rtshub_new(FU_USB_DEVICE(device), dock_type, uod); if (hub_device == NULL) { g_set_error(error, FWUPD_ERROR, @@ -182,12 +190,12 @@ uod = fu_plugin_get_config_value_boolean(plugin, FWUPD_DELL_KESTREL_PLUGIN_CONFIG_UOD); - ec_dev = fu_dell_kestrel_ec_new(device, uod); + ec_dev = fu_dell_kestrel_ec_new(FU_USB_DEVICE(device), uod); if (ec_dev == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "can't create EC V2 device"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "can't create EC V2 device"); return FALSE; } @@ -267,12 +275,12 @@ FuDevice *device_usb4 = fu_plugin_cache_lookup(plugin, "usb4"); FuDevice *device_mst = fu_plugin_cache_lookup(plugin, "mst"); - if (device_ec && device_usb4 && !fu_device_get_parent(device_usb4)) { + if (device_ec && device_usb4 && !fu_device_get_parent(device_usb4, NULL)) { fu_device_add_child(device_ec, device_usb4); fu_plugin_cache_remove(plugin, "usb4"); } - if (device_ec && device_mst && !fu_device_get_parent(device_mst)) { + if (device_ec && device_mst && !fu_device_get_parent(device_mst, NULL)) { fu_device_add_child(device_ec, device_mst); fu_plugin_cache_remove(plugin, "mst"); } @@ -295,6 +303,7 @@ /* activation should already done when device is added */ fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_EXPLICIT_ORDER); + fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART); fu_plugin_cache_add(plugin, "usb4", device); } @@ -323,7 +332,7 @@ { for (guint i = 0; i < devices->len; i++) { FuDevice *dev = g_ptr_array_index(devices, i); - FuDevice *parent = fu_device_get_parent(dev); + FuDevice *parent = fu_device_get_parent(dev, NULL); if (parent == NULL) parent = dev; @@ -350,6 +359,20 @@ if (locker == NULL) return FALSE; + /* update on connected, i.e., uod is false */ + if (!fu_plugin_get_config_value_boolean(plugin, FWUPD_DELL_KESTREL_PLUGIN_CONFIG_UOD)) { + /* releasing the dock will activate devices immediately */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *dev = g_ptr_array_index(devices, i); + + if (fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { + fu_device_remove_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + g_debug("uoc dropped needs-activation flag for %s", + fu_device_get_name(dev)); + } + } + } + /* release the dock */ if (!fu_dell_kestrel_ec_own_dock(FU_DELL_KESTREL_EC(ec_dev), FALSE, error)) return FALSE; @@ -374,7 +397,7 @@ return FALSE; /* check if dock is ready to process updates */ - if (!fu_dell_kestrel_ec_is_dock_ready4update(ec_dev, error)) + if (!fu_dell_kestrel_ec_is_dock_ready_for_update(FU_DELL_KESTREL_EC(ec_dev), error)) return FALSE; /* own the dock */ @@ -415,10 +438,18 @@ fu_dell_kestrel_plugin_backend_device_removed(FuPlugin *plugin, FuDevice *device, GError **error) { const gchar *cache_keys[] = {"ec", "mst", "usb4"}; - FuDevice *parent = fu_device_get_parent(device); + FuDevice *parent = fu_device_get_parent(device, NULL); if (parent == NULL) return TRUE; + + /* uod: device is managed to activate when disconnected */ + if (fu_plugin_get_config_value_boolean(plugin, FWUPD_DELL_KESTREL_PLUGIN_CONFIG_UOD) && + fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + g_debug("uod dropped needs-activation flag for %s", fu_device_get_name(device)); + } + if (!FU_IS_DELL_KESTREL_EC(parent)) return TRUE; @@ -434,28 +465,10 @@ return TRUE; } -static gboolean -fu_dell_kestrel_plugin_prepare(FuPlugin *plugin, - FuDevice *device, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - /* usb4 device reboot is suppressed, let ec handle it in passive update */ - if (fu_device_has_guid(device, DELL_KESTREL_T4_DEVID) || - fu_device_has_guid(device, DELL_KESTREL_T5_DEVID)) { - /* uod requires needs-activate from intel-usb4 plugin */ - if (fu_plugin_get_config_value_boolean(plugin, - FWUPD_DELL_KESTREL_PLUGIN_CONFIG_UOD)) - fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART); - } - - return TRUE; -} - static void fu_dell_kestrel_plugin_init(FuDellKestrelPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void @@ -463,6 +476,8 @@ { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); + /* allow these to be built by quirks */ fu_plugin_add_device_gtype(plugin, FU_TYPE_DELL_KESTREL_PACKAGE); fu_plugin_add_device_gtype(plugin, FU_TYPE_DELL_KESTREL_PD); @@ -477,7 +492,7 @@ fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_DELL_KESTREL_RTSHUB_FIRMWARE); /* defaults changed here will also be reflected in the fwupd.conf man page */ - fu_plugin_set_config_default(plugin, FWUPD_DELL_KESTREL_PLUGIN_CONFIG_UOD, "true"); + fu_plugin_set_config_default(plugin, FWUPD_DELL_KESTREL_PLUGIN_CONFIG_UOD, "false"); } static void @@ -491,5 +506,4 @@ plugin_class->composite_prepare = fu_dell_kestrel_plugin_composite_prepare; plugin_class->composite_cleanup = fu_dell_kestrel_plugin_composite_cleanup; plugin_class->modify_config = fu_dell_kestrel_plugin_modify_config; - plugin_class->prepare = fu_dell_kestrel_plugin_prepare; } diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-rmm.c fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rmm.c --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-rmm.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rmm.c 2026-02-26 11:36:18.000000000 +0000 @@ -20,18 +20,29 @@ return fu_version_from_uint32_hex(version_raw, fu_device_get_version_format(device)); } -void -fu_dell_kestrel_rmm_fix_version(FuDellKestrelRmm *self) +gboolean +fu_dell_kestrel_rmm_fix_version(FuDellKestrelRmm *self, GError **error) { - FuDevice *parent = NULL; + FuDevice *parent = fu_device_get_parent(FU_DEVICE(self), NULL); - /* use version given by parent */ - parent = fu_device_get_parent(FU_DEVICE(self)); - if (parent != NULL) { + /* RMM version is given by the parent EC device */ + if (parent != NULL && FU_IS_DELL_KESTREL_EC(parent)) { guint32 rmm_version; + g_autoptr(FuDeviceLocker) locker = NULL; + + locker = fu_device_locker_new(parent, error); + if (locker == NULL) + return FALSE; + + /* RMM might be added after EC, reload for the latest dock info */ + if (!fu_device_reload(parent, error)) + return FALSE; + rmm_version = fu_dell_kestrel_ec_get_rmm_version(FU_DELL_KESTREL_EC(parent)); fu_device_set_version_raw(FU_DEVICE(self), rmm_version); } + + return TRUE; } static gboolean @@ -43,7 +54,11 @@ if (!FU_DEVICE_CLASS(fu_dell_kestrel_rmm_parent_class)->setup(device, error)) return FALSE; - fu_dell_kestrel_rmm_fix_version(self); + if (!fu_dell_kestrel_rmm_fix_version(self, error)) { + g_prefix_error_literal(error, "failed to fix RMM version: "); + return FALSE; + } + return TRUE; } @@ -63,7 +78,7 @@ } static void -fu_dell_kestrel_rmm_set_progress(FuDevice *self, FuProgress *progress) +fu_dell_kestrel_rmm_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -82,6 +97,7 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INSTALL_SKIP_VERSION_CHECK); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_EXPLICIT_ORDER); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); } diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-rmm.h fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rmm.h --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-rmm.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rmm.h 2026-02-26 11:36:18.000000000 +0000 @@ -20,5 +20,5 @@ FuDellKestrelRmm * fu_dell_kestrel_rmm_new(FuUsbDevice *device); -void -fu_dell_kestrel_rmm_fix_version(FuDellKestrelRmm *self); +gboolean +fu_dell_kestrel_rmm_fix_version(FuDellKestrelRmm *self, GError **error); diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-rtshub-firmware.c fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rtshub-firmware.c --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-rtshub-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rtshub-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,7 @@ #define DOCK_RTSHUB_GEN1_PID_OFFSET 0x7FAA struct _FuDellKestrelRtshubFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; guint16 pid; }; @@ -63,7 +63,7 @@ static gboolean fu_dell_kestrel_rtshub_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuDellKestrelRtshubFirmware *self = FU_DELL_KESTREL_RTSHUB_FIRMWARE(firmware); @@ -97,6 +97,7 @@ static void fu_dell_kestrel_rtshub_firmware_init(FuDellKestrelRtshubFirmware *self) { + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION); fu_firmware_set_version_format(FU_FIRMWARE(self), FWUPD_VERSION_FORMAT_PAIR); } diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-rtshub.c fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rtshub.c --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-rtshub.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rtshub.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,38 +11,38 @@ #include "fu-dell-kestrel-rtshub-struct.h" #include "fu-dell-kestrel-rtshub.h" -struct _FuDellKestrelRtsHub { +struct _FuDellKestrelRtshub { FuHidDevice parent_instance; FuDellDockBaseType dock_type; gboolean fw_auth; gboolean dual_bank; }; -G_DEFINE_TYPE(FuDellKestrelRtsHub, fu_dell_kestrel_rtshub, FU_TYPE_HID_DEVICE) +G_DEFINE_TYPE(FuDellKestrelRtshub, fu_dell_kestrel_rtshub, FU_TYPE_HID_DEVICE) static void fu_dell_kestrel_rtshub_to_string(FuDevice *device, guint idt, GString *str) { - FuDellKestrelRtsHub *self = FU_DELL_KESTREL_RTSHUB(device); + FuDellKestrelRtshub *self = FU_DELL_KESTREL_RTSHUB(device); fwupd_codec_string_append_bool(str, idt, "FwAuth", self->fw_auth); fwupd_codec_string_append_bool(str, idt, "DualBank", self->dual_bank); fwupd_codec_string_append_hex(str, idt, "DockType", self->dock_type); } static gboolean -fu_dell_kestrel_rtshub_set_clock_mode(FuDellKestrelRtsHub *self, gboolean enable, GError **error) +fu_dell_kestrel_rtshub_set_clock_mode(FuDellKestrelRtshub *self, gboolean enable, GError **error) { - g_autoptr(GByteArray) cmd_buf = fu_struct_rtshub_hid_cmd_buf_new(); + g_autoptr(FuStructRtshubHidCmdBuf) st_cmd = fu_struct_rtshub_hid_cmd_buf_new(); - fu_struct_rtshub_hid_cmd_buf_set_cmd(cmd_buf, RTSHUB_CMD_WRITE_DATA); - fu_struct_rtshub_hid_cmd_buf_set_ext(cmd_buf, RTSHUB_EXT_MCUMODIFYCLOCK); - fu_struct_rtshub_hid_cmd_buf_set_regaddr(cmd_buf, (guint8)enable); - fu_struct_rtshub_hid_cmd_buf_set_bufferlen(cmd_buf, 0); + fu_struct_rtshub_hid_cmd_buf_set_cmd(st_cmd, RTSHUB_CMD_WRITE_DATA); + fu_struct_rtshub_hid_cmd_buf_set_ext(st_cmd, RTSHUB_EXT_MCUMODIFYCLOCK); + fu_struct_rtshub_hid_cmd_buf_set_regaddr(st_cmd, (guint8)enable); + fu_struct_rtshub_hid_cmd_buf_set_bufferlen(st_cmd, 0); if (!fu_hid_device_set_report(FU_HID_DEVICE(self), 0x0, - cmd_buf->data, - cmd_buf->len, + st_cmd->buf->data, + st_cmd->buf->len, DELL_KESTREL_RTSHUB_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error)) { @@ -53,44 +53,44 @@ } static gboolean -fu_dell_kestrel_rtshub_erase_spare_bank(FuDellKestrelRtsHub *self, GError **error) +fu_dell_kestrel_rtshub_erase_spare_bank(FuDellKestrelRtshub *self, GError **error) { - g_autoptr(GByteArray) cmd_buf = fu_struct_rtshub_hid_cmd_buf_new(); + g_autoptr(FuStructRtshubHidCmdBuf) st_cmd = fu_struct_rtshub_hid_cmd_buf_new(); - fu_struct_rtshub_hid_cmd_buf_set_cmd(cmd_buf, RTSHUB_CMD_WRITE_DATA); - fu_struct_rtshub_hid_cmd_buf_set_ext(cmd_buf, RTSHUB_EXT_ERASEBANK); - fu_struct_rtshub_hid_cmd_buf_set_regaddr(cmd_buf, 0x0100); - fu_struct_rtshub_hid_cmd_buf_set_bufferlen(cmd_buf, 0); + fu_struct_rtshub_hid_cmd_buf_set_cmd(st_cmd, RTSHUB_CMD_WRITE_DATA); + fu_struct_rtshub_hid_cmd_buf_set_ext(st_cmd, RTSHUB_EXT_ERASEBANK); + fu_struct_rtshub_hid_cmd_buf_set_regaddr(st_cmd, 0x0100); + fu_struct_rtshub_hid_cmd_buf_set_bufferlen(st_cmd, 0); if (!fu_hid_device_set_report(FU_HID_DEVICE(self), 0x0, - cmd_buf->data, - cmd_buf->len, + st_cmd->buf->data, + st_cmd->buf->len, DELL_KESTREL_RTSHUB_TIMEOUT * 3, FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to erase spare bank: "); + g_prefix_error_literal(error, "failed to erase spare bank: "); return FALSE; } return TRUE; } static gboolean -fu_dell_kestrel_rtshub_verify_update_fw(FuDellKestrelRtsHub *self, +fu_dell_kestrel_rtshub_verify_update_fw(FuDellKestrelRtshub *self, FuProgress *progress, GError **error) { - g_autoptr(GByteArray) cmd_buf = fu_struct_rtshub_hid_cmd_buf_new(); + g_autoptr(FuStructRtshubHidCmdBuf) st_cmd = fu_struct_rtshub_hid_cmd_buf_new(); - fu_struct_rtshub_hid_cmd_buf_set_cmd(cmd_buf, RTSHUB_CMD_WRITE_DATA); - fu_struct_rtshub_hid_cmd_buf_set_ext(cmd_buf, RTSHUB_EXT_VERIFYUPDATE); - fu_struct_rtshub_hid_cmd_buf_set_regaddr(cmd_buf, 0x01); - fu_struct_rtshub_hid_cmd_buf_set_bufferlen(cmd_buf, 0); + fu_struct_rtshub_hid_cmd_buf_set_cmd(st_cmd, RTSHUB_CMD_WRITE_DATA); + fu_struct_rtshub_hid_cmd_buf_set_ext(st_cmd, RTSHUB_EXT_VERIFYUPDATE); + fu_struct_rtshub_hid_cmd_buf_set_regaddr(st_cmd, 0x01); + fu_struct_rtshub_hid_cmd_buf_set_bufferlen(st_cmd, 0); if (!fu_hid_device_set_report(FU_HID_DEVICE(self), 0x0, - cmd_buf->data, - cmd_buf->len, + st_cmd->buf->data, + st_cmd->buf->len, DELL_KESTREL_RTSHUB_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error)) @@ -98,15 +98,15 @@ fu_device_sleep_full(FU_DEVICE(self), 4000, progress); /* ms */ if (!fu_hid_device_get_report(FU_HID_DEVICE(self), 0x0, - cmd_buf->data, - cmd_buf->len, + st_cmd->buf->data, + st_cmd->buf->len, DELL_KESTREL_RTSHUB_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error)) return FALSE; /* check device status, 1 for success otherwise fail */ - if (cmd_buf->data[0] != 0x01) { + if (st_cmd->buf->data[0] != 0x01) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "firmware flash failed"); return FALSE; } @@ -116,29 +116,51 @@ } static gboolean -fu_dell_kestrel_rtshub_write_flash(FuDellKestrelRtsHub *self, +fu_dell_kestrel_rtshub_reset_device(FuDellKestrelRtshub *self, GError **error) +{ + g_autoptr(FuStructRtshubHidCmdBuf) st_cmd = fu_struct_rtshub_hid_cmd_buf_new(); + + fu_struct_rtshub_hid_cmd_buf_set_cmd(st_cmd, RTSHUB_CMD_WRITE_DATA); + fu_struct_rtshub_hid_cmd_buf_set_ext(st_cmd, RTSHUB_EXT_RESET_TO_FLASH); + + if (!fu_hid_device_set_report(FU_HID_DEVICE(self), + 0x0, + st_cmd->buf->data, + st_cmd->buf->len, + DELL_KESTREL_RTSHUB_TIMEOUT, + FU_HID_DEVICE_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to soft reset: "); + return FALSE; + } + g_debug("soft reset completed for %s", fu_device_get_name(FU_DEVICE(self))); + return TRUE; +} + +static gboolean +fu_dell_kestrel_rtshub_write_flash(FuDellKestrelRtshub *self, guint32 addr, const guint8 *data, guint16 data_sz, GError **error) { - g_autoptr(GByteArray) cmd_buf = fu_struct_rtshub_hid_cmd_buf_new(); + g_autoptr(FuStructRtshubHidCmdBuf) st_cmd = fu_struct_rtshub_hid_cmd_buf_new(); g_return_val_if_fail(data_sz <= 128, FALSE); g_return_val_if_fail(data != NULL, FALSE); g_return_val_if_fail(data_sz != 0, FALSE); - fu_struct_rtshub_hid_cmd_buf_set_cmd(cmd_buf, RTSHUB_CMD_WRITE_DATA); - fu_struct_rtshub_hid_cmd_buf_set_ext(cmd_buf, RTSHUB_EXT_WRITEFLASH); - fu_struct_rtshub_hid_cmd_buf_set_regaddr(cmd_buf, addr); - fu_struct_rtshub_hid_cmd_buf_set_bufferlen(cmd_buf, data_sz); - if (!fu_struct_rtshub_hid_cmd_buf_set_data(cmd_buf, data, data_sz, error)) + fu_struct_rtshub_hid_cmd_buf_set_cmd(st_cmd, RTSHUB_CMD_WRITE_DATA); + fu_struct_rtshub_hid_cmd_buf_set_ext(st_cmd, RTSHUB_EXT_WRITEFLASH); + fu_struct_rtshub_hid_cmd_buf_set_regaddr(st_cmd, addr); + fu_struct_rtshub_hid_cmd_buf_set_bufferlen(st_cmd, data_sz); + if (!fu_struct_rtshub_hid_cmd_buf_set_data(st_cmd, data, data_sz, error)) return FALSE; if (!fu_hid_device_set_report(FU_HID_DEVICE(self), 0x0, - cmd_buf->data, - cmd_buf->len, + st_cmd->buf->data, + st_cmd->buf->len, DELL_KESTREL_RTSHUB_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error)) { @@ -155,7 +177,7 @@ FwupdInstallFlags flags, GError **error) { - FuDellKestrelRtsHub *self = FU_DELL_KESTREL_RTSHUB(device); + FuDellKestrelRtshub *self = FU_DELL_KESTREL_RTSHUB(device); g_autoptr(GInputStream) stream = NULL; g_autoptr(FuChunkArray) chunks = NULL; @@ -221,48 +243,59 @@ return FALSE; fu_progress_step_done(progress); + /* non-uod: reset the device immediately */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE)) { + /* rts5g2 only */ + if (fu_device_get_pid(device) == DELL_KESTREL_USB_RTS5_G2_PID) { + if (!fu_dell_kestrel_rtshub_reset_device(self, error)) { + g_prefix_error_literal(error, "failed to reset rts5g2 device: "); + return FALSE; + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } + } + /* success! */ return TRUE; } static gboolean -fu_dell_kestrel_rtshub_get_status(FuDevice *device, GError **error) +fu_dell_kestrel_rtshub_get_status(FuDellKestrelRtshub *self, GError **error) { - FuDellKestrelRtsHub *self = FU_DELL_KESTREL_RTSHUB(device); g_autofree gchar *version = NULL; - g_autoptr(GByteArray) cmd_buf = fu_struct_rtshub_hid_cmd_buf_new(); + g_autoptr(FuStructRtshubHidCmdBuf) st_cmd = fu_struct_rtshub_hid_cmd_buf_new(); - fu_struct_rtshub_hid_cmd_buf_set_cmd(cmd_buf, RTSHUB_CMD_READ_DATA); - fu_struct_rtshub_hid_cmd_buf_set_ext(cmd_buf, RTSHUB_EXT_READ_STATUS); - fu_struct_rtshub_hid_cmd_buf_set_regaddr(cmd_buf, 0x00); - fu_struct_rtshub_hid_cmd_buf_set_bufferlen(cmd_buf, 12); + fu_struct_rtshub_hid_cmd_buf_set_cmd(st_cmd, RTSHUB_CMD_READ_DATA); + fu_struct_rtshub_hid_cmd_buf_set_ext(st_cmd, RTSHUB_EXT_READ_STATUS); + fu_struct_rtshub_hid_cmd_buf_set_regaddr(st_cmd, 0x00); + fu_struct_rtshub_hid_cmd_buf_set_bufferlen(st_cmd, 12); if (!fu_hid_device_set_report(FU_HID_DEVICE(self), 0x0, - cmd_buf->data, - cmd_buf->len, + st_cmd->buf->data, + st_cmd->buf->len, DELL_KESTREL_RTSHUB_TIMEOUT, FU_HID_DEVICE_FLAG_RETRY_FAILURE, error)) return FALSE; if (!fu_hid_device_get_report(FU_HID_DEVICE(self), 0x0, - cmd_buf->data, - cmd_buf->len, + st_cmd->buf->data, + st_cmd->buf->len, DELL_KESTREL_RTSHUB_TIMEOUT, FU_HID_DEVICE_FLAG_RETRY_FAILURE, error)) return FALSE; /* version: index 10, subversion: index 11 */ - version = g_strdup_printf("%x.%x", cmd_buf->data[10], cmd_buf->data[11]); - fu_device_set_version(device, version); + version = g_strdup_printf("%x.%x", st_cmd->buf->data[10], st_cmd->buf->data[11]); + fu_device_set_version(FU_DEVICE(self), version); /* dual bank capability */ - self->dual_bank = (cmd_buf->data[13] & 0xf0) == 0x80; + self->dual_bank = (st_cmd->buf->data[13] & 0xf0) == 0x80; /* authentication capability */ - self->fw_auth = (cmd_buf->data[13] & 0x02) > 0; + self->fw_auth = (st_cmd->buf->data[13] & 0x02) > 0; return TRUE; } @@ -270,13 +303,13 @@ static gboolean fu_dell_kestrel_rtshub_setup(FuDevice *device, GError **error) { - FuDellKestrelRtsHub *self = FU_DELL_KESTREL_RTSHUB(device); + FuDellKestrelRtshub *self = FU_DELL_KESTREL_RTSHUB(device); /* FuUsbDevice->setup */ if (!FU_DEVICE_CLASS(fu_dell_kestrel_rtshub_parent_class)->setup(device, error)) return FALSE; - if (!fu_dell_kestrel_rtshub_get_status(device, error)) + if (!fu_dell_kestrel_rtshub_get_status(self, error)) return FALSE; if (self->dual_bank) @@ -292,7 +325,7 @@ fu_dell_kestrel_rtshub_probe(FuDevice *device, GError **error) { g_autofree const gchar *logical_id = NULL; - FuDellKestrelRtsHub *self = FU_DELL_KESTREL_RTSHUB(device); + FuDellKestrelRtshub *self = FU_DELL_KESTREL_RTSHUB(device); /* not interesting */ if (fu_device_get_vid(device) != DELL_VID) { @@ -335,21 +368,8 @@ return TRUE; } -static gboolean -fu_dell_kestrel_rtshub_open(FuDevice *device, GError **error) -{ - FuDevice *parent = fu_device_get_parent(device); - - if (!FU_DEVICE_CLASS(fu_dell_kestrel_rtshub_parent_class)->open(device, error)) - return FALSE; - - if (parent != NULL) - return fu_device_open(parent, error); - return TRUE; -} - static void -fu_dell_kestrel_rtshub_set_progress(FuDevice *self, FuProgress *progress) +fu_dell_kestrel_rtshub_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -361,7 +381,7 @@ } static void -fu_dell_kestrel_rtshub_init(FuDellKestrelRtsHub *self) +fu_dell_kestrel_rtshub_init(FuDellKestrelRtshub *self) { fu_device_add_protocol(FU_DEVICE(self), "com.dell.kestrel"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); @@ -376,7 +396,7 @@ } static void -fu_dell_kestrel_rtshub_class_init(FuDellKestrelRtsHubClass *klass) +fu_dell_kestrel_rtshub_class_init(FuDellKestrelRtshubClass *klass) { FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); device_class->to_string = fu_dell_kestrel_rtshub_to_string; @@ -384,15 +404,17 @@ device_class->probe = fu_dell_kestrel_rtshub_probe; device_class->write_firmware = fu_dell_kestrel_rtshub_write_firmware; device_class->set_progress = fu_dell_kestrel_rtshub_set_progress; - device_class->open = fu_dell_kestrel_rtshub_open; } -FuDellKestrelRtsHub * -fu_dell_kestrel_rtshub_new(FuUsbDevice *device, FuDellDockBaseType dock_type) +FuDellKestrelRtshub * +fu_dell_kestrel_rtshub_new(FuUsbDevice *device, FuDellDockBaseType dock_type, gboolean uod) { - FuDellKestrelRtsHub *self = g_object_new(FU_TYPE_DELL_KESTREL_RTSHUB, NULL); + FuDellKestrelRtshub *self = g_object_new(FU_TYPE_DELL_KESTREL_RTSHUB, NULL); fu_device_incorporate(FU_DEVICE(self), FU_DEVICE(device), FU_DEVICE_INCORPORATE_FLAG_ALL); self->dock_type = dock_type; + + if (uod) + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); return self; } diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-rtshub.h fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rtshub.h --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-rtshub.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-rtshub.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,7 @@ #include #define FU_TYPE_DELL_KESTREL_RTSHUB (fu_dell_kestrel_rtshub_get_type()) -G_DECLARE_FINAL_TYPE(FuDellKestrelRtsHub, +G_DECLARE_FINAL_TYPE(FuDellKestrelRtshub, fu_dell_kestrel_rtshub, FU, DELL_KESTREL_RTSHUB, @@ -35,5 +35,5 @@ #define DELL_KESTREL_RTSHUB_BUFFER_SIZE 192 #define DELL_KESTREL_RTSHUB_TRANSFER_BLOCK_SIZE 128 -FuDellKestrelRtsHub * -fu_dell_kestrel_rtshub_new(FuUsbDevice *device, FuDellDockBaseType dock_type); +FuDellKestrelRtshub * +fu_dell_kestrel_rtshub_new(FuUsbDevice *device, FuDellDockBaseType dock_type, gboolean uod); diff -Nru fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-wtpd.c fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-wtpd.c --- fwupd-2.0.8/plugins/dell-kestrel/fu-dell-kestrel-wtpd.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dell-kestrel/fu-dell-kestrel-wtpd.c 2026-02-26 11:36:18.000000000 +0000 @@ -23,13 +23,19 @@ static gboolean fu_dell_kestrel_wtpd_setup(FuDevice *device, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); - FuDellDockBaseType dock_type = fu_dell_kestrel_ec_get_dock_type(FU_DELL_KESTREL_EC(proxy)); - FuDellKestrelDockSku dock_sku = fu_dell_kestrel_ec_get_dock_sku(FU_DELL_KESTREL_EC(proxy)); + FuDevice *proxy; + FuDellDockBaseType dock_type; + FuDellKestrelDockSku dock_sku; FuDellKestrelEcDevType dev_type = FU_DELL_KESTREL_EC_DEV_TYPE_WTPD; guint32 wtpd_version; g_autofree gchar *devname = NULL; + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + dock_type = fu_dell_kestrel_ec_get_dock_type(FU_DELL_KESTREL_EC(proxy)); + dock_sku = fu_dell_kestrel_ec_get_dock_sku(FU_DELL_KESTREL_EC(proxy)); + /* name */ devname = g_strdup_printf("%s", fu_dell_kestrel_ec_devicetype_to_str(dev_type, 0, 0)); fu_device_set_name(device, devname); @@ -55,7 +61,9 @@ FwupdInstallFlags flags, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; return fu_dell_kestrel_hid_device_write_firmware(FU_DELL_KESTREL_HID_DEVICE(proxy), firmware, progress, @@ -65,7 +73,7 @@ } static void -fu_dell_kestrel_wtpd_set_progress(FuDevice *self, FuProgress *progress) +fu_dell_kestrel_wtpd_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -85,7 +93,9 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INSTALL_SKIP_VERSION_CHECK); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_DELL_KESTREL_EC); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_EXPLICIT_ORDER); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN); } diff -Nru fwupd-2.0.8/plugins/devlink/README.md fwupd-2.0.20/plugins/devlink/README.md --- fwupd-2.0.8/plugins/devlink/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,146 @@ +--- +title: Plugin: Devlink +--- + +## Introduction + +This plugin provides firmware update support for network interface cards that support the Linux devlink interface. +This is a generic plugin that can work with any device that implements devlink functionality. + +## Supported Devices + +The plugin supports any device that implements the devlink interface, regardless the bus it resides on. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +a packed binary file format. + +This plugin supports the following protocol ID: + +* `org.kernel.devlink` + +## GUID Generation + +These devices use custom instance IDs consisting of the component name. + +* `PCI\VEN_15B3&DEV_1021&COMPONENT_fw` + +Optionally, additional GUID might get generated as specified in the squirk file, see below. + +### Device Identification + +Devices are identified using their in the format: + +```text +BUS_NAME/DEV_NAME +``` + +For PCI, this is for example: + +```text +pci/0000:01:00.0 +``` + +## Requirements + +* Linux kernel with devlink support +* Device driver with devlink implementation +* Root privileges (required for devlink write commands) + +## Update Behavior + +The plugin uses the Linux devlink netlink interface to communicate with the kernel and perform firmware updates. +The process involves: + +1. **Netlink Communication**: Opens Netlink socket to communicate with the devlink subsystem +2. **Device Detection**: Gets all existing devlink devices +3. **Firmware Upload**: Writes the firmware file to `CACHE_DIRECTORY` and instructs devlink to flash it +4. **Progress Monitoring**: Monitors devlink status messages to provide real-time progress updates +5. **Firmware Activation**: Activates firmware using devlink reload activate action + +### Devlink Protocol + +The plugin implements the devlink generic netlink protocol: + +1. **Device Enumeration**: Sends `DEVLINK_CMD_GET` with dump flag to discover all devlink devices +2. **Device Monitoring**: Receives `DEVLINK_CMD_NEW` notifications when devices are added +3. **Device Removal**: Receives `DEVLINK_CMD_DEL` notifications when devices are removed +4. **Device Information**: Sends `DEVLINK_CMD_INFO_GET` to retrieve device details and versions +5. **Flash Command**: Sends `DEVLINK_CMD_FLASH_UPDATE` with device and file information +6. **Status Monitoring**: Receives `DEVLINK_CMD_FLASH_UPDATE_STATUS` messages for progress +7. **Completion**: Waits for `DEVLINK_CMD_FLASH_UPDATE_END` to confirm completion +8. **Firmware Activation**: Sends `DEVLINK_CMD_RELOAD` with fw_activate action to activate updated firmware + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### DevlinkFixedVersions + +Specifies a comma-separated list of "fixed version" names that should be used +to generate additional GUID instance IDs for component matching. This is useful to +target specific device according to device IDs, like ASIC ID, Board ID, etc. + +**Example usage in quirk file:** + +```ini +[PCI\VEN_15B3&DEV_1021] +DevlinkFixedVersions = fw.psid +``` + +Since: 2.0.15 + +## Private Flags + +The plugin supports the following private flags: + +### `Flags=omit-component-name` + +When this flag is set, the plugin will not include the `DEVLINK_ATTR_FLASH_UPDATE_COMPONENT` +attribute in the flash command. +This is useful for devices that don't support component-specific updates. For such, +passing `DEVLINK_ATTR_FLASH_UPDATE_COMPONENT` in flash netlink message +would cause an error. + +**Usage in metainfo XML:** + +```xml + + omit-component-name + +``` + +Since: 2.0.15 + +### Error Handling + +The plugin handles various error conditions: + +* Kernel without devlink support +* Device without devlink implementation +* Device without devlink flash implementation +* Firmware file access issues +* Flash operation failures + +## Security Considerations + +* Firmware files are temporarily stored in `/lib/firmware/` +* Root privileges are required for devlink write commands +* Temporary files are cleaned up after the operation + +## Vendor ID Security + +The vendor ID is set from the PCI vendor. + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. + +## Version Considerations + +This plugin has been available since fwupd version `2.0.15`. + +## References + +* [Linux Devlink Documentation](https://www.kernel.org/doc/html/latest/networking/devlink/) diff -Nru fwupd-2.0.8/plugins/devlink/devlink.quirk fwupd-2.0.20/plugins/devlink/devlink.quirk --- fwupd-2.0.8/plugins/devlink/devlink.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/devlink.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,29 @@ +[DEVLINK\COMPONENT_fw] +Name = Composite +[DEVLINK\COMPONENT_fw.mgmt] +Name = Management + +# emulated device is owned by the Linux Foundation +[NETDEVSIM\COMPONENT_fw.mgmt] +Vendor = Linux Foundation +VendorId = PCI:0x1D6B + +# NVIDIA ConnectX-6 +[PCI\VEN_15B3&DEV_101B] +DevlinkFixedVersions = fw.psid + +# NVIDIA ConnectX-6 Dx +[PCI\VEN_15B3&DEV_101D] +DevlinkFixedVersions = fw.psid + +# NVIDIA ConnectX-6 Lx +[PCI\VEN_15B3&DEV_101F] +DevlinkFixedVersions = fw.psid + +# NVIDIA ConnectX-7 +[PCI\VEN_15B3&DEV_1021] +DevlinkFixedVersions = fw.psid + +# NVIDIA ConnectX-8 +[PCI\VEN_15B3&DEV_1023] +DevlinkFixedVersions = fw.psid diff -Nru fwupd-2.0.8/plugins/devlink/fu-devlink-backend.c fwupd-2.0.20/plugins/devlink/fu-devlink-backend.c --- fwupd-2.0.8/plugins/devlink/fu-devlink-backend.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/fu-devlink-backend.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,196 @@ +/* + * Copyright 2025 NVIDIA Corporation & Affiliates + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-devlink-backend.h" +#include "fu-devlink-device.h" + +struct _FuDevlinkBackend { + FuBackend parent_instance; +}; + +G_DEFINE_TYPE(FuDevlinkBackend, fu_devlink_backend, FU_TYPE_BACKEND) + +static FuDevice * +fu_devlink_backend_create_pci_parent(FuDevlinkBackend *self, + const gchar *bus_name, + const gchar *dev_name, + GError **error) +{ + FuContext *ctx = fu_backend_get_context(FU_BACKEND(self)); + FuBackend *udev_backend = NULL; + g_autofree gchar *pci_sysfs_path = NULL; + g_autofree gchar *pci_sysfs_real = NULL; + g_autoptr(FuDevice) pci_device = NULL; + g_autoptr(GError) error_local = NULL; + + /* get the udev backend to create PCI parent device */ + udev_backend = fu_context_get_backend_by_name(ctx, "udev", &error_local); + if (udev_backend == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "udev backend not available: %s", + error_local->message); + return NULL; + } + + /* construct PCI sysfs path from bus_name (e.g., "pci/0000:01:00.0") */ + pci_sysfs_path = g_strdup_printf("/sys/bus/pci/devices/%s", dev_name); + pci_sysfs_real = fu_path_make_absolute(pci_sysfs_path, error); + if (pci_sysfs_real == NULL) + return NULL; + + /* create PCI device from sysfs path */ + pci_device = fu_backend_create_device(udev_backend, pci_sysfs_real, &error_local); + if (pci_device == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "failed to create PCI device for %s: %s", + pci_sysfs_path, + error_local->message); + return NULL; + } + + /* ensure PCI device is probed to get vendor/device info */ + if (!fu_device_probe(pci_device, error)) { + g_prefix_error_literal(error, "failed to probe PCI device: "); + return NULL; + } + + return g_steal_pointer(&pci_device); +} + +static FuDevice * +fu_devlink_backend_create_netdevsim_parent(FuDevlinkBackend *self, + const gchar *bus_name, + const gchar *dev_name, + GError **error) +{ + FuContext *ctx = fu_backend_get_context(FU_BACKEND(self)); + g_autoptr(FuDevice) netdevsim_device = NULL; + g_autofree gchar *physical_id = NULL; + + /* create a fake netdevsim parent device for testing */ + netdevsim_device = fu_device_new(ctx); + + /* set netdevsim device properties */ + physical_id = g_strdup_printf("netdevsim-%s", dev_name); + fu_device_set_physical_id(netdevsim_device, physical_id); + fu_device_set_name(netdevsim_device, "Network Device Simulator"); + + return g_steal_pointer(&netdevsim_device); +} + +FuDevice * +fu_devlink_backend_device_added(FuDevlinkBackend *self, + const gchar *bus_name, + const gchar *dev_name, + const gchar *serial_number, + GError **error) +{ + FuContext *ctx = fu_backend_get_context(FU_BACKEND(self)); + FuDevice *old_devlink_device; + g_autoptr(FuDevice) devlink_device = NULL; + g_autoptr(FuDevice) parent_device = NULL; + + g_return_val_if_fail(FU_IS_DEVLINK_BACKEND(self), NULL); + g_return_val_if_fail(bus_name != NULL, NULL); + g_return_val_if_fail(dev_name != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* only support PCI and netdevsim buses */ + if (g_strcmp0(bus_name, "pci") == 0) { + parent_device = + fu_devlink_backend_create_pci_parent(self, bus_name, dev_name, error); + if (parent_device == NULL) + return NULL; + } else if (g_strcmp0(bus_name, "netdevsim") == 0) { + parent_device = + fu_devlink_backend_create_netdevsim_parent(self, bus_name, dev_name, error); + if (parent_device == NULL) + return NULL; + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unsupported bus type: %s (only 'pci' and 'netdevsim' are supported)", + bus_name); + return NULL; + } + + /* create devlink device */ + devlink_device = fu_devlink_device_new(ctx, bus_name, dev_name, serial_number); + if (devlink_device == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to create devlink device"); + return NULL; + } + + /* only add one device for the PCI card, use serial number as backend id */ + if (serial_number != NULL) + fu_device_set_backend_id(devlink_device, serial_number); + + /* in case the device with the same backend id already exists, skip this one */ + old_devlink_device = + fu_backend_lookup_by_id(FU_BACKEND(self), fu_device_get_backend_id(devlink_device)); + if (old_devlink_device != NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "device with the same backend id already exists: %s", + fu_device_get_backend_id(devlink_device)); + return NULL; + } + + /* incorporate information from parent device (without setting hierarchy) */ + fu_device_incorporate(devlink_device, + parent_device, + FU_DEVICE_INCORPORATE_FLAG_BASECLASS | + FU_DEVICE_INCORPORATE_FLAG_VENDOR | + FU_DEVICE_INCORPORATE_FLAG_VENDOR_IDS | + FU_DEVICE_INCORPORATE_FLAG_VID | FU_DEVICE_INCORPORATE_FLAG_PID); + + /* only add the devlink device to the backend - parent is managed by its own backend */ + fu_backend_device_added(FU_BACKEND(self), devlink_device); + return g_steal_pointer(&devlink_device); +} + +void +fu_devlink_backend_device_removed(FuDevlinkBackend *self, FuDevice *devlink_device) +{ + g_return_if_fail(FU_IS_DEVLINK_BACKEND(self)); + g_return_if_fail(FU_IS_DEVICE(devlink_device)); + + fu_backend_device_removed(FU_BACKEND(self), devlink_device); +} + +static void +fu_devlink_backend_init(FuDevlinkBackend *self) +{ +} + +static void +fu_devlink_backend_class_init(FuDevlinkBackendClass *klass) +{ +} + +FuBackend * +fu_devlink_backend_new(FuContext *ctx) +{ + return FU_BACKEND(g_object_new(FU_TYPE_DEVLINK_BACKEND, + "name", + "devlink", + "context", + ctx, + "device-gtype", + FU_TYPE_DEVLINK_DEVICE, + NULL)); +} diff -Nru fwupd-2.0.8/plugins/devlink/fu-devlink-backend.h fwupd-2.0.20/plugins/devlink/fu-devlink-backend.h --- fwupd-2.0.8/plugins/devlink/fu-devlink-backend.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/fu-devlink-backend.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,26 @@ +/* + * Copyright 2025 NVIDIA Corporation & Affiliates + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_DEVLINK_BACKEND (fu_devlink_backend_get_type()) +G_DECLARE_FINAL_TYPE(FuDevlinkBackend, fu_devlink_backend, FU, DEVLINK_BACKEND, FuBackend) + +FuBackend * +fu_devlink_backend_new(FuContext *ctx) G_GNUC_NON_NULL(1); + +FuDevice * +fu_devlink_backend_device_added(FuDevlinkBackend *self, + const gchar *bus_name, + const gchar *dev_name, + const gchar *serial_number, + GError **error) G_GNUC_NON_NULL(1, 2, 3); + +void +fu_devlink_backend_device_removed(FuDevlinkBackend *self, FuDevice *devlink_device) + G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/plugins/devlink/fu-devlink-component.c fwupd-2.0.20/plugins/devlink/fu-devlink-component.c --- fwupd-2.0.8/plugins/devlink/fu-devlink-component.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/fu-devlink-component.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,223 @@ +/* + * Copyright 2025 NVIDIA Corporation & Affiliates + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-devlink-component.h" +#include "fu-devlink-device.h" + +/** + * FU_DEVLINK_DEVICE_FLAG_OMIT_COMPONENT_NAME: + * + * Do not set the DEVLINK_ATTR_FLASH_UPDATE_COMPONENT attribute when flashing firmware. + * This allows for firmware updates without specifying a specific component name. + * + * Since: 2.0.15 + */ +#define FU_DEVLINK_DEVICE_FLAG_OMIT_COMPONENT_NAME "omit-component-name" + +struct _FuDevlinkComponent { + FuDevice parent_instance; + GPtrArray *instance_keys; +}; + +G_DEFINE_TYPE(FuDevlinkComponent, fu_devlink_component, FU_TYPE_DEVICE) + +static gboolean +fu_devlink_component_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevlinkComponent *self = FU_DEVLINK_COMPONENT(device); + FuDevice *proxy; + gboolean omit_component_name = + fu_device_has_private_flag(FU_DEVICE(self), FU_DEVLINK_DEVICE_FLAG_OMIT_COMPONENT_NAME); + + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + return fu_devlink_device_write_firmware_component(FU_DEVLINK_DEVICE(proxy), + fu_device_get_logical_id(device), + omit_component_name, + firmware, + progress, + flags, + error); +} + +void +fu_devlink_component_add_instance_keys(FuDevlinkComponent *self, gchar **keys) +{ + if (self->instance_keys == NULL) + self->instance_keys = g_ptr_array_new_with_free_func((GDestroyNotify)g_strfreev); + g_ptr_array_add(self->instance_keys, keys); +} + +static gboolean +fu_devlink_component_probe(FuDevice *device, GError **error) +{ + FuDevlinkComponent *self = FU_DEVLINK_COMPONENT(device); + FuDevice *proxy; + g_autofree gchar *subsystem = NULL; + g_autoptr(GStrvBuilder) basekeys_builder = NULL; + g_auto(GStrv) basekeys = NULL; + + /* declare all variables at the beginning */ + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + subsystem = g_ascii_strup(fu_devlink_device_get_bus_name(FU_DEVLINK_DEVICE(proxy)), -1); + + basekeys_builder = g_strv_builder_new(); + if (fu_device_get_instance_str(device, "VEN") != NULL && + fu_device_get_instance_str(device, "DEV") != NULL) { + g_strv_builder_add(basekeys_builder, "VEN"); + g_strv_builder_add(basekeys_builder, "DEV"); + } + g_strv_builder_add(basekeys_builder, "COMPONENT"); + basekeys = g_strv_builder_end(basekeys_builder); + + /* build instance id just for component name */ + if (!fu_device_build_instance_id_strv(device, subsystem, basekeys, error)) + return FALSE; + + if (self->instance_keys == NULL) + return TRUE; + + /* Build instance id for each fixed versions array from quirk file for which + kernel provides all fixed version values. */ + for (guint i = 0; i < self->instance_keys->len; i++) { + GStrv instance_keys = g_ptr_array_index(self->instance_keys, i); + guint j; + g_autoptr(GStrvBuilder) keys_builder = g_strv_builder_new(); + g_auto(GStrv) keys = NULL; + + /* future optimization: use g_strv_builder_addv() when available */ + for (j = 0; basekeys[j] != NULL; j++) + g_strv_builder_add(keys_builder, basekeys[j]); + for (j = 0; instance_keys[j] != NULL; j++) + g_strv_builder_add(keys_builder, instance_keys[j]); + keys = g_strv_builder_end(keys_builder); + if (!fu_device_build_instance_id_strv(device, subsystem, keys, error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_devlink_component_reload(FuDevice *device, GError **error) +{ + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + return fu_device_reload(proxy, error); +} + +static gboolean +fu_devlink_component_activate(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + return fu_device_activate(proxy, progress, error); +} + +static gboolean +fu_devlink_component_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + return fu_device_prepare(proxy, progress, flags, error); +} + +static gboolean +fu_devlink_component_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + return fu_device_cleanup(proxy, progress, flags, error); +} + +static void +fu_devlink_component_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 57, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 43, "reload"); +} + +FuDevlinkComponent * +fu_devlink_component_new(FuDevice *proxy, const gchar *logical_id) +{ + g_autoptr(FuDevlinkComponent) self = NULL; + + g_return_val_if_fail(logical_id != NULL, NULL); + + self = + g_object_new(FU_TYPE_DEVLINK_COMPONENT, "proxy", proxy, "logical-id", logical_id, NULL); + fu_device_add_instance_str(FU_DEVICE(self), "COMPONENT", logical_id); + return g_steal_pointer(&self); +} + +static void +fu_devlink_component_init(FuDevlinkComponent *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + fu_device_add_protocol(FU_DEVICE(self), "org.kernel.devlink"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FALLBACK); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_DEVLINK_DEVICE); + fu_device_register_private_flag(FU_DEVICE(self), + FU_DEVLINK_DEVICE_FLAG_OMIT_COMPONENT_NAME); +} + +static void +fu_devlink_component_finalize(GObject *object) +{ + FuDevlinkComponent *self = FU_DEVLINK_COMPONENT(object); + + if (self->instance_keys != NULL) + g_ptr_array_unref(self->instance_keys); + + G_OBJECT_CLASS(fu_devlink_component_parent_class)->finalize(object); +} + +static void +fu_devlink_component_class_init(FuDevlinkComponentClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + + object_class->finalize = fu_devlink_component_finalize; + device_class->write_firmware = fu_devlink_component_write_firmware; + device_class->set_progress = fu_devlink_component_set_progress; + device_class->probe = fu_devlink_component_probe; + device_class->reload = fu_devlink_component_reload; + device_class->activate = fu_devlink_component_activate; + device_class->prepare = fu_devlink_component_prepare; + device_class->cleanup = fu_devlink_component_cleanup; +} diff -Nru fwupd-2.0.8/plugins/devlink/fu-devlink-component.h fwupd-2.0.20/plugins/devlink/fu-devlink-component.h --- fwupd-2.0.8/plugins/devlink/fu-devlink-component.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/fu-devlink-component.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright 2025 NVIDIA Corporation & Affiliates + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_DEVLINK_COMPONENT (fu_devlink_component_get_type()) +G_DECLARE_FINAL_TYPE(FuDevlinkComponent, fu_devlink_component, FU, DEVLINK_COMPONENT, FuDevice) + +void +fu_devlink_component_add_instance_keys(FuDevlinkComponent *self, gchar **keys) + G_GNUC_NON_NULL(1, 2); + +FuDevlinkComponent * +fu_devlink_component_new(FuDevice *proxy, const gchar *logical_id) G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/plugins/devlink/fu-devlink-device.c fwupd-2.0.20/plugins/devlink/fu-devlink-device.c --- fwupd-2.0.8/plugins/devlink/fu-devlink-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/fu-devlink-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,1007 @@ +/* + * Copyright 2025 NVIDIA Corporation & Affiliates + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include +#include + +#include "fu-devlink-component.h" +#include "fu-devlink-device.h" +#include "fu-devlink-netlink.h" + +struct _FuDevlinkDevice { + FuDevice parent_instance; + gchar *bus_name; + gchar *dev_name; + FuDevlinkGenSocket *nlg; + FuKernelSearchPathLocker *search_path_locker; + GPtrArray *fixed_versions; +}; + +G_DEFINE_TYPE(FuDevlinkDevice, fu_devlink_device, FU_TYPE_DEVICE) + +typedef struct { + gchar *fixed; + gchar *running; + gchar *stored; +} FuDevlinkVersionInfo; + +static void +fu_devlink_device_version_info_free(FuDevlinkVersionInfo *version_info) +{ + g_free(version_info->fixed); + g_free(version_info->running); + g_free(version_info->stored); + g_free(version_info); +} + +static GHashTable * +fu_devlink_device_populate_attrs_map(const struct nlmsghdr *nlh) +{ + FuDevlinkVersionInfo *version_info; + struct nlattr *attr; + g_autoptr(GHashTable) version_table = NULL; + + version_table = g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify)fu_devlink_device_version_info_free); + mnl_attr_for_each(attr, nlh, sizeof(struct genlmsghdr)) + { + struct nlattr *ver_tb[DEVLINK_ATTR_MAX + 1] = {}; + g_autofree gchar *name = NULL; + g_autofree gchar *value = NULL; + + if (mnl_attr_get_type(attr) != DEVLINK_ATTR_INFO_VERSION_FIXED && + mnl_attr_get_type(attr) != DEVLINK_ATTR_INFO_VERSION_RUNNING && + mnl_attr_get_type(attr) != DEVLINK_ATTR_INFO_VERSION_STORED) + continue; + if (mnl_attr_parse_nested(attr, fu_devlink_netlink_attr_cb, ver_tb) != MNL_CB_OK) + continue; + if (ver_tb[DEVLINK_ATTR_INFO_VERSION_NAME] == NULL || + ver_tb[DEVLINK_ATTR_INFO_VERSION_VALUE] == NULL) + continue; + + name = + fu_strsafe(mnl_attr_get_str(ver_tb[DEVLINK_ATTR_INFO_VERSION_NAME]), G_MAXSIZE); + value = fu_strsafe(mnl_attr_get_str(ver_tb[DEVLINK_ATTR_INFO_VERSION_VALUE]), + G_MAXSIZE); + version_info = g_hash_table_lookup(version_table, name); + if (version_info == NULL) { + version_info = g_new0(FuDevlinkVersionInfo, 1); + g_hash_table_insert(version_table, g_strdup(name), version_info); + } + + /* There are three types of versions: "fixed", "running", "stored". When "running" + and "stored" are tightly coupled and describe one component, "fixed" is + a different beast. "fixed" is used for static device identification, like ASIC + ID, ASIC revision, BOARD ID, etc. */ + switch (mnl_attr_get_type(attr)) { + case DEVLINK_ATTR_INFO_VERSION_FIXED: + version_info->fixed = g_strdup(value); + break; + case DEVLINK_ATTR_INFO_VERSION_RUNNING: + version_info->running = g_strdup(value); + break; + case DEVLINK_ATTR_INFO_VERSION_STORED: + version_info->stored = g_strdup(value); + break; + default: + continue; + } + } + + return g_steal_pointer(&version_table); +} + +typedef struct { + const gchar *component_name; + gboolean *needs_activation; +} FuDevlinkDeviceNeedsActivationHelper; + +static gint +fu_devlink_device_needs_activation_cb(const struct nlmsghdr *nlh, gpointer data) +{ + FuDevlinkDeviceNeedsActivationHelper *helper = data; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + FuDevlinkVersionInfo *version_info; + g_autoptr(GHashTable) version_table = NULL; + + if (genl->cmd != DEVLINK_CMD_INFO_GET) + return MNL_CB_OK; + version_table = fu_devlink_device_populate_attrs_map(nlh); + version_info = g_hash_table_lookup(version_table, helper->component_name); + if (version_info == NULL) + return MNL_CB_OK; + *helper->needs_activation = version_info->stored != NULL && version_info->running != NULL && + g_strcmp0(version_info->stored, version_info->running) != 0; + return MNL_CB_OK; +} + +static gboolean +fu_devlink_device_needs_activation(FuDevlinkDevice *self, + const gchar *component_name, + gboolean *needs_activation, + GError **error) +{ + FuDevlinkDeviceNeedsActivationHelper helper = { + .component_name = component_name, + .needs_activation = needs_activation, + }; + struct nlmsghdr *nlh; + + /* prepare dev info command */ + nlh = fu_devlink_netlink_cmd_prepare(self->nlg, DEVLINK_CMD_INFO_GET, FALSE); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, self->bus_name); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, self->dev_name); + + /* send command and process response */ + if (!fu_devlink_netlink_msg_send_recv(self->nlg, + nlh, + fu_devlink_device_needs_activation_cb, + &helper, + error)) { + g_prefix_error_literal(error, "failed to get device info: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +/* perform firmware activation using devlink reload with fw_activate action */ +static gboolean +fu_devlink_device_ensure_activate(FuDevlinkDevice *self, + const gchar *component_name, + GError **error) +{ + struct nlmsghdr *nlh; + gboolean needs_activation = FALSE; + + if (!fu_devlink_device_needs_activation(self, component_name, &needs_activation, error)) + return FALSE; + if (!needs_activation) + return TRUE; + + g_debug("activating firmware for %s/%s", self->bus_name, self->dev_name); + + /* prepare reload command with fw_activate action */ + nlh = fu_devlink_netlink_cmd_prepare(self->nlg, DEVLINK_CMD_RELOAD, FALSE); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, self->bus_name); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, self->dev_name); + mnl_attr_put_u8(nlh, DEVLINK_ATTR_RELOAD_ACTION, DEVLINK_RELOAD_ACTION_FW_ACTIVATE); + + g_debug("sending devlink reload command with fw_activate action for %s/%s", + self->bus_name, + self->dev_name); + + /* send command and wait for response */ + if (!fu_devlink_netlink_msg_send(self->nlg, nlh, error)) { + g_prefix_error_literal(error, "failed to send devlink reload command: "); + return FALSE; + } + + /* success */ + g_debug("firmware activation completed for %s/%s", self->bus_name, self->dev_name); + return TRUE; +} + +/* flash status context for monitoring progress */ +typedef struct { + FuProgress *progress; + FuDevlinkDevice *self; + FuDevlinkGenSocket *nlg; +} FuDevlinkFlashMonHelper; + +/* flash send context for thread */ +typedef struct { + FuDevlinkDevice *self; + const gchar *component_name; + const gchar *filename; + GError **error; + GMainLoop *loop; +} FuDevlinkFlashSendHelper; + +/* handle flash update status and end messages */ +static gint +fu_devlink_device_flash_mon_cb(const struct nlmsghdr *nlh, gpointer data) +{ + FuDevlinkFlashMonHelper *helper = data; + const gchar *bus_name; + const gchar *dev_name; + guint64 done = 0; + guint64 total = 0; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + + /* only handle flash update status and end messages */ + if (genl->cmd != DEVLINK_CMD_FLASH_UPDATE_STATUS && + genl->cmd != DEVLINK_CMD_FLASH_UPDATE_END) + return MNL_CB_OK; + + /* parse message attributes */ + mnl_attr_parse(nlh, sizeof(*genl), fu_devlink_netlink_attr_cb, tb); + + /* verify this is for our device */ + if (tb[DEVLINK_ATTR_BUS_NAME] == NULL || tb[DEVLINK_ATTR_DEV_NAME] == NULL) + return MNL_CB_OK; + + bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + + if (g_strcmp0(bus_name, helper->self->bus_name) != 0 || + g_strcmp0(dev_name, helper->self->dev_name) != 0) + return MNL_CB_OK; + + if (genl->cmd == DEVLINK_CMD_FLASH_UPDATE_END) { + fu_progress_set_percentage(helper->progress, 100); + return MNL_CB_STOP; + } + + /* extract progress information from status message */ + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE] != NULL) + done = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_DONE]); + if (tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL] != NULL) + total = mnl_attr_get_u64(tb[DEVLINK_ATTR_FLASH_UPDATE_STATUS_TOTAL]); + + if (total > 0) + fu_progress_set_percentage_full(helper->progress, done, total); + + return MNL_CB_OK; +} + +/* send flash command */ +static gboolean +fu_devlink_device_flash_send(FuDevlinkDevice *self, + const gchar *component_name, + const gchar *filename, + GError **error) +{ + struct nlmsghdr *nlh; + + /* prepare flash update command */ + nlh = fu_devlink_netlink_cmd_prepare(self->nlg, DEVLINK_CMD_FLASH_UPDATE, FALSE); + + mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, self->bus_name); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, self->dev_name); + + if (component_name != NULL) { + mnl_attr_put_strz(nlh, DEVLINK_ATTR_FLASH_UPDATE_COMPONENT, component_name); + g_debug("sending flash update command for %s/%s with component %s and file %s", + self->bus_name, + self->dev_name, + component_name, + filename); + } else { + g_debug("sending flash update command for %s/%s with file %s", + self->bus_name, + self->dev_name, + filename); + } + + mnl_attr_put_strz(nlh, DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME, filename); + + /* send flash update command - this will block until completion */ + return fu_devlink_netlink_msg_send(self->nlg, nlh, error); +} + +/* thread function that sends the flash update command and quits main loop */ +static gpointer +fu_devlink_device_flash_send_thread(gpointer user_data) +{ + FuDevlinkFlashSendHelper *helper = user_data; + gboolean ret; + + g_debug("flash send thread started for %s/%s", + helper->self->bus_name, + helper->self->dev_name); + ret = fu_devlink_device_flash_send(helper->self, + helper->component_name, + helper->filename, + helper->error); + + /* signal completion by quitting the main loop */ + g_main_loop_quit(helper->loop); + + return GINT_TO_POINTER(ret ? 1 : 0); +} + +/* netlink callback for flash progress monitoring */ +static gboolean +fu_devlink_device_flash_mon_netlink_cb(GIOChannel *channel, + GIOCondition condition, + gpointer user_data) +{ + FuDevlinkFlashMonHelper *helper = user_data; + gsize len; + GIOStatus status; + g_autoptr(GError) error = NULL; + + if (condition & (G_IO_ERR | G_IO_HUP)) { + g_debug("devlink netlink socket error during flash monitoring"); + return FALSE; + } + + /* read netlink message via GIOChannel */ + status = g_io_channel_read_chars(channel, + fu_devlink_netlink_gen_socket_get_buf(helper->nlg), + FU_DEVLINK_NETLINK_BUF_SIZE, + &len, + &error); + if (status != G_IO_STATUS_NORMAL) { + if (error != NULL) + g_debug("failed to read devlink netlink message: %s", error->message); + return TRUE; + } + + /* process netlink messages */ + if (!fu_devlink_netlink_msg_run(helper->nlg, + len, + 0, + fu_devlink_device_flash_mon_cb, + helper, + &error)) { + g_warning("failed to process netlink message: %s", error->message); + /* we should not return FALSE here, because we want to continue monitoring */ + } + + /* success */ + return TRUE; +} + +static gboolean +fu_devlink_device_flash(FuDevlinkDevice *self, + const gchar *component_name, + const gchar *filename, + FuProgress *progress, + GError **error) +{ + FuDevlinkFlashMonHelper flash_mon_ctx = { + .progress = progress, + .self = self, + }; + g_autoptr(GMainLoop) loop = g_main_loop_new(NULL, FALSE); + FuDevlinkFlashSendHelper flash_send_ctx = { + .self = self, + .component_name = component_name, + .filename = filename, + .loop = loop, + .error = error, + }; + guint watch_id; + gint fd; + gpointer thread_result; + gboolean ret; + g_autoptr(FuDevlinkGenSocket) nlg = NULL; + g_autoptr(GIOChannel) channel = NULL; + g_autoptr(GThread) flash_send_thread = NULL; + + /* open netlink socket and subscribe to multicast */ + nlg = fu_devlink_netlink_gen_socket_open(NULL, error); + if (nlg == NULL) + return FALSE; + if (!fu_devlink_netlink_mcast_group_subscribe(nlg, error)) + return FALSE; + + flash_mon_ctx.nlg = nlg; + /* setup netlink channel and watch */ + fd = fu_devlink_netlink_gen_socket_get_fd(nlg); + channel = g_io_channel_unix_new(fd); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + watch_id = g_io_add_watch(channel, + G_IO_IN | G_IO_ERR | G_IO_HUP, + fu_devlink_device_flash_mon_netlink_cb, + &flash_mon_ctx); + + fu_progress_set_percentage(progress, 0); + + /* start the flash send thread */ + flash_send_thread = g_thread_new("devlink-flash-send", + fu_devlink_device_flash_send_thread, + &flash_send_ctx); + if (flash_send_thread == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to create flash send thread"); + g_source_remove(watch_id); + return FALSE; + } + + /* run the main loop until completion */ + g_main_loop_run(loop); + + g_source_remove(watch_id); + + /* get thread result */ + thread_result = g_thread_join(flash_send_thread); + g_steal_pointer(&flash_send_thread); + ret = GPOINTER_TO_INT(thread_result) != 0; + if (ret) + fu_progress_set_percentage(progress, 100); + + return ret; +} + +gboolean +fu_devlink_device_write_firmware_component(FuDevlinkDevice *self, + const gchar *component_name, + gboolean omit_component_name, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + gboolean ret; + const gchar *fw_search_path; + g_autofree gchar *fw_basename = NULL; + g_autofree gchar *fw_fullpath = NULL; + g_autoptr(GBytes) fw = NULL; + + /* get firmware data */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* create firmware file in the kernel search path for devlink */ + fw_search_path = fu_kernel_search_path_locker_get_path(self->search_path_locker); + fw_basename = g_strdup_printf("%s-%s-%s.bin", + self->bus_name, + self->dev_name, + omit_component_name ? "default" : component_name); + fw_fullpath = g_build_filename(fw_search_path, fw_basename, NULL); + g_debug("writing firmware to %s", fw_fullpath); + + /* write firmware to kernel search path */ + if (!fu_bytes_set_contents(fw_fullpath, fw, error)) + return FALSE; + + ret = fu_devlink_device_flash(self, + omit_component_name ? NULL : component_name, + fw_basename, + progress, + error); + + /* clean up temporary firmware file */ + if (g_unlink(fw_fullpath) != 0) { + g_warning("failed to delete temporary firmware file %s: %s", + fw_fullpath, + fwupd_strerror(errno)); + } + + if (!ret) + return FALSE; + + /* check if activation is needed */ + return fu_devlink_device_ensure_activate(self, component_name, error); +} + +static FuDevlinkComponent * +fu_devlink_device_get_component_by_logical_id(FuDevlinkDevice *self, const gchar *name) +{ + GPtrArray *children = fu_device_get_children(FU_DEVICE(self)); + for (guint i = 0; i < children->len; i++) { + FuDevice *component = g_ptr_array_index(children, i); + if (g_strcmp0(fu_device_get_logical_id(component), name) == 0) + return g_object_ref(FU_DEVLINK_COMPONENT(component)); + } + return NULL; +} + +typedef struct { + FuDevlinkDevice *self; + GHashTable *version_table; +} FuDevlinkDeviceUpdateComponentHelper; + +static void +fu_devlink_device_add_component_instance_strs(FuDevlinkComponent *component, + FuDevlinkDeviceUpdateComponentHelper *helper) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(helper->self); + if (self->fixed_versions == NULL) + return; + + /* There might me multiple arrays of fixed versions obtained from quirk file. + Iterate over all of them and add instance strings to component device. */ + for (guint i = 0; i < self->fixed_versions->len; i++) { + gboolean complete_set = TRUE; + const gchar **names = g_ptr_array_index(self->fixed_versions, i); + g_autoptr(GStrvBuilder) keys_builder = g_strv_builder_new(); + + for (guint j = 0; names[j] != NULL; j++) { + FuDevlinkVersionInfo *version_info = + g_hash_table_lookup(helper->version_table, names[j]); + g_autofree gchar *key = NULL; + + if (version_info == NULL || version_info->fixed == NULL) { + complete_set = FALSE; + continue; + } + key = g_ascii_strup(names[j], -1); + g_strv_builder_add(keys_builder, key); + + /* avoid re-insertion of the same key */ + if (fu_device_get_instance_str(FU_DEVICE(component), key) == NULL) { + fu_device_add_instance_str(FU_DEVICE(component), + key, + version_info->fixed); + } + } + /* In case all keys are present in version table obtained from kernel, + add the set to component to build instance id for it during probe. */ + if (complete_set) + fu_devlink_component_add_instance_keys(component, + g_strv_builder_end(keys_builder)); + } +} + +static void +fu_devlink_device_update_component_cb(gpointer key, gpointer value, gpointer user_data) +{ + FuDevlinkDeviceUpdateComponentHelper *helper = user_data; + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(helper->self); + const gchar *name = (const gchar *)key; + FuDevlinkVersionInfo *version_info = value; + const gchar *version; + g_autofree gchar *instance_id = g_strdup_printf("DEVLINK\\COMPONENT_%s", name); + g_autoptr(FuDevlinkComponent) component = NULL; + g_autoptr(GError) error_local = NULL; + + /* "fw.bootloader" is a special case. If there is a fixed version of it present, + set it as the bootloader version. */ + if (g_strcmp0(name, "fw.bootloader") == 0) + fu_device_set_version_bootloader(FU_DEVICE(self), version_info->fixed); + + /* A component and running-stored tuple has 1:1 relationship. No guarantee + that both are present, if either is present, try to create component. */ + if (version_info->stored != NULL) + version = version_info->stored; + else if (version_info->running != NULL) + version = version_info->running; + else + return; + + component = fu_devlink_device_get_component_by_logical_id(self, name); + if (component != NULL) { + fu_device_set_version(FU_DEVICE(component), version); + g_debug("updated component %s (version: %s)", name, version); + return; + } + + /* create new component and lookup quirk to added as a child */ + component = fu_devlink_component_new(FU_DEVICE(self), name); + fu_device_add_instance_id_full(FU_DEVICE(component), + instance_id, + FU_DEVICE_INSTANCE_FLAG_QUIRKS); + if (fu_device_get_name(FU_DEVICE(component)) == NULL) { + g_debug("ignoring %s", name); + return; + } + fu_device_incorporate(FU_DEVICE(component), + FU_DEVICE(self), + FU_DEVICE_INCORPORATE_FLAG_INSTANCE_KEYS); + fu_devlink_device_add_component_instance_strs(component, helper); + fu_device_set_version_format(FU_DEVICE(component), fu_version_guess_format(version)); + fu_device_set_version(FU_DEVICE(component), version); + if (!fu_device_probe(FU_DEVICE(component), &error_local)) { + g_warning("failed to probe %s: %s", name, error_local->message); + return; + } + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(component)); + g_debug("added component %s (version: %s)", name, version); +} + +/* callback for parsing devlink dev info response */ +static gint +fu_devlink_device_info_cb(const struct nlmsghdr *nlh, gpointer data) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(data); + FuDevlinkDeviceUpdateComponentHelper helper = {0}; + GPtrArray *children = fu_device_get_children(FU_DEVICE(self)); + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + g_autoptr(GHashTable) version_table = NULL; + g_autoptr(GPtrArray) components_to_remove = g_ptr_array_new(); + + if (genl->cmd != DEVLINK_CMD_INFO_GET) + return MNL_CB_OK; + + /* parse main attributes */ + mnl_attr_parse(nlh, sizeof(*genl), fu_devlink_netlink_attr_cb, tb); + + version_table = fu_devlink_device_populate_attrs_map(nlh); + + /* remove components that are not in the attrs map */ + for (guint i = 0; i < children->len; i++) { + FuDevice *component = g_ptr_array_index(children, i); + const gchar *logical_id = fu_device_get_logical_id(component); + FuDevlinkVersionInfo *version_info = g_hash_table_lookup(version_table, logical_id); + + if (version_info == NULL) { + g_debug("removed component %s", logical_id); + g_ptr_array_add(components_to_remove, component); + } + } + for (guint i = 0; i < components_to_remove->len; i++) { + FuDevice *component = g_ptr_array_index(components_to_remove, i); + fu_device_remove_child(FU_DEVICE(self), component); + } + + helper.self = self; + helper.version_table = version_table; + g_hash_table_foreach(version_table, fu_devlink_device_update_component_cb, &helper); + + return MNL_CB_OK; +} + +/* get device information using devlink dev info */ +static gboolean +fu_devlink_device_get_info(FuDevlinkDevice *self, GError **error) +{ + struct nlmsghdr *nlh; + + /* prepare dev info command */ + nlh = fu_devlink_netlink_cmd_prepare(self->nlg, DEVLINK_CMD_INFO_GET, FALSE); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, self->bus_name); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, self->dev_name); + + /* send command and process response */ + if (!fu_devlink_netlink_msg_send_recv(self->nlg, + nlh, + fu_devlink_device_info_cb, + self, + error)) { + g_prefix_error_literal(error, "failed to get device info: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_devlink_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(device); + + fwupd_codec_string_append(str, idt, "BusName", self->bus_name); + fwupd_codec_string_append(str, idt, "DevName", self->dev_name); +} + +static gboolean +fu_devlink_device_setup(FuDevice *device, GError **error) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(device); + g_autofree gchar *subsystem = NULL; + g_autofree gchar *summary = NULL; + + /* check if device has been properly initialized */ + if (self->bus_name == NULL || self->dev_name == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "devlink device not properly initialized"); + return FALSE; + } + + subsystem = g_ascii_strup(self->bus_name, -1); + + /* set summary with devlink handle for better user visibility */ + summary = g_strdup_printf("Devlink device (%s/%s)", self->bus_name, self->dev_name); + fu_device_set_summary(device, summary); + + /* use quirk database for a better name */ + if (fu_device_get_vid(device) != 0 && fu_device_get_pid(device) != 0) { + fu_device_add_instance_u16(device, "VEN", fu_device_get_vid(device)); + fu_device_add_instance_u16(device, "DEV", fu_device_get_pid(device)); + if (!fu_device_build_instance_id_full(device, + FU_DEVICE_INSTANCE_FLAG_QUIRKS, + error, + subsystem, + "VEN", + "DEV", + NULL)) { + g_prefix_error_literal(error, "failed to create quirk for name: "); + return FALSE; + } + } + + /* get device information and version */ + return fu_devlink_device_get_info(self, error); +} + +const gchar * +fu_devlink_device_get_bus_name(FuDevlinkDevice *self) +{ + return self->bus_name; +} + +static void +fu_devlink_device_set_bus_name(FuDevlinkDevice *self, const gchar *bus_name) +{ + g_free(self->bus_name); + self->bus_name = g_strdup(bus_name); +} + +static void +fu_devlink_device_set_dev_name(FuDevlinkDevice *self, const gchar *dev_name) +{ + g_free(self->dev_name); + self->dev_name = g_strdup(dev_name); +} + +FuDevice * +fu_devlink_device_new(FuContext *ctx, + const gchar *bus_name, + const gchar *dev_name, + const gchar *serial_number) +{ + g_autoptr(FuDevlinkDevice) self = NULL; + g_autofree gchar *device_id = NULL; + + g_return_val_if_fail(bus_name != NULL, NULL); + g_return_val_if_fail(dev_name != NULL, NULL); + + /* create object and assign the strings */ + self = g_object_new(FU_TYPE_DEVLINK_DEVICE, "context", ctx, NULL); + fu_devlink_device_set_bus_name(self, bus_name); + fu_devlink_device_set_dev_name(self, dev_name); + + if (serial_number != NULL) { + fu_device_set_serial(FU_DEVICE(self), serial_number); + fu_device_set_physical_id(FU_DEVICE(self), serial_number); + } else { + device_id = g_strdup_printf("%s/%s", bus_name, dev_name); + fu_device_set_physical_id(FU_DEVICE(self), device_id); + } + + return FU_DEVICE(g_steal_pointer(&self)); +} + +static gboolean +fu_devlink_device_open(FuDevice *device, GError **error) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(device); + + /* open devlink netlink socket */ + self->nlg = fu_devlink_netlink_gen_socket_open(device, error); + if (self->nlg == NULL) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_devlink_device_close(FuDevice *device, GError **error) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(device); + + /* close devlink netlink socket */ + fu_devlink_netlink_gen_socket_close(self->nlg); + + return TRUE; +} + +static FuKernelSearchPathLocker * +fu_devlink_device_search_path_locker_new(FuDevlinkDevice *self, GError **error) +{ + g_autofree gchar *devlink_fw_dir = NULL; + g_autoptr(FuKernelSearchPathLocker) locker = NULL; + + /* create a directory to store firmware files for devlink plugin */ + devlink_fw_dir = fu_path_build(FU_PATH_KIND_CACHEDIR_PKG, "devlink", "firmware", NULL); + if (g_mkdir_with_parents(devlink_fw_dir, 0700) == -1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to create '%s': %s", + devlink_fw_dir, + fwupd_strerror(errno)); + return NULL; + } + locker = fu_kernel_search_path_locker_new(devlink_fw_dir, error); + if (locker == NULL) + return NULL; + return g_steal_pointer(&locker); +} + +static gboolean +fu_devlink_device_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(device); + + /* setup kernel firmware search path for devlink device */ + self->search_path_locker = fu_devlink_device_search_path_locker_new(self, error); + if (self->search_path_locker == NULL) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_devlink_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(device); + + /* restore the firmware search path */ + g_clear_object(&self->search_path_locker); + + return TRUE; +} + +static void +fu_devlink_device_add_json(FuDevice *device, JsonBuilder *builder, FwupdCodecFlags flags) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(device); + GPtrArray *events = fu_device_get_events(device); + + /* add device type identifier */ + fwupd_codec_json_append(builder, "GType", "FuDevlinkDevice"); + + /* add devlink-specific properties for regular devices */ + fwupd_codec_json_append(builder, "BusName", self->bus_name); + fwupd_codec_json_append(builder, "DevName", self->dev_name); + + /* serialize recorded events */ + if (events->len > 0) { + json_builder_set_member_name(builder, "Events"); + json_builder_begin_array(builder); + for (guint i = 0; i < events->len; i++) { + FuDeviceEvent *event = g_ptr_array_index(events, i); + + json_builder_begin_object(builder); + fwupd_codec_to_json(FWUPD_CODEC(event), + builder, + events->len > 1000 ? flags | FWUPD_CODEC_FLAG_COMPRESSED + : flags); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + } +} + +static gboolean +fu_devlink_device_from_json(FuDevice *device, JsonObject *json_object, GError **error) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(device); + const gchar *bus_name; + const gchar *dev_name; + g_autofree gchar *device_id = NULL; + + /* devlink-specific properties */ + bus_name = json_object_get_string_member_with_default(json_object, "BusName", NULL); + dev_name = json_object_get_string_member_with_default(json_object, "DevName", NULL); + + if (bus_name == NULL || dev_name == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "BusName and DevName required for devlink device"); + return FALSE; + } + + fu_devlink_device_set_bus_name(self, bus_name); + fu_devlink_device_set_dev_name(self, dev_name); + + device_id = g_strdup_printf("%s/%s", bus_name, dev_name); + fu_device_set_physical_id(FU_DEVICE(self), device_id); + fu_device_set_name(FU_DEVICE(self), device_id); + fu_device_set_backend_id(device, device_id); + + /* array of events */ + if (json_object_has_member(json_object, "Events")) { + JsonArray *json_array = json_object_get_array_member(json_object, "Events"); + + for (guint i = 0; i < json_array_get_length(json_array); i++) { + JsonNode *node_tmp = json_array_get_element(json_array, i); + g_autoptr(FuDeviceEvent) event = fu_device_event_new(NULL); + + if (!fwupd_codec_from_json(FWUPD_CODEC(event), node_tmp, error)) + return FALSE; + fu_device_add_event(device, event); + } + } + + /* success */ + return TRUE; +} + +static void +fu_devlink_device_incorporate(FuDevice *device, FuDevice *donor_device) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(device); + FuDevlinkDevice *donor = FU_DEVLINK_DEVICE(donor_device); + + g_return_if_fail(FU_IS_DEVLINK_DEVICE(device)); + g_return_if_fail(FU_IS_DEVLINK_DEVICE(donor_device)); + + /* copy bus_name if not already set */ + if (self->bus_name == NULL && donor->bus_name != NULL) + fu_devlink_device_set_bus_name(self, donor->bus_name); + + /* copy dev_name if not already set */ + if (self->dev_name == NULL && donor->dev_name != NULL) + fu_devlink_device_set_dev_name(self, donor->dev_name); +} + +static void +fu_devlink_device_add_fixed_versions(FuDevlinkDevice *self, gchar **fixed_versions) +{ + if (self->fixed_versions == NULL) + self->fixed_versions = g_ptr_array_new_with_free_func((GDestroyNotify)g_strfreev); + g_ptr_array_add(self->fixed_versions, fixed_versions); +} + +static gboolean +fu_devlink_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(device); + + if (g_strcmp0(key, "DevlinkFixedVersions") == 0) { + fu_devlink_device_add_fixed_versions(self, g_strsplit(value, ",", -1)); + return TRUE; + } + + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_devlink_device_init(FuDevlinkDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "org.kernel.devlink"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); + fu_device_add_possible_plugin(FU_DEVICE(self), "devlink"); +} + +static void +fu_devlink_device_finalize(GObject *object) +{ + FuDevlinkDevice *self = FU_DEVLINK_DEVICE(object); + + g_free(self->bus_name); + g_free(self->dev_name); + if (self->fixed_versions != NULL) + g_ptr_array_unref(self->fixed_versions); + + G_OBJECT_CLASS(fu_devlink_device_parent_class)->finalize(object); +} + +static void +fu_devlink_device_class_init(FuDevlinkDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + + object_class->finalize = fu_devlink_device_finalize; + device_class->open = fu_devlink_device_open; + device_class->close = fu_devlink_device_close; + device_class->prepare = fu_devlink_device_prepare; + device_class->cleanup = fu_devlink_device_cleanup; + device_class->to_string = fu_devlink_device_to_string; + device_class->setup = fu_devlink_device_setup; + device_class->reload = fu_devlink_device_setup; + device_class->add_json = fu_devlink_device_add_json; + device_class->from_json = fu_devlink_device_from_json; + device_class->incorporate = fu_devlink_device_incorporate; + device_class->set_quirk_kv = fu_devlink_device_set_quirk_kv; +} diff -Nru fwupd-2.0.8/plugins/devlink/fu-devlink-device.h fwupd-2.0.20/plugins/devlink/fu-devlink-device.h --- fwupd-2.0.8/plugins/devlink/fu-devlink-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/fu-devlink-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,30 @@ +/* + * Copyright 2025 NVIDIA Corporation & Affiliates + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_DEVLINK_DEVICE (fu_devlink_device_get_type()) +G_DECLARE_FINAL_TYPE(FuDevlinkDevice, fu_devlink_device, FU, DEVLINK_DEVICE, FuDevice) + +FuDevice * +fu_devlink_device_new(FuContext *ctx, + const gchar *bus_name, + const gchar *dev_name, + const gchar *serial_number) G_GNUC_NON_NULL(1, 2, 3); + +const gchar * +fu_devlink_device_get_bus_name(FuDevlinkDevice *self) G_GNUC_NON_NULL(1); + +gboolean +fu_devlink_device_write_firmware_component(FuDevlinkDevice *self, + const gchar *component_name, + gboolean omit_component_name, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) G_GNUC_NON_NULL(1, 2, 4, 5); diff -Nru fwupd-2.0.8/plugins/devlink/fu-devlink-netlink.c fwupd-2.0.20/plugins/devlink/fu-devlink-netlink.c --- fwupd-2.0.8/plugins/devlink/fu-devlink-netlink.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/fu-devlink-netlink.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,653 @@ +/* + * Copyright 2025 NVIDIA Corporation & Affiliates + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "fu-devlink-netlink.h" + +static struct nlmsghdr * +fu_devlink_netlink_msg_prepare(gpointer buf, + guint32 nlmsg_type, + gboolean dump, + gpointer extra_header, + gsize extra_header_size) +{ + struct nlmsghdr *nlh; + gpointer eh; + + nlh = mnl_nlmsg_put_header(buf); + nlh->nlmsg_type = nlmsg_type; + nlh->nlmsg_flags = NLM_F_REQUEST | NLM_F_ACK; + if (dump) + nlh->nlmsg_flags |= NLM_F_DUMP; + nlh->nlmsg_seq = (guint32)time(NULL); + + eh = mnl_nlmsg_put_extra_header(nlh, extra_header_size); + memcpy(eh, extra_header, extra_header_size); /* nocheck:blocked */ + + return nlh; +} + +/* attribute parser callback for netlink error attributes */ +static gint +fu_devlink_netlink_nlmsgerr_attr_cb(const struct nlattr *attr, gpointer data) +{ + const struct nlattr **tb = data; + gint type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, NLMSGERR_ATTR_MAX) < 0) + return MNL_CB_OK; + + tb[type] = attr; + return MNL_CB_OK; +} + +typedef struct { + gpointer user_data; + mnl_cb_t user_cb; + GError **error; +} FuDevlinkCbHelper; + +static gboolean +fu_devlink_netlink_error_cb_extack(const struct nlmsghdr *nlh, GError **error) +{ + const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[NLMSGERR_ATTR_MAX + 1] = {}; + guint hlen = sizeof(struct nlmsgerr); + + if ((nlh->nlmsg_flags & NLM_F_ACK_TLVS) == 0) + return FALSE; + if ((nlh->nlmsg_flags & NLM_F_CAPPED) == 0) + hlen += mnl_nlmsg_get_payload_len(&err->msg); + if (mnl_attr_parse(nlh, hlen, fu_devlink_netlink_nlmsgerr_attr_cb, tb) != MNL_CB_OK) + return FALSE; + if (tb[NLMSGERR_ATTR_MSG] == NULL) + return FALSE; + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "netlink error: %s; %s", + fwupd_strerror(-err->error), + mnl_attr_get_str(tb[NLMSGERR_ATTR_MSG])); + return TRUE; +} + +/* error callback parses extack messages */ +static gint +fu_devlink_netlink_error_cb(const struct nlmsghdr *nlh, gpointer data) +{ + const struct nlmsgerr *err = mnl_nlmsg_get_payload(nlh); + FuDevlinkCbHelper *helper = data; + + if (mnl_nlmsg_get_payload_len(nlh) < sizeof(*err)) + return MNL_CB_STOP; + if (err->error == 0) + return MNL_CB_STOP; + if (fu_devlink_netlink_error_cb_extack(nlh, helper->error)) + return MNL_CB_ERROR; + g_set_error(helper->error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "netlink error: %s", + fwupd_strerror(-err->error)); + return MNL_CB_ERROR; +} + +static gint +fu_devlink_netlink_noop_cb(const struct nlmsghdr *nlh, gpointer data) +{ + return MNL_CB_STOP; +} + +static gint +fu_devlink_netlink_done_cb(const struct nlmsghdr *nlh, gpointer data) +{ + return MNL_CB_STOP; +} + +/* data callback wrapper that calls the user callback */ +static gint +fu_devlink_netlink_data_cb(const struct nlmsghdr *nlh, gpointer data) +{ + FuDevlinkCbHelper *helper = data; + + if (helper->user_cb == NULL) + return MNL_CB_OK; + return helper->user_cb(nlh, helper->user_data); +} + +static gint +fu_devlink_netlink_msg_cb_run(FuDevlinkGenSocket *nlg, + gsize len, + guint32 seq, + mnl_cb_t cb, + gpointer data, + GError **error) +{ + guint32 portid; + FuDevlinkCbHelper helper = { + .user_data = data, + .user_cb = cb, + .error = error, + }; + mnl_cb_t cbs[NLMSG_MIN_TYPE] = { + [NLMSG_NOOP] = fu_devlink_netlink_noop_cb, + [NLMSG_ERROR] = fu_devlink_netlink_error_cb, + [NLMSG_DONE] = fu_devlink_netlink_done_cb, + [NLMSG_OVERRUN] = fu_devlink_netlink_noop_cb, + }; + + if (nlg->is_emulated) { + struct nlmsghdr *nlh = (gpointer)nlg->buf; + + nlh->nlmsg_seq = seq; + portid = nlh->nlmsg_pid; + } else { + portid = mnl_socket_get_portid(nlg->nl); + } + + return mnl_cb_run2(nlg->buf, + len, + seq, + portid, + fu_devlink_netlink_data_cb, + &helper, + cbs, + MNL_ARRAY_SIZE(cbs)); +} + +/* run callback on netlink messages */ +gboolean +fu_devlink_netlink_msg_run(FuDevlinkGenSocket *nlg, + gsize len, + guint32 seq, + mnl_cb_t cb, + gpointer data, + GError **error) +{ + g_return_val_if_fail(nlg != NULL, FALSE); + return fu_devlink_netlink_msg_cb_run(nlg, len, seq, cb, data, error) != MNL_CB_ERROR; +} + +/* callback for extracting event_id from devlink messages */ +static gint +fu_devlink_netlink_event_id_msg_cb(const struct nlmsghdr *nlh, gpointer data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + GStrvBuilder *tuples_builder = data; + + switch (genl->cmd) { + case DEVLINK_CMD_GET: + g_strv_builder_add(tuples_builder, "cmd=get"); + break; + case DEVLINK_CMD_RELOAD: + g_strv_builder_add(tuples_builder, "cmd=reload"); + break; + case DEVLINK_CMD_INFO_GET: + g_strv_builder_add(tuples_builder, "cmd=info_get"); + break; + case DEVLINK_CMD_FLASH_UPDATE: + g_strv_builder_add(tuples_builder, "cmd=flash_update"); + break; + default: + break; + } + + /* parse attributes */ + mnl_attr_parse(nlh, sizeof(*genl), fu_devlink_netlink_attr_cb, tb); + + /* extract attribute name and value based on type */ + if (tb[DEVLINK_ATTR_BUS_NAME] != NULL) { + g_strv_builder_add( + tuples_builder, + g_strdup_printf("bus_name=%s", mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]))); + } + if (tb[DEVLINK_ATTR_DEV_NAME] != NULL) { + g_strv_builder_add( + tuples_builder, + g_strdup_printf("dev_name=%s", mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]))); + } + if (tb[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME] != NULL) { + g_strv_builder_add( + tuples_builder, + g_strdup_printf("file_name=%s", + mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_FILE_NAME]))); + } + if (tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT] != NULL) { + g_strv_builder_add( + tuples_builder, + g_strdup_printf("component=%s", + mnl_attr_get_str(tb[DEVLINK_ATTR_FLASH_UPDATE_COMPONENT]))); + } + if (tb[DEVLINK_ATTR_RELOAD_ACTION] != NULL) { + g_strv_builder_add( + tuples_builder, + g_strdup_printf("reload_action=%u", + mnl_attr_get_u8(tb[DEVLINK_ATTR_RELOAD_ACTION]))); + } + + return MNL_CB_OK; +} + +static gchar * +fu_devlink_netlink_get_event_id(FuDevlinkGenSocket *nlg, struct nlmsghdr *nlh) +{ + g_autoptr(GStrvBuilder) tuples_builder = g_strv_builder_new(); + g_auto(GStrv) strv = NULL; + + /* parse the message and build get event_id tuples */ + mnl_cb_run(nlh, + nlh->nlmsg_len, + nlh->nlmsg_seq, + nlh->nlmsg_pid, + fu_devlink_netlink_event_id_msg_cb, + tuples_builder); + + strv = g_strv_builder_end(tuples_builder); + + /* return the constructed event_id */ + return g_strjoinv(",", strv); +} + +/* receive and run callback on netlink messages */ +static gboolean +fu_devlink_netlink_msg_recv_run(FuDevlinkGenSocket *nlg, + struct nlmsghdr *nlh, + mnl_cb_t cb, + gpointer data, + GError **error) +{ + FuDeviceEvent *event = NULL; + guint i = 0; + guint32 seq = nlh->nlmsg_seq; + gint rc; + g_autofree gchar *event_id = NULL; + + /* generate event ID if device is available and we need emulation/recording */ + if (nlg->is_emulated || nlg->save_events) + event_id = fu_devlink_netlink_get_event_id(nlg, nlh); + + if (nlg->is_emulated) { + event = fu_device_load_event(nlg->device, event_id, error); + if (event == NULL) + return FALSE; + } + + if (nlg->save_events) + event = fu_device_save_event(nlg->device, event_id); + + do { + if (nlg->is_emulated) { + const guint8 *response_buf; + gsize response_len; + g_autofree gchar *response_key = g_strdup_printf("ResponseData%u", i++); + g_autoptr(GBytes) response_data = + fu_device_event_get_bytes(event, response_key, NULL); + + if (response_data == NULL) + return TRUE; + + response_buf = g_bytes_get_data(response_data, &response_len); + if (response_len > (gsize)FU_DEVLINK_NETLINK_BUF_SIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "recorded response too large: %zu > %ld", + response_len, + FU_DEVLINK_NETLINK_BUF_SIZE); + return FALSE; + } + memcpy(nlg->buf, response_buf, response_len); /* nocheck:blocked */ + rc = response_len; + } else { + rc = mnl_socket_recvfrom(nlg->nl, nlg->buf, MNL_SOCKET_BUFFER_SIZE); + if (rc == 0) + return TRUE; + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to receive netlink message: %s", + fwupd_strerror(errno)); + return FALSE; + } + if (nlg->save_events) { + g_autoptr(GBytes) response_data = g_bytes_new(nlg->buf, rc); + g_autofree gchar *response_key = + g_strdup_printf("ResponseData%u", i++); + + fu_device_event_set_bytes(event, response_key, response_data); + } + } + + rc = fu_devlink_netlink_msg_cb_run(nlg, rc, seq, cb, data, error); + } while (rc > MNL_CB_STOP); + + return rc == MNL_CB_ERROR ? FALSE : TRUE; +} + +/* send message and receive response */ +gboolean +fu_devlink_netlink_msg_send_recv(FuDevlinkGenSocket *nlg, + struct nlmsghdr *nlh, + mnl_cb_t cb, + gpointer data, + GError **error) +{ + gint rc; + + g_return_val_if_fail(nlg != NULL, FALSE); + g_return_val_if_fail(nlh != NULL, FALSE); + + if (!nlg->is_emulated) { + /* send netlink message */ + rc = mnl_socket_sendto(nlg->nl, nlh, nlh->nlmsg_len); + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to send netlink message: %s", + fwupd_strerror(errno)); + return FALSE; + } + } + return fu_devlink_netlink_msg_recv_run(nlg, nlh, cb, data, error); +} + +gboolean +fu_devlink_netlink_msg_send(FuDevlinkGenSocket *nlg, struct nlmsghdr *nlh, GError **error) +{ + g_return_val_if_fail(nlg != NULL, FALSE); + return fu_devlink_netlink_msg_send_recv(nlg, nlh, NULL, NULL, error); +} + +/* generic netlink control attribute callback */ +static gint +fu_devlink_netlink_genl_ctrl_attr_cb(const struct nlattr *attr, gpointer data) +{ + const struct nlattr **tb = data; + gint type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MAX) < 0) + return MNL_CB_OK; + + tb[type] = attr; + return MNL_CB_OK; +} + +static gint +fu_devlink_netlink_genl_mcast_group_attr_cb(const struct nlattr *attr, gpointer data) +{ + const struct nlattr **tb = data; + gint type = mnl_attr_get_type(attr); + + if (mnl_attr_type_valid(attr, CTRL_ATTR_MCAST_GRP_MAX) < 0) + return MNL_CB_OK; + + tb[type] = attr; + return MNL_CB_OK; +} + +/* family resolution callback */ +static gint +fu_devlink_netlink_fu_devlink_netlink_genl_family_get_cb(const struct nlmsghdr *nlh, gpointer data) +{ + FuDevlinkGenSocket *nlg = data; + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[CTRL_ATTR_MAX + 1] = {}; + struct nlattr *mcgrp; + + g_return_val_if_fail(nlh != NULL, -1); + + mnl_attr_parse(nlh, sizeof(*genl), fu_devlink_netlink_genl_ctrl_attr_cb, tb); + if (tb[CTRL_ATTR_FAMILY_ID] == NULL) + return MNL_CB_ERROR; + nlg->family_id = mnl_attr_get_u16(tb[CTRL_ATTR_FAMILY_ID]); + + if (tb[CTRL_ATTR_MCAST_GROUPS] == NULL) + return MNL_CB_ERROR; + + mnl_attr_for_each_nested(mcgrp, tb[CTRL_ATTR_MCAST_GROUPS]) + { + struct nlattr *tb_grp[CTRL_ATTR_MCAST_GRP_MAX + 1] = {}; + + mnl_attr_parse_nested(mcgrp, fu_devlink_netlink_genl_mcast_group_attr_cb, tb_grp); + + if (tb_grp[CTRL_ATTR_MCAST_GRP_NAME] == NULL || + tb_grp[CTRL_ATTR_MCAST_GRP_ID] == NULL) + continue; + + if (g_strcmp0(mnl_attr_get_str(tb_grp[CTRL_ATTR_MCAST_GRP_NAME]), + DEVLINK_GENL_MCGRP_CONFIG_NAME) == 0) { + nlg->config_group_id = mnl_attr_get_u32(tb_grp[CTRL_ATTR_MCAST_GRP_ID]); + return MNL_CB_OK; + } + } + + return MNL_CB_ERROR; +} + +/* get generic netlink family ID */ +static gboolean +fu_devlink_netlink_genl_family_get(FuDevlinkGenSocket *nlg, + const gchar *family_name, + GError **error) +{ + struct genlmsghdr hdr = { + .cmd = CTRL_CMD_GETFAMILY, + .version = 0x1, + }; + struct nlmsghdr *nlh; + + g_return_val_if_fail(nlg != NULL, FALSE); + g_return_val_if_fail(family_name != NULL, FALSE); + + nlh = fu_devlink_netlink_msg_prepare(nlg->buf, GENL_ID_CTRL, FALSE, &hdr, sizeof(hdr)); + mnl_attr_put_strz(nlh, CTRL_ATTR_FAMILY_NAME, family_name); + return fu_devlink_netlink_msg_send_recv( + nlg, + nlh, + fu_devlink_netlink_fu_devlink_netlink_genl_family_get_cb, + nlg, + error); +} + +/* nocheck:name + * open generic netlink socket for devlink family */ +FuDevlinkGenSocket * +fu_devlink_netlink_gen_socket_open(FuDevice *device, GError **error) +{ + g_autoptr(FuDevlinkGenSocket) nlg = NULL; + gint one = 1; + gint rc; + + g_return_val_if_fail(FU_IS_DEVICE(device) || device == NULL, NULL); + + /* allocate and initialize structure */ + nlg = g_new0(FuDevlinkGenSocket, 1); + + /* initialize structure with properly aligned buffer */ + nlg->buf = g_malloc0(FU_DEVLINK_NETLINK_BUF_SIZE); + + if (device != NULL) { + nlg->device = g_object_ref(device); + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) { + /* skip actual socket operations if emulated */ + /* create dummy pipe for emulation */ +#if GLIB_CHECK_VERSION(2, 77, 0) + int flags = O_CLOEXEC; +#else + int flags = FD_CLOEXEC; +#endif + if (!g_unix_open_pipe(nlg->pipe_fds, flags, error)) { + g_prefix_error_literal(error, + "failed to create pipe for emulation: "); + return NULL; + } + nlg->is_emulated = TRUE; + + /* set family ID to a valid value */ + nlg->family_id = NLMSG_MIN_TYPE + 1; + return g_steal_pointer(&nlg); + } + } + + /* open netlink socket */ + nlg->nl = mnl_socket_open(NETLINK_GENERIC); + if (nlg->nl == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to open netlink socket: %s", + fwupd_strerror(errno)); + return NULL; + } + + rc = mnl_socket_setsockopt(nlg->nl, NETLINK_CAP_ACK, &one, sizeof(one)); + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to set netlink CAP_ACK: %s", + fwupd_strerror(errno)); + return NULL; + } + + rc = mnl_socket_setsockopt(nlg->nl, NETLINK_EXT_ACK, &one, sizeof(one)); + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to set netlink EXT_ACK: %s", + fwupd_strerror(errno)); + return NULL; + } + + rc = mnl_socket_bind(nlg->nl, 0, MNL_SOCKET_AUTOPID); + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to bind netlink socket: %s", + fwupd_strerror(errno)); + return NULL; + } + + /* resolve devlink family ID dynamically */ + if (!fu_devlink_netlink_genl_family_get(nlg, DEVLINK_GENL_NAME, error)) { + g_prefix_error_literal(error, "failed to resolve devlink family ID: "); + return NULL; + } + + if (device != NULL && + fu_context_has_flag(fu_device_get_context(nlg->device), FU_CONTEXT_FLAG_SAVE_EVENTS)) { + nlg->save_events = TRUE; + } + + return g_steal_pointer(&nlg); +} + +/* close generic netlink socket */ +void +fu_devlink_netlink_gen_socket_close(FuDevlinkGenSocket *nlg) +{ + if (nlg == NULL) + return; + if (nlg->nl != NULL) + mnl_socket_close(nlg->nl); + /* close both ends of the pipe if they were created */ + if (nlg->pipe_fds[0] != 0) + g_close(nlg->pipe_fds[0], NULL); + if (nlg->pipe_fds[1] != 0) + g_close(nlg->pipe_fds[1], NULL); + g_free(nlg->buf); + if (nlg->device != NULL) + g_object_unref(nlg->device); + g_free(nlg); +} + +gint +fu_devlink_netlink_gen_socket_get_fd(FuDevlinkGenSocket *nlg) +{ + g_return_val_if_fail(nlg != NULL, -1); + + /* return read side of pipe for emulated devices */ + if (nlg->is_emulated) + return nlg->pipe_fds[0]; /* read end of the pipe */ + return mnl_socket_get_fd(nlg->nl); +} + +gchar * +fu_devlink_netlink_gen_socket_get_buf(FuDevlinkGenSocket *nlg) +{ + g_return_val_if_fail(nlg != NULL, NULL); + return nlg->buf; +} + +/* prepare devlink command message */ +struct nlmsghdr * +fu_devlink_netlink_cmd_prepare(FuDevlinkGenSocket *nlg, guint8 cmd, gboolean dump) +{ + struct genlmsghdr hdr = { + .cmd = cmd, + .version = DEVLINK_GENL_VERSION, + }; + + g_return_val_if_fail(nlg != NULL, NULL); + + return fu_devlink_netlink_msg_prepare(nlg->buf, nlg->family_id, dump, &hdr, sizeof(hdr)); +} + +gboolean +fu_devlink_netlink_mcast_group_subscribe(FuDevlinkGenSocket *nlg, GError **error) +{ + guint32 devlink_config_grp = nlg->config_group_id; + gint rc; + + g_return_val_if_fail(nlg != NULL, FALSE); + + /* skip multicast subscription for emulated devices */ + if (nlg->is_emulated) + return TRUE; + + rc = mnl_socket_setsockopt(nlg->nl, + NETLINK_ADD_MEMBERSHIP, + &devlink_config_grp, + sizeof(devlink_config_grp)); + if (rc < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to subscribe to devlink notifications: %s", + fwupd_strerror(errno)); + return FALSE; + } + + /* success */ + return TRUE; +} + +/* simple attribute parser callback for devlink attributes */ +gint +fu_devlink_netlink_attr_cb(const struct nlattr *attr, gpointer data) +{ + const struct nlattr **tb = data; + gint type = mnl_attr_get_type(attr); + gint rc; + + rc = mnl_attr_type_valid(attr, DEVLINK_ATTR_MAX); + if (rc < 0) + return MNL_CB_OK; + + tb[type] = attr; + return MNL_CB_OK; +} diff -Nru fwupd-2.0.8/plugins/devlink/fu-devlink-netlink.h fwupd-2.0.20/plugins/devlink/fu-devlink-netlink.h --- fwupd-2.0.8/plugins/devlink/fu-devlink-netlink.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/fu-devlink-netlink.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,79 @@ +/* + * Copyright 2025 NVIDIA Corporation & Affiliates + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#include +#include +#include +#include + +/* generic netlink socket wrapper */ +typedef struct { + struct mnl_socket *nl; + gchar *buf; + guint32 family_id; + guint32 config_group_id; + FuDevice *device; /* device for emulation recording/playback */ + gboolean is_emulated; + gboolean save_events; + gint pipe_fds[2]; /* dummy pipe for emulation (read=0, write=1) */ +} FuDevlinkGenSocket; + +/* do zero check to silence warnings*/ +#define FU_DEVLINK_NETLINK_BUF_SIZE (MNL_SOCKET_BUFFER_SIZE > 0 ? MNL_SOCKET_BUFFER_SIZE : 0) + +/* send/receive functions */ +gboolean +fu_devlink_netlink_msg_run(FuDevlinkGenSocket *nlg, + gsize len, + guint32 seq, + mnl_cb_t cb, + gpointer data, + GError **error) G_GNUC_NON_NULL(1, 4); + +gboolean +fu_devlink_netlink_msg_send_recv(FuDevlinkGenSocket *nlg, + struct nlmsghdr *nlh, + mnl_cb_t cb, + gpointer data, + GError **error) G_GNUC_NON_NULL(1, 2); + +gboolean +fu_devlink_netlink_msg_send(FuDevlinkGenSocket *nlg, struct nlmsghdr *nlh, GError **error) + G_GNUC_NON_NULL(1, 2); + +/* socket management */ +FuDevlinkGenSocket * +fu_devlink_netlink_gen_socket_open(FuDevice *device, GError **error); + +void +fu_devlink_netlink_gen_socket_close(FuDevlinkGenSocket *nlg); + +gint +fu_devlink_netlink_gen_socket_get_fd(FuDevlinkGenSocket *nlg) G_GNUC_NON_NULL(1); + +gchar * +fu_devlink_netlink_gen_socket_get_buf(FuDevlinkGenSocket *nlg) G_GNUC_NON_NULL(1); + +/* prepare devlink command message */ +struct nlmsghdr * +fu_devlink_netlink_cmd_prepare(FuDevlinkGenSocket *nlg, guint8 cmd, gboolean dump) + G_GNUC_NON_NULL(1); + +/* multicast group management */ +gboolean +fu_devlink_netlink_mcast_group_subscribe(FuDevlinkGenSocket *nlg, GError **error) + G_GNUC_NON_NULL(1); + +/* attribute parsing callback */ +gint +fu_devlink_netlink_attr_cb(const struct nlattr *attr, gpointer data) G_GNUC_NON_NULL(1, 2); + +/* cleanup function for auto cleanup */ +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuDevlinkGenSocket, fu_devlink_netlink_gen_socket_close) diff -Nru fwupd-2.0.8/plugins/devlink/fu-devlink-plugin.c fwupd-2.0.20/plugins/devlink/fu-devlink-plugin.c --- fwupd-2.0.8/plugins/devlink/fu-devlink-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/fu-devlink-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,374 @@ +/* + * Copyright 2025 NVIDIA Corporation & Affiliates + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-devlink-backend.h" +#include "fu-devlink-device.h" +#include "fu-devlink-netlink.h" +#include "fu-devlink-plugin.h" + +struct _FuDevlinkPlugin { + FuPlugin parent_instance; + FuDevlinkGenSocket *nlg; + GSource *netlink_source; + FuDevlinkBackend *backend; +}; + +G_DEFINE_TYPE(FuDevlinkPlugin, fu_devlink_plugin, FU_TYPE_PLUGIN) + +static gint +fu_devlink_plugin_get_serial_number_cb(const struct nlmsghdr *nlh, gpointer data) +{ + struct genlmsghdr *genl = mnl_nlmsg_get_payload(nlh); + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + gchar **serial_number = data; + + if (genl->cmd != DEVLINK_CMD_INFO_GET) + return MNL_CB_OK; + + mnl_attr_parse(nlh, sizeof(*genl), fu_devlink_netlink_attr_cb, tb); + + if (tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER] != NULL) + *serial_number = + fu_strsafe(mnl_attr_get_str(tb[DEVLINK_ATTR_INFO_SERIAL_NUMBER]), G_MAXSIZE); + + return MNL_CB_OK; +} + +static gboolean +fu_devlink_plugin_get_serial_number(const gchar *bus_name, + const gchar *dev_name, + gchar **serial_number, + GError **error) +{ + struct nlmsghdr *nlh; + g_autoptr(FuDevlinkGenSocket) nlg = NULL; + + nlg = fu_devlink_netlink_gen_socket_open(NULL, error); + if (nlg == NULL) + return FALSE; + + /* prepare dev info command */ + nlh = fu_devlink_netlink_cmd_prepare(nlg, DEVLINK_CMD_INFO_GET, FALSE); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_BUS_NAME, bus_name); + mnl_attr_put_strz(nlh, DEVLINK_ATTR_DEV_NAME, dev_name); + + /* send command and process response */ + if (!fu_devlink_netlink_msg_send_recv(nlg, + nlh, + fu_devlink_plugin_get_serial_number_cb, + serial_number, + error)) { + g_prefix_error_literal(error, "failed to get device info: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_devlink_plugin_device_added_from_netlink(FuDevlinkPlugin *self, const struct nlmsghdr *nlh) +{ + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + const gchar *bus_name = NULL; + const gchar *dev_name = NULL; + g_autoptr(FuDevice) devlink_device = NULL; + g_autoptr(GError) error_local = NULL; + g_autofree gchar *serial_number = NULL; + g_autofree gchar *cache_id = NULL; + + /* parse netlink attributes using libmnl */ + mnl_attr_parse(nlh, sizeof(struct genlmsghdr), fu_devlink_netlink_attr_cb, tb); + + /* extract bus and device names */ + if (tb[DEVLINK_ATTR_BUS_NAME] != NULL) + bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + if (tb[DEVLINK_ATTR_DEV_NAME] != NULL) + dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + + if (bus_name == NULL || dev_name == NULL) { + g_debug("devlink device notification missing bus_name or dev_name"); + return; + } + + g_debug("devlink device added: %s/%s", bus_name, dev_name); + + if (!fu_devlink_plugin_get_serial_number(bus_name, + dev_name, + &serial_number, + &error_local)) { + g_warning("failed to get serial number for devlink device %s/%s: %s", + bus_name, + dev_name, + error_local->message); + return; + } + + /* use backend to create device with proper hierarchy */ + devlink_device = fu_devlink_backend_device_added(self->backend, + bus_name, + dev_name, + serial_number, + &error_local); + if (devlink_device == NULL) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) || + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_debug("failed to add devlink device %s/%s: %s", + bus_name, + dev_name, + error_local->message); + } else { + g_warning("failed to add devlink device %s/%s: %s", + bus_name, + dev_name, + error_local->message); + } + return; + } + cache_id = g_strdup_printf("%s/%s", bus_name, dev_name); + fu_plugin_cache_add(FU_PLUGIN(self), cache_id, devlink_device); +} + +static void +fu_devlink_plugin_device_removed_from_netlink(FuDevlinkPlugin *self, const struct nlmsghdr *nlh) +{ + struct nlattr *tb[DEVLINK_ATTR_MAX + 1] = {}; + const gchar *bus_name = NULL; + const gchar *dev_name = NULL; + g_autofree gchar *cache_id = NULL; + FuDevice *devlink_device = NULL; + + /* parse netlink attributes using libmnl */ + mnl_attr_parse(nlh, sizeof(struct genlmsghdr), fu_devlink_netlink_attr_cb, tb); + + /* extract bus and device names */ + if (tb[DEVLINK_ATTR_BUS_NAME] != NULL) + bus_name = mnl_attr_get_str(tb[DEVLINK_ATTR_BUS_NAME]); + if (tb[DEVLINK_ATTR_DEV_NAME] != NULL) + dev_name = mnl_attr_get_str(tb[DEVLINK_ATTR_DEV_NAME]); + + if (bus_name == NULL || dev_name == NULL) { + g_debug("devlink device removal notification missing bus_name or dev_name"); + return; + } + + g_debug("devlink device removed: %s/%s", bus_name, dev_name); + + cache_id = g_strdup_printf("%s/%s", bus_name, dev_name); + devlink_device = fu_plugin_cache_lookup(FU_PLUGIN(self), cache_id); + if (devlink_device == NULL) + return; + fu_devlink_backend_device_removed(self->backend, devlink_device); + fu_plugin_cache_remove(FU_PLUGIN(self), cache_id); +} + +/* callback for processing individual netlink messages */ +static gint +fu_devlink_plugin_process_message_cb(const struct nlmsghdr *nlh, gpointer data) +{ + FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(data); + struct genlmsghdr *genl; + + genl = mnl_nlmsg_get_payload(nlh); + switch (genl->cmd) { + case DEVLINK_CMD_NEW: + fu_devlink_plugin_device_added_from_netlink(self, nlh); + break; + case DEVLINK_CMD_DEL: + fu_devlink_plugin_device_removed_from_netlink(self, nlh); + break; + default: + break; + } + + return MNL_CB_OK; +} + +static gboolean +fu_devlink_plugin_netlink_cb(GIOChannel *channel, GIOCondition condition, gpointer user_data) +{ + FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(user_data); + gsize len; + GIOStatus status; + g_autoptr(GError) error_local = NULL; + + if (condition & (G_IO_ERR | G_IO_HUP)) { + g_debug("devlink netlink socket error"); + return FALSE; + } + + /* read netlink message via GIOChannel */ + status = g_io_channel_read_chars(channel, + fu_devlink_netlink_gen_socket_get_buf(self->nlg), + FU_DEVLINK_NETLINK_BUF_SIZE, + &len, + &error_local); + if (status != G_IO_STATUS_NORMAL) { + if (error_local != NULL) + g_debug("failed to read devlink netlink message: %s", error_local->message); + return TRUE; + } + + /* process netlink messages */ + if (!fu_devlink_netlink_msg_run(self->nlg, + len, + 0, + fu_devlink_plugin_process_message_cb, + self, + &error_local)) + g_warning("failed to process netlink message: %s", error_local->message); + + return TRUE; +} + +static gboolean +fu_devlink_plugin_setup_netlink(FuDevlinkPlugin *self, GError **error) +{ + gint fd; + guint watch_id; + g_autoptr(GIOChannel) channel = NULL; + + /* open devlink netlink socket */ + self->nlg = fu_devlink_netlink_gen_socket_open(NULL, error); + if (self->nlg == NULL) + return FALSE; + + /* subscribe to devlink multicast notifications */ + if (!fu_devlink_netlink_mcast_group_subscribe(self->nlg, error)) + return FALSE; + + /* create GIOChannel for the netlink socket */ + fd = fu_devlink_netlink_gen_socket_get_fd(self->nlg); + channel = g_io_channel_unix_new(fd); + g_io_channel_set_encoding(channel, NULL, NULL); + g_io_channel_set_buffered(channel, FALSE); + + /* setup monitoring source using proper GIO pattern */ + watch_id = g_io_add_watch(channel, + G_IO_IN | G_IO_ERR | G_IO_HUP, + fu_devlink_plugin_netlink_cb, + self); + self->netlink_source = g_main_context_find_source_by_id(NULL, watch_id); + if (self->netlink_source != NULL) + g_source_ref(self->netlink_source); + + return TRUE; +} + +/* device enumeration callback */ +static gint +fu_devlink_plugin_enumerate_cb(const struct nlmsghdr *nlh, gpointer data) +{ + FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(data); + struct genlmsghdr *genl; + + genl = mnl_nlmsg_get_payload(nlh); + if (genl->cmd == DEVLINK_CMD_NEW) + fu_devlink_plugin_device_added_from_netlink(self, nlh); + + return MNL_CB_OK; +} + +static gboolean +fu_devlink_plugin_enumerate_devices(FuDevlinkPlugin *self, GError **error) +{ + struct nlmsghdr *nlh; + + /* prepare device enumeration command */ + nlh = fu_devlink_netlink_cmd_prepare(self->nlg, DEVLINK_CMD_GET, TRUE); + + /* send enumeration request and receive responses */ + if (!fu_devlink_netlink_msg_send_recv(self->nlg, + nlh, + fu_devlink_plugin_enumerate_cb, + self, + error)) { + g_prefix_error_literal(error, "failed to enumerate devlink devices: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_devlink_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(plugin); + + /* setup devlink netlink monitoring */ + if (!fu_devlink_plugin_setup_netlink(self, error)) { + g_prefix_error_literal(error, "failed to setup devlink netlink: "); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_devlink_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(plugin); + + /* enumerate existing devlink devices */ + if (!fu_devlink_plugin_enumerate_devices(self, error)) { + g_prefix_error_literal(error, "failed to enumerate devlink devices: "); + return FALSE; + } + + return TRUE; +} + +static void +fu_devlink_plugin_finalize(GObject *object) +{ + FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(object); + + /* clean up netlink resources */ + if (self->netlink_source != NULL) { + g_source_destroy(self->netlink_source); + g_source_unref(self->netlink_source); + } + + fu_devlink_netlink_gen_socket_close(self->nlg); + + /* clean up backend */ + if (self->backend != NULL) + g_object_unref(self->backend); + + G_OBJECT_CLASS(fu_devlink_plugin_parent_class)->finalize(object); +} + +static void +fu_devlink_plugin_constructed(GObject *obj) +{ + FuDevlinkPlugin *self = FU_DEVLINK_PLUGIN(obj); + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + + /* create and add devlink backend */ + self->backend = FU_DEVLINK_BACKEND(fu_devlink_backend_new(ctx)); + fu_context_add_backend(ctx, FU_BACKEND(self->backend)); + fu_context_add_quirk_key(ctx, "DevlinkFixedVersions"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_DEVLINK_DEVICE); +} + +static void +fu_devlink_plugin_init(FuDevlinkPlugin *self) +{ +} + +static void +fu_devlink_plugin_class_init(FuDevlinkPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_devlink_plugin_finalize; + object_class->constructed = fu_devlink_plugin_constructed; + plugin_class->startup = fu_devlink_plugin_startup; + plugin_class->coldplug = fu_devlink_plugin_coldplug; +} diff -Nru fwupd-2.0.8/plugins/devlink/fu-devlink-plugin.h fwupd-2.0.20/plugins/devlink/fu-devlink-plugin.h --- fwupd-2.0.8/plugins/devlink/fu-devlink-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/fu-devlink-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * Copyright 2025 NVIDIA Corporation & Affiliates + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuDevlinkPlugin, fu_devlink_plugin, FU, DEVLINK_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/devlink/fu-self-test.c fwupd-2.0.20/plugins/devlink/fu-self-test.c --- fwupd-2.0.8/plugins/devlink/fu-self-test.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,203 @@ +/* + * Copyright 2025 NVIDIA Corporation & Affiliates + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-context-private.h" +#include "fu-devlink-component.h" +#include "fu-devlink-device.h" +#include "fu-devlink-plugin.h" +#include "fu-plugin-private.h" + +typedef struct { + guint device_id; +} FuDevlinkNetdevsim; + +static gboolean +fu_devlink_file_write_helper(const gchar *path, const guint value, GError **error) +{ + g_autofree gchar *value_str = g_strdup_printf("%u", value); + g_autoptr(FuIOChannel) io = NULL; + + /* check if file exists first */ + if (!g_file_test(path, G_FILE_TEST_IS_REGULAR)) { + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "file not found: %s", path); + return FALSE; + } + + io = fu_io_channel_new_file(path, FU_IO_CHANNEL_OPEN_FLAG_WRITE, error); + if (io == NULL) + return FALSE; + + return fu_io_channel_write_raw(io, + (const guint8 *)value_str, + strlen(value_str), + 1000, + FU_IO_CHANNEL_FLAG_NONE, + error); +} + +static gboolean +fu_devlink_netdevsim_sysfs_write(const gchar *filename, const guint value, GError **error) +{ + g_autofree gchar *path = + fu_path_build(FU_PATH_KIND_SYSFSDIR, "bus", "netdevsim", filename, NULL); + + return fu_devlink_file_write_helper(path, value, error); +} + +static gboolean +fu_devlink_netdevsim_debugfs_write(const guint device_id, + const gchar *filename, + const guint value, + GError **error) +{ + g_autofree gchar *device_id_str = g_strdup_printf("netdevsim%u", device_id); + g_autofree gchar *path = + fu_path_build(FU_PATH_KIND_DEBUGFSDIR, "netdevsim", device_id_str, filename, NULL); + + return fu_devlink_file_write_helper(path, value, error); +} + +static void +fu_devlink_netdevsim_cleanup(FuDevlinkNetdevsim *ndsim) +{ + g_autoptr(GError) error_local = NULL; + + if (ndsim->device_id != 0) { + /* remove netdevsim device */ + if (!fu_devlink_netdevsim_sysfs_write("del_device", + ndsim->device_id, + &error_local)) { + g_debug("Failed to remove netdevsim device %u: %s", + ndsim->device_id, + error_local->message); + } + } + g_free(ndsim); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuDevlinkNetdevsim, fu_devlink_netdevsim_cleanup) + +#define FU_DEVLINK_NETDEVSIM_FW_UPDATE_FLASH_CHUNK_TIME_MS 1 /* 1ms */ + +static FuDevlinkNetdevsim * +fu_devlink_netdevsim_new(guint device_id, GError **error) +{ + g_autoptr(FuDevlinkNetdevsim) ndsim = g_new0(FuDevlinkNetdevsim, 1); + g_autoptr(GError) error_local = NULL; + + /* create netdevsim device */ + if (!fu_devlink_netdevsim_sysfs_write("new_device", device_id, error)) + return NULL; + + ndsim->device_id = device_id; + + if (!fu_devlink_netdevsim_debugfs_write(device_id, + "fw_update_flash_chunk_time_ms", + FU_DEVLINK_NETDEVSIM_FW_UPDATE_FLASH_CHUNK_TIME_MS, + &error_local)) { + g_debug("Failed to write fw_update_flash_chunk_time_ms: %s", error_local->message); + g_clear_error(&error_local); + } + + return g_steal_pointer(&ndsim); +} + +#define FU_DEVLINK_NETDEVSIM_DEVICE_ID 472187 +#define FU_DEVLINK_NETDEVSIM_DEVICE_NAME "netdevsim" G_STRINGIFY(FU_DEVLINK_NETDEVSIM_DEVICE_ID) + +static void +fu_devlink_plugin_flash_func(void) +{ + gboolean ret; + const gchar *fw_content = "FWUPD_TEST_FIRMWARE_v2.0.0\nTest firmware for devlink device"; + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDevlinkComponent) component = NULL; + g_autoptr(FuDevlinkNetdevsim) ndsim = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuFirmware) firmware = fu_firmware_new(); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GBytes) fw_data = NULL; + g_autoptr(GError) error_local = NULL; + + /* create test netdevsim to set up netdevsim device */ + ndsim = fu_devlink_netdevsim_new(FU_DEVLINK_NETDEVSIM_DEVICE_ID, &error_local); + if (ndsim == NULL) { + g_autofree gchar *msg = + g_strdup_printf("failed to create netdevsim device: %s", error_local->message); + g_test_skip(msg); + return; + } + + /* create device with valid bus and device names */ + device = fu_devlink_device_new(ctx, "netdevsim", FU_DEVLINK_NETDEVSIM_DEVICE_NAME, NULL); + g_assert_nonnull(device); + + /* probe device first */ + ret = fu_device_probe(device, &error_local); + g_assert_true(ret); + + /* open device */ + ret = fu_device_open(device, &error_local); + + g_assert_true(ret); + + /* create fw.mgmt component */ + component = fu_devlink_component_new(device, "fw.mgmt"); + g_assert_nonnull(component); + + /* set up parent-child relationship */ + fu_device_add_child(device, FU_DEVICE(component)); + + /* set component version for testing */ + fu_device_set_version(FU_DEVICE(component), "1.0.0"); + + /* create firmware */ + fw_data = g_bytes_new(fw_content, strlen(fw_content)); + fu_firmware_set_bytes(firmware, fw_data); + fu_firmware_set_version(firmware, "2.0.0"); + + /* prepare the component */ + ret = fu_device_prepare(FU_DEVICE(component), + progress, + FWUPD_INSTALL_FLAG_NONE, + &error_local); + g_assert_true(ret); + + /* test firmware flashing on the fw.mgmt component */ + g_test_message("Testing firmware flash for fw.mgmt component on netdevsim/%s", + FU_DEVLINK_NETDEVSIM_DEVICE_NAME); + ret = fu_device_write_firmware(FU_DEVICE(component), + firmware, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error_local); + g_assert_true(ret); + + g_test_message("Firmware flash completed successfully for fw.mgmt component!"); + g_assert_cmpuint(fu_progress_get_percentage(progress), ==, 100); + + /* cleanup the component */ + ret = fu_device_cleanup(FU_DEVICE(component), + progress, + FWUPD_INSTALL_FLAG_NONE, + &error_local); + g_assert_true(ret); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + + /* tests go here */ + g_test_add_func("/devlink/plugin/flash", fu_devlink_plugin_flash_func); + return g_test_run(); +} diff -Nru fwupd-2.0.8/plugins/devlink/meson.build fwupd-2.0.20/plugins/devlink/meson.build --- fwupd-2.0.8/plugins/devlink/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,61 @@ +host_machine.system() == 'linux' or subdir_done() +libmnl.found() or subdir_done() +cc.has_header_symbol('linux/devlink.h', 'DEVLINK_ATTR_RELOAD_ACTION', required: false) or subdir_done() + +cargs = ['-DG_LOG_DOMAIN="FuPluginDevlink"'] +plugins += {meson.current_source_dir().split('/')[-1]: true} + +plugin_quirks += files('devlink.quirk') +plugin_builtin_devlink = static_library('fu_plugin_devlink', + sources: [ + 'fu-devlink-plugin.c', + 'fu-devlink-backend.c', + 'fu-devlink-device.c', + 'fu-devlink-component.c', + 'fu-devlink-netlink.c', + ], + include_directories: plugin_incdirs, + link_with: [ + fwupdplugin, + fwupd, + ], + c_args: cargs, + dependencies: [ + plugin_deps, + valgrind, + libmnl, + ], +) +plugin_builtins += plugin_builtin_devlink + +device_tests += files( + 'tests/devlink-netdevsim.json', +) + +if get_option('tests') + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + e = executable( + 'devlink-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: [ + plugin_deps, + valgrind, + libmnl, + ], + link_with: [ + plugin_builtin_devlink, + fwupdplugin, + fwupd, + ], + install: true, + install_rpath: libdir_pkg, + install_tag: 'tests', + install_dir: installed_test_bindir, + ) + test('devlink-self-test', e, env: env) +endif diff -Nru fwupd-2.0.8/plugins/devlink/tests/devlink-netdevsim.json fwupd-2.0.20/plugins/devlink/tests/devlink-netdevsim.json --- fwupd-2.0.8/plugins/devlink/tests/devlink-netdevsim.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/devlink/tests/devlink-netdevsim.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,20 @@ +{ + "name": "Devlink Netdevsim Setup", + "interactive": false, + "steps": [ + { + "url": "fbdca7ac3b80f7df24b1a1b435963a46aee19b987ba55b5cff7dddb4de158664-netdevsim_test_firmware_new.cab", + "emulation-url": "def54dc305e6d84bafcc8d8fe551efcd31c314b89f54ec5b76734ee954f2032a-netdevsim_setup_install_record_new.zip", + "components": [ + { + "name": "fw.mgmt", + "protocol": "org.kernel.devlink", + "version": "10.20.30", + "guids": [ + "6e9dfa4e-17f5-53dd-80d9-59a200c48b2d" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/dfu/contrib/parse-avrdude-conf.py fwupd-2.0.20/plugins/dfu/contrib/parse-avrdude-conf.py --- fwupd-2.0.8/plugins/dfu/contrib/parse-avrdude-conf.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/contrib/parse-avrdude-conf.py 2026-02-26 11:36:18.000000000 +0000 @@ -4,7 +4,7 @@ # # SPDX-License-Identifier: LGPL-2.1-or-later -""" This parses avrdude.conf and generates quirks for fwupd """ +"""This parses avrdude.conf and generates quirks for fwupd""" # pylint: disable=wrong-import-position,pointless-string-statement diff -Nru fwupd-2.0.8/plugins/dfu/dfu.quirk fwupd-2.0.20/plugins/dfu/dfu.quirk --- fwupd-2.0.8/plugins/dfu/dfu.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/dfu.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -6,7 +6,6 @@ [USB\VID_28E9&PID_0189] Flags = gd32,force-dfu-mode,will-disappear Name = GD32VF103 -Vendor = GDMicroelectronics # Realtek USB camera [USB\VID_0BDA&PID_5850] @@ -442,10 +441,42 @@ Plugin = dfu Flags = manifest-poll,allow-zero-polltimeout,signed-payload,index-force-detach RemoveDelay = 180000 +[USB\VID_095D&PID_92C8] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload,index-force-detach +RemoveDelay = 180000 [USB\VID_095D&PID_92C7] Plugin = dfu Flags = manifest-poll,allow-zero-polltimeout,signed-payload RemoveDelay = 180000 + +# Poly Studio V72 +[USB\VID_095D&PID_92D8] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload,index-force-detach +RemoveDelay = 180000 +[USB\VID_095D&PID_92DA] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload,index-force-detach +RemoveDelay = 180000 +[USB\VID_095D&PID_92D9] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload +RemoveDelay = 180000 + +# Poly Studio V12 +[USB\VID_095D&PID_92D2] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload,index-force-detach +RemoveDelay = 180000 +[USB\VID_095D&PID_92D4] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload,index-force-detach +RemoveDelay = 180000 +[USB\VID_095D&PID_92D3] +Plugin = dfu +Flags = manifest-poll,allow-zero-polltimeout,signed-payload +RemoveDelay = 180000 # AVer ATLAS CAM [USB\VID_34AD&PID_0006] diff -Nru fwupd-2.0.8/plugins/dfu/fu-dfu-device.c fwupd-2.0.20/plugins/dfu/fu-dfu-device.c --- fwupd-2.0.8/plugins/dfu/fu-dfu-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/fu-dfu-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -52,7 +52,6 @@ FuDfuStatus status; GPtrArray *targets; gboolean done_upload_or_download; - gboolean claimed_interface; gchar *chip_id; guint16 version; guint16 force_version; @@ -83,7 +82,6 @@ idt, "DoneUploadOrDownload", priv->done_upload_or_download); - fwupd_codec_string_append_bool(str, idt, "ClaimedInterface", priv->claimed_interface); fwupd_codec_string_append(str, idt, "ChipId", priv->chip_id); fwupd_codec_string_append_hex(str, idt, "Version", priv->version); if (priv->force_version != G_MAXUINT16) @@ -115,7 +113,7 @@ fu_dfu_device_get_transfer_size(FuDfuDevice *self) { FuDfuDevicePrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0xffff); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), G_MAXUINT16); return priv->transfer_size; } @@ -131,7 +129,7 @@ fu_dfu_device_get_version(FuDfuDevice *self) { FuDfuDevicePrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0xffff); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), G_MAXUINT16); return priv->version; } @@ -315,7 +313,7 @@ g_info("DFU v1.1 assumed"); priv->version = FU_DFU_FIRMARE_VERSION_DFU_1_1; } else { - g_warning("DFU version 0x%04x invalid, v1.1 assumed", priv->version); + g_debug("DFU version 0x%04x invalid, v1.1 assumed", priv->version); priv->version = FU_DFU_FIRMARE_VERSION_DFU_1_1; } @@ -358,6 +356,7 @@ /* add target */ priv->iface_number = fu_usb_interface_get_number(iface); + fu_usb_device_add_interface(FU_USB_DEVICE(self), priv->iface_number); g_ptr_array_add(priv->targets, target); fu_dfu_device_guess_state_from_iface(self, iface); } @@ -467,7 +466,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, - "No target with alt-setting %i", + "no target with alt-setting %i", alt_setting); return NULL; } @@ -514,39 +513,6 @@ priv->status = status; } -gboolean -fu_dfu_device_ensure_interface(FuDfuDevice *self, GError **error) -{ - FuDfuDevicePrivate *priv = GET_PRIVATE(self); - g_autoptr(GError) error_local = NULL; - - /* already done */ - if (priv->claimed_interface) - return TRUE; - - /* nothing set */ - if (priv->iface_number == 0xff) - return TRUE; - - /* claim, without detaching kernel driver */ - if (!fu_usb_device_claim_interface(FU_USB_DEVICE(self), - (gint)priv->iface_number, - FU_USB_DEVICE_CLAIM_FLAG_KERNEL_DRIVER, - &error_local)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "cannot claim interface %i: %s", - priv->iface_number, - error_local->message); - return FALSE; - } - - /* success */ - priv->claimed_interface = TRUE; - return TRUE; -} - static gboolean fu_dfu_device_refresh_and_clear(FuDfuDevice *self, GError **error) { @@ -602,10 +568,6 @@ fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_NO_DFU_RUNTIME)) return TRUE; - /* ensure interface is claimed */ - if (!fu_dfu_device_ensure_interface(self, error)) - return FALSE; - /* Device that cannot communicate via the USB after the * Manifestation phase indicated this limitation to the * host by clearing bmAttributes bit bitManifestationTolerant. @@ -695,7 +657,6 @@ &error_local)) { /* some devices just reboot and stall the endpoint :/ */ if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) || - // g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_READ) || g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL)) { g_debug("ignoring while detaching: %s", error_local->message); } else { @@ -739,10 +700,6 @@ fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_NO_DFU_RUNTIME)) return TRUE; - /* ensure interface is claimed */ - if (!fu_dfu_device_ensure_interface(self, error)) - return FALSE; - /* inform UI there's going to be a detach:attach */ if (!fu_dfu_device_request_detach(self, progress, error)) return FALSE; @@ -788,10 +745,6 @@ return FALSE; } - /* ensure interface is claimed */ - if (!fu_dfu_device_ensure_interface(self, error)) - return FALSE; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_CLASS, @@ -834,10 +787,6 @@ return FALSE; } - /* ensure interface is claimed */ - if (!fu_dfu_device_ensure_interface(self, error)) - return FALSE; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_CLASS, @@ -873,7 +822,7 @@ fu_dfu_device_get_interface(FuDfuDevice *self) { FuDfuDevicePrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FU_IS_DFU_DEVICE(self), 0xff); + g_return_val_if_fail(FU_IS_DFU_DEVICE(self), G_MAXUINT8); return priv->iface_number; } @@ -952,39 +901,6 @@ return TRUE; } -/** - * fu_dfu_device_close: - * @self: a #FuDfuDevice - * @error: (nullable): optional return location for an error - * - * Closes a DFU device. - * - * Returns: %TRUE for success - **/ -static gboolean -fu_dfu_device_close(FuDevice *device, GError **error) -{ - FuDfuDevice *self = FU_DFU_DEVICE(device); - FuDfuDevicePrivate *priv = GET_PRIVATE(self); - - /* release interface */ - if (priv->claimed_interface) { - g_autoptr(GError) error_local = NULL; - if (!fu_usb_device_release_interface(FU_USB_DEVICE(device), - (gint)priv->iface_number, - 0, - &error_local)) { - if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { - g_warning("failed to release interface: %s", error_local->message); - } - } - priv->claimed_interface = FALSE; - } - - /* FuUsbDevice->close */ - return FU_DEVICE_CLASS(fu_dfu_device_parent_class)->close(device, error); -} - static gboolean fu_dfu_device_probe(FuDevice *device, GError **error) { @@ -1010,7 +926,7 @@ * write -- there's no way to avoid blocking the daemon like this... */ if (fu_device_has_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_ATTACH_EXTRA_RESET)) { - g_debug("blocking wait to work around Jabra hardware..."); + g_debug("blocking wait to work around Jabra hardware…"); fu_device_sleep(device, 10000); } @@ -1089,7 +1005,7 @@ fu_device_has_private_flag(FU_DEVICE(self), FU_DFU_DEVICE_FLAG_WILL_DETACH)) { g_info("bus reset is not required; device will reboot to normal"); } else if (!fu_dfu_target_attach(target, progress, error)) { - g_prefix_error(error, "failed to attach target: "); + g_prefix_error_literal(error, "failed to attach target: "); return FALSE; } @@ -1113,10 +1029,6 @@ gboolean use_dfuse = FALSE; g_autoptr(FuFirmware) firmware = NULL; - /* ensure interface is claimed */ - if (!fu_dfu_device_ensure_interface(self, error)) - return NULL; - /* choose the most appropriate type */ for (guint i = 0; i < priv->targets->len; i++) { FuDfuTarget *target = g_ptr_array_index(priv->targets, i); @@ -1152,7 +1064,7 @@ if (!fu_dfu_target_upload(target, firmware, fu_progress_get_child(progress), - DFU_TARGET_TRANSFER_FLAG_NONE, + FU_DFU_TARGET_TRANSFER_FLAG_NONE, error)) return NULL; fu_progress_step_done(progress); @@ -1208,35 +1120,31 @@ guint16 firmware_pid = 0xffff; guint16 firmware_vid = 0xffff; - /* ensure interface is claimed */ - if (!fu_dfu_device_ensure_interface(self, error)) - return FALSE; - /* firmware supports footer? */ if (FU_IS_DFU_FIRMWARE(firmware)) { firmware_vid = fu_dfu_firmware_get_vid(FU_DFU_FIRMWARE(firmware)); firmware_pid = fu_dfu_firmware_get_pid(FU_DFU_FIRMWARE(firmware)); } else { - flags |= DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID; - flags |= DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID; + flags |= FU_DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID; + flags |= FU_DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID; } /* do we allow wildcard VID:PID matches */ - if ((flags & DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID) == 0) { + if ((flags & FU_DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID) == 0) { if (firmware_vid == 0xffff) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "firmware vendor ID not specified"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware vendor ID not specified"); return FALSE; } } - if ((flags & DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID) == 0) { + if ((flags & FU_DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID) == 0) { if (firmware_pid == 0xffff) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "firmware product ID not specified"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware product ID not specified"); return FALSE; } } @@ -1292,7 +1200,7 @@ } for (guint i = 0; i < images->len; i++) { FuFirmware *image = g_ptr_array_index(images, i); - FuDfuTargetTransferFlags flags_local = DFU_TARGET_TRANSFER_FLAG_NONE; + FuDfuTargetTransferFlags flags_local = FU_DFU_TARGET_TRANSFER_FLAG_NONE; guint8 alt; g_autoptr(FuDfuTarget) target_tmp = NULL; @@ -1306,11 +1214,11 @@ fu_device_get_logical_id(FU_DEVICE(target_tmp))); /* download onto target */ - if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY) - flags_local = DFU_TARGET_TRANSFER_FLAG_VERIFY; + if (flags & FU_DFU_TARGET_TRANSFER_FLAG_VERIFY) + flags_local = FU_DFU_TARGET_TRANSFER_FLAG_VERIFY; if (!FU_IS_DFU_FIRMWARE(firmware) || fu_dfu_firmware_get_version(FU_DFU_FIRMWARE(firmware)) == 0x0) - flags_local |= DFU_TARGET_TRANSFER_FLAG_ADDR_HEURISTIC; + flags_local |= FU_DFU_TARGET_TRANSFER_FLAG_ADDR_HEURISTIC; ret = fu_dfu_target_download(target_tmp, image, fu_progress_get_child(progress), @@ -1338,7 +1246,7 @@ return; /* not the right error to query */ - if (!g_error_matches(*error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) + if (!g_error_matches(*error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) /* nocheck:error */ return; /* get the status */ @@ -1355,10 +1263,10 @@ /* ignore */ break; case FU_DFU_STATUS_ERR_VENDOR: - g_prefix_error(error, "read protection is active: "); + g_prefix_error_literal(error, "read protection is active: "); /* nocheck:error */ break; default: - g_prefix_error(error, + g_prefix_error(error, /* nocheck:error */ "[%s,%s]: ", fu_dfu_state_to_string(priv->state), fu_dfu_status_to_string(priv->status)); @@ -1376,7 +1284,7 @@ g_debug("uploading from device->host"); if (!fu_dfu_device_refresh_and_clear(self, error)) return NULL; - firmware = fu_dfu_device_upload(self, progress, DFU_TARGET_TRANSFER_FLAG_NONE, error); + firmware = fu_dfu_device_upload(self, progress, FU_DFU_TARGET_TRANSFER_FLAG_NONE, error); if (firmware == NULL) return NULL; @@ -1388,7 +1296,7 @@ fu_dfu_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { return fu_firmware_new_from_gtypes(stream, @@ -1410,14 +1318,14 @@ GError **error) { FuDfuDevice *self = FU_DFU_DEVICE(device); - FuDfuTargetTransferFlags transfer_flags = DFU_TARGET_TRANSFER_FLAG_VERIFY; + FuDfuTargetTransferFlags transfer_flags = FU_DFU_TARGET_TRANSFER_FLAG_VERIFY; /* open it */ if (!fu_dfu_device_refresh_and_clear(self, error)) return FALSE; - if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) { - transfer_flags |= DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID; - transfer_flags |= DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID; + if (flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) { + transfer_flags |= FU_DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID; + transfer_flags |= FU_DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID; } /* hit hardware */ @@ -1463,7 +1371,7 @@ } static void -fu_dfu_device_set_progress(FuDevice *self, FuProgress *progress) +fu_dfu_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -1511,7 +1419,6 @@ device_class->detach = fu_dfu_device_detach; device_class->reload = fu_dfu_device_reload; device_class->open = fu_dfu_device_open; - device_class->close = fu_dfu_device_close; device_class->probe = fu_dfu_device_probe; device_class->set_progress = fu_dfu_device_set_progress; object_class->dispose = fu_dfu_device_dispose; diff -Nru fwupd-2.0.8/plugins/dfu/fu-dfu-device.h fwupd-2.0.20/plugins/dfu/fu-dfu-device.h --- fwupd-2.0.8/plugins/dfu/fu-dfu-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/fu-dfu-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -45,5 +45,3 @@ fu_dfu_device_error_fixup(FuDfuDevice *self, GError **error); guint fu_dfu_device_get_download_timeout(FuDfuDevice *self); -gboolean -fu_dfu_device_ensure_interface(FuDfuDevice *self, GError **error); diff -Nru fwupd-2.0.8/plugins/dfu/fu-dfu-plugin.c fwupd-2.0.20/plugins/dfu/fu-dfu-plugin.c --- fwupd-2.0.8/plugins/dfu/fu-dfu-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/fu-dfu-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -28,6 +28,7 @@ fu_context_add_quirk_key(ctx, "DfuAltName"); fu_context_add_quirk_key(ctx, "DfuForceTimeout"); fu_context_add_quirk_key(ctx, "DfuForceVersion"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_DFU_DEVICE); } diff -Nru fwupd-2.0.8/plugins/dfu/fu-dfu-self-test.c fwupd-2.0.20/plugins/dfu/fu-dfu-self-test.c --- fwupd-2.0.8/plugins/dfu/fu-dfu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/fu-dfu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -100,10 +100,10 @@ tmp, "Zone:0, Sec#:0, Addr:0x08000000, Size:0x0400, Caps:0x1 [readable]\n" "Zone:0, Sec#:0, Addr:0x08000400, Size:0x0400, Caps:0x1 [readable]\n" - "Zone:0, Sec#:1, Addr:0x08000800, Size:0x0400, Caps:0x7 [readable,writeable,erasable]\n" - "Zone:0, Sec#:1, Addr:0x08000c00, Size:0x0400, Caps:0x7 [readable,writeable,erasable]\n" - "Zone:0, Sec#:1, Addr:0x08001000, Size:0x0400, Caps:0x7 [readable,writeable,erasable]\n" - "Zone:0, Sec#:1, Addr:0x08001400, Size:0x0400, Caps:0x7 [readable,writeable,erasable]", + "Zone:0, Sec#:1, Addr:0x08000800, Size:0x0400, Caps:0x7 [readable,writable,erasable]\n" + "Zone:0, Sec#:1, Addr:0x08000c00, Size:0x0400, Caps:0x7 [readable,writable,erasable]\n" + "Zone:0, Sec#:1, Addr:0x08001000, Size:0x0400, Caps:0x7 [readable,writable,erasable]\n" + "Zone:0, Sec#:1, Addr:0x08001400, Size:0x0400, Caps:0x7 [readable,writable,erasable]", &error); g_assert_no_error(error); g_assert_true(ret); @@ -122,11 +122,11 @@ "Zone:0, Sec#:0, Addr:0x0000f064, Size:0x0064, Caps:0x1 [readable]\n" "Zone:0, Sec#:0, Addr:0x0000f0c8, Size:0x0064, Caps:0x1 [readable]\n" "Zone:0, Sec#:0, Addr:0x0000f12c, Size:0x0064, Caps:0x1 [readable]\n" - "Zone:1, Sec#:0, Addr:0x0000e000, Size:0x2000, Caps:0x7 [readable,writeable,erasable]\n" - "Zone:1, Sec#:0, Addr:0x00010000, Size:0x2000, Caps:0x7 [readable,writeable,erasable]\n" - "Zone:1, Sec#:0, Addr:0x00012000, Size:0x2000, Caps:0x7 [readable,writeable,erasable]\n" - "Zone:2, Sec#:0, Addr:0x00080000, Size:0x6000, Caps:0x7 [readable,writeable,erasable]\n" - "Zone:2, Sec#:0, Addr:0x00086000, Size:0x6000, Caps:0x7 [readable,writeable,erasable]", + "Zone:1, Sec#:0, Addr:0x0000e000, Size:0x2000, Caps:0x7 [readable,writable,erasable]\n" + "Zone:1, Sec#:0, Addr:0x00010000, Size:0x2000, Caps:0x7 [readable,writable,erasable]\n" + "Zone:1, Sec#:0, Addr:0x00012000, Size:0x2000, Caps:0x7 [readable,writable,erasable]\n" + "Zone:2, Sec#:0, Addr:0x00080000, Size:0x6000, Caps:0x7 [readable,writable,erasable]\n" + "Zone:2, Sec#:0, Addr:0x00086000, Size:0x6000, Caps:0x7 [readable,writable,erasable]", &error); g_assert_no_error(error); g_assert_true(ret); diff -Nru fwupd-2.0.8/plugins/dfu/fu-dfu-target-avr.c fwupd-2.0.20/plugins/dfu/fu-dfu-target-avr.c --- fwupd-2.0.8/plugins/dfu/fu-dfu-target-avr.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/fu-dfu-target-avr.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,6 +12,7 @@ #include "fu-dfu-common.h" #include "fu-dfu-device.h" #include "fu-dfu-sector.h" +#include "fu-dfu-struct.h" #include "fu-dfu-target-avr.h" #include "fu-dfu-target-private.h" /* waive-pre-commit */ @@ -38,38 +39,28 @@ G_DEFINE_TYPE_WITH_PRIVATE(FuDfuTargetAvr, fu_dfu_target_avr, FU_TYPE_DFU_TARGET) #define GET_PRIVATE(o) (fu_dfu_target_avr_get_instance_private(o)) -/* ATMEL AVR version of DFU: - * http://www.atmel.com/Images/doc7618.pdf */ -#define DFU_AVR_CMD_PROG_START 0x01 -#define DFU_AVR_CMD_DISPLAY_DATA 0x03 -#define DFU_AVR_CMD_WRITE_COMMAND 0x04 -#define DFU_AVR_CMD_READ_COMMAND 0x05 -#define DFU_AVR_CMD_CHANGE_BASE_ADDR 0x06 - /* Atmel AVR32 version of DFU: * http://www.atmel.com/images/doc32131.pdf */ -#define DFU_AVR32_GROUP_SELECT 0x06 /** SELECT */ -#define DFU_AVR32_CMD_SELECT_MEMORY 0x03 -#define DFU_AVR32_MEMORY_UNIT 0x00 -#define DFU_AVR32_MEMORY_PAGE 0x01 -#define DFU_AVR32_MEMORY_UNIT_FLASH 0x00 -#define DFU_AVR32_MEMORY_UNIT_EEPROM 0x01 -#define DFU_AVR32_MEMORY_UNIT_SECURITY 0x02 -#define DFU_AVR32_MEMORY_UNIT_CONFIGURATION 0x03 -#define DFU_AVR32_MEMORY_UNIT_BOOTLOADER 0x04 -#define DFU_AVR32_MEMORY_UNIT_SIGNATURE 0x05 -#define DFU_AVR32_MEMORY_UNIT_USER 0x06 -#define DFU_AVR32_GROUP_DOWNLOAD 0x01 /** DOWNLOAD */ -#define DFU_AVR32_CMD_PROGRAM_START 0x00 -#define DFU_AVR32_GROUP_UPLOAD 0x03 /** UPLOAD */ -#define DFU_AVR32_CMD_READ_MEMORY 0x00 -#define DFU_AVR32_CMD_BLANK_CHECK 0x01 -#define DFU_AVR32_GROUP_EXEC 0x04 /** EXEC */ -#define DFU_AVR32_CMD_ERASE 0x00 -#define DFU_AVR32_ERASE_EVERYTHING 0xff -#define DFU_AVR32_CMD_START_APPLI 0x03 -#define DFU_AVR32_START_APPLI_RESET 0x00 -#define DFU_AVR32_START_APPLI_NO_RESET 0x01 + +/* SELECT */ +#define DFU_AVR32_CMD_SELECT_MEMORY 0x03 + +#define DFU_AVR32_MEMORY_UNIT 0x00 +#define DFU_AVR32_MEMORY_PAGE 0x01 + +/* DOWNLOAD */ +#define DFU_AVR32_CMD_PROGRAM_START 0x00 + +/* UPLOAD */ +#define DFU_AVR32_CMD_READ_MEMORY 0x00 +#define DFU_AVR32_CMD_BLANK_CHECK 0x01 + +/* EXEC */ +#define DFU_AVR32_CMD_ERASE 0x00 +#define DFU_AVR32_ERASE_EVERYTHING 0xff +#define DFU_AVR32_CMD_START_APPLI 0x03 +#define DFU_AVR32_START_APPLI_RESET 0x00 +#define DFU_AVR32_START_APPLI_NO_RESET 0x01 #define ATMEL_64KB_PAGE 0x10000 #define ATMEL_MAX_TRANSFER_SIZE 0x0400 @@ -85,11 +76,11 @@ g_autoptr(GByteArray) buf = g_byte_array_new(); /* format buffer */ - fu_byte_array_append_uint8(buf, DFU_AVR32_GROUP_EXEC); + fu_byte_array_append_uint8(buf, FU_DFU_AVR32_GROUP_EXEC); fu_byte_array_append_uint8(buf, DFU_AVR32_CMD_ERASE); fu_byte_array_append_uint8(buf, 0xFF); if (!fu_dfu_target_download_chunk(target, 0, buf, 5000, progress, error)) { - g_prefix_error(error, "cannot mass-erase: "); + g_prefix_error_literal(error, "cannot mass-erase: "); return FALSE; } return TRUE; @@ -108,7 +99,7 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 50, "download-zero"); /* format buffer */ - fu_byte_array_append_uint8(buf, DFU_AVR32_GROUP_EXEC); + fu_byte_array_append_uint8(buf, FU_DFU_AVR32_GROUP_EXEC); fu_byte_array_append_uint8(buf, DFU_AVR32_CMD_START_APPLI); fu_byte_array_append_uint8(buf, DFU_AVR32_START_APPLI_RESET); if (!fu_dfu_target_download_chunk(target, @@ -153,7 +144,7 @@ /** * fu_dfu_target_avr_select_memory_unit: * @target: a #FuDfuTarget - * @memory_unit: a unit, e.g. %DFU_AVR32_MEMORY_UNIT_FLASH + * @memory_unit: a unit, e.g. %FU_DFU_AVR32_MEMORY_UNIT_FLASH * @error: (nullable): optional return location for an error * * Selects the memory unit for the device. @@ -166,23 +157,26 @@ FuProgress *progress, GError **error) { + FuDevice *proxy; g_autoptr(GByteArray) buf = g_byte_array_new(); /* check legacy protocol quirk */ - if (fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(target)), - FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { + proxy = fu_device_get_proxy(FU_DEVICE(target), error); + if (proxy == NULL) + return FALSE; + if (fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { g_debug("ignoring select memory unit as legacy protocol"); return TRUE; } /* format buffer */ - fu_byte_array_append_uint8(buf, DFU_AVR32_GROUP_SELECT); + fu_byte_array_append_uint8(buf, FU_DFU_AVR32_GROUP_SELECT); fu_byte_array_append_uint8(buf, DFU_AVR32_CMD_SELECT_MEMORY); fu_byte_array_append_uint8(buf, DFU_AVR32_MEMORY_UNIT); fu_byte_array_append_uint8(buf, memory_unit); g_debug("selecting memory unit 0x%02x", (guint)memory_unit); if (!fu_dfu_target_download_chunk(target, 0, buf, 0, progress, error)) { - g_prefix_error(error, "cannot select memory unit: "); + g_prefix_error_literal(error, "cannot select memory unit: "); return FALSE; } return TRUE; @@ -218,13 +212,13 @@ } /* format buffer */ - fu_byte_array_append_uint8(buf, DFU_AVR_CMD_CHANGE_BASE_ADDR); + fu_byte_array_append_uint8(buf, FU_DFU_AVR_CMD_CHANGE_BASE_ADDR); fu_byte_array_append_uint8(buf, 0x03); fu_byte_array_append_uint8(buf, 0x00); fu_byte_array_append_uint8(buf, memory_page & 0xFF); g_debug("selecting memory page 0x%01x", (guint)memory_page); if (!fu_dfu_target_download_chunk(target, 0, buf, 0, progress, error)) { - g_prefix_error(error, "cannot select memory page: "); + g_prefix_error_literal(error, "cannot select memory page: "); return FALSE; } return TRUE; @@ -249,13 +243,13 @@ g_autoptr(GByteArray) buf = g_byte_array_new(); /* format buffer */ - fu_byte_array_append_uint8(buf, DFU_AVR32_GROUP_SELECT); + fu_byte_array_append_uint8(buf, FU_DFU_AVR32_GROUP_SELECT); fu_byte_array_append_uint8(buf, DFU_AVR32_CMD_SELECT_MEMORY); fu_byte_array_append_uint8(buf, DFU_AVR32_MEMORY_PAGE); fu_byte_array_append_uint16(buf, memory_page, G_BIG_ENDIAN); g_debug("selecting memory page 0x%02x", (guint)memory_page); if (!fu_dfu_target_download_chunk(target, 0, buf, 0, progress, error)) { - g_prefix_error(error, "cannot select memory page: "); + g_prefix_error_literal(error, "cannot select memory page: "); return FALSE; } return TRUE; @@ -282,7 +276,7 @@ g_autoptr(GByteArray) buf = g_byte_array_new(); /* format buffer */ - fu_byte_array_append_uint8(buf, DFU_AVR32_GROUP_UPLOAD); + fu_byte_array_append_uint8(buf, FU_DFU_AVR32_GROUP_UPLOAD); fu_byte_array_append_uint8(buf, DFU_AVR32_CMD_READ_MEMORY); fu_byte_array_append_uint16(buf, addr_start, G_BIG_ENDIAN); fu_byte_array_append_uint16(buf, addr_end, G_BIG_ENDIAN); @@ -300,7 +294,7 @@ /** * fu_dfu_target_avr_read_command: * @target: a #FuDfuTarget - * @memory_unit: a unit, e.g. %DFU_AVR32_MEMORY_UNIT_FLASH + * @memory_unit: a unit, e.g. %FU_DFU_AVR32_MEMORY_UNIT_FLASH * @error: (nullable): optional return location for an error * * Performs a read operation on the device. @@ -317,12 +311,12 @@ g_autoptr(GByteArray) buf = g_byte_array_new(); /* format buffer */ - fu_byte_array_append_uint8(buf, DFU_AVR_CMD_READ_COMMAND); + fu_byte_array_append_uint8(buf, FU_DFU_AVR_CMD_READ_COMMAND); fu_byte_array_append_uint8(buf, page); fu_byte_array_append_uint8(buf, addr); g_debug("read command page:0x%02x addr:0x%02x", (guint)page, (guint)addr); if (!fu_dfu_target_download_chunk(target, 0, buf, 0, progress, error)) { - g_prefix_error(error, "cannot read command page: "); + g_prefix_error_literal(error, "cannot read command page: "); return FALSE; } return TRUE; @@ -352,7 +346,7 @@ /* select unit, and request 4 bytes */ if (!fu_dfu_target_avr_select_memory_unit(target, - DFU_AVR32_MEMORY_UNIT_SIGNATURE, + FU_DFU_AVR32_MEMORY_UNIT_SIGNATURE, fu_progress_get_child(progress), error)) return NULL; @@ -472,6 +466,7 @@ { FuDfuDevice *device; FuDfuTargetAvr *self = FU_DFU_TARGET_AVR(target); + FuDevice *proxy; FuDfuTargetAvrPrivate *priv = GET_PRIVATE(self); const gchar *chip_id; const guint8 *buf; @@ -485,15 +480,17 @@ return TRUE; /* different methods for AVR vs. AVR32 */ - if (fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(target)), - FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { + proxy = fu_device_get_proxy(FU_DEVICE(target), error); + if (proxy == NULL) + return FALSE; + if (fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { chunk_sig = fu_dfu_target_avr_get_chip_signature(target, progress, error); if (chunk_sig == NULL) return FALSE; } else { chunk_sig = fu_dfu_target_avr32_get_chip_signature(target, progress, error); if (chunk_sig == NULL) { - g_prefix_error(error, "failed to get chip signature: "); + g_prefix_error_literal(error, "failed to get chip signature: "); return FALSE; } } @@ -502,7 +499,7 @@ buf = g_bytes_get_data(chunk_sig, &sz); fu_dump_bytes(G_LOG_DOMAIN, "AVR:CID", chunk_sig); if (!fu_memread_uint32_safe(buf, sz, 0x0, &priv->device_id, G_BIG_ENDIAN, error)) { - g_prefix_error(error, "cannot read config memory: "); + g_prefix_error_literal(error, "cannot read config memory: "); return FALSE; } @@ -523,16 +520,14 @@ } /* set the alt-name using the chip ID via a quirk */ - device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(target))); + device = FU_DFU_DEVICE(proxy); fu_device_add_instance_str(FU_DEVICE(device), "CID", chip_id_guid); if (!fu_device_build_instance_id(FU_DEVICE(device), error, "DFU_AVR", "CID", NULL)) return FALSE; chip_id = fu_dfu_device_get_chip_id(device); if (chip_id == NULL) { - fu_device_remove_private_flag(fu_device_get_proxy(FU_DEVICE(target)), - FU_DFU_DEVICE_FLAG_CAN_DOWNLOAD); - fu_device_remove_private_flag(fu_device_get_proxy(FU_DEVICE(target)), - FU_DFU_DEVICE_FLAG_CAN_UPLOAD); + fu_device_remove_private_flag(proxy, FU_DFU_DEVICE_FLAG_CAN_DOWNLOAD); + fu_device_remove_private_flag(proxy, FU_DFU_DEVICE_FLAG_CAN_UPLOAD); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -553,6 +548,7 @@ FuProgress *progress, GError **error) { + FuDevice *proxy; const guint8 footer[] = {0x00, 0x00, 0x00, @@ -570,6 +566,10 @@ 0xff, 0xff}; /* release */ + proxy = fu_device_get_proxy(FU_DEVICE(target), error); + if (proxy == NULL) + return FALSE; + /* progress */ fu_progress_set_id(progress, G_STRLOC); fu_progress_set_steps(progress, chunks->len); @@ -581,8 +581,7 @@ /* select page if required */ if (fu_chunk_get_page(chk) != *page_last) { g_autoptr(FuProgress) progress_tmp = fu_progress_new(G_STRLOC); - if (fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(target)), - FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { + if (fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { if (!fu_dfu_target_avr_select_memory_page(target, fu_chunk_get_page(chk), progress_tmp, @@ -599,7 +598,7 @@ } /* create chunk with header and footer */ - fu_byte_array_append_uint8(buf, DFU_AVR32_GROUP_DOWNLOAD); + fu_byte_array_append_uint8(buf, FU_DFU_AVR32_GROUP_DOWNLOAD); fu_byte_array_append_uint8(buf, DFU_AVR32_CMD_PROGRAM_START); fu_byte_array_append_uint16(buf, fu_chunk_get_address(chk), G_BIG_ENDIAN); fu_byte_array_append_uint16(buf, @@ -637,6 +636,7 @@ FuDfuTargetTransferFlags flags, GError **error) { + FuDevice *proxy; FuDfuSector *sector; const guint8 *data; gsize header_sz = ATMEL_AVR32_CONTROL_BLOCK_SIZE; @@ -690,10 +690,11 @@ } /* the original AVR protocol uses a half-size control block */ - if (fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(target)), - FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { + proxy = fu_device_get_proxy(FU_DEVICE(target), error); + if (proxy == NULL) + return FALSE; + if (fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) header_sz = ATMEL_AVR_CONTROL_BLOCK_SIZE; - } /* chunk up the memory space into pages */ data = g_bytes_get_data(blob, NULL); @@ -763,6 +764,7 @@ FuProgress *progress, GError **error) { + FuDevice *proxy; guint16 page_last = G_MAXUINT16; guint chunk_valid = G_MAXUINT; g_autoptr(FuChunk) chk2 = NULL; @@ -775,6 +777,9 @@ fu_progress_set_steps(progress, chunks->len); /* process each chunk */ + proxy = fu_device_get_proxy(FU_DEVICE(target), error); + if (proxy == NULL) + return NULL; blobs = g_ptr_array_new_with_free_func((GDestroyNotify)g_bytes_unref); for (guint i = 0; i < chunks->len; i++) { GBytes *blob_tmp = NULL; @@ -783,8 +788,7 @@ /* select page if required */ if (fu_chunk_get_page(chk) != page_last) { g_autoptr(FuProgress) progress_tmp = fu_progress_new(G_STRLOC); - if (fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(target)), - FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { + if (fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_LEGACY_PROTOCOL)) { if (!fu_dfu_target_avr_select_memory_page(target, fu_chunk_get_page(chk), progress_tmp, diff -Nru fwupd-2.0.8/plugins/dfu/fu-dfu-target-stm.c fwupd-2.0.20/plugins/dfu/fu-dfu-target-stm.c --- fwupd-2.0.8/plugins/dfu/fu-dfu-target-stm.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/fu-dfu-target-stm.c 2026-02-26 11:36:18.000000000 +0000 @@ -52,7 +52,7 @@ /* format buffer */ fu_byte_array_append_uint8(buf, DFU_STM_CMD_ERASE); if (!fu_dfu_target_download_chunk(target, 0, buf, 35000, progress, error)) { - g_prefix_error(error, "cannot mass-erase: "); + g_prefix_error_literal(error, "cannot mass-erase: "); return FALSE; } @@ -90,18 +90,21 @@ FuProgress *progress, GError **error) { - FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(target))); + FuDfuDevice *proxy; FuDfuSector *sector; FuChunk *chk = NULL; GBytes *chunk_tmp; guint32 offset = address; guint percentage_size = expected_size > 0 ? expected_size : maximum_size; gsize total_size = 0; - guint16 transfer_size = fu_dfu_device_get_transfer_size(device); g_autoptr(GBytes) contents = NULL; g_autoptr(GBytes) contents_truncated = NULL; g_autoptr(GPtrArray) chunks = NULL; + proxy = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(target), error)); + if (proxy == NULL) + return NULL; + /* progress */ fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 40, "set-addr"); @@ -136,7 +139,7 @@ fu_progress_step_done(progress); /* abort back to IDLE */ - if (!fu_dfu_device_abort(device, error)) + if (!fu_dfu_device_abort(proxy, error)) return NULL; fu_progress_step_done(progress); @@ -174,7 +177,7 @@ } /* detect short write as EOF */ - if (chunk_size < transfer_size) + if (chunk_size < fu_dfu_device_get_transfer_size(proxy)) break; /* more data than we needed */ @@ -184,7 +187,7 @@ fu_progress_step_done(progress); /* abort back to IDLE */ - if (!fu_dfu_device_abort(device, error)) + if (!fu_dfu_device_abort(proxy, error)) return NULL; fu_progress_step_done(progress); @@ -284,7 +287,7 @@ (guint)address + offset_dev); return FALSE; } - if (!fu_dfu_sector_has_cap(sector, FU_DFU_SECTOR_CAP_WRITEABLE)) { + if (!fu_dfu_sector_has_cap(sector, FU_DFU_SECTOR_CAP_WRITABLE)) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -419,7 +422,7 @@ FuDfuTargetTransferFlags flags, GError **error) { - FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(target))); + FuDfuDevice *proxy; g_autoptr(GBytes) bytes = NULL; g_autoptr(FuChunkArray) chunks = NULL; g_autoptr(GPtrArray) sectors_array = g_ptr_array_new(); @@ -431,11 +434,14 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); /* 1st pass: work out which sectors need erasing */ + proxy = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(target), error)); + if (proxy == NULL) + return FALSE; bytes = fu_chunk_get_bytes(chk); chunks = fu_chunk_array_new_from_bytes(bytes, fu_chunk_get_address(chk), FU_CHUNK_PAGESZ_NONE, - fu_dfu_device_get_transfer_size(device)); + fu_dfu_device_get_transfer_size(proxy)); if (!fu_dfu_target_stm_download_element1(target, chunks, sectors_array, diff -Nru fwupd-2.0.8/plugins/dfu/fu-dfu-target.c fwupd-2.0.20/plugins/dfu/fu-dfu-target.c --- fwupd-2.0.8/plugins/dfu/fu-dfu-target.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/fu-dfu-target.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,7 +25,6 @@ #include "fu-dfu-common.h" #include "fu-dfu-device.h" #include "fu-dfu-sector.h" -#include "fu-dfu-struct.h" #include "fu-dfu-target-private.h" /* waive-pre-commit */ #define DFU_TARGET_MANIFEST_MAX_POLLING_TRIES 200 @@ -45,6 +44,8 @@ { FuDfuTargetPrivate *priv = GET_PRIVATE(self); priv->sectors = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_DFU_DEVICE); } static void @@ -99,6 +100,7 @@ { FuDfuTargetPrivate *priv = GET_PRIVATE(self); FuDfuSectorCap cap = FU_DFU_SECTOR_CAP_NONE; + FuDevice *proxy; gchar *tmp; guint32 addr_offset = 0; guint64 nr_sectors; @@ -110,7 +112,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "Invalid number of sectors: %s", + "invalid number of sectors: %s", dfuse_sector_id); return FALSE; } @@ -120,7 +122,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "Invalid sector ID: %s", + "invalid sector ID: %s", dfuse_sector_id); return FALSE; } @@ -131,14 +133,16 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "Invalid sector size: %s", + "invalid sector size: %s", dfuse_sector_id); return FALSE; } /* handle weirdness */ - if (fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(self)), - FU_DFU_DEVICE_FLAG_ABSENT_SECTOR_SIZE)) { + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_ABSENT_SECTOR_SIZE)) { if (tmp[1] == '\0') { tmp[1] = tmp[0]; tmp[0] = 'B'; @@ -150,17 +154,17 @@ case 'B': /* byte */ case ' ': /* byte, ST reference bootloader :/ */ break; - case 'K': /* Kilo */ + case 'K': /* kilo */ sector_size *= 0x400; break; - case 'M': /* Mega */ + case 'M': /* mega */ sector_size *= 0x100000; break; default: g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "Invalid sector multiplier: %s", + "invalid sector multiplier: %s", tmp); return FALSE; } @@ -177,23 +181,23 @@ cap = FU_DFU_SECTOR_CAP_READABLE | FU_DFU_SECTOR_CAP_ERASABLE; break; case 'd': - cap = FU_DFU_SECTOR_CAP_WRITEABLE; + cap = FU_DFU_SECTOR_CAP_WRITABLE; break; case 'e': - cap = FU_DFU_SECTOR_CAP_READABLE | FU_DFU_SECTOR_CAP_WRITEABLE; + cap = FU_DFU_SECTOR_CAP_READABLE | FU_DFU_SECTOR_CAP_WRITABLE; break; case 'f': - cap = FU_DFU_SECTOR_CAP_ERASABLE | FU_DFU_SECTOR_CAP_WRITEABLE; + cap = FU_DFU_SECTOR_CAP_ERASABLE | FU_DFU_SECTOR_CAP_WRITABLE; break; case 'g': cap = FU_DFU_SECTOR_CAP_READABLE | FU_DFU_SECTOR_CAP_ERASABLE | - FU_DFU_SECTOR_CAP_WRITEABLE; + FU_DFU_SECTOR_CAP_WRITABLE; break; default: g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "Invalid sector type: %s", + "invalid sector type: %s", tmp); return FALSE; } @@ -226,7 +230,7 @@ if (alt_name == NULL) return TRUE; - /* From the Neo Freerunner */ + /* from the Neo Freerunner */ if (g_str_has_prefix(alt_name, "RAM 0x")) { FuDfuSector *sector; guint64 addr_tmp = 0; @@ -244,7 +248,7 @@ 0x0, /* zone */ 0x0, /* number */ FU_DFU_SECTOR_CAP_ERASABLE | FU_DFU_SECTOR_CAP_READABLE | - FU_DFU_SECTOR_CAP_WRITEABLE); + FU_DFU_SECTOR_CAP_WRITABLE); g_ptr_array_add(priv->sectors, sector); } @@ -277,7 +281,7 @@ G_MAXUINT32, FU_INTEGER_BASE_16, error)) { - g_prefix_error(error, "sector address invalid: "); + g_prefix_error_literal(error, "sector address invalid: "); return FALSE; } addr = (guint32)addr_tmp; @@ -300,7 +304,7 @@ (i - 1) / 2, j, error)) { - g_prefix_error(error, "Failed to parse: '%s': ", sectors[j]); + g_prefix_error(error, "failed to parse: '%s': ", sectors[j]); return FALSE; } } @@ -409,16 +413,19 @@ static gboolean fu_dfu_target_manifest_wait(FuDfuTarget *self, GError **error) { - FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuDfuDevice *proxy; guint polling_count = 0; /* get the status */ - if (!fu_dfu_device_refresh(device, 0, error)) + proxy = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + if (!fu_dfu_device_refresh(proxy, 0, error)) return FALSE; /* wait for FU_DFU_STATE_DFU_MANIFEST to not be set */ - while (fu_dfu_device_get_state(device) == FU_DFU_STATE_DFU_MANIFEST_SYNC || - fu_dfu_device_get_state(device) == FU_DFU_STATE_DFU_MANIFEST) { + while (fu_dfu_device_get_state(proxy) == FU_DFU_STATE_DFU_MANIFEST_SYNC || + fu_dfu_device_get_state(proxy) == FU_DFU_STATE_DFU_MANIFEST) { g_debug("waiting for FU_DFU_STATE_DFU_MANIFEST to clear"); if (polling_count++ > DFU_TARGET_MANIFEST_MAX_POLLING_TRIES) { @@ -429,19 +436,18 @@ return FALSE; } - fu_device_sleep(FU_DEVICE(device), - fu_dfu_device_get_download_timeout(device) + 1000); - if (!fu_dfu_device_refresh(device, 0, error)) + fu_device_sleep(FU_DEVICE(proxy), fu_dfu_device_get_download_timeout(proxy) + 1000); + if (!fu_dfu_device_refresh(proxy, 0, error)) return FALSE; } /* in an error state */ - if (fu_dfu_device_get_state(device) == FU_DFU_STATE_DFU_ERROR) { + if (fu_dfu_device_get_state(proxy) == FU_DFU_STATE_DFU_ERROR) { g_set_error_literal( error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - fu_dfu_target_status_to_error_msg(fu_dfu_device_get_status(device))); + fu_dfu_target_status_to_error_msg(fu_dfu_device_get_status(proxy))); return FALSE; } @@ -451,19 +457,22 @@ gboolean fu_dfu_target_check_status(FuDfuTarget *self, GError **error) { - FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuDfuDevice *proxy; FuDfuStatus status; g_autoptr(GTimer) timer = g_timer_new(); /* get the status */ - if (!fu_dfu_device_refresh(device, 0, error)) + proxy = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + if (!fu_dfu_device_refresh(proxy, 0, error)) return FALSE; /* wait for dfuDNBUSY to not be set */ - while (fu_dfu_device_get_state(device) == FU_DFU_STATE_DFU_DNBUSY) { + while (fu_dfu_device_get_state(proxy) == FU_DFU_STATE_DFU_DNBUSY) { g_debug("waiting for FU_DFU_STATE_DFU_DNBUSY to clear"); - fu_device_sleep(FU_DEVICE(device), fu_dfu_device_get_download_timeout(device)); - if (!fu_dfu_device_refresh(device, 0, error)) + fu_device_sleep(FU_DEVICE(proxy), fu_dfu_device_get_download_timeout(proxy)); + if (!fu_dfu_device_refresh(proxy, 0, error)) return FALSE; /* this is a really long time to save fwupd in case * the device has got wedged */ @@ -477,24 +486,24 @@ } /* not in an error state */ - if (fu_dfu_device_get_state(device) != FU_DFU_STATE_DFU_ERROR) + if (fu_dfu_device_get_state(proxy) != FU_DFU_STATE_DFU_ERROR) return TRUE; /* STM32-specific long errors */ - status = fu_dfu_device_get_status(device); - if (fu_dfu_device_get_version(device) == FU_DFU_FIRMARE_VERSION_DFUSE) { + status = fu_dfu_device_get_status(proxy); + if (fu_dfu_device_get_version(proxy) == FU_DFU_FIRMARE_VERSION_DFUSE) { if (status == FU_DFU_STATUS_ERR_VENDOR) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Read protection is active"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "read protection is active"); return FALSE; } if (status == FU_DFU_STATUS_ERR_TARGET) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Address is wrong or unsupported"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "address is wrong or unsupported"); return FALSE; } } @@ -519,21 +528,20 @@ static gboolean fu_dfu_target_use_alt_setting(FuDfuTarget *self, GError **error) { - FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); FuDfuTargetPrivate *priv = GET_PRIVATE(self); + FuDfuDevice *proxy; g_autoptr(GError) error_local = NULL; g_return_val_if_fail(FU_IS_DFU_TARGET(self), FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - /* ensure interface is claimed */ - if (!fu_dfu_device_ensure_interface(device, error)) - return FALSE; - /* use the correct setting */ - if (fu_device_has_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { - if (!fu_usb_device_set_interface_alt(FU_USB_DEVICE(device), - fu_dfu_device_get_interface(device), + proxy = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + if (fu_device_has_flag(FU_DEVICE(proxy), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (!fu_usb_device_set_interface_alt(FU_USB_DEVICE(proxy), + fu_dfu_device_get_interface(proxy), priv->alt_setting, &error_local)) { g_set_error(error, @@ -541,7 +549,7 @@ FWUPD_ERROR_NOT_SUPPORTED, "cannot set alternate setting 0x%02x on interface %i: %s", priv->alt_setting, - fu_dfu_device_get_interface(device), + fu_dfu_device_get_interface(proxy), error_local->message); return FALSE; } @@ -564,7 +572,7 @@ { FuDfuTargetClass *klass = FU_DFU_TARGET_GET_CLASS(self); FuDfuTargetPrivate *priv = GET_PRIVATE(self); - FuDevice *device = fu_device_get_proxy(FU_DEVICE(self)); + FuDevice *proxy; g_return_val_if_fail(FU_IS_DFU_TARGET(self), FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); @@ -580,14 +588,17 @@ } /* GD32VF103 devices features and peripheral list */ + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; if (priv->alt_setting == 0x0 && - fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_GD32)) { + fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_GD32)) { /* RB R8 R6 R4 VB V8 * Flash (KB) 128 64 32 16 128 64 * TB T8 T6 T4 CB C8 C6 C4 * Flash (KB) 128 64 32 16 128 64 32 16 */ - const gchar *serial = fu_device_get_serial(device); + const gchar *serial = fu_device_get_serial(proxy); if (serial == NULL || strlen(serial) < 4 || serial[3] != 'J') { g_set_error(error, FWUPD_ERROR, @@ -628,7 +639,7 @@ if (priv->alt_idx != 0x00 && fu_device_get_logical_id(FU_DEVICE(self)) == NULL) { g_autofree gchar *alt_name = NULL; alt_name = - fu_usb_device_get_string_descriptor(FU_USB_DEVICE(device), priv->alt_idx, NULL); + fu_usb_device_get_string_descriptor(FU_USB_DEVICE(proxy), priv->alt_idx, NULL); fu_device_set_logical_id(FU_DEVICE(self), alt_name); } @@ -643,13 +654,12 @@ /* add a dummy entry */ if (priv->sectors->len == 0) { FuDfuSector *sector; - sector = - fu_dfu_sector_new(0x0, /* addr */ - 0x0, /* size */ - 0x0, /* size_left */ - 0x0, /* zone */ - 0x0, /* number */ - FU_DFU_SECTOR_CAP_READABLE | FU_DFU_SECTOR_CAP_WRITEABLE); + sector = fu_dfu_sector_new(0x0, /* addr */ + 0x0, /* size */ + 0x0, /* size_left */ + 0x0, /* zone */ + 0x0, /* number */ + FU_DFU_SECTOR_CAP_READABLE | FU_DFU_SECTOR_CAP_WRITABLE); g_debug("no UM0424 sector description in %s", fu_device_get_logical_id(FU_DEVICE(self))); g_ptr_array_add(priv->sectors, sector); @@ -667,23 +677,26 @@ FuProgress *progress, GError **error) { - FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuDfuDevice *proxy; g_autoptr(GError) error_local = NULL; gsize actual_length; /* fall back to default */ + proxy = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; if (timeout_ms == 0) - timeout_ms = fu_dfu_device_get_timeout(device); + timeout_ms = fu_dfu_device_get_timeout(proxy); /* low level packet debugging */ fu_dump_raw(G_LOG_DOMAIN, "Message", buf->data, buf->len); - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(proxy), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_CLASS, FU_USB_RECIPIENT_INTERFACE, FU_DFU_REQUEST_DNLOAD, index, - fu_dfu_device_get_interface(device), + fu_dfu_device_get_interface(proxy), buf->data, buf->len, &actual_length, @@ -691,7 +704,7 @@ NULL, &error_local)) { /* refresh the error code */ - fu_dfu_device_error_fixup(device, &error_local); + fu_dfu_device_error_fixup(proxy, &error_local); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -702,22 +715,22 @@ /* for STM32 devices, the action only occurs when we do GetStatus -- * and it can take a long time to complete! */ - if (fu_dfu_device_get_version(device) == FU_DFU_FIRMARE_VERSION_DFUSE) { - if (!fu_dfu_device_refresh(device, 35000, error)) + if (fu_dfu_device_get_version(proxy) == FU_DFU_FIRMARE_VERSION_DFUSE) { + if (!fu_dfu_device_refresh(proxy, 35000, error)) return FALSE; } /* wait for the device to write contents to the EEPROM */ - if (buf->len == 0 && fu_dfu_device_get_download_timeout(device) > 0) + if (buf->len == 0 && fu_dfu_device_get_download_timeout(proxy) > 0) fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_BUSY); - if (fu_dfu_device_get_download_timeout(device) > 0) { - g_debug("sleeping for %ums…", fu_dfu_device_get_download_timeout(device)); - fu_device_sleep(FU_DEVICE(device), fu_dfu_device_get_download_timeout(device)); + if (fu_dfu_device_get_download_timeout(proxy) > 0) { + g_debug("sleeping for %ums…", fu_dfu_device_get_download_timeout(proxy)); + fu_device_sleep(FU_DEVICE(proxy), fu_dfu_device_get_download_timeout(proxy)); } /* find out if the write was successful, waiting for BUSY to clear */ if (!fu_dfu_target_check_status(self, error)) { - g_prefix_error(error, "cannot wait for busy: "); + g_prefix_error_literal(error, "cannot wait for busy: "); return FALSE; } @@ -732,31 +745,34 @@ FuProgress *progress, GError **error) { - FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuDfuDevice *proxy; g_autoptr(GError) error_local = NULL; guint8 *buf; gsize actual_length; /* unset */ + proxy = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return NULL; if (buf_sz == 0) - buf_sz = (gsize)fu_dfu_device_get_transfer_size(device); + buf_sz = (gsize)fu_dfu_device_get_transfer_size(proxy); buf = g_new0(guint8, buf_sz); - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(proxy), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_CLASS, FU_USB_RECIPIENT_INTERFACE, FU_DFU_REQUEST_UPLOAD, index, - fu_dfu_device_get_interface(device), + fu_dfu_device_get_interface(proxy), buf, buf_sz, &actual_length, - fu_dfu_device_get_timeout(device), + fu_dfu_device_get_timeout(proxy), NULL, &error_local)) { /* refresh the error code */ - fu_dfu_device_error_fixup(device, &error_local); + fu_dfu_device_error_fixup(proxy, &error_local); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -788,7 +804,7 @@ gboolean fu_dfu_target_attach(FuDfuTarget *self, FuProgress *progress, GError **error) { - FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuDfuDevice *proxy; FuDfuTargetClass *klass = FU_DFU_TARGET_GET_CLASS(self); /* ensure populated */ @@ -800,7 +816,10 @@ return klass->attach(self, progress, error); /* normal DFU mode just needs a bus reset */ - return fu_dfu_device_reset(device, progress, error); + proxy = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + return fu_dfu_device_reset(proxy, progress, error); } static FuChunk * @@ -811,14 +830,17 @@ FuProgress *progress, GError **error) { - FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuDfuDevice *proxy; GBytes *chunk_tmp; guint percentage_size = expected_size > 0 ? expected_size : maximum_size; gsize total_size = 0; - guint16 transfer_size = fu_dfu_device_get_transfer_size(device); g_autoptr(GBytes) contents = NULL; g_autoptr(GPtrArray) chunks = NULL; + proxy = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return NULL; + /* update UI */ fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); @@ -851,7 +873,7 @@ fu_progress_set_percentage_full(progress, total_size, percentage_size); /* detect short write as EOF */ - if (chunk_size < transfer_size) + if (chunk_size < fu_dfu_device_get_transfer_size(proxy)) break; } @@ -923,6 +945,7 @@ GError **error) { FuDfuTargetPrivate *priv = GET_PRIVATE(self); + FuDevice *proxy; FuDfuSector *sector; guint16 zone_cur; guint32 zone_size = 0; @@ -937,8 +960,10 @@ return FALSE; /* can the target do this? */ - if (!fu_device_has_private_flag(fu_device_get_proxy(FU_DEVICE(self)), - FU_DFU_DEVICE_FLAG_CAN_UPLOAD)) { + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (!fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_CAN_UPLOAD)) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -999,39 +1024,7 @@ } /* success */ - fu_firmware_add_image(firmware, image); - return TRUE; -} - -static gchar * -_g_bytes_compare_verbose(GBytes *bytes1, GBytes *bytes2) -{ - const guint8 *data1; - const guint8 *data2; - gsize length1; - gsize length2; - - data1 = g_bytes_get_data(bytes1, &length1); - data2 = g_bytes_get_data(bytes2, &length2); - - /* not the same length */ - if (length1 != length2) { - return g_strdup_printf("got %" G_GSIZE_FORMAT " bytes, " - "expected %" G_GSIZE_FORMAT, - length1, - length2); - } - - /* return 00 01 02 03 */ - for (guint i = 0; i < length1; i++) { - if (data1[i] != data2[i]) { - return g_strdup_printf("got 0x%02x, expected 0x%02x @ 0x%04x", - data1[i], - data2[i], - i); - } - } - return NULL; + return fu_firmware_add_image(firmware, image, error); } static gboolean @@ -1041,11 +1034,16 @@ FuDfuTargetTransferFlags flags, GError **error) { - FuDfuDevice *device = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuDfuDevice *proxy; guint32 nr_chunks; - guint16 transfer_size = fu_dfu_device_get_transfer_size(device); + guint16 transfer_size; g_autoptr(GBytes) bytes = NULL; + proxy = FU_DFU_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + transfer_size = fu_dfu_device_get_transfer_size(proxy); + /* round up as we have to transfer incomplete blocks */ bytes = fu_chunk_get_bytes(chk); nr_chunks = (guint)ceil((gdouble)g_bytes_get_size(bytes) / (gdouble)transfer_size); @@ -1097,12 +1095,12 @@ FuDfuTargetTransferFlags flags, GError **error) { - FuDevice *device = fu_device_get_proxy(FU_DEVICE(self)); + FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self), error); FuDfuTargetClass *klass = FU_DFU_TARGET_GET_CLASS(self); /* progress */ - if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY && - fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_CAN_UPLOAD)) { + if (flags & FU_DFU_TARGET_TRANSFER_FLAG_VERIFY && + fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_CAN_UPLOAD)) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 96, NULL); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 4, NULL); @@ -1130,8 +1128,8 @@ fu_progress_step_done(progress); /* verify */ - if (flags & DFU_TARGET_TRANSFER_FLAG_VERIFY && - fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_CAN_UPLOAD)) { + if (flags & FU_DFU_TARGET_TRANSFER_FLAG_VERIFY && + fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_CAN_UPLOAD)) { g_autoptr(GBytes) bytes = NULL; g_autoptr(GBytes) bytes_tmp = NULL; g_autoptr(FuChunk) chunk_tmp = NULL; @@ -1145,14 +1143,8 @@ if (chunk_tmp == NULL) return FALSE; bytes_tmp = fu_chunk_get_bytes(chunk_tmp); - if (g_bytes_compare(bytes_tmp, bytes) != 0) { - g_autofree gchar *bytes_cmp_str = NULL; - bytes_cmp_str = _g_bytes_compare_verbose(bytes_tmp, bytes); - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "verify failed: %s", - bytes_cmp_str); + if (!fu_bytes_compare(bytes_tmp, bytes, error)) { + g_prefix_error_literal(error, "verify failed: "); return FALSE; } fu_progress_step_done(progress); @@ -1165,7 +1157,7 @@ * fu_dfu_target_download: * @self: a #FuDfuTarget * @image: a #FuFirmware - * @flags: DFU target flags, e.g. %DFU_TARGET_TRANSFER_FLAG_VERIFY + * @flags: DFU target flags, e.g. %FU_DFU_TARGET_TRANSFER_FLAG_VERIFY * @error: (nullable): optional return location for an error * * Downloads firmware from the host to the target, optionally verifying @@ -1180,7 +1172,7 @@ FuDfuTargetTransferFlags flags, GError **error) { - FuDevice *device = fu_device_get_proxy(FU_DEVICE(self)); + FuDevice *proxy; FuDfuTargetPrivate *priv = GET_PRIVATE(self); g_autoptr(GPtrArray) chunks = NULL; @@ -1193,7 +1185,10 @@ return FALSE; /* can the target do this? */ - if (!fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_CAN_DOWNLOAD)) { + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + if (!fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_CAN_DOWNLOAD)) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -1225,7 +1220,7 @@ /* auto-detect missing firmware address -- this assumes * that the first target is the main program memory and that * there is only one element in the firmware file */ - if (flags & DFU_TARGET_TRANSFER_FLAG_ADDR_HEURISTIC && + if (flags & FU_DFU_TARGET_TRANSFER_FLAG_ADDR_HEURISTIC && fu_chunk_get_address(chk) == 0x0 && chunks->len == 1 && priv->sectors->len > 0) { FuDfuSector *sector = g_ptr_array_index(priv->sectors, 0); @@ -1244,8 +1239,8 @@ fu_progress_step_done(progress); } - if (fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_MANIFEST_POLL) && - fu_device_has_private_flag(device, FU_DFU_DEVICE_FLAG_MANIFEST_TOL)) + if (fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_MANIFEST_POLL) && + fu_device_has_private_flag(proxy, FU_DFU_DEVICE_FLAG_MANIFEST_TOL)) if (!fu_dfu_target_manifest_wait(self, error)) return FALSE; @@ -1265,7 +1260,7 @@ fu_dfu_target_get_alt_setting(FuDfuTarget *self) { FuDfuTargetPrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FU_IS_DFU_TARGET(self), 0xff); + g_return_val_if_fail(FU_IS_DFU_TARGET(self), G_MAXUINT8); return priv->alt_setting; } diff -Nru fwupd-2.0.8/plugins/dfu/fu-dfu-target.h fwupd-2.0.20/plugins/dfu/fu-dfu-target.h --- fwupd-2.0.8/plugins/dfu/fu-dfu-target.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/fu-dfu-target.h 2026-02-26 11:36:18.000000000 +0000 @@ -10,30 +10,11 @@ #include "fu-dfu-common.h" #include "fu-dfu-sector.h" +#include "fu-dfu-struct.h" #define FU_TYPE_DFU_TARGET (fu_dfu_target_get_type()) G_DECLARE_DERIVABLE_TYPE(FuDfuTarget, fu_dfu_target, FU, DFU_TARGET, FuDevice) -/** - * FuDfuTargetTransferFlags: - * @DFU_TARGET_TRANSFER_FLAG_NONE: No flags set - * @DFU_TARGET_TRANSFER_FLAG_VERIFY: Verify the download once complete - * @DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID: Allow downloading images with wildcard VIDs - * @DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID: Allow downloading images with wildcard PIDs - * @DFU_TARGET_TRANSFER_FLAG_ADDR_HEURISTIC: Automatically detect the address to use - * - * The optional flags used for transferring firmware. - **/ -typedef enum { - DFU_TARGET_TRANSFER_FLAG_NONE = 0, - DFU_TARGET_TRANSFER_FLAG_VERIFY = (1 << 0), - DFU_TARGET_TRANSFER_FLAG_WILDCARD_VID = (1 << 4), - DFU_TARGET_TRANSFER_FLAG_WILDCARD_PID = (1 << 5), - DFU_TARGET_TRANSFER_FLAG_ADDR_HEURISTIC = (1 << 7), - /*< private >*/ - DFU_TARGET_TRANSFER_FLAG_LAST -} FuDfuTargetTransferFlags; - struct _FuDfuTargetClass { FuDeviceClass parent_class; gboolean (*setup)(FuDfuTarget *self, GError **error); diff -Nru fwupd-2.0.8/plugins/dfu/fu-dfu.rs fwupd-2.0.20/plugins/dfu/fu-dfu.rs --- fwupd-2.0.8/plugins/dfu/fu-dfu.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/fu-dfu.rs 2026-02-26 11:36:18.000000000 +0000 @@ -9,6 +9,13 @@ CanAccelerate = 1 << 7, } +enum FuDfuTargetTransferFlags { + None = 0, + Verify = 1 << 0, + WildcardVid = 1 << 4, + WildcardPid = 1 << 5, + AddrHeuristic = 1 << 7, // automatically detect +} enum FuDfuRequest { Detach, @@ -55,10 +62,36 @@ DfuError, } -#[derive(ToBitString)] +#[derive(ToString)] enum FuDfuSectorCap { None = 0, // No operations possible Readable = 1 << 0, - Writeable = 1 << 1, + Writable = 1 << 1, Erasable = 1 << 2, } + +// ATMEL AVR version of DFU: http://www.atmel.com/Images/doc7618.pdf +enum FuDfuAvrCmd { + ProgStart = 0x01, + DisplayData = 0x03, + WriteCommand = 0x04, + ReadCommand = 0x05, + ChangeBaseAddr = 0x06, +} + +enum FuDfuAvr32MemoryUnit { + Flash, + Eeprom, + Security, + Configuration, + Bootloader, + Signature, + User, +} + +enum FuDfuAvr32Group { + Download = 0x01, + Upload = 0x03, + Exec = 0x04, + Select = 0x06, +} diff -Nru fwupd-2.0.8/plugins/dfu/meson.build fwupd-2.0.20/plugins/dfu/meson.build --- fwupd-2.0.8/plugins/dfu/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -11,6 +11,10 @@ python3, join_paths(meson.project_source_root(), 'libfwupdplugin', 'rustgen.py'), '@INPUT@', '@OUTPUT0@', '@OUTPUT1@', + '--include', + 'fwupdplugin.h', + '--prefix', + 'Fu', ], ) plugin_builtin_dfu = static_library('fu_plugin_dfu', @@ -63,6 +67,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', diff -Nru fwupd-2.0.8/plugins/dfu/tests/fwupd-a3bu-xplained.json fwupd-2.0.20/plugins/dfu/tests/fwupd-a3bu-xplained.json --- fwupd-2.0.8/plugins/dfu/tests/fwupd-a3bu-xplained.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/tests/fwupd-a3bu-xplained.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/f5bbeaba1037dce31dd12f349e8148ae35f98b61-a3bu-xplained123.cab", - "emulation-url": "https://fwupd.org/downloads/01b95b0206f1a42a2bf95a432d162ef1f9f1f71edb5696127c923ceffadfdf68-a3bu-xplained123.zip", + "url": "f5bbeaba1037dce31dd12f349e8148ae35f98b61-a3bu-xplained123.cab", + "emulation-url": "01b95b0206f1a42a2bf95a432d162ef1f9f1f71edb5696127c923ceffadfdf68-a3bu-xplained123.zip", "components": [ { "version": "1.23", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/24d838541efe0340bf67e1cc5a9b95526e4d3702-a3bu-xplained124.cab", - "emulation-url": "https://fwupd.org/downloads/357483755bbc4f3a33c0b5bc05a0ef49654be0a15c14cbde2bdf0cd9c203d632-a3bu-xplained124.zip", + "url": "24d838541efe0340bf67e1cc5a9b95526e4d3702-a3bu-xplained124.cab", + "emulation-url": "357483755bbc4f3a33c0b5bc05a0ef49654be0a15c14cbde2bdf0cd9c203d632-a3bu-xplained124.zip", "components": [ { "version": "1.24", diff -Nru fwupd-2.0.8/plugins/dfu/tests/fwupd-at90usbkey.json fwupd-2.0.20/plugins/dfu/tests/fwupd-at90usbkey.json --- fwupd-2.0.8/plugins/dfu/tests/fwupd-at90usbkey.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/tests/fwupd-at90usbkey.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/b6bef375597e848971f230cf992c9740f7bf5b92-at90usbkey123.cab", - "emulation-url": "https://fwupd.org/downloads/57c8acd0de45ff01d91da5ecec1f826fbb438cc3f7b11ca732a8fbfdc2ce24e1-at90usbkey123.zip", + "url": "b6bef375597e848971f230cf992c9740f7bf5b92-at90usbkey123.cab", + "emulation-url": "57c8acd0de45ff01d91da5ecec1f826fbb438cc3f7b11ca732a8fbfdc2ce24e1-at90usbkey123.zip", "components": [ { "version": "1.23", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/47807fd4a94a4d5514ac6bf7a73038e00ed63225-at90usbkey124.cab", - "emulation-url": "https://fwupd.org/downloads/e65432a2dd63c3ce666cc13a0f5ae13eb6d15cebbc1d022cafcff46b892cdcc4-at90usbkey124.zip", + "url": "47807fd4a94a4d5514ac6bf7a73038e00ed63225-at90usbkey124.cab", + "emulation-url": "e65432a2dd63c3ce666cc13a0f5ae13eb6d15cebbc1d022cafcff46b892cdcc4-at90usbkey124.zip", "components": [ { "version": "1.24", diff -Nru fwupd-2.0.8/plugins/dfu/tests/realtek-rts5855.json fwupd-2.0.20/plugins/dfu/tests/realtek-rts5855.json --- fwupd-2.0.8/plugins/dfu/tests/realtek-rts5855.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/dfu/tests/realtek-rts5855.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/ff989c4b71c92a4a217dfb2f82c1c87691b8eb31-rts5855_v0.4.cab", - "emulation-url": "https://fwupd.org/downloads/75d6c26e3842bbd00991a585078e872cae66c7a81e15c5736fc478ba5ec0f77d-rts5855_v0.4.zip", + "url": "ff989c4b71c92a4a217dfb2f82c1c87691b8eb31-rts5855_v0.4.cab", + "emulation-url": "75d6c26e3842bbd00991a585078e872cae66c7a81e15c5736fc478ba5ec0f77d-rts5855_v0.4.zip", "components": [ { "version": "0.4", @@ -15,7 +15,7 @@ ] }, { - "url": "https://fwupd.org/downloads/ed5c411d6b74c363209f408f87618fa5c31b50ab-v0.3.cab", + "url": "ed5c411d6b74c363209f408f87618fa5c31b50ab-v0.3.cab", "components": [ { "version": "0.3", diff -Nru fwupd-2.0.8/plugins/ebitdo/fu-ebitdo-device.c fwupd-2.0.20/plugins/ebitdo/fu-ebitdo-device.c --- fwupd-2.0.8/plugins/ebitdo/fu-ebitdo-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ebitdo/fu-ebitdo-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -37,10 +37,10 @@ { gsize actual_length; guint8 ep_out = FU_EBITDO_USB_RUNTIME_EP_OUT; - g_autoptr(GByteArray) st_hdr = fu_struct_ebitdo_pkt_new(); + g_autoptr(FuStructEbitdoPkt) st_hdr = fu_struct_ebitdo_pkt_new(); g_autoptr(GError) error_local = NULL; - fu_byte_array_set_size(st_hdr, FU_EBITDO_USB_EP_SIZE, 0x0); + fu_byte_array_set_size(st_hdr->buf, FU_EBITDO_USB_EP_SIZE, 0x0); /* different */ if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) @@ -48,7 +48,10 @@ /* check size */ if (in_len > 64 - 8) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "input buffer too large"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "input buffer too large"); return FALSE; } @@ -61,8 +64,8 @@ fu_struct_ebitdo_pkt_set_cmd_len(st_hdr, in_len + 3); fu_struct_ebitdo_pkt_set_cmd(st_hdr, cmd); fu_struct_ebitdo_pkt_set_payload_len(st_hdr, in_len); - if (!fu_memcpy_safe(st_hdr->data, - st_hdr->len, + if (!fu_memcpy_safe(st_hdr->buf->data, + st_hdr->buf->len, FU_STRUCT_EBITDO_PKT_SIZE, /* dst */ in, in_len, @@ -76,13 +79,13 @@ fu_struct_ebitdo_pkt_set_cmd(st_hdr, cmd); fu_struct_ebitdo_pkt_set_pkt_len(st_hdr, 5); } - fu_dump_raw(G_LOG_DOMAIN, "->DEVICE", st_hdr->data, st_hdr->len); + fu_dump_raw(G_LOG_DOMAIN, "->DEVICE", st_hdr->buf->data, st_hdr->buf->len); /* get data from device */ if (!fu_usb_device_interrupt_transfer(FU_USB_DEVICE(self), ep_out, - st_hdr->data, - st_hdr->len, + st_hdr->buf->data, + st_hdr->buf->len, &actual_length, FU_EBITDO_USB_TIMEOUT, NULL, /* cancellable */ @@ -104,7 +107,7 @@ guint8 packet[FU_EBITDO_USB_EP_SIZE] = {0}; gsize actual_length; guint8 ep_in = FU_EBITDO_USB_RUNTIME_EP_IN; - g_autoptr(GByteArray) st_hdr = NULL; + g_autoptr(FuStructEbitdoPkt) st_hdr = NULL; g_autoptr(GError) error_local = NULL; /* different */ @@ -144,9 +147,8 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "payload too small, expected %" G_GSIZE_FORMAT - " got %u", - out_len, + "payload too small, expected 0x%x got 0x%x", + (guint)out_len, fu_struct_ebitdo_pkt_get_payload_len(st_hdr)); return FALSE; } @@ -195,9 +197,8 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "outbuf size wrong, expected %" G_GSIZE_FORMAT - " got %i", - out_len, + "outbuf size wrong, expected 0x%x got 0x%i", + (guint)out_len, fu_struct_ebitdo_pkt_get_cmd_len(st_hdr)); return FALSE; } @@ -254,10 +255,10 @@ /* verify the vendor prefix against a allowlist */ ven = fu_device_get_vendor(FU_DEVICE(self)); if (ven == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "could not check vendor descriptor: "); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "could not check vendor descriptor"); return FALSE; } for (guint i = 0; allowlist[i] != NULL; i++) { @@ -339,9 +340,8 @@ error)) { return FALSE; } - if (!fu_ebitdo_device_receive(self, (guint8 *)&version_tmp, sizeof(version_tmp), error)) { + if (!fu_ebitdo_device_receive(self, (guint8 *)&version_tmp, sizeof(version_tmp), error)) return FALSE; - } fu_device_set_version_raw(device, GUINT32_FROM_LE(version_tmp)); /* nocheck:blocked */ /* get verification ID */ @@ -354,9 +354,8 @@ error)) { return FALSE; } - if (!fu_ebitdo_device_receive(self, (guint8 *)&serial_tmp, sizeof(serial_tmp), error)) { + if (!fu_ebitdo_device_receive(self, (guint8 *)&serial_tmp, sizeof(serial_tmp), error)) return FALSE; - } for (guint i = 0; i < 9; i++) self->serial[i] = GUINT32_FROM_LE(serial_tmp[i]); /* nocheck:blocked */ @@ -447,7 +446,7 @@ FuEbitdoDevice *self = FU_EBITDO_DEVICE(device); const guint8 *buf; gsize bufsz = 0; - guint32 serial_new[3]; + guint32 serial_new[3] = {0}; g_autoptr(GBytes) fw_hdr = NULL; g_autoptr(GInputStream) stream_payload = NULL; g_autoptr(GError) error_local = NULL; @@ -501,11 +500,11 @@ buf, bufsz, error)) { - g_prefix_error(error, "failed to set up firmware header: "); + g_prefix_error_literal(error, "failed to set up firmware header: "); return FALSE; } if (!fu_ebitdo_device_receive(self, NULL, 0, error)) { - g_prefix_error(error, "failed to get ACK for fw update header: "); + g_prefix_error_literal(error, "failed to get ACK for fw update header: "); return FALSE; } fu_progress_step_done(progress); @@ -566,7 +565,7 @@ (guint8 *)serial_new, sizeof(serial_new), error)) { - g_prefix_error(error, "failed to set encoding ID: "); + g_prefix_error_literal(error, "failed to set encoding ID: "); return FALSE; } @@ -578,11 +577,12 @@ NULL, 0, error)) { - g_prefix_error(error, "failed to mark firmware as successful: "); + g_prefix_error_literal(error, "failed to mark firmware as successful: "); return FALSE; } if (!fu_ebitdo_device_receive(self, NULL, 0, &error_local)) { - g_prefix_error(&error_local, "failed to get ACK for mark firmware as successful: "); + g_prefix_error_literal(&error_local, + "failed to get ACK for mark firmware as successful: "); if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) { fu_device_set_remove_delay(device, 0); g_debug("%s", error_local->message); @@ -605,7 +605,7 @@ /* when doing a soft-reboot the device does not re-enumerate properly * so manually reboot the FuUsbDevice */ if (!fu_usb_device_reset(FU_USB_DEVICE(device), &error_local)) { - g_prefix_error(&error_local, "failed to force-reset device: "); + g_prefix_error_literal(&error_local, "failed to force-reset device: "); if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) { fu_device_set_remove_delay(device, 0); g_debug("%s", error_local->message); @@ -637,7 +637,7 @@ fu_device_set_vendor(device, "8BitDo"); /* add a hardcoded icon name */ - fu_device_add_icon(device, "input-gaming"); + fu_device_add_icon(device, FU_DEVICE_ICON_INPUT_GAMING); /* only the bootloader can do the update */ if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { @@ -654,7 +654,7 @@ } static void -fu_ebitdo_device_set_progress(FuDevice *self, FuProgress *progress) +fu_ebitdo_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); diff -Nru fwupd-2.0.8/plugins/ebitdo/fu-ebitdo-firmware.c fwupd-2.0.20/plugins/ebitdo/fu-ebitdo-firmware.c --- fwupd-2.0.8/plugins/ebitdo/fu-ebitdo-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ebitdo/fu-ebitdo-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,7 @@ #include "fu-ebitdo-struct.h" struct _FuEbitdoFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; }; G_DEFINE_TYPE(FuEbitdoFirmware, fu_ebitdo_firmware, FU_TYPE_FIRMWARE) @@ -18,14 +18,14 @@ static gboolean fu_ebitdo_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint32 payload_len; guint32 version; gsize streamsz = 0; g_autoptr(FuFirmware) img_hdr = fu_firmware_new(); - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructEbitdoHdr) st = NULL; g_autoptr(GInputStream) stream_hdr = NULL; g_autoptr(GInputStream) stream_payload = NULL; @@ -35,7 +35,7 @@ return FALSE; if (!fu_input_stream_size(stream, &streamsz, error)) return FALSE; - payload_len = (guint32)(streamsz - st->len); + payload_len = (guint32)(streamsz - st->buf->len); if (payload_len != fu_struct_ebitdo_hdr_get_destination_len(st)) { g_set_error(error, FWUPD_ERROR, @@ -51,16 +51,17 @@ fu_firmware_set_version_raw(firmware, version); /* add header */ - stream_hdr = fu_partial_input_stream_new(stream, 0x0, st->len, error); + stream_hdr = fu_partial_input_stream_new(stream, 0x0, st->buf->len, error); if (stream_hdr == NULL) return FALSE; if (!fu_firmware_parse_stream(img_hdr, stream_hdr, 0x0, flags, error)) return FALSE; fu_firmware_set_id(img_hdr, FU_FIRMWARE_ID_HEADER); - fu_firmware_add_image(firmware, img_hdr); + if (!fu_firmware_add_image(firmware, img_hdr, error)) + return FALSE; /* add payload */ - stream_payload = fu_partial_input_stream_new(stream, st->len, payload_len, error); + stream_payload = fu_partial_input_stream_new(stream, st->buf->len, payload_len, error); if (stream_payload == NULL) return FALSE; if (!fu_firmware_set_stream(firmware, stream_payload, error)) @@ -73,7 +74,7 @@ static GByteArray * fu_ebitdo_firmware_write(FuFirmware *firmware, GError **error) { - g_autoptr(GByteArray) st = fu_struct_ebitdo_hdr_new(); + g_autoptr(FuStructEbitdoHdr) st = fu_struct_ebitdo_hdr_new(); g_autoptr(GBytes) blob = NULL; /* header then payload */ @@ -83,8 +84,8 @@ fu_struct_ebitdo_hdr_set_version(st, fu_firmware_get_version_raw(firmware)); fu_struct_ebitdo_hdr_set_destination_addr(st, fu_firmware_get_addr(firmware)); fu_struct_ebitdo_hdr_set_destination_len(st, g_bytes_get_size(blob)); - fu_byte_array_append_bytes(st, blob); - return g_steal_pointer(&st); + fu_byte_array_append_bytes(st->buf, blob); + return g_steal_pointer(&st->buf); } static gchar * diff -Nru fwupd-2.0.8/plugins/ebitdo/fu-ebitdo-plugin.c fwupd-2.0.20/plugins/ebitdo/fu-ebitdo-plugin.c --- fwupd-2.0.8/plugins/ebitdo/fu-ebitdo-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ebitdo/fu-ebitdo-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,6 +25,7 @@ fu_ebitdo_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_EBITDO_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_EBITDO_FIRMWARE); } diff -Nru fwupd-2.0.8/plugins/ebitdo/tests/8bitdo-nes30pro.json fwupd-2.0.20/plugins/ebitdo/tests/8bitdo-nes30pro.json --- fwupd-2.0.8/plugins/ebitdo/tests/8bitdo-nes30pro.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ebitdo/tests/8bitdo-nes30pro.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/5b7c4df860695bf000967e140682082b44cea2c033da27fabeffc91be5d2d30c-8Bitdo-SFC30PRO_NES30PRO-4.01.cab", + "url": "5b7c4df860695bf000967e140682082b44cea2c033da27fabeffc91be5d2d30c-8Bitdo-SFC30PRO_NES30PRO-4.01.cab", "components": [ { "version": "4.01", diff -Nru fwupd-2.0.8/plugins/ebitdo/tests/8bitdo-sf30pro.json fwupd-2.0.20/plugins/ebitdo/tests/8bitdo-sf30pro.json --- fwupd-2.0.8/plugins/ebitdo/tests/8bitdo-sf30pro.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ebitdo/tests/8bitdo-sf30pro.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": true, "steps": [ { - "url": "https://fwupd.org/downloads/3d3a65ee2e8581647fb09d752fa7e21ee1566481-8Bitdo-SF30_Pro-SN30_Pro-1.26.cab", + "url": "3d3a65ee2e8581647fb09d752fa7e21ee1566481-8Bitdo-SF30_Pro-SN30_Pro-1.26.cab", "components": [ { "version": "1.26", diff -Nru fwupd-2.0.8/plugins/ebitdo/tests/8bitdo-sfc30.json fwupd-2.0.20/plugins/ebitdo/tests/8bitdo-sfc30.json --- fwupd-2.0.8/plugins/ebitdo/tests/8bitdo-sfc30.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ebitdo/tests/8bitdo-sfc30.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": true, "steps": [ { - "url": "https://fwupd.org/downloads/fe066b57c69265f4cce8a999a5f8ab90d1c13b24-8Bitdo-SFC30_NES30_SFC30_SNES30-4.01.cab", + "url": "fe066b57c69265f4cce8a999a5f8ab90d1c13b24-8Bitdo-SFC30_NES30_SFC30_SNES30-4.01.cab", "components": [ { "version": "4.01", diff -Nru fwupd-2.0.8/plugins/egis-moc/README.md fwupd-2.0.20/plugins/egis-moc/README.md --- fwupd-2.0.8/plugins/egis-moc/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/egis-moc/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,39 @@ +--- +title: Plugin: Egis Fingerprint Sensor +--- + +## Introduction + +The plugin used for update firmware for fingerprint sensors from Egis. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +a packed binary file format. + +This plugin supports the following protocol ID: + +* `com.egistec.usb` + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +* `USB\VID_1C7A&PID_05AE` + +## Update Behavior + +The firmware is deployed when the device is in normal runtime mode, and the +device will reset when the new firmware has been written. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x1C7A` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. + +## Version Considerations + +This plugin has been available since fwupd version `2.0.14`. diff -Nru fwupd-2.0.8/plugins/egis-moc/egis-moc.quirk fwupd-2.0.20/plugins/egis-moc/egis-moc.quirk --- fwupd-2.0.8/plugins/egis-moc/egis-moc.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/egis-moc/egis-moc.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,4 @@ +# Egis Fingerprint sensor +[USB\VID_1C7A&PID_05AE] +Plugin = egis_moc +Flags = enforce-requires diff -Nru fwupd-2.0.8/plugins/egis-moc/fu-egis-moc-common.c fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-common.c --- fwupd-2.0.8/plugins/egis-moc/fu-egis-moc-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright 2025 Jason Huang + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-egis-moc-common.h" + +guint32 +fu_egis_moc_checksum_add(guint32 csum, const guint8 *buf, gsize bufsz) +{ + if (bufsz > 0) { + for (gsize i = 0; i < bufsz - 1; i += 2) + csum += fu_memread_uint16(buf + i, G_LITTLE_ENDIAN); + } + if (bufsz % 2) + csum += buf[bufsz - 1]; + return csum; +} + +static guint16 +fu_egis_moc_checksum_fold(guint32 csum) +{ + while (csum > 0xFFFF) + csum = (csum >> 16) + (csum & 0xFFFF); + return (guint16)csum; +} + +guint16 +fu_egis_moc_checksum_finish(guint32 csum) +{ + return ~fu_egis_moc_checksum_fold(csum); +} diff -Nru fwupd-2.0.8/plugins/egis-moc/fu-egis-moc-common.h fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-common.h --- fwupd-2.0.8/plugins/egis-moc/fu-egis-moc-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,14 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +guint32 +fu_egis_moc_checksum_add(guint32 csum, const guint8 *buf, gsize bufsz); +guint16 +fu_egis_moc_checksum_finish(guint32 csum); diff -Nru fwupd-2.0.8/plugins/egis-moc/fu-egis-moc-device.c fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-device.c --- fwupd-2.0.8/plugins/egis-moc/fu-egis-moc-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,536 @@ +/* + * Copyright 2025 Jason Huang + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-egis-moc-common.h" +#include "fu-egis-moc-device.h" +#include "fu-egis-moc-struct.h" + +struct _FuEgisMocDevice { + FuUsbDevice parent_instance; +}; + +G_DEFINE_TYPE(FuEgisMocDevice, fu_egis_moc_device, FU_TYPE_USB_DEVICE) + +#define FU_EGIS_MOC_USB_BULK_EP_IN (1 | 0x80) +#define FU_EGIS_MOC_USB_BULK_EP_OUT (2 | 0x00) +#define FU_EGIS_MOC_USB_INTERFACE 0 + +#define FU_EGIS_MOC_USB_TRANSFER_TIMEOUT 1500 /* ms */ +#define FU_EGIS_MOC_FLASH_TRANSFER_BLOCK_SIZE 4096 /* byte */ + +#define FU_EGIS_MOC_OTA_CHALLENGE_SIZE 32 +#define FU_EGIS_MOC_HMAC_SHA256_SIZE 32 +#define FU_EGIS_MOC_OTA_CHALLENGE_HMAC_KEY "EgistecUsbVcTest" + +#define FU_EGIS_MOC_APDU_VERSION_LEN 0x0C + +static guint16 +fu_egis_moc_device_pkg_header_checksum(GByteArray *buf) +{ + guint32 csum = 0; + guint16 csum_be = 0; + csum = fu_egis_moc_checksum_add(csum, buf->data, 8); + if (buf->len > 10) + csum = fu_egis_moc_checksum_add(csum, buf->data + 10, buf->len - 10); + csum_be = fu_egis_moc_checksum_finish(csum); + csum_be = csum_be >> 8 | csum_be << 8; + return csum_be; +} + +static gboolean +fu_egis_moc_device_ctrl_cmd(FuEgisMocDevice *self, + FuEgisMocCmd cmd, + guint16 value, + guint16 index, + guint8 *data, + gsize length, + gboolean device2host, + GError **error) +{ + gsize actual_len = 0; + + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), + device2host ? FU_USB_DIRECTION_DEVICE_TO_HOST + : FU_USB_DIRECTION_HOST_TO_DEVICE, + FU_USB_REQUEST_TYPE_VENDOR, + FU_USB_RECIPIENT_DEVICE, + cmd, + value, + index, + data, + length, + length ? &actual_len : NULL, + FU_EGIS_MOC_USB_TRANSFER_TIMEOUT, + NULL, + error)) { + fwupd_error_convert(error); + return FALSE; + } + if (actual_len != length) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "only sent 0x%04x of 0x%04x", + (guint)actual_len, + (guint)length); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_egis_moc_device_cmd_send(FuEgisMocDevice *self, GByteArray *req, GError **error) +{ + gsize actual_len = 0; + g_autoptr(FuStructEgisMocPkgHeader) st_hdr = fu_struct_egis_moc_pkg_header_new(); + + /* build header */ + fu_struct_egis_moc_pkg_header_set_sync(st_hdr, 0x45474953); + fu_struct_egis_moc_pkg_header_set_id(st_hdr, 0x00000001); + fu_struct_egis_moc_pkg_header_set_len(st_hdr, req->len); + g_byte_array_append(st_hdr->buf, req->data, req->len); + fu_struct_egis_moc_pkg_header_set_chksum( + st_hdr, + fu_egis_moc_device_pkg_header_checksum(st_hdr->buf)); + + /* send data */ + if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), + FU_EGIS_MOC_USB_BULK_EP_OUT, + st_hdr->buf->data, + st_hdr->buf->len, + &actual_len, + FU_EGIS_MOC_USB_TRANSFER_TIMEOUT, + NULL, + error)) { + g_prefix_error_literal(error, "failed to req: "); + return FALSE; + } + if (actual_len != st_hdr->buf->len) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid length"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_egis_moc_device_cmd_recv_cb(FuDevice *device, gpointer user_data, GError **error) +{ + gsize actual_len = 0; + guint32 csum = 0; + guint16 status = 0; + GByteArray *buf_payload = (GByteArray *)user_data; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(FuStructEgisMocPkgHeader) st_hdr = NULL; + + /* package format = | zlp | ack | zlp | data | */ + fu_byte_array_set_size(buf, FU_EGIS_MOC_FLASH_TRANSFER_BLOCK_SIZE, 0x00); + if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(device), + FU_EGIS_MOC_USB_BULK_EP_IN, + buf->data, + buf->len, + &actual_len, /* allowed to return short read */ + FU_EGIS_MOC_USB_TRANSFER_TIMEOUT, + NULL, + error)) { + g_prefix_error_literal(error, "failed to reply: "); + return FALSE; + } + g_byte_array_set_size(buf, actual_len); + if (buf->len < 2) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid data"); + return FALSE; + } + fu_dump_full(G_LOG_DOMAIN, "reply", buf->data, buf->len, 16, FU_DUMP_FLAGS_SHOW_ADDRESSES); + + /* parse package header */ + st_hdr = fu_struct_egis_moc_pkg_header_parse(buf->data, buf->len, 0x0, error); + if (st_hdr == NULL) + return FALSE; + csum = fu_egis_moc_device_pkg_header_checksum(buf); + if (fu_struct_egis_moc_pkg_header_get_chksum(st_hdr) != csum) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid checksum, got 0x%x, expected 0x%x", + csum, + fu_struct_egis_moc_pkg_header_get_chksum(st_hdr)); + return FALSE; + } + if (!fu_memread_uint16_safe(buf->data, + buf->len, + buf->len - 2, + &status, + G_BIG_ENDIAN, + error)) + return FALSE; + if (status != FU_EGIS_MOC_STATUS_SUCCESS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "status error, 0x%x", + status); + return FALSE; + } + + /* copy out payload */ + if (!fu_memcpy_safe(buf_payload->data, + buf_payload->len, + 0, + buf->data, + buf->len, + FU_STRUCT_EGIS_MOC_PKG_HEADER_SIZE, + buf->len - FU_STRUCT_EGIS_MOC_PKG_HEADER_SIZE - sizeof(status), + error)) + return FALSE; + + /* success */ + return TRUE; +} + +static GByteArray * +fu_egis_moc_device_fw_cmd(FuEgisMocDevice *self, + FuStructEgisMocCmdReq *st_req, + gsize bufsz, + GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + + if (!fu_egis_moc_device_cmd_send(self, st_req->buf, error)) + return NULL; + fu_byte_array_set_size(buf, bufsz, 0x00); + if (!fu_device_retry(FU_DEVICE(self), fu_egis_moc_device_cmd_recv_cb, 10, buf, error)) + return NULL; + + /* success */ + return g_steal_pointer(&buf); +} + +static gboolean +fu_egis_moc_device_ensure_version(FuEgisMocDevice *self, GError **error) +{ + g_autofree gchar *version = NULL; + g_autoptr(FuStructEgisMocCmdReq) st_req = fu_struct_egis_moc_cmd_req_new(); + g_autoptr(GByteArray) buf = NULL; + + fu_struct_egis_moc_cmd_req_set_ins(st_req, FU_EGIS_MOC_CMD_APDU_VERSION); + fu_struct_egis_moc_cmd_req_set_lc3(st_req, FU_EGIS_MOC_APDU_VERSION_LEN); + + buf = fu_egis_moc_device_fw_cmd(self, st_req, FU_STRUCT_EGIS_MOC_VERSION_INFO_SIZE, error); + if (buf == NULL) + return FALSE; + if (buf->len < 3) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid version data"); + return FALSE; + } + version = fu_strsafe((const gchar *)buf->data + 3, buf->len - 3); + fu_device_set_version(FU_DEVICE(self), version); + + /* success */ + return TRUE; +} + +static gboolean +fu_egis_moc_device_update_init(FuEgisMocDevice *self, GError **error) +{ + guint8 challenge[FU_EGIS_MOC_OTA_CHALLENGE_SIZE] = {0}; + guchar hmac_key[FU_EGIS_MOC_HMAC_SHA256_SIZE] = FU_EGIS_MOC_OTA_CHALLENGE_HMAC_KEY; + guint8 digest[FU_EGIS_MOC_HMAC_SHA256_SIZE] = {0}; + gsize digest_len = FU_EGIS_MOC_HMAC_SHA256_SIZE; + g_autoptr(GHmac) hmac = g_hmac_new(G_CHECKSUM_SHA256, (guchar *)hmac_key, sizeof(hmac_key)); + + /* get challenge */ + if (!fu_egis_moc_device_ctrl_cmd(self, + FU_EGIS_MOC_CMD_CHALLENGE_GET, + 0x0, + 0x0, + challenge, + sizeof(challenge), + TRUE, + error)) { + g_prefix_error_literal(error, "failed to get challenge: "); + return FALSE; + } + g_hmac_update(hmac, (guchar *)challenge, sizeof(challenge)); + g_hmac_get_digest(hmac, digest, &digest_len); + + /* switch into OTA Mode */ + if (!fu_egis_moc_device_ctrl_cmd(self, + FU_EGIS_MOC_CMD_ENTER_OTA_MODE, + 0x0, + 0x0, + digest, + sizeof(digest), + FALSE, + error)) { + g_prefix_error_literal(error, "failed to go to OTA mode: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_egis_moc_device_ensure_op_mode(FuEgisMocDevice *self, GError **error) +{ + guint8 op_mode[8] = {0}; + + if (!fu_egis_moc_device_ctrl_cmd(self, + FU_EGIS_MOC_CMD_OP_MODE_GET, + 0x0, + 0x0, + op_mode, + sizeof(op_mode), + TRUE, + error)) { + g_prefix_error_literal(error, "failed to get mode: "); + return FALSE; + } + if (op_mode[0] == FU_EGIS_MOC_OP_MODE_BOOTLOADER) { + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } else { + fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_egis_moc_device_setup(FuDevice *device, GError **error) +{ + FuEgisMocDevice *self = FU_EGIS_MOC_DEVICE(device); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_egis_moc_device_parent_class)->setup(device, error)) + return FALSE; + + if (!fu_egis_moc_device_ensure_op_mode(self, error)) { + g_prefix_error_literal(error, "failed to get device mode: "); + return FALSE; + } + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + fu_device_set_version(FU_DEVICE(self), "0.0.0.1"); + } else { + if (!fu_egis_moc_device_ensure_version(self, error)) { + g_prefix_error_literal(error, "failed to get firmware version: "); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_egis_moc_device_write_packets(FuEgisMocDevice *self, + FuChunkArray *chunks, + FuProgress *progress, + GError **error) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); + for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { + g_autoptr(FuChunk) chk = NULL; + g_autoptr(GByteArray) req = g_byte_array_new(); + guint32 offset; + + /* prepare chunk */ + chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) + return FALSE; + if (i == fu_chunk_array_length(chunks) - 1) { + g_byte_array_append(req, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk) - + FU_EGIS_MOC_HMAC_SHA256_SIZE); + } else { + g_byte_array_append(req, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + } + offset = fu_chunk_get_address(chk); + if (!fu_egis_moc_device_ctrl_cmd(self, + FU_EGIS_MOC_CMD_OTA_WRITE, + (guint16)(offset & 0xFFFF), + (guint16)((offset >> 16) & 0xFFFF), + req->data, + req->len, + FALSE, + error)) { + g_prefix_error_literal(error, "failed to write: "); + return FALSE; + } + + /* update progress */ + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_egis_moc_device_write_checksum(FuEgisMocDevice *self, FuChunkArray *chunks, GError **error) +{ + guint8 hmac[FU_EGIS_MOC_HMAC_SHA256_SIZE] = {0}; + g_autoptr(FuChunk) chk = NULL; + + chk = fu_chunk_array_index(chunks, fu_chunk_array_length(chunks) - 1, error); + if (chk == NULL) + return FALSE; + if (!fu_memcpy_safe(hmac, + sizeof(hmac), + 0, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + fu_chunk_get_data_sz(chk) - sizeof(hmac), + sizeof(hmac), + error)) + return FALSE; + if (!fu_egis_moc_device_ctrl_cmd(self, + FU_EGIS_MOC_CMD_OTA_FINAL, + 0x0, + 0x0, + hmac, + sizeof(hmac), + FALSE, + error)) { + g_prefix_error_literal(error, "failed to send OTA final: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_egis_moc_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuEgisMocDevice *self = FU_EGIS_MOC_DEVICE(device); + g_autoptr(FuChunkArray) chunks = NULL; + g_autoptr(GBytes) fw = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "fini"); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* write each block */ + chunks = fu_chunk_array_new_from_bytes(fw, + FU_CHUNK_ADDR_OFFSET_NONE, + FU_CHUNK_PAGESZ_NONE, + FU_EGIS_MOC_FLASH_TRANSFER_BLOCK_SIZE); + if (!fu_egis_moc_device_write_packets(self, chunks, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* write checksum */ + if (!fu_egis_moc_device_write_checksum(self, chunks, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static gboolean +fu_egis_moc_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuEgisMocDevice *self = FU_EGIS_MOC_DEVICE(device); + + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + + /* success */ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_egis_moc_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuEgisMocDevice *self = FU_EGIS_MOC_DEVICE(device); + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in bootloader mode, skipping"); + return TRUE; + } + if (!fu_egis_moc_device_update_init(self, error)) { + g_prefix_error_literal(error, "failed to detach: "); + return FALSE; + } + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static void +fu_egis_moc_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 7, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 42, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 51, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static gchar * +fu_egis_moc_device_convert_version(FuDevice *device, guint64 version_raw) +{ + return fu_version_from_uint32(version_raw, fu_device_get_version_format(device)); +} + +static void +fu_egis_moc_device_init(FuEgisMocDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_RUNTIME_VERSION); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_remove_delay(FU_DEVICE(self), 10000); + fu_device_add_protocol(FU_DEVICE(self), "com.egistec.usb"); + fu_device_set_summary(FU_DEVICE(self), "Fingerprint Device"); + fu_device_set_install_duration(FU_DEVICE(self), 15); + fu_device_set_firmware_size_min(FU_DEVICE(self), 0x20000); + fu_device_set_firmware_size_max(FU_DEVICE(self), 0x50000); + fu_usb_device_add_interface(FU_USB_DEVICE(self), FU_EGIS_MOC_USB_INTERFACE); +} + +static void +fu_egis_moc_device_class_init(FuEgisMocDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + + device_class->write_firmware = fu_egis_moc_device_write_firmware; + device_class->setup = fu_egis_moc_device_setup; + device_class->reload = fu_egis_moc_device_setup; + device_class->attach = fu_egis_moc_device_attach; + device_class->detach = fu_egis_moc_device_detach; + device_class->set_progress = fu_egis_moc_device_set_progress; + device_class->convert_version = fu_egis_moc_device_convert_version; +} diff -Nru fwupd-2.0.8/plugins/egis-moc/fu-egis-moc-device.h fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-device.h --- fwupd-2.0.8/plugins/egis-moc/fu-egis-moc-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * Copyright 2025 Jason Huang + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_EGIS_MOC_DEVICE (fu_egis_moc_device_get_type()) +G_DECLARE_FINAL_TYPE(FuEgisMocDevice, fu_egis_moc_device, FU, EGIS_MOC_DEVICE, FuUsbDevice) diff -Nru fwupd-2.0.8/plugins/egis-moc/fu-egis-moc-plugin.c fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-plugin.c --- fwupd-2.0.8/plugins/egis-moc/fu-egis-moc-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright 2025 Jason Huang + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-egis-moc-device.h" +#include "fu-egis-moc-plugin.h" + +struct _FuEgisMocPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuEgisMocPlugin, fu_egis_moc_plugin, FU_TYPE_PLUGIN) + +static void +fu_egis_moc_plugin_init(FuEgisMocPlugin *self) +{ +} + +static void +fu_egis_moc_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_EGIS_MOC_DEVICE); +} + +static void +fu_egis_moc_plugin_class_init(FuEgisMocPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->constructed = fu_egis_moc_plugin_constructed; +} diff -Nru fwupd-2.0.8/plugins/egis-moc/fu-egis-moc-plugin.h fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-plugin.h --- fwupd-2.0.8/plugins/egis-moc/fu-egis-moc-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/egis-moc/fu-egis-moc-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Jason Huang + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuEgisMocPlugin, fu_egis_moc_plugin, FU, EGIS_MOC_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/egis-moc/fu-egis-moc.rs fwupd-2.0.20/plugins/egis-moc/fu-egis-moc.rs --- fwupd-2.0.8/plugins/egis-moc/fu-egis-moc.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/egis-moc/fu-egis-moc.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,54 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +enum FuEgisMocStatus { + Success = 0x9000, +} + +enum FuEgisMocOpMode { + Bootloader = 0x0B, +} + +#[repr(u8)] +enum FuEgisMocCla { + ApduGeneral = 0x50, +} + +#[repr(u8)] +enum FuEgisMocCmd { + OpModeGet = 0x52, + ChallengeGet = 0x54, + EnterOtaMode = 0x58, + OtaWrite = 0x5A, + OtaFinal = 0x5C, + ApduVersion = 0x7F, +} + +#[derive(New, Default)] +struct FuStructEgisMocCmdReq { + cla: FuEgisMocCla == ApduGeneral, + ins: FuEgisMocCmd, + _p1: u8, + _p2: u8, + _lc1: u8, + _lc2: u8, + lc3: u8, +} + +struct FuStructEgisMocVersionInfo { + platform: [u8; 4], + _dot1: u8, + major_version: u8, + _dot2: u8, + minor_version: u8, + _dot3: u8, + revision: [u8; 2], +} + +#[derive(New, Parse)] +struct FuStructEgisMocPkgHeader { + sync: u32be, + id: u32be, + chksum: u16be, + len: u32be, +} diff -Nru fwupd-2.0.8/plugins/egis-moc/fu-self-test.c fwupd-2.0.20/plugins/egis-moc/fu-self-test.c --- fwupd-2.0.8/plugins/egis-moc/fu-self-test.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/egis-moc/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-egis-moc-common.h" + +static void +fu_egis_moc_checksum_func(void) +{ + guint32 temp_chksum; + const guint8 buf[] = {0x40, 0xC3, 0xE6, 0xC8, 0xDF, 0x5B}; + + /* even */ + temp_chksum = fu_egis_moc_checksum_add(0, buf, sizeof(buf)); + g_assert_cmpint(temp_chksum, ==, 124933); + temp_chksum = fu_egis_moc_checksum_finish(temp_chksum); + g_assert_cmpint(temp_chksum, ==, 6137); + + /* odd */ + temp_chksum = fu_egis_moc_checksum_add(0, buf, sizeof(buf) - 1); + g_assert_cmpint(temp_chksum, ==, 101637); + temp_chksum = fu_egis_moc_checksum_finish(temp_chksum); + g_assert_cmpint(temp_chksum, ==, 29433); + + /* chained */ + temp_chksum = fu_egis_moc_checksum_add(0, buf, sizeof(buf)); + g_assert_cmpint(temp_chksum, ==, 124933); + temp_chksum = fu_egis_moc_checksum_add(temp_chksum, buf, sizeof(buf) - 1); + g_assert_cmpint(temp_chksum, ==, 226570); + temp_chksum = fu_egis_moc_checksum_finish(temp_chksum); + g_assert_cmpint(temp_chksum, ==, 35570); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + /* tests go here */ + g_test_add_func("/egic-moc/checksum", fu_egis_moc_checksum_func); + return g_test_run(); +} diff -Nru fwupd-2.0.8/plugins/egis-moc/meson.build fwupd-2.0.20/plugins/egis-moc/meson.build --- fwupd-2.0.8/plugins/egis-moc/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/egis-moc/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,37 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginEgisMoc"'] +plugins += {meson.current_source_dir().split('/')[-1]: true} + +plugin_quirks += join_paths(meson.current_source_dir(), 'egis-moc.quirk') +plugin_builtin_egis_moc = static_library('fu_plugin_egis_moc', + rustgen.process('fu-egis-moc.rs'), + sources: [ + 'fu-egis-moc-common.c', + 'fu-egis-moc-device.c', + 'fu-egis-moc-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_egis_moc + +device_tests += files( + 'tests/egis-moc.json', +) + +if get_option('tests') + e = executable( + 'egis-moc-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_egis_moc, + ], + ) + test('egis-moc-self-test', e) +endif diff -Nru fwupd-2.0.8/plugins/egis-moc/tests/egis-moc.json fwupd-2.0.20/plugins/egis-moc/tests/egis-moc.json --- fwupd-2.0.8/plugins/egis-moc/tests/egis-moc.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/egis-moc/tests/egis-moc.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +{ + "name": "Egis ETU905Axx", + "interactive": false, + "steps": [ + { + "url": "d956c84cf48a1a470d29ba121e068567ae24ac90fab57e3842782fd9855367c9-firmware_5326_test.cab", + "emulation-url": "e0cfac0bc57119fc3b5e9ecc5116b31c71cf80c742c1d4169f8db3102eff2064-egismoc_emu.zip", + "components": [ + { + "version": "0.5.3.26", + "guids": [ + "e23ec6df-3672-54ae-8c0d-926e02210d29" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/elan-kbd/README.md fwupd-2.0.20/plugins/elan-kbd/README.md --- fwupd-2.0.8/plugins/elan-kbd/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elan-kbd/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -40,10 +40,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.5`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Richard Hughes: @hughsie diff -Nru fwupd-2.0.8/plugins/elan-kbd/fu-elan-kbd-debug-device.c fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-debug-device.c --- fwupd-2.0.8/plugins/elan-kbd/fu-elan-kbd-debug-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-debug-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -45,7 +45,7 @@ } static void -fu_elan_kbd_debug_device_set_progress(FuDevice *self, FuProgress *progress) +fu_elan_kbd_debug_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -59,12 +59,13 @@ static void fu_elan_kbd_debug_device_init(FuElanKbdDebugDevice *self) { - fu_device_set_name(FU_DEVICE(self), "ELAN USB Keyboard (debug)"); + fu_device_set_name(FU_DEVICE(self), "Debug"); fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); fu_device_add_protocol(FU_DEVICE(self), "com.elan.kbd"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - fu_device_add_icon(FU_DEVICE(self), "input-keyboard"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_INPUT_KEYBOARD); } static void diff -Nru fwupd-2.0.8/plugins/elan-kbd/fu-elan-kbd-device.c fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-device.c --- fwupd-2.0.8/plugins/elan-kbd/fu-elan-kbd-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -180,7 +180,7 @@ fu_struct_elan_kbd_software_reset_req_new(); g_autoptr(GByteArray) buf = NULL; - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; if (!fu_elan_kbd_device_status_check(self, buf, error)) @@ -197,7 +197,7 @@ g_autoptr(FuStructElanKbdGetVerSpecRes) st_res = NULL; g_autoptr(GByteArray) buf = NULL; - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; st_res = fu_struct_elan_kbd_get_ver_spec_res_parse(buf->data, buf->len, 0x0, error); @@ -215,7 +215,7 @@ g_autofree gchar *version = NULL; g_autoptr(GByteArray) buf = NULL; - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; st_res = fu_struct_elan_kbd_get_ver_fw_res_parse(buf->data, buf->len, 0x0, error); @@ -233,7 +233,7 @@ g_autoptr(FuStructElanKbdGetStatusRes) st_res = NULL; g_autoptr(GByteArray) buf = NULL; - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; st_res = fu_struct_elan_kbd_get_status_res_parse(buf->data, buf->len, 0x0, error); @@ -251,7 +251,7 @@ g_autoptr(FuStructElanKbdBootConditionRes) st_res = NULL; g_autoptr(GByteArray) buf = NULL; - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; st_res = fu_struct_elan_kbd_boot_condition_res_parse(buf->data, buf->len, 0x0, error); @@ -267,7 +267,7 @@ g_autoptr(FuStructElanKbdAbortReq) st_req = fu_struct_elan_kbd_abort_req_new(); g_autoptr(GByteArray) buf = NULL; - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; return fu_elan_kbd_device_status_check(self, buf, error); @@ -308,7 +308,7 @@ g_autoptr(GByteArray) buf = NULL; fu_struct_elan_kbd_read_rom_finished_req_set_csum(st_req, csum); - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; return fu_elan_kbd_device_status_check(self, buf, error); @@ -332,7 +332,7 @@ /* set up read */ fu_struct_elan_kbd_read_rom_req_set_addr(st_req, addr); fu_struct_elan_kbd_read_rom_req_set_len(st_req, len); - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return NULL; if (!fu_elan_kbd_device_status_check(self, buf, error)) @@ -357,7 +357,7 @@ g_autoptr(GByteArray) buf = NULL; fu_struct_elan_kbd_read_option_finished_req_set_csum(st_req, csum); - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; return fu_elan_kbd_device_status_check(self, buf, error); @@ -376,7 +376,7 @@ fu_progress_set_steps(progress, len / FU_ELAN_KBD_DEVICE_EP_DATA_SIZE); /* set up read */ - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return NULL; if (!fu_elan_kbd_device_status_check(self, buf, error)) @@ -429,12 +429,13 @@ fu_progress_get_child(progress), error); if (blob_bootloader == NULL) { - g_prefix_error(error, "failed to read ROM: "); + g_prefix_error_literal(error, "failed to read ROM: "); return NULL; } img_bootloader = fu_firmware_new_from_bytes(blob_bootloader); fu_firmware_set_id(img_bootloader, "bootloader"); - fu_firmware_add_image(firmware, img_bootloader); + if (!fu_firmware_add_image(firmware, img_bootloader, error)) + return NULL; fu_progress_step_done(progress); /* app */ @@ -444,24 +445,26 @@ fu_progress_get_child(progress), error); if (blob_app == NULL) { - g_prefix_error(error, "failed to read ROM: "); + g_prefix_error_literal(error, "failed to read ROM: "); return NULL; } img_app = fu_firmware_new_from_bytes(blob_app); fu_firmware_set_idx(img_app, FU_ELAN_KBD_FIRMWARE_IDX_APP); - fu_firmware_add_image(firmware, img_app); + if (!fu_firmware_add_image(firmware, img_app, error)) + return NULL; fu_progress_step_done(progress); /* option */ blob_option = fu_elan_kbd_device_cmd_read_option(self, fu_progress_get_child(progress), error); if (blob_option == NULL) { - g_prefix_error(error, "failed to read ROM: "); + g_prefix_error_literal(error, "failed to read ROM: "); return NULL; } img_option = fu_firmware_new_from_bytes(blob_option); fu_firmware_set_idx(img_option, FU_ELAN_KBD_FIRMWARE_IDX_OPTION); - fu_firmware_add_image(firmware, img_option); + if (!fu_firmware_add_image(firmware, img_option, error)) + return NULL; fu_progress_step_done(progress); /* success */ @@ -476,7 +479,7 @@ g_autoptr(FuStructElanKbdGetAuthLockRes) st_res = NULL; g_autoptr(GByteArray) buf = NULL; - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; st_res = fu_struct_elan_kbd_get_auth_lock_res_parse(buf->data, buf->len, 0x0, error); @@ -494,7 +497,7 @@ g_autoptr(GByteArray) buf = NULL; fu_struct_elan_kbd_set_auth_lock_req_set_key(st_req, key ^ 0x58); - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; return fu_elan_kbd_device_status_check(self, buf, error); @@ -515,7 +518,7 @@ g_autoptr(FuStructElanKbdEntryIapReq) st_req = fu_struct_elan_kbd_entry_iap_req_new(); g_autoptr(GByteArray) buf = NULL; - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; return fu_elan_kbd_device_status_check(self, buf, error); @@ -527,7 +530,7 @@ g_autoptr(FuStructElanKbdFinishedIapReq) st_req = fu_struct_elan_kbd_finished_iap_req_new(); g_autoptr(GByteArray) buf = NULL; - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; return fu_elan_kbd_device_status_check(self, buf, error); @@ -541,7 +544,7 @@ g_autoptr(GByteArray) buf = NULL; fu_struct_elan_kbd_write_rom_finished_req_set_csum(st_req, csum); - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; return fu_elan_kbd_device_status_check(self, buf, error); @@ -566,7 +569,7 @@ /* set up write */ fu_struct_elan_kbd_write_rom_req_set_addr(st_req, addr); fu_struct_elan_kbd_write_rom_req_set_len(st_req, bufsz); - buf = fu_elan_kbd_device_cmd(self, st_req, error); + buf = fu_elan_kbd_device_cmd(self, st_req->buf, error); if (buf == NULL) return FALSE; if (!fu_elan_kbd_device_status_check(self, buf, error)) @@ -602,14 +605,14 @@ /* unlock */ if (!fu_elan_kbd_device_cmd_unlock(self, error)) { - g_prefix_error(error, "failed to unlock: "); + g_prefix_error_literal(error, "failed to unlock: "); return FALSE; } fu_progress_step_done(progress); /* enter IAP */ if (!fu_elan_kbd_device_cmd_entry_iap(self, error)) { - g_prefix_error(error, "failed to entry IAP: "); + g_prefix_error_literal(error, "failed to entry IAP: "); return FALSE; } fu_progress_step_done(progress); @@ -623,14 +626,14 @@ blob, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write ROM: "); + g_prefix_error_literal(error, "failed to write ROM: "); return FALSE; } fu_progress_step_done(progress); /* finish IAP */ if (!fu_elan_kbd_device_cmd_finished_iap(self, error)) { - g_prefix_error(error, "failed to finish IAP: "); + g_prefix_error_literal(error, "failed to finish IAP: "); return FALSE; } fu_progress_step_done(progress); @@ -642,7 +645,7 @@ fu_progress_get_child(progress), error); if (blob_verify == NULL) { - g_prefix_error(error, "failed to read ROM: "); + g_prefix_error_literal(error, "failed to read ROM: "); return FALSE; } if (!fu_bytes_compare(blob, blob_verify, error)) @@ -654,7 +657,7 @@ } static void -fu_elan_kbd_device_set_progress(FuDevice *self, FuProgress *progress) +fu_elan_kbd_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -681,7 +684,7 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); - fu_device_add_icon(FU_DEVICE(self), "input-keyboard"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_INPUT_KEYBOARD); fu_usb_device_add_interface(FU_USB_DEVICE(self), 0x01); fu_usb_device_add_interface(FU_USB_DEVICE(self), 0x02); fu_usb_device_add_interface(FU_USB_DEVICE(self), 0x03); diff -Nru fwupd-2.0.8/plugins/elan-kbd/fu-elan-kbd-firmware.c fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-firmware.c --- fwupd-2.0.8/plugins/elan-kbd/fu-elan-kbd-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -28,7 +28,7 @@ static gboolean fu_elan_kbd_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) firmware_app = fu_firmware_new(); @@ -48,7 +48,8 @@ if (!fu_firmware_set_stream(firmware_bootloader, stream_bootloader, error)) return FALSE; fu_firmware_set_idx(firmware_bootloader, FU_ELAN_KBD_FIRMWARE_IDX_BOOTLOADER); - fu_firmware_add_image(firmware, firmware_bootloader); + if (!fu_firmware_add_image(firmware, firmware_bootloader, error)) + return FALSE; /* app */ stream_app = fu_partial_input_stream_new(stream, @@ -60,7 +61,8 @@ if (!fu_firmware_set_stream(firmware_app, stream_app, error)) return FALSE; fu_firmware_set_idx(firmware_app, FU_ELAN_KBD_FIRMWARE_IDX_APP); - fu_firmware_add_image(firmware, firmware_app); + if (!fu_firmware_add_image(firmware, firmware_app, error)) + return FALSE; /* option */ stream_option = fu_partial_input_stream_new(stream, @@ -72,7 +74,8 @@ if (!fu_firmware_set_stream(firmware_option, stream_option, error)) return FALSE; fu_firmware_set_idx(firmware_option, FU_ELAN_KBD_FIRMWARE_IDX_OPTION); - fu_firmware_add_image(firmware, firmware_option); + if (!fu_firmware_add_image(firmware, firmware_option, error)) + return FALSE; /* success */ return TRUE; diff -Nru fwupd-2.0.8/plugins/elan-kbd/fu-elan-kbd-plugin.c fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-plugin.c --- fwupd-2.0.8/plugins/elan-kbd/fu-elan-kbd-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,12 +21,14 @@ static void fu_elan_kbd_plugin_init(FuElanKbdPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void fu_elan_kbd_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_ELAN_KBD_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_ELAN_KBD_DEBUG_DEVICE); fu_plugin_set_device_gtype_default(plugin, FU_TYPE_ELAN_KBD_RUNTIME); diff -Nru fwupd-2.0.8/plugins/elan-kbd/fu-elan-kbd-runtime.c fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-runtime.c --- fwupd-2.0.8/plugins/elan-kbd/fu-elan-kbd-runtime.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elan-kbd/fu-elan-kbd-runtime.c 2026-02-26 11:36:18.000000000 +0000 @@ -47,7 +47,7 @@ } static void -fu_elan_kbd_runtime_set_progress(FuDevice *self, FuProgress *progress) +fu_elan_kbd_runtime_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -66,7 +66,8 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); - fu_device_add_icon(FU_DEVICE(self), "input-keyboard"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_INPUT_KEYBOARD); fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_ELAN_KBD_FIRMWARE); fu_device_add_instance_id_full(FU_DEVICE(self), "USB\\VID_04F3&PID_0905", diff -Nru fwupd-2.0.8/plugins/elan-kbd/tests/elan-kbd.json fwupd-2.0.20/plugins/elan-kbd/tests/elan-kbd.json --- fwupd-2.0.8/plugins/elan-kbd/tests/elan-kbd.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elan-kbd/tests/elan-kbd.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/43cab4d273c9fff54abe4ddfb1ec3ac7d8a902f0119d575c0dfc591e305d188f-08D9.cab", - "emulation-url": "https://fwupd.org/downloads/1c7e5391bc51a6d0817ca7e1f22dfa966ef50b6b9a2914a335012a17c77c846b-08D9.zip", + "url": "43cab4d273c9fff54abe4ddfb1ec3ac7d8a902f0119d575c0dfc591e305d188f-08D9.cab", + "emulation-url": "1c7e5391bc51a6d0817ca7e1f22dfa966ef50b6b9a2914a335012a17c77c846b-08D9.zip", "components": [ { "version": "1.3", diff -Nru fwupd-2.0.8/plugins/elanfp/README.md fwupd-2.0.20/plugins/elanfp/README.md --- fwupd-2.0.8/plugins/elanfp/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elanfp/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -36,10 +36,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.7.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Michael Cheng: @MichaelCheng04 diff -Nru fwupd-2.0.8/plugins/elanfp/elanfp.quirk fwupd-2.0.20/plugins/elanfp/elanfp.quirk --- fwupd-2.0.8/plugins/elanfp/elanfp.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elanfp/elanfp.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -13,3 +13,6 @@ [USB\VID_04F3&PID_0CA3] Plugin = elanfp Flags = usb-bulk-transfer,enforce-requires +[USB\VID_04F3&PID_0CA8] +Plugin = elanfp +Flags = usb-bulk-transfer,enforce-requires diff -Nru fwupd-2.0.8/plugins/elanfp/fu-elanfp-device.c fwupd-2.0.20/plugins/elanfp/fu-elanfp-device.c --- fwupd-2.0.8/plugins/elanfp/fu-elanfp-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elanfp/fu-elanfp-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -83,7 +83,7 @@ BULK_SEND_TIMEOUT_MS, NULL, error)) { - g_prefix_error(error, "failed to send command (bulk): "); + g_prefix_error_literal(error, "failed to send command (bulk): "); return FALSE; } } else { @@ -100,7 +100,7 @@ CTRL_SEND_TIMEOUT_MS, NULL, error)) { - g_prefix_error(error, "failed to send command (ctrl transfer): "); + g_prefix_error_literal(error, "failed to send command (ctrl transfer): "); return FALSE; } } @@ -124,9 +124,8 @@ guint8 endpoint = ELAN_EP_CMD_IN; gsize actual = 0; - if (fu_device_has_private_flag(FU_DEVICE(self), FU_ELAN_FP_DEVICE_FLAG_USB_BULK_TRANSFER)) { + if (fu_device_has_private_flag(FU_DEVICE(self), FU_ELAN_FP_DEVICE_FLAG_USB_BULK_TRANSFER)) endpoint = ELAN_EP_IMG_IN; - } if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), endpoint, @@ -136,7 +135,7 @@ BULK_RECV_TIMEOUT_MS, NULL, error)) { - g_prefix_error(error, "failed to receive status: "); + g_prefix_error_literal(error, "failed to receive status: "); return FALSE; } if (actual != bufsz) { @@ -235,7 +234,7 @@ TRUE, NULL, error)) { - g_prefix_error(error, "failed to device setup: "); + g_prefix_error_literal(error, "failed to device setup: "); return FALSE; } fw_ver = fu_memread_uint16(usb_buf, G_BIG_ENDIAN); @@ -301,7 +300,7 @@ 0x0, /* src */ fu_chunk_get_data_sz(chk), error)) { - g_prefix_error(error, "memory copy for payload fail: "); + g_prefix_error_literal(error, "memory copy for payload fail: "); return FALSE; } if (!fu_elanfp_device_iap_send_command(self, @@ -311,11 +310,11 @@ sizeof(databuf), sizeof(recvbuf), error)) { - g_prefix_error(error, "send payload command fail: "); + g_prefix_error_literal(error, "send payload command fail: "); return FALSE; } if (!fu_elanfp_device_iap_recv_status(self, recvbuf, sizeof(recvbuf), error)) { - g_prefix_error(error, "received payload status fail: "); + g_prefix_error_literal(error, "received payload status fail: "); return FALSE; } if (recvbuf[5] != FU_CFU_CONTENT_STATUS_SUCCESS) { @@ -374,11 +373,11 @@ g_bytes_get_size(offer), g_bytes_get_size(offer) + 1, error)) { - g_prefix_error(error, "send offer command fail: "); + g_prefix_error_literal(error, "send offer command fail: "); return FALSE; } if (!fu_elanfp_device_iap_recv_status(self, recvbuf, sizeof(recvbuf), error)) { - g_prefix_error(error, "received offer status fail: "); + g_prefix_error_literal(error, "received offer status fail: "); return FALSE; } g_debug("offer-%s status:%s reject:%s", @@ -413,7 +412,7 @@ TRUE, NULL, error)) { - g_prefix_error(error, "failed to hardware reset "); + g_prefix_error_literal(error, "failed to hardware reset: "); return FALSE; } fu_progress_step_done(progress); @@ -437,7 +436,6 @@ fu_device_add_protocol(FU_DEVICE(self), "tw.com.emc.elanfp"); fu_device_set_name(FU_DEVICE(self), "Fingerprint Sensor"); fu_device_set_summary(FU_DEVICE(self), "Match-On-Chip Fingerprint Sensor"); - fu_device_set_vendor(FU_DEVICE(self), "Elan"); fu_device_set_install_duration(FU_DEVICE(self), 10); fu_device_set_firmware_size_min(FU_DEVICE(self), 0x20000); fu_device_set_firmware_size_max(FU_DEVICE(self), 0x90000); @@ -447,7 +445,7 @@ } static void -fu_elanfp_device_set_progress(FuDevice *self, FuProgress *progress) +fu_elanfp_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/elanfp/fu-elanfp-firmware.c fwupd-2.0.20/plugins/elanfp/fu-elanfp-firmware.c --- fwupd-2.0.8/plugins/elanfp/fu-elanfp-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elanfp/fu-elanfp-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,7 @@ #include "fu-elanfp-struct.h" struct _FuElanfpFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; guint32 format_version; }; @@ -50,7 +50,7 @@ static gboolean fu_elanfp_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuElanfpFirmware *self = FU_ELANFP_FIRMWARE(firmware); @@ -137,10 +137,10 @@ if (!fu_firmware_parse_stream(img, stream_tmp, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return FALSE; - if (!fu_firmware_add_image_full(firmware, img, error)) + if (!fu_firmware_add_image(firmware, img, error)) return FALSE; offset += 0x10; diff -Nru fwupd-2.0.8/plugins/elanfp/fu-elanfp-plugin.c fwupd-2.0.20/plugins/elanfp/fu-elanfp-plugin.c --- fwupd-2.0.8/plugins/elanfp/fu-elanfp-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elanfp/fu-elanfp-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,6 +25,7 @@ fu_elanfp_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_ELANFP_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_ELANFP_FIRMWARE); } diff -Nru fwupd-2.0.8/plugins/elanfp/tests/elan-p1515e.json fwupd-2.0.20/plugins/elanfp/tests/elan-p1515e.json --- fwupd-2.0.8/plugins/elanfp/tests/elan-p1515e.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elanfp/tests/elan-p1515e.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/ac64be4971c96c777bd503eede3b2b1999df32f425740ac22ea29c5b92a0c31d-F867_V0414_Release.cab", - "emulation-url": "https://fwupd.org/downloads/1dd8da35a6ce2305c58f9a87a84c5ab586a34e0eebce08c0850593490ad2a139-F867_V0414_Release.zip", + "url": "ac64be4971c96c777bd503eede3b2b1999df32f425740ac22ea29c5b92a0c31d-F867_V0414_Release.cab", + "emulation-url": "1dd8da35a6ce2305c58f9a87a84c5ab586a34e0eebce08c0850593490ad2a139-F867_V0414_Release.zip", "components": [ { "version": "0414", diff -Nru fwupd-2.0.8/plugins/elantp/README.md fwupd-2.0.20/plugins/elantp/README.md --- fwupd-2.0.8/plugins/elantp/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elantp/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -81,11 +81,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.5.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Jingle Wu: @jinglewu -* Josh Chen: @josh-chen-elan diff -Nru fwupd-2.0.8/plugins/elantp/fu-elantp-common.h fwupd-2.0.20/plugins/elantp/fu-elantp-common.h --- fwupd-2.0.8/plugins/elantp/fu-elantp-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elantp/fu-elantp-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,40 +8,6 @@ #include -#define ETP_CMD_GET_HID_DESCRIPTOR 0x0001 -#define ETP_CMD_GET_HARDWARE_ID 0x0100 -#define ETP_CMD_GET_MODULE_ID 0x0101 -#define ETP_CMD_I2C_FW_CHECKSUM 0x030F -#define ETP_CMD_I2C_FW_VERSION 0x0102 -#define ETP_CMD_I2C_IAP 0x0311 -#define ETP_CMD_I2C_IAP_CHECKSUM 0x0315 -#define ETP_CMD_I2C_IAP_CTRL 0x0310 -#define ETP_CMD_I2C_IAP_ICBODY 0x0110 -#define ETP_CMD_I2C_IAP_RESET 0x0314 -#define ETP_CMD_I2C_IAP_VERSION 0x0111 -#define ETP_CMD_I2C_IAP_VERSION_2 0x0110 -#define ETP_CMD_I2C_OSM_VERSION 0x0103 -#define ETP_CMD_I2C_GET_HID_ID 0x0100 -#define ETP_CMD_I2C_IAP_TYPE 0x0304 -#define ETP_CMD_I2C_FW_PW 0x030E -#define ETP_CMD_FORCE_ADDR 0x03AD - -#define ETP_CMD_I2C_FORCE_TYPE_ENABLE 0x0104 -#define ETP_CMD_I2C_SET_EEPROM_CTRL 0x0321 -#define ETP_CMD_I2C_GET_EEPROM_FW_VERSION 0x0710 -#define ETP_CMD_I2C_GET_EEPROM_IAP_VERSION 0x0711 -#define ETP_CMD_I2C_SET_EEPROM_ENTER_IAP 0x0607 -#define ETP_CMD_I2C_SET_EEPROM_LEAVE_IAP 0x0606 -#define ETP_CMD_I2C_SET_EEPROM_DATATYPE 0x0702 -#define ETP_CMD_I2C_CALC_EEPROM_CHECKSUM 0x060F -#define ETP_CMD_I2C_READ_EEPROM_CHECKSUM 0x070A -#define ETP_CMD_I2C_HAPTIC_RESTART 0x0601 -#define ETP_CMD_I2C_EEPROM_SETTING 0x0322 -#define ETP_CMD_I2C_EEPROM_LONG_TRANS_ENABLE 0x4607 -#define ETP_CMD_I2C_EEPROM_SETTING_INITIAL 0x0000 -#define ETP_CMD_I2C_EEPROM_WRITE_INFORMATION 0x4600 -#define ETP_CMD_I2C_EEPROM_WRITE_CHECKSUM 0x048B - #define ETP_I2C_IC13_IAPV5_PW 0x37CA #define ETP_FW_FORCE_TYPE_ENABLE_BIT 0x1 diff -Nru fwupd-2.0.8/plugins/elantp/fu-elantp-firmware.c fwupd-2.0.20/plugins/elantp/fu-elantp-firmware.c --- fwupd-2.0.8/plugins/elantp/fu-elantp-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elantp/fu-elantp-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,7 @@ #include "fu-elantp-struct.h" struct _FuElantpFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; guint16 module_id; guint16 ic_type; guint16 iap_addr; @@ -111,7 +111,7 @@ static gboolean fu_elantp_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuElantpFirmware *self = FU_ELANTP_FIRMWARE(firmware); diff -Nru fwupd-2.0.8/plugins/elantp/fu-elantp-haptic-firmware.c fwupd-2.0.20/plugins/elantp/fu-elantp-haptic-firmware.c --- fwupd-2.0.8/plugins/elantp/fu-elantp-haptic-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elantp/fu-elantp-haptic-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,7 @@ #include "fu-elantp-struct.h" struct _FuElantpHapticFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; guint16 driver_ic; }; @@ -45,7 +45,7 @@ static gboolean fu_elantp_haptic_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuElantpHapticFirmware *self = FU_ELANTP_HAPTIC_FIRMWARE(firmware); diff -Nru fwupd-2.0.8/plugins/elantp/fu-elantp-hid-device.c fwupd-2.0.20/plugins/elantp/fu-elantp-hid-device.c --- fwupd-2.0.8/plugins/elantp/fu-elantp-hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elantp/fu-elantp-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,6 +10,7 @@ #include "fu-elantp-firmware.h" #include "fu-elantp-hid-device.h" #include "fu-elantp-hid-haptic-device.h" +#include "fu-elantp-struct.h" struct _FuElantpHidDevice { FuHidrawDevice parent_instance; @@ -29,7 +30,7 @@ G_DEFINE_TYPE(FuElantpHidDevice, fu_elantp_hid_device, FU_TYPE_HIDRAW_DEVICE) static gboolean -fu_elantp_hid_device_detach(FuDevice *device, FuProgress *progress, GError **error); +fu_elantp_hid_device_detach(FuElantpHidDevice *self, FuProgress *progress, GError **error); static void fu_elantp_hid_device_to_string(FuDevice *device, guint idt, GString *str) @@ -137,8 +138,12 @@ fu_elantp_hid_device_ensure_iap_ctrl(FuElantpHidDevice *self, GError **error) { guint8 buf[2] = {0x0}; - if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_IAP_CTRL, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IAPControl: "); + if (!fu_elantp_hid_device_read_cmd(self, + FU_ETP_CMD_I2C_IAP_CTRL, + buf, + sizeof(buf), + error)) { + g_prefix_error_literal(error, "failed to read IAPControl: "); return FALSE; } self->iap_ctrl = fu_memread_uint16(buf, G_LITTLE_ENDIAN); @@ -167,26 +172,26 @@ guint16 value; if (!fu_elantp_hid_device_read_cmd(self, - ETP_CMD_I2C_FORCE_TYPE_ENABLE, + FU_ETP_CMD_I2C_FORCE_TYPE_ENABLE, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read force type cmd: "); + g_prefix_error_literal(error, "failed to read force type cmd: "); return FALSE; } value = fu_memread_uint16(buf, G_LITTLE_ENDIAN); - if (value == 0xFFFF || value == ETP_CMD_I2C_FORCE_TYPE_ENABLE) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "forcetype cmd not supported"); + if (value == 0xFFFF || value == FU_ETP_CMD_I2C_FORCE_TYPE_ENABLE) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "forcetype cmd not supported"); return FALSE; } if ((buf[0] & ETP_FW_FORCE_TYPE_ENABLE_BIT) == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "force type table not supported"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "force type table not supported"); return FALSE; } @@ -200,25 +205,25 @@ guint8 buf[2] = {0x0}; guint16 value; if (!fu_elantp_hid_device_read_cmd(self, - ETP_CMD_I2C_FORCE_TYPE_ENABLE, + FU_ETP_CMD_I2C_FORCE_TYPE_ENABLE, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read haptic enable cmd: "); + g_prefix_error_literal(error, "failed to read haptic enable cmd: "); return FALSE; } value = fu_memread_uint16(buf, G_LITTLE_ENDIAN); - if (value == 0xFFFF || value == ETP_CMD_I2C_FORCE_TYPE_ENABLE) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not hapticpad"); + if (value == 0xFFFF || value == FU_ETP_CMD_I2C_FORCE_TYPE_ENABLE) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not hapticpad"); return FALSE; } if ((buf[0] & ETP_FW_FORCE_TYPE_ENABLE_BIT) == 0 || (buf[0] & ETP_FW_EEPROM_ENABLE_BIT) == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "the haptic eeprom not supported"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "the haptic eeprom not supported"); return FALSE; } @@ -236,8 +241,8 @@ self->force_table_addr = 0xFF40 * 2; return TRUE; } - if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_FORCE_ADDR, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read force table address cmd: "); + if (!fu_elantp_hid_device_read_cmd(self, FU_ETP_CMD_FORCE_ADDR, buf, sizeof(buf), error)) { + g_prefix_error_literal(error, "failed to read force table address cmd: "); return FALSE; } addr_wrds = fu_memread_uint16(buf, G_LITTLE_ENDIAN); @@ -269,13 +274,13 @@ if (iap_ver < 0x5 || ic_type != 0x13) return TRUE; - if (!fu_elantp_hid_device_write_cmd(self, ETP_CMD_I2C_FW_PW, pw, error)) { - g_prefix_error(error, "failed to write fw password cmd: "); + if (!fu_elantp_hid_device_write_cmd(self, FU_ETP_CMD_I2C_FW_PW, pw, error)) { + g_prefix_error_literal(error, "failed to write fw password cmd: "); return FALSE; } - if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_FW_PW, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read fw password cmd: "); + if (!fu_elantp_hid_device_read_cmd(self, FU_ETP_CMD_I2C_FW_PW, buf, sizeof(buf), error)) { + g_prefix_error_literal(error, "failed to read fw password cmd: "); return FALSE; } value = fu_memread_uint16(buf, G_LITTLE_ENDIAN); @@ -304,31 +309,39 @@ g_autoptr(GError) error_forcetable = NULL; /* get pattern */ - if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_GET_HID_ID, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read HID ID: "); + if (!fu_elantp_hid_device_read_cmd(self, + FU_ETP_CMD_I2C_GET_HID_ID, + buf, + sizeof(buf), + error)) { + g_prefix_error_literal(error, "failed to read HID ID: "); return FALSE; } tmp = fu_memread_uint16(buf, G_LITTLE_ENDIAN); self->pattern = tmp != 0xFFFF ? (tmp & 0xFF00) >> 8 : 0; /* get current firmware version */ - if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_I2C_FW_VERSION, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read fw version: "); + if (!fu_elantp_hid_device_read_cmd(self, + FU_ETP_CMD_I2C_FW_VERSION, + buf, + sizeof(buf), + error)) { + g_prefix_error_literal(error, "failed to read fw version: "); return FALSE; } fwver = fu_memread_uint16(buf, G_LITTLE_ENDIAN); - if (fwver == 0xFFFF || fwver == ETP_CMD_I2C_FW_VERSION) + if (fwver == 0xFFFF || fwver == FU_ETP_CMD_I2C_FW_VERSION) fwver = 0; fu_device_set_version_raw(device, fwver); /* get IAP firmware version */ if (!fu_elantp_hid_device_read_cmd(self, - self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION - : ETP_CMD_I2C_IAP_VERSION_2, + self->pattern == 0 ? FU_ETP_CMD_I2C_IAP_VERSION + : FU_ETP_CMD_I2C_IAP_VERSION_2, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read bootloader version: "); + g_prefix_error_literal(error, "failed to read bootloader version: "); return FALSE; } if (self->pattern >= 1) { @@ -340,8 +353,12 @@ fu_device_set_version_bootloader(device, version_bl); /* get module ID */ - if (!fu_elantp_hid_device_read_cmd(self, ETP_CMD_GET_MODULE_ID, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read module ID: "); + if (!fu_elantp_hid_device_read_cmd(self, + FU_ETP_CMD_GET_MODULE_ID, + buf, + sizeof(buf), + error)) { + g_prefix_error_literal(error, "failed to read module ID: "); return FALSE; } self->module_id = fu_memread_uint16(buf, G_LITTLE_ENDIAN); @@ -355,21 +372,21 @@ /* get OSM version */ if (!fu_elantp_hid_device_read_cmd(self, - ETP_CMD_I2C_OSM_VERSION, + FU_ETP_CMD_I2C_OSM_VERSION, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read OSM version: "); + g_prefix_error_literal(error, "failed to read OSM version: "); return FALSE; } tmp = fu_memread_uint16(buf, G_LITTLE_ENDIAN); - if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (tmp == FU_ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { if (!fu_elantp_hid_device_read_cmd(self, - ETP_CMD_I2C_IAP_ICBODY, + FU_ETP_CMD_I2C_IAP_ICBODY, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IC body: "); + g_prefix_error_literal(error, "failed to read IC body: "); return FALSE; } self->ic_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN) & 0xFF; @@ -399,7 +416,7 @@ return FALSE; } - /* The ic_page_count is based on 64 bytes/page. */ + /* ic_page_count is based on 64 bytes/page */ fu_device_set_firmware_size(device, (guint64)self->ic_page_count * (guint64)64); /* is in bootloader mode */ @@ -413,7 +430,7 @@ g_debug("no forcetable detected: %s", error_forcetable->message); } else { if (!fu_elantp_hid_device_get_forcetable_address(self, error)) { - g_prefix_error(error, "get forcetable address fail: "); + g_prefix_error_literal(error, "get forcetable address fail: "); return FALSE; } self->force_table_support = TRUE; @@ -425,12 +442,12 @@ if (!fu_elantp_hid_device_read_haptic_enable(self, &error_local)) { g_debug("no haptic device detected: %s", error_local->message); } else { - g_autoptr(FuElantpHidHapticDevice) cfg = fu_elantp_hid_haptic_device_new(device); + g_autoptr(FuElantpHidHapticDevice) cfg = fu_elantp_hid_haptic_device_new(); fu_device_add_child(FU_DEVICE(device), FU_DEVICE(cfg)); } - /* fix an unsuitable i²c name, e.g. `VEN 04F3:00 04F3:3XXX` */ - if (g_str_has_prefix(fu_device_get_name(device), "VEN 04F3:00 04F3:3")) + /* fix an unsuitable i²c name, e.g. `VEN 04F3:00 04F3:3XXX` or `0672:00 04F3:3187` */ + if (g_strstr_len(fu_device_get_name(device), -1, ":00 ") != NULL) fu_device_set_name(device, "Touchpad"); /* success */ @@ -441,7 +458,7 @@ fu_elantp_hid_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device); @@ -476,10 +493,10 @@ force_table_support = fu_elantp_firmware_get_forcetable_support(FU_ELANTP_FIRMWARE(firmware)); if (self->force_table_support != force_table_support) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "firmware incompatible, forcetable incorrect."); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware incompatible, forcetable incorrect."); return NULL; } if (self->force_table_support) { @@ -515,13 +532,12 @@ } static gboolean -fu_elantp_hid_device_filling_forcetable_firmware(FuDevice *device, +fu_elantp_hid_device_filling_forcetable_firmware(FuElantpHidDevice *self, guint8 *fw_data, gsize fw_size, guint32 force_table_addr, GError **error) { - FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device); const guint8 fillature[] = {0x77, 0x33, 0x44, 0xaa}; const guint8 signature[] = {0xAA, 0x55, 0xCC, 0x33, 0xFF, 0xFF}; guint8 buf[64] = {[0 ... 63] = 0xFF}; @@ -535,7 +551,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "forcetable address wrong (%x,%x): ", + "forcetable address wrong (0x%x, 0x%x)", force_table_addr, self->force_table_addr); return FALSE; @@ -613,7 +629,7 @@ return FALSE; /* detach */ - if (!fu_elantp_hid_device_detach(device, fu_progress_get_child(progress), error)) + if (!fu_elantp_hid_device_detach(self, fu_progress_get_child(progress), error)) return FALSE; fu_progress_step_done(progress); @@ -636,15 +652,15 @@ return FALSE; if (!fu_elantp_hid_device_filling_forcetable_firmware( - device, + self, buf2, bufsz, fu_elantp_firmware_get_forcetable_addr(FU_ELANTP_FIRMWARE(firmware)), error)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "filling forcetable failed"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "filling forcetable failed"); return FALSE; } chunks = fu_chunk_array_new(buf2 + iap_addr, @@ -715,7 +731,7 @@ /* verify the written checksum */ if (!fu_elantp_hid_device_read_cmd(self, - ETP_CMD_I2C_IAP_CHECKSUM, + FU_ETP_CMD_I2C_IAP_CHECKSUM, csum_buf, sizeof(csum_buf), error)) @@ -747,19 +763,18 @@ } static gboolean -fu_elantp_hid_device_detach(FuDevice *device, FuProgress *progress, GError **error) +fu_elantp_hid_device_detach(FuElantpHidDevice *self, FuProgress *progress, GError **error) { - FuElantpHidDevice *self = FU_ELANTP_HID_DEVICE(device); guint16 iap_ver; guint16 ic_type; guint8 buf[2] = {0x0}; guint16 tmp; /* sanity check */ - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { g_info("in bootloader mode, reset IC"); if (!fu_elantp_hid_device_write_cmd(self, - ETP_CMD_I2C_IAP_RESET, + FU_ETP_CMD_I2C_IAP_RESET, ETP_I2C_IAP_RESET, error)) return FALSE; @@ -768,21 +783,21 @@ /* get OSM version */ if (!fu_elantp_hid_device_read_cmd(self, - ETP_CMD_I2C_OSM_VERSION, + FU_ETP_CMD_I2C_OSM_VERSION, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read OSM version: "); + g_prefix_error_literal(error, "failed to read OSM version: "); return FALSE; } tmp = fu_memread_uint16(buf, G_LITTLE_ENDIAN); - if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (tmp == FU_ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { if (!fu_elantp_hid_device_read_cmd(self, - ETP_CMD_I2C_IAP_ICBODY, + FU_ETP_CMD_I2C_IAP_ICBODY, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IC body: "); + g_prefix_error_literal(error, "failed to read IC body: "); return FALSE; } ic_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN) & 0xFF; @@ -792,12 +807,12 @@ /* get IAP firmware version */ if (!fu_elantp_hid_device_read_cmd(self, - self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION - : ETP_CMD_I2C_IAP_VERSION_2, + self->pattern == 0 ? FU_ETP_CMD_I2C_IAP_VERSION + : FU_ETP_CMD_I2C_IAP_VERSION_2, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read bootloader version: "); + g_prefix_error_literal(error, "failed to read bootloader version: "); return FALSE; } if (self->pattern >= 1) { @@ -818,16 +833,16 @@ } if (!fu_elantp_hid_device_write_cmd(self, - ETP_CMD_I2C_IAP_TYPE, + FU_ETP_CMD_I2C_IAP_TYPE, self->fw_page_size / 2, error)) return FALSE; if (!fu_elantp_hid_device_read_cmd(self, - ETP_CMD_I2C_IAP_TYPE, + FU_ETP_CMD_I2C_IAP_TYPE, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IAP type: "); + g_prefix_error_literal(error, "failed to read IAP type: "); return FALSE; } self->iap_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN); @@ -842,7 +857,7 @@ } if (!fu_elantp_hid_device_write_fw_password(self, ic_type, iap_ver, error)) return FALSE; - if (!fu_elantp_hid_device_write_cmd(self, ETP_CMD_I2C_IAP, self->iap_password, error)) + if (!fu_elantp_hid_device_write_cmd(self, FU_ETP_CMD_I2C_IAP, self->iap_password, error)) return FALSE; fu_device_sleep(FU_DEVICE(self), ELANTP_DELAY_UNLOCK); if (!fu_elantp_hid_device_ensure_iap_ctrl(self, error)) @@ -871,18 +886,21 @@ } /* reset back to runtime */ - if (!fu_elantp_hid_device_write_cmd(self, ETP_CMD_I2C_IAP_RESET, ETP_I2C_IAP_RESET, error)) + if (!fu_elantp_hid_device_write_cmd(self, + FU_ETP_CMD_I2C_IAP_RESET, + ETP_I2C_IAP_RESET, + error)) return FALSE; fu_device_sleep(FU_DEVICE(self), ELANTP_DELAY_RESET); if (!fu_elantp_hid_device_write_cmd(self, - ETP_CMD_I2C_IAP_RESET, + FU_ETP_CMD_I2C_IAP_RESET, ETP_I2C_ENABLE_REPORT, error)) { - g_prefix_error(error, "cannot enable TP report: "); + g_prefix_error_literal(error, "cannot enable TP report: "); return FALSE; } if (!fu_elantp_hid_device_write_cmd(self, 0x0306, 0x003, error)) { - g_prefix_error(error, "cannot switch to TP PTP mode: "); + g_prefix_error_literal(error, "cannot switch to TP PTP mode: "); return FALSE; } if (!fu_elantp_hid_device_ensure_iap_ctrl(self, error)) @@ -921,7 +939,7 @@ } static void -fu_elantp_hid_device_set_progress(FuDevice *self, FuProgress *progress) +fu_elantp_hid_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -944,10 +962,8 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - fu_device_set_summary(FU_DEVICE(self), "Touchpad"); - fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_INPUT_TOUCHPAD); fu_device_add_protocol(FU_DEVICE(self), "tw.com.emc.elantp"); - fu_device_set_vendor(FU_DEVICE(self), "ELAN Microelectronics"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); fu_device_set_priority(FU_DEVICE(self), 1); /* better than i2c */ fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); diff -Nru fwupd-2.0.8/plugins/elantp/fu-elantp-hid-haptic-device.c fwupd-2.0.20/plugins/elantp/fu-elantp-hid-haptic-device.c --- fwupd-2.0.8/plugins/elantp/fu-elantp-hid-haptic-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elantp/fu-elantp-hid-haptic-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,6 +9,7 @@ #include "fu-elantp-common.h" #include "fu-elantp-haptic-firmware.h" #include "fu-elantp-hid-haptic-device.h" +#include "fu-elantp-struct.h" struct _FuElantpHidHapticDevice { FuUdevDevice parent_instance; @@ -28,19 +29,10 @@ G_DEFINE_TYPE(FuElantpHidHapticDevice, fu_elantp_hid_haptic_device, FU_TYPE_UDEV_DEVICE) -static FuElantpHidDevice * -fu_elantp_hid_haptic_device_get_parent(FuDevice *self, GError **error) -{ - FuDevice *parent = fu_device_get_parent(FU_DEVICE(self)); - if (parent == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no parent set"); - return NULL; - } - return FU_ELANTP_HID_DEVICE(FU_UDEV_DEVICE(parent)); -} - static gboolean -fu_elantp_hid_haptic_device_detach(FuDevice *device, FuProgress *progress, GError **error); +fu_elantp_hid_haptic_device_detach(FuElantpHidHapticDevice *self, + FuProgress *progress, + GError **error); static void fu_elantp_hid_haptic_device_to_string(FuDevice *device, guint idt, GString *str) @@ -58,7 +50,7 @@ } static gboolean -fu_elantp_hid_haptic_device_send_cmd(FuDevice *self, +fu_elantp_hid_haptic_device_send_cmd(FuElantpHidDevice *parent, const guint8 *tx, gsize txsz, guint8 *rx, @@ -68,7 +60,7 @@ g_autofree guint8 *buf = NULL; gsize bufsz = rxsz + 3; - if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), + if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(parent), tx, txsz, FU_IOCTL_FLAG_NONE, @@ -80,7 +72,7 @@ /* GetFeature */ buf = g_malloc0(bufsz); buf[0] = tx[0]; /* report number */ - if (!fu_hidraw_device_get_feature(FU_HIDRAW_DEVICE(self), + if (!fu_hidraw_device_get_feature(FU_HIDRAW_DEVICE(parent), buf, bufsz, FU_IOCTL_FLAG_NONE, @@ -99,7 +91,7 @@ } static gboolean -fu_elantp_hid_haptic_device_read_cmd(FuDevice *self, +fu_elantp_hid_haptic_device_read_cmd(FuElantpHidDevice *parent, guint16 reg, guint8 *buf, gsize bufz, @@ -107,30 +99,33 @@ { guint8 tmp[5] = {0x0D, 0x05, 0x03}; fu_memwrite_uint16(tmp + 0x3, reg, G_LITTLE_ENDIAN); - return fu_elantp_hid_haptic_device_send_cmd(self, tmp, sizeof(tmp), buf, bufz, error); + return fu_elantp_hid_haptic_device_send_cmd(parent, tmp, sizeof(tmp), buf, bufz, error); } static gint -fu_elantp_hid_haptic_device_write_cmd(FuDevice *self, guint16 reg, guint16 cmd, GError **error) +fu_elantp_hid_haptic_device_write_cmd(FuElantpHidDevice *parent, + guint16 reg, + guint16 cmd, + GError **error) { guint8 buf[5] = {0x0D}; fu_memwrite_uint16(buf + 0x1, reg, G_LITTLE_ENDIAN); fu_memwrite_uint16(buf + 0x3, cmd, G_LITTLE_ENDIAN); - return fu_elantp_hid_haptic_device_send_cmd(self, buf, sizeof(buf), NULL, 0, error); + return fu_elantp_hid_haptic_device_send_cmd(parent, buf, sizeof(buf), NULL, 0, error); } static gboolean -fu_elantp_hid_haptic_device_ensure_iap_ctrl(FuDevice *parent, - FuElantpHidHapticDevice *self, +fu_elantp_hid_haptic_device_ensure_iap_ctrl(FuElantpHidHapticDevice *self, + FuElantpHidDevice *parent, GError **error) { guint8 buf[2] = {0x0}; if (!fu_elantp_hid_haptic_device_read_cmd(parent, - ETP_CMD_I2C_IAP_CTRL, + FU_ETP_CMD_I2C_IAP_CTRL, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IAPControl: "); + g_prefix_error_literal(error, "failed to read IAPControl: "); return FALSE; } self->tp_iap_ctrl = fu_memread_uint16(buf, G_LITTLE_ENDIAN); @@ -153,27 +148,27 @@ } static gboolean -fu_elantp_hid_haptic_device_ensure_eeprom_iap_ctrl(FuDevice *parent, - FuElantpHidHapticDevice *self, +fu_elantp_hid_haptic_device_ensure_eeprom_iap_ctrl(FuElantpHidHapticDevice *self, + FuElantpHidDevice *parent, GError **error) { guint8 buf[2] = {0x0}; if (!fu_elantp_hid_haptic_device_read_cmd(parent, - ETP_CMD_I2C_SET_EEPROM_CTRL, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IAPControl: "); + g_prefix_error_literal(error, "failed to read IAPControl: "); return FALSE; } self->iap_ctrl = fu_memread_uint16(buf, G_LITTLE_ENDIAN); if ((self->iap_ctrl & 0x800) != 0x800) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "bit11 fail"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "bit11 fail"); return FALSE; } if ((self->iap_ctrl & 0x1000) == 0x1000) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "bit12 fail, resend"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "bit12 fail, resend"); return FALSE; } @@ -181,22 +176,22 @@ } static gboolean -fu_elantp_hid_haptic_device_get_haptic_driver_ic(FuDevice *parent, - FuElantpHidHapticDevice *self, +fu_elantp_hid_haptic_device_get_haptic_driver_ic(FuElantpHidHapticDevice *self, + FuElantpHidDevice *parent, GError **error) { guint8 buf[2] = {0x0}; guint16 value; if (!fu_elantp_hid_haptic_device_read_cmd(parent, - ETP_CMD_I2C_FORCE_TYPE_ENABLE, + FU_ETP_CMD_I2C_FORCE_TYPE_ENABLE, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read haptic enable cmd: "); + g_prefix_error_literal(error, "failed to read haptic enable cmd: "); return FALSE; } value = fu_memread_uint16(buf, G_LITTLE_ENDIAN); - if (value == 0xFFFF || value == ETP_CMD_I2C_FORCE_TYPE_ENABLE) { + if (value == 0xFFFF || value == FU_ETP_CMD_I2C_FORCE_TYPE_ENABLE) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -219,8 +214,8 @@ } static gboolean -fu_elantp_hid_haptic_device_get_version(FuDevice *parent, - FuElantpHidHapticDevice *self, +fu_elantp_hid_haptic_device_get_version(FuElantpHidHapticDevice *self, + FuElantpHidDevice *parent, GError **error) { guint16 v_s = 0; @@ -230,16 +225,16 @@ guint8 buf[2] = {0x0}; if (!fu_elantp_hid_haptic_device_write_cmd(parent, - ETP_CMD_I2C_SET_EEPROM_CTRL, - ETP_CMD_I2C_GET_EEPROM_FW_VERSION, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, + FU_ETP_CMD_I2C_GET_EEPROM_FW_VERSION, error)) { - g_prefix_error(error, "failed to write haptic version cmd: "); + g_prefix_error_literal(error, "failed to write haptic version cmd: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), ELANTP_DELAY_RESET); if (!fu_elantp_hid_haptic_device_read_cmd(parent, 0x0321, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read haptic version cmd: "); + g_prefix_error_literal(error, "failed to read haptic version cmd: "); return FALSE; } v_d = buf[0]; @@ -247,16 +242,16 @@ v_s = (buf[1] & 0xF0) >> 4; if (!fu_elantp_hid_haptic_device_write_cmd(parent, - ETP_CMD_I2C_SET_EEPROM_CTRL, - ETP_CMD_I2C_GET_EEPROM_IAP_VERSION, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, + FU_ETP_CMD_I2C_GET_EEPROM_IAP_VERSION, error)) { - g_prefix_error(error, "failed to write haptic iap version cmd: "); + g_prefix_error_literal(error, "failed to write haptic iap version cmd: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), ELANTP_DELAY_RESET); if (!fu_elantp_hid_haptic_device_read_cmd(parent, 0x0321, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read haptic iap version cmd: "); + g_prefix_error_literal(error, "failed to read haptic iap version cmd: "); return FALSE; } v_y = buf[0]; @@ -273,7 +268,7 @@ } static gboolean -fu_elantp_hid_haptic_device_write_fw_password(FuDevice *parent, +fu_elantp_hid_haptic_device_write_fw_password(FuElantpHidDevice *parent, guint16 tp_ic_type, guint16 tp_iap_ver, GError **error) @@ -285,17 +280,17 @@ if (tp_iap_ver < 0x5 || tp_ic_type != 0x13) return TRUE; - if (!fu_elantp_hid_haptic_device_write_cmd(parent, ETP_CMD_I2C_FW_PW, pw, error)) { - g_prefix_error(error, "failed to write fw password cmd: "); + if (!fu_elantp_hid_haptic_device_write_cmd(parent, FU_ETP_CMD_I2C_FW_PW, pw, error)) { + g_prefix_error_literal(error, "failed to write fw password cmd: "); return FALSE; } if (!fu_elantp_hid_haptic_device_read_cmd(parent, - ETP_CMD_I2C_FW_PW, + FU_ETP_CMD_I2C_FW_PW, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read fw password cmd: "); + g_prefix_error_literal(error, "failed to read fw password cmd: "); return FALSE; } value = fu_memread_uint16(buf, G_LITTLE_ENDIAN); @@ -320,34 +315,35 @@ } FuElantpHaptictpWaitFlashEEPROMChecksumHelper; static gboolean -fu_elantp_hid_haptic_device_write_checksum_cb(FuDevice *parent, gpointer user_data, GError **error) +fu_elantp_hid_haptic_device_write_checksum_cb(FuDevice *device, gpointer user_data, GError **error) { + FuElantpHidDevice *parent = FU_ELANTP_HID_DEVICE(device); guint8 buf[2] = {0x0}; guint16 value; FuElantpHaptictpWaitFlashEEPROMChecksumHelper *helper = user_data; if (!fu_elantp_hid_haptic_device_write_cmd(parent, - ETP_CMD_I2C_EEPROM_SETTING, - ETP_CMD_I2C_EEPROM_WRITE_INFORMATION, + FU_ETP_CMD_I2C_EEPROM_SETTING, + FU_ETP_CMD_I2C_EEPROM_WRITE_INFORMATION, error)) { - g_prefix_error(error, "failed to write haptic info: "); + g_prefix_error_literal(error, "failed to write haptic info: "); return FALSE; } if (!fu_elantp_hid_haptic_device_read_cmd(parent, - ETP_CMD_I2C_EEPROM_SETTING, + FU_ETP_CMD_I2C_EEPROM_SETTING, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read haptic info: "); + g_prefix_error_literal(error, "failed to read haptic info: "); return FALSE; } value = fu_memread_uint16(buf, G_LITTLE_ENDIAN); - if ((value & 0xFFFF) != ETP_CMD_I2C_EEPROM_WRITE_INFORMATION) { + if ((value & 0xFFFF) != FU_ETP_CMD_I2C_EEPROM_WRITE_INFORMATION) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, - "failed to set haptic info (0x%04x): ", + "failed to set haptic info (0x%04x)", value); return FALSE; } @@ -357,32 +353,32 @@ error)) return FALSE; if (!fu_elantp_hid_haptic_device_write_cmd(parent, - ETP_CMD_I2C_IAP, + FU_ETP_CMD_I2C_IAP, helper->iap_password, error)) { - g_prefix_error(error, "failed to write iap password: "); + g_prefix_error_literal(error, "failed to write iap password: "); return FALSE; } if (!fu_elantp_hid_haptic_device_write_cmd(parent, - ETP_CMD_I2C_EEPROM_WRITE_CHECKSUM, + FU_ETP_CMD_I2C_EEPROM_WRITE_CHECKSUM, helper->checksum, error)) { - g_prefix_error(error, "failed to write eeprom checksum: "); + g_prefix_error_literal(error, "failed to write eeprom checksum: "); return FALSE; } if (!fu_elantp_hid_haptic_device_write_cmd(parent, - ETP_CMD_I2C_EEPROM_SETTING, - ETP_CMD_I2C_EEPROM_SETTING_INITIAL, + FU_ETP_CMD_I2C_EEPROM_SETTING, + FU_ETP_CMD_I2C_EEPROM_SETTING_INITIAL, error)) { - g_prefix_error(error, "failed to set haptic initial setting: "); + g_prefix_error_literal(error, "failed to set haptic initial setting: "); return FALSE; } if (!fu_elantp_hid_haptic_device_read_cmd(parent, - ETP_CMD_I2C_EEPROM_WRITE_CHECKSUM, + FU_ETP_CMD_I2C_EEPROM_WRITE_CHECKSUM, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read haptic checksum: "); + g_prefix_error_literal(error, "failed to read haptic checksum: "); return FALSE; } value = fu_memread_uint16(buf, G_LITTLE_ENDIAN); @@ -390,7 +386,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "eeprom checksum failed 0x%04x != 0x%04x : ", + "eeprom checksum failed 0x%04x != 0x%04x", value, helper->checksum); return FALSE; @@ -401,26 +397,27 @@ } static gboolean -fu_elantp_hid_haptic_device_wait_calc_checksum_cb(FuDevice *parent, +fu_elantp_hid_haptic_device_wait_calc_checksum_cb(FuDevice *device, gpointer user_data, GError **error) { + FuElantpHidDevice *parent = FU_ELANTP_HID_DEVICE(device); guint16 ctrl; guint8 buf[2] = {0x0}; if (!fu_elantp_hid_haptic_device_write_cmd(parent, - ETP_CMD_I2C_SET_EEPROM_CTRL, - ETP_CMD_I2C_SET_EEPROM_DATATYPE, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, + FU_ETP_CMD_I2C_SET_EEPROM_DATATYPE, error)) { - g_prefix_error(error, "failed to write eeprom datatype: "); + g_prefix_error_literal(error, "failed to write eeprom datatype: "); return FALSE; } if (!fu_elantp_hid_haptic_device_read_cmd(parent, - ETP_CMD_I2C_SET_EEPROM_CTRL, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read calc haptic cmd: "); + g_prefix_error_literal(error, "failed to read calc haptic cmd: "); return FALSE; } ctrl = fu_memread_uint16(buf, G_LITTLE_ENDIAN); @@ -437,17 +434,19 @@ } static gboolean -fu_elantp_hid_haptic_device_get_checksum(FuDevice *parent, guint16 *checksum, GError **error) +fu_elantp_hid_haptic_device_get_checksum(FuElantpHidDevice *parent, + guint16 *checksum, + GError **error) { guint8 buf[2] = {0x0}; g_autoptr(GError) error_local = NULL; if (!fu_elantp_hid_haptic_device_write_cmd(parent, - ETP_CMD_I2C_SET_EEPROM_CTRL, - ETP_CMD_I2C_CALC_EEPROM_CHECKSUM, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, + FU_ETP_CMD_I2C_CALC_EEPROM_CHECKSUM, error)) return FALSE; - if (!fu_device_retry_full(parent, + if (!fu_device_retry_full(FU_DEVICE(parent), fu_elantp_hid_haptic_device_wait_calc_checksum_cb, 100, ELANTP_EEPROM_READ_DELAY, @@ -461,16 +460,16 @@ return FALSE; } if (!fu_elantp_hid_haptic_device_write_cmd(parent, - ETP_CMD_I2C_SET_EEPROM_CTRL, - ETP_CMD_I2C_READ_EEPROM_CHECKSUM, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, + FU_ETP_CMD_I2C_READ_EEPROM_CHECKSUM, error)) return FALSE; if (!fu_elantp_hid_haptic_device_read_cmd(parent, - ETP_CMD_I2C_SET_EEPROM_CTRL, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read haptic checksum cmd: "); + g_prefix_error_literal(error, "failed to read haptic checksum cmd: "); return FALSE; } *checksum = fu_memread_uint16(buf, G_LITTLE_ENDIAN); @@ -481,56 +480,53 @@ static gboolean fu_elantp_hid_haptic_device_setup(FuDevice *device, GError **error) { - FuElantpHidDevice *parent; FuElantpHidHapticDevice *self = FU_ELANTP_HID_HAPTIC_DEVICE(device); - FuUdevDevice *udev_parent; + FuElantpHidDevice *parent; guint8 ic_type; guint16 tmp; guint8 buf[2] = {0x0}; g_autofree gchar *version_bl = NULL; - parent = fu_elantp_hid_haptic_device_get_parent(device, error); + parent = FU_ELANTP_HID_DEVICE(fu_device_get_parent(FU_DEVICE(self), error)); if (parent == NULL) return FALSE; - - if (!fu_elantp_hid_haptic_device_get_haptic_driver_ic(FU_DEVICE(parent), self, error)) { - g_prefix_error(error, "this module is not support haptic EEPROM: "); + if (!fu_elantp_hid_haptic_device_get_haptic_driver_ic(self, parent, error)) { + g_prefix_error_literal(error, "this module is not support haptic EEPROM: "); return FALSE; } /* get pattern */ - if (!fu_elantp_hid_haptic_device_read_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_GET_HID_ID, + if (!fu_elantp_hid_haptic_device_read_cmd(parent, + FU_ETP_CMD_I2C_GET_HID_ID, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read HID ID: "); + g_prefix_error_literal(error, "failed to read HID ID: "); return FALSE; } tmp = fu_memread_uint16(buf, G_LITTLE_ENDIAN); self->pattern = tmp != 0xFFFF ? (tmp & 0xFF00) >> 8 : 0; - if (!fu_elantp_hid_haptic_device_get_version(FU_DEVICE(parent), self, error)) + if (!fu_elantp_hid_haptic_device_get_version(self, parent, error)) return FALSE; version_bl = fu_version_from_uint16(self->iap_ver, FWUPD_VERSION_FORMAT_HEX); fu_device_set_version_bootloader(device, version_bl); /* get module ID */ - if (!fu_elantp_hid_haptic_device_read_cmd(FU_DEVICE(parent), - ETP_CMD_GET_MODULE_ID, + if (!fu_elantp_hid_haptic_device_read_cmd(parent, + FU_ETP_CMD_GET_MODULE_ID, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read module ID: "); + g_prefix_error_literal(error, "failed to read module ID: "); return FALSE; } self->module_id = fu_memread_uint16(buf, G_LITTLE_ENDIAN); /* define the extra instance IDs */ - udev_parent = FU_UDEV_DEVICE(parent); - fu_device_add_instance_u16(device, "VEN", fu_device_get_vid(FU_DEVICE(udev_parent))); - fu_device_add_instance_u16(device, "DEV", fu_device_get_pid(FU_DEVICE(udev_parent))); + fu_device_add_instance_u16(device, "VEN", fu_device_get_vid(FU_DEVICE(parent))); + fu_device_add_instance_u16(device, "DEV", fu_device_get_pid(FU_DEVICE(parent))); fu_device_add_instance_u16(device, "DRIVERIC", self->driver_ic); fu_device_add_instance_u16(device, "MOD", self->module_id); if (!fu_device_build_instance_id(device, @@ -544,22 +540,22 @@ return FALSE; /* get OSM version */ - if (!fu_elantp_hid_haptic_device_read_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_OSM_VERSION, + if (!fu_elantp_hid_haptic_device_read_cmd(parent, + FU_ETP_CMD_I2C_OSM_VERSION, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read OSM version: "); + g_prefix_error_literal(error, "failed to read OSM version: "); return FALSE; } tmp = fu_memread_uint16(buf, G_LITTLE_ENDIAN); - if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { - if (!fu_elantp_hid_haptic_device_read_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_IAP_ICBODY, + if (tmp == FU_ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (!fu_elantp_hid_haptic_device_read_cmd(parent, + FU_ETP_CMD_I2C_IAP_ICBODY, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IC body: "); + g_prefix_error_literal(error, "failed to read IC body: "); return FALSE; } ic_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN) & 0xFF; @@ -598,7 +594,7 @@ fu_device_set_firmware_size(device, 32768); /* find out if in bootloader mode */ - if (!fu_elantp_hid_haptic_device_ensure_iap_ctrl(FU_DEVICE(parent), self, error)) + if (!fu_elantp_hid_haptic_device_ensure_iap_ctrl(self, parent, error)) return FALSE; /* success */ @@ -609,7 +605,7 @@ fu_elantp_hid_haptic_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuElantpHidHapticDevice *self = FU_ELANTP_HID_HAPTIC_DEVICE(device); @@ -651,7 +647,7 @@ g_autoptr(FuChunkArray) chunks = NULL; /* use parent */ - parent = fu_elantp_hid_haptic_device_get_parent(device, error); + parent = FU_ELANTP_HID_DEVICE(fu_device_get_parent(FU_DEVICE(self), error)); if (parent == NULL) return FALSE; @@ -716,26 +712,21 @@ G_BIG_ENDIAN); } - if (!fu_elantp_hid_haptic_device_send_cmd(FU_DEVICE(parent), - blk, - blksz, - NULL, - 0, - error)) + if (!fu_elantp_hid_haptic_device_send_cmd(parent, blk, blksz, NULL, 0, error)) return FALSE; fu_device_sleep(device, self->fw_page_size == 512 ? ELANTP_DELAY_WRITE_BLOCK_512 : ELANTP_DELAY_WRITE_BLOCK); - if (!fu_elantp_hid_haptic_device_write_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_SET_EEPROM_CTRL, - ETP_CMD_I2C_SET_EEPROM_DATATYPE, + if (!fu_elantp_hid_haptic_device_write_cmd(parent, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, + FU_ETP_CMD_I2C_SET_EEPROM_DATATYPE, error)) return FALSE; - if (!fu_elantp_hid_haptic_device_ensure_eeprom_iap_ctrl(FU_DEVICE(parent), - self, + if (!fu_elantp_hid_haptic_device_ensure_eeprom_iap_ctrl(self, + parent, &error_iapctrl)) { g_set_error(error, FWUPD_ERROR, @@ -769,7 +760,6 @@ const gchar *fw_ver; const gchar *fw_ver_device; g_autoptr(GBytes) fw = NULL; - g_autoptr(GError) error_local = NULL; FuElantpHaptictpWaitFlashEEPROMChecksumHelper helper = {0x0}; FuElantpHaptictpWriteHelper helper_write = {0x0}; @@ -785,13 +775,8 @@ if (fw == NULL) return FALSE; - /* use parent */ - parent = fu_elantp_hid_haptic_device_get_parent(device, error); - if (parent == NULL) - return FALSE; - /* detach */ - if (!fu_elantp_hid_haptic_device_detach(device, fu_progress_get_child(progress), error)) + if (!fu_elantp_hid_haptic_device_detach(self, fu_progress_get_child(progress), error)) return FALSE; fu_progress_step_done(progress); @@ -807,23 +792,26 @@ return FALSE; fu_progress_step_done(progress); - if (!fu_elantp_hid_haptic_device_write_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_EEPROM_SETTING, - ETP_CMD_I2C_EEPROM_SETTING_INITIAL, + parent = FU_ELANTP_HID_DEVICE(fu_device_get_parent(FU_DEVICE(self), error)); + if (parent == NULL) + return FALSE; + if (!fu_elantp_hid_haptic_device_write_cmd(parent, + FU_ETP_CMD_I2C_EEPROM_SETTING, + FU_ETP_CMD_I2C_EEPROM_SETTING_INITIAL, error)) { - g_prefix_error(error, "cannot disable EEPROM Long Transmission mode: "); + g_prefix_error_literal(error, "cannot disable EEPROM Long Transmission mode: "); return FALSE; } - if (!fu_elantp_hid_haptic_device_write_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_SET_EEPROM_CTRL, - ETP_CMD_I2C_SET_EEPROM_LEAVE_IAP, + if (!fu_elantp_hid_haptic_device_write_cmd(parent, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, + FU_ETP_CMD_I2C_SET_EEPROM_LEAVE_IAP, error)) { - g_prefix_error(error, "cannot leave EEPROM IAP: "); + g_prefix_error_literal(error, "cannot leave EEPROM IAP: "); return FALSE; } fu_device_sleep(device, ELANTP_DELAY_RESET); - if (!fu_elantp_hid_haptic_device_get_checksum(FU_DEVICE(parent), &checksum_device, error)) { - g_prefix_error(error, "read device checksum fail: "); + if (!fu_elantp_hid_haptic_device_get_checksum(parent, &checksum_device, error)) { + g_prefix_error_literal(error, "read device checksum fail: "); return FALSE; } if (helper_write.checksum != checksum_device) { @@ -845,12 +833,12 @@ 3, ELANTP_DELAY_WRITE_BLOCK, &helper, - &error_local)) { - g_prefix_error(error, "write device checksum fail (%s): ", error_local->message); + error)) { + g_prefix_error_literal(error, "writing checksum failed: "); return FALSE; } - if (!fu_elantp_hid_haptic_device_get_version(FU_DEVICE(parent), self, error)) + if (!fu_elantp_hid_haptic_device_get_version(self, parent, error)) return FALSE; fw_ver_device = fu_device_get_version(device); fw_ver = fu_firmware_get_version(firmware); @@ -866,11 +854,11 @@ } fu_progress_step_done(progress); - if (!fu_elantp_hid_haptic_device_write_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_SET_EEPROM_CTRL, - ETP_CMD_I2C_HAPTIC_RESTART, + if (!fu_elantp_hid_haptic_device_write_cmd(parent, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, + FU_ETP_CMD_I2C_HAPTIC_RESTART, error)) { - g_prefix_error(error, "cannot restart haptic DriverIC: "); + g_prefix_error_literal(error, "cannot restart haptic DriverIC: "); return FALSE; } fu_progress_step_done(progress); @@ -879,20 +867,21 @@ } static gboolean -fu_elantp_hid_haptic_device_detach(FuDevice *device, FuProgress *progress, GError **error) +fu_elantp_hid_haptic_device_detach(FuElantpHidHapticDevice *self, + FuProgress *progress, + GError **error) { FuElantpHidDevice *parent; - FuElantpHidHapticDevice *self = FU_ELANTP_HID_HAPTIC_DEVICE(device); guint8 buf[2] = {0x0}; guint16 ctrl; guint16 tmp; /* haptic EEPROM IAP process runs in the TP main code */ - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "in touchpad bootloader mode"); + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "in touchpad bootloader mode"); return FALSE; } @@ -900,32 +889,32 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "no support for EEPROM IAP 0x%x 0x%x: ", + "no support for EEPROM IAP 0x%x 0x%x", (guint)self->driver_ic, (guint)self->iap_ver); return FALSE; } - parent = fu_elantp_hid_haptic_device_get_parent(device, error); - if (parent == NULL) - return FALSE; /* get OSM version */ - if (!fu_elantp_hid_haptic_device_read_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_OSM_VERSION, + parent = FU_ELANTP_HID_DEVICE(fu_device_get_parent(FU_DEVICE(self), error)); + if (parent == NULL) + return FALSE; + if (!fu_elantp_hid_haptic_device_read_cmd(parent, + FU_ETP_CMD_I2C_OSM_VERSION, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read OSM version: "); + g_prefix_error_literal(error, "failed to read OSM version: "); return FALSE; } tmp = fu_memread_uint16(buf, G_LITTLE_ENDIAN); - if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { - if (!fu_elantp_hid_haptic_device_read_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_IAP_ICBODY, + if (tmp == FU_ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (!fu_elantp_hid_haptic_device_read_cmd(parent, + FU_ETP_CMD_I2C_IAP_ICBODY, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IC body: "); + g_prefix_error_literal(error, "failed to read IC body: "); return FALSE; } self->tp_ic_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN) & 0xFF; @@ -933,13 +922,13 @@ self->tp_ic_type = (tmp >> 8) & 0xFF; /* get IAP firmware version */ - if (!fu_elantp_hid_haptic_device_read_cmd(FU_DEVICE(parent), - self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION - : ETP_CMD_I2C_IAP_VERSION_2, + if (!fu_elantp_hid_haptic_device_read_cmd(parent, + self->pattern == 0 ? FU_ETP_CMD_I2C_IAP_VERSION + : FU_ETP_CMD_I2C_IAP_VERSION_2, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read bootloader version: "); + g_prefix_error_literal(error, "failed to read bootloader version: "); return FALSE; } if (self->pattern >= 1) @@ -959,17 +948,17 @@ self->fw_page_size = 128; } - if (!fu_elantp_hid_haptic_device_write_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_IAP_TYPE, + if (!fu_elantp_hid_haptic_device_write_cmd(parent, + FU_ETP_CMD_I2C_IAP_TYPE, self->fw_page_size / 2, error)) return FALSE; - if (!fu_elantp_hid_haptic_device_read_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_IAP_TYPE, + if (!fu_elantp_hid_haptic_device_read_cmd(parent, + FU_ETP_CMD_I2C_IAP_TYPE, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IAP type: "); + g_prefix_error_literal(error, "failed to read IAP type: "); return FALSE; } self->iap_type = fu_memread_uint16(buf, G_LITTLE_ENDIAN); @@ -983,24 +972,24 @@ } } - if (!fu_elantp_hid_haptic_device_write_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_EEPROM_SETTING, - ETP_CMD_I2C_EEPROM_LONG_TRANS_ENABLE, + if (!fu_elantp_hid_haptic_device_write_cmd(parent, + FU_ETP_CMD_I2C_EEPROM_SETTING, + FU_ETP_CMD_I2C_EEPROM_LONG_TRANS_ENABLE, error)) { - g_prefix_error(error, "cannot enable EEPROM Long Transmission mode: "); + g_prefix_error_literal(error, "cannot enable EEPROM Long Transmission mode: "); return FALSE; } - if (!fu_elantp_hid_haptic_device_write_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_SET_EEPROM_CTRL, - ETP_CMD_I2C_SET_EEPROM_ENTER_IAP, + if (!fu_elantp_hid_haptic_device_write_cmd(parent, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, + FU_ETP_CMD_I2C_SET_EEPROM_ENTER_IAP, error)) { - g_prefix_error(error, "cannot enter EEPROM IAP: "); + g_prefix_error_literal(error, "cannot enter EEPROM IAP: "); return FALSE; } - if (!fu_elantp_hid_haptic_device_read_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_SET_EEPROM_CTRL, + if (!fu_elantp_hid_haptic_device_read_cmd(parent, + FU_ETP_CMD_I2C_SET_EEPROM_CTRL, buf, sizeof(buf), error)) @@ -1025,36 +1014,35 @@ FuElantpHidDevice *parent; FuElantpHidHapticDevice *self = FU_ELANTP_HID_HAPTIC_DEVICE(device); - parent = fu_elantp_hid_haptic_device_get_parent(device, error); + /* reset back to runtime */ + parent = FU_ELANTP_HID_DEVICE(fu_device_get_parent(FU_DEVICE(self), error)); if (parent == NULL) return FALSE; - - /* reset back to runtime */ - if (!fu_elantp_hid_haptic_device_write_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_IAP_RESET, + if (!fu_elantp_hid_haptic_device_write_cmd(parent, + FU_ETP_CMD_I2C_IAP_RESET, ETP_I2C_IAP_RESET, error)) { - g_prefix_error(error, "cannot reset TP: "); + g_prefix_error_literal(error, "cannot reset TP: "); return FALSE; } fu_device_sleep(device, ELANTP_DELAY_RESET); - if (!fu_elantp_hid_haptic_device_write_cmd(FU_DEVICE(parent), - ETP_CMD_I2C_IAP_RESET, + if (!fu_elantp_hid_haptic_device_write_cmd(parent, + FU_ETP_CMD_I2C_IAP_RESET, ETP_I2C_ENABLE_REPORT, error)) { - g_prefix_error(error, "cannot enable TP report: "); + g_prefix_error_literal(error, "cannot enable TP report: "); return FALSE; } - if (!fu_elantp_hid_haptic_device_write_cmd(FU_DEVICE(parent), 0x0306, 0x003, error)) { - g_prefix_error(error, "cannot switch to TP PTP mode: "); + if (!fu_elantp_hid_haptic_device_write_cmd(parent, 0x0306, 0x003, error)) { + g_prefix_error_literal(error, "cannot switch to TP PTP mode: "); return FALSE; } - if (!fu_elantp_hid_haptic_device_ensure_iap_ctrl(FU_DEVICE(parent), self, error)) + if (!fu_elantp_hid_haptic_device_ensure_iap_ctrl(self, parent, error)) return FALSE; /* sanity check */ if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_READ, "in bootloader mode"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, "in bootloader mode"); return FALSE; } @@ -1091,7 +1079,7 @@ } static void -fu_elantp_hid_haptic_device_set_progress(FuDevice *self, FuProgress *progress) +fu_elantp_hid_haptic_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -1107,7 +1095,7 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN); - fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_INPUT_TOUCHPAD); fu_device_add_protocol(FU_DEVICE(self), "tw.com.emc.elantp.haptic"); fu_device_set_name(FU_DEVICE(self), "HapticPad EEPROM"); fu_device_set_logical_id(FU_DEVICE(self), "eeprom"); @@ -1130,7 +1118,7 @@ } FuElantpHidHapticDevice * -fu_elantp_hid_haptic_device_new(FuDevice *device) +fu_elantp_hid_haptic_device_new(void) { FuElantpHidHapticDevice *self; self = g_object_new(FU_TYPE_ELANTP_HID_HAPTIC_DEVICE, NULL); diff -Nru fwupd-2.0.8/plugins/elantp/fu-elantp-hid-haptic-device.h fwupd-2.0.20/plugins/elantp/fu-elantp-hid-haptic-device.h --- fwupd-2.0.8/plugins/elantp/fu-elantp-hid-haptic-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elantp/fu-elantp-hid-haptic-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -18,4 +18,4 @@ FuUdevDevice) FuElantpHidHapticDevice * -fu_elantp_hid_haptic_device_new(FuDevice *device); +fu_elantp_hid_haptic_device_new(void); diff -Nru fwupd-2.0.8/plugins/elantp/fu-elantp-i2c-device.c fwupd-2.0.20/plugins/elantp/fu-elantp-i2c-device.c --- fwupd-2.0.8/plugins/elantp/fu-elantp-i2c-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elantp/fu-elantp-i2c-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,6 +11,7 @@ #include "fu-elantp-common.h" #include "fu-elantp-firmware.h" #include "fu-elantp-i2c-device.h" +#include "fu-elantp-struct.h" struct _FuElantpI2cDevice { FuI2cDevice parent_instance; @@ -31,7 +32,7 @@ #define FU_ELANTP_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ static gboolean -fu_elantp_i2c_device_detach(FuDevice *device, FuProgress *progress, GError **error); +fu_elantp_i2c_device_detach(FuElantpI2cDevice *self, FuProgress *progress, GError **error); static void fu_elantp_i2c_device_to_string(FuDevice *device, guint idt, GString *str) @@ -81,10 +82,10 @@ g_autofree gchar *bind_fn = g_build_filename(self->bind_path, "bind", NULL); if (self->bind_path == NULL || self->bind_id == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no Path or ID for rebind driver"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no Path or ID for rebind driver"); return FALSE; } @@ -149,7 +150,7 @@ static gboolean fu_elantp_i2c_device_write_cmd(FuElantpI2cDevice *self, guint16 reg, guint16 cmd, GError **error) { - guint8 buf[4]; + guint8 buf[4]; /* nocheck:zero-init */ fu_memwrite_uint16(buf + 0x0, reg, G_LITTLE_ENDIAN); fu_memwrite_uint16(buf + 0x2, cmd, G_LITTLE_ENDIAN); return fu_elantp_i2c_device_send_cmd(self, buf, sizeof(buf), NULL, 0, error); @@ -162,7 +163,7 @@ gsize rxsz, GError **error) { - guint8 buf[2]; + guint8 buf[2]; /* nocheck:zero-init */ fu_memwrite_uint16(buf + 0x0, reg, G_LITTLE_ENDIAN); return fu_elantp_i2c_device_send_cmd(self, buf, sizeof(buf), rx, rxsz, error); } @@ -171,8 +172,12 @@ fu_elantp_i2c_device_ensure_iap_ctrl(FuElantpI2cDevice *self, GError **error) { guint8 buf[2] = {0x0}; - if (!fu_elantp_i2c_device_read_cmd(self, ETP_CMD_I2C_IAP_CTRL, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IAPControl: "); + if (!fu_elantp_i2c_device_read_cmd(self, + FU_ETP_CMD_I2C_IAP_CTRL, + buf, + sizeof(buf), + error)) { + g_prefix_error_literal(error, "failed to read IAPControl: "); return FALSE; } if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &self->iap_ctrl, G_LITTLE_ENDIAN, error)) @@ -202,11 +207,11 @@ /* read the I2C descriptor */ if (!fu_elantp_i2c_device_read_cmd(self, - ETP_CMD_GET_HID_DESCRIPTOR, + FU_ETP_CMD_GET_HID_DESCRIPTOR, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to get HID descriptor: "); + g_prefix_error_literal(error, "failed to get HID descriptor: "); return FALSE; } if (!fu_memread_uint16_safe(buf, sizeof(buf), 20, &vid, G_LITTLE_ENDIAN, error)) @@ -222,8 +227,12 @@ return FALSE; /* get pattern */ - if (!fu_elantp_i2c_device_read_cmd(self, ETP_CMD_I2C_GET_HID_ID, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read I2C ID: "); + if (!fu_elantp_i2c_device_read_cmd(self, + FU_ETP_CMD_I2C_GET_HID_ID, + buf, + sizeof(buf), + error)) { + g_prefix_error_literal(error, "failed to read I2C ID: "); return FALSE; } if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &tmp, G_LITTLE_ENDIAN, error)) @@ -231,24 +240,28 @@ self->pattern = tmp != 0xffff ? (tmp & 0xff00) >> 8 : 0; /* get current firmware version */ - if (!fu_elantp_i2c_device_read_cmd(self, ETP_CMD_I2C_FW_VERSION, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read fw version: "); + if (!fu_elantp_i2c_device_read_cmd(self, + FU_ETP_CMD_I2C_FW_VERSION, + buf, + sizeof(buf), + error)) { + g_prefix_error_literal(error, "failed to read fw version: "); return FALSE; } if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &fwver, G_LITTLE_ENDIAN, error)) return FALSE; - if (fwver == 0xFFFF || fwver == ETP_CMD_I2C_FW_VERSION) + if (fwver == 0xFFFF || fwver == FU_ETP_CMD_I2C_FW_VERSION) fwver = 0; fu_device_set_version_raw(device, fwver); /* get IAP firmware version */ if (!fu_elantp_i2c_device_read_cmd(self, - self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION - : ETP_CMD_I2C_IAP_VERSION_2, + self->pattern == 0 ? FU_ETP_CMD_I2C_IAP_VERSION + : FU_ETP_CMD_I2C_IAP_VERSION_2, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read bootloader version: "); + g_prefix_error_literal(error, "failed to read bootloader version: "); return FALSE; } if (self->pattern >= 1) { @@ -266,8 +279,12 @@ fu_device_set_version_bootloader(device, version_bl); /* get module ID */ - if (!fu_elantp_i2c_device_read_cmd(self, ETP_CMD_GET_MODULE_ID, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read module ID: "); + if (!fu_elantp_i2c_device_read_cmd(self, + FU_ETP_CMD_GET_MODULE_ID, + buf, + sizeof(buf), + error)) { + g_prefix_error_literal(error, "failed to read module ID: "); return FALSE; } if (!fu_memread_uint16_safe(buf, @@ -287,22 +304,22 @@ /* get OSM version */ if (!fu_elantp_i2c_device_read_cmd(self, - ETP_CMD_I2C_OSM_VERSION, + FU_ETP_CMD_I2C_OSM_VERSION, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read OSM version: "); + g_prefix_error_literal(error, "failed to read OSM version: "); return FALSE; } if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &tmp, G_LITTLE_ENDIAN, error)) return FALSE; - if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (tmp == FU_ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { if (!fu_elantp_i2c_device_read_cmd(self, - ETP_CMD_I2C_IAP_ICBODY, + FU_ETP_CMD_I2C_IAP_ICBODY, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IC body: "); + g_prefix_error_literal(error, "failed to read IC body: "); return FALSE; } if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &tmp, G_LITTLE_ENDIAN, error)) @@ -383,7 +400,7 @@ fu_elantp_i2c_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE(device); @@ -439,7 +456,7 @@ return FALSE; /* detach */ - if (!fu_elantp_i2c_device_detach(device, fu_progress_get_child(progress), error)) + if (!fu_elantp_i2c_device_detach(self, fu_progress_get_child(progress), error)) return FALSE; fu_progress_step_done(progress); @@ -508,7 +525,7 @@ /* verify the written checksum */ if (!fu_elantp_i2c_device_read_cmd(self, - ETP_CMD_I2C_IAP_CHECKSUM, + FU_ETP_CMD_I2C_IAP_CHECKSUM, csum_buf, sizeof(csum_buf), error)) @@ -540,42 +557,41 @@ } static gboolean -fu_elantp_i2c_device_detach(FuDevice *device, FuProgress *progress, GError **error) +fu_elantp_i2c_device_detach(FuElantpI2cDevice *self, FuProgress *progress, GError **error) { guint16 iap_ver; guint16 ic_type; guint8 buf[2] = {0x0}; guint16 tmp; - FuElantpI2cDevice *self = FU_ELANTP_I2C_DEVICE(device); /* sanity check */ - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { g_info("in bootloader mode, reset IC"); if (!fu_elantp_i2c_device_write_cmd(self, - ETP_CMD_I2C_IAP_RESET, + FU_ETP_CMD_I2C_IAP_RESET, ETP_I2C_IAP_RESET, error)) return FALSE; - fu_device_sleep(device, ELANTP_DELAY_RESET); + fu_device_sleep(FU_DEVICE(self), ELANTP_DELAY_RESET); } /* get OSM version */ if (!fu_elantp_i2c_device_read_cmd(self, - ETP_CMD_I2C_OSM_VERSION, + FU_ETP_CMD_I2C_OSM_VERSION, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read OSM version: "); + g_prefix_error_literal(error, "failed to read OSM version: "); return FALSE; } if (!fu_memread_uint16_safe(buf, sizeof(buf), 0x0, &tmp, G_LITTLE_ENDIAN, error)) return FALSE; - if (tmp == ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { + if (tmp == FU_ETP_CMD_I2C_OSM_VERSION || tmp == 0xFFFF) { if (!fu_elantp_i2c_device_read_cmd(self, - ETP_CMD_I2C_IAP_ICBODY, + FU_ETP_CMD_I2C_IAP_ICBODY, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IC body: "); + g_prefix_error_literal(error, "failed to read IC body: "); return FALSE; } if (!fu_memread_uint16_safe(buf, @@ -591,12 +607,12 @@ /* get IAP firmware version */ if (!fu_elantp_i2c_device_read_cmd(self, - self->pattern == 0 ? ETP_CMD_I2C_IAP_VERSION - : ETP_CMD_I2C_IAP_VERSION_2, + self->pattern == 0 ? FU_ETP_CMD_I2C_IAP_VERSION + : FU_ETP_CMD_I2C_IAP_VERSION_2, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read bootloader version: "); + g_prefix_error_literal(error, "failed to read bootloader version: "); return FALSE; } if (self->pattern >= 1) { @@ -622,16 +638,16 @@ } /* set the IAP type, presumably some kind of ABI */ if (!fu_elantp_i2c_device_write_cmd(self, - ETP_CMD_I2C_IAP_TYPE, + FU_ETP_CMD_I2C_IAP_TYPE, self->fw_page_size / 2, error)) return FALSE; if (!fu_elantp_i2c_device_read_cmd(self, - ETP_CMD_I2C_IAP_TYPE, + FU_ETP_CMD_I2C_IAP_TYPE, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read IAP type: "); + g_prefix_error_literal(error, "failed to read IAP type: "); return FALSE; } if (!fu_memread_uint16_safe(buf, @@ -650,9 +666,9 @@ } } } - if (!fu_elantp_i2c_device_write_cmd(self, ETP_CMD_I2C_IAP, self->iap_password, error)) + if (!fu_elantp_i2c_device_write_cmd(self, FU_ETP_CMD_I2C_IAP, self->iap_password, error)) return FALSE; - fu_device_sleep(device, ELANTP_DELAY_UNLOCK); + fu_device_sleep(FU_DEVICE(self), ELANTP_DELAY_UNLOCK); if (!fu_elantp_i2c_device_ensure_iap_ctrl(self, error)) return FALSE; if ((self->iap_ctrl & ETP_FW_IAP_CHECK_PW) == 0) { @@ -679,14 +695,17 @@ } /* reset back to runtime */ - if (!fu_elantp_i2c_device_write_cmd(self, ETP_CMD_I2C_IAP_RESET, ETP_I2C_IAP_RESET, error)) + if (!fu_elantp_i2c_device_write_cmd(self, + FU_ETP_CMD_I2C_IAP_RESET, + ETP_I2C_IAP_RESET, + error)) return FALSE; fu_device_sleep(device, ELANTP_DELAY_RESET); if (!fu_elantp_i2c_device_write_cmd(self, - ETP_CMD_I2C_IAP_RESET, + FU_ETP_CMD_I2C_IAP_RESET, ETP_I2C_ENABLE_REPORT, error)) { - g_prefix_error(error, "cannot enable TP report: "); + g_prefix_error_literal(error, "cannot enable TP report: "); return FALSE; } @@ -697,7 +716,7 @@ g_autoptr(GError) error_local = NULL; if (!fu_elantp_i2c_device_write_cmd(self, 0x0300, 0x001, error)) { - g_prefix_error(error, "cannot switch to TP ABS mode: "); + g_prefix_error_literal(error, "cannot switch to TP ABS mode: "); return FALSE; } @@ -713,7 +732,7 @@ } } else { if (!fu_elantp_i2c_device_write_cmd(self, 0x0306, 0x003, error)) { - g_prefix_error(error, "cannot switch to TP PTP mode: "); + g_prefix_error_literal(error, "cannot switch to TP PTP mode: "); return FALSE; } } @@ -757,7 +776,7 @@ } static void -fu_elantp_i2c_device_set_progress(FuDevice *self, FuProgress *progress) +fu_elantp_i2c_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -780,10 +799,10 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_set_name(FU_DEVICE(self), "Touchpad"); - fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_INPUT_TOUCHPAD); fu_device_add_protocol(FU_DEVICE(self), "tw.com.emc.elantp"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); - fu_device_set_vendor(FU_DEVICE(self), "ELAN Microelectronics"); + fu_device_set_vendor(FU_DEVICE(self), "Elan"); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); fu_device_register_private_flag(FU_DEVICE(self), FU_ELANTP_I2C_DEVICE_ABSOLUTE); diff -Nru fwupd-2.0.8/plugins/elantp/fu-elantp.rs fwupd-2.0.20/plugins/elantp/fu-elantp.rs --- fwupd-2.0.8/plugins/elantp/fu-elantp.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elantp/fu-elantp.rs 2026-02-26 11:36:18.000000000 +0000 @@ -12,3 +12,38 @@ struct FuStructElantpHapticFirmwareHdr { magic: [u8; 4] == 0xFF40A25B, } + +enum FuEtpCmd { + I2cEepromSettingInitial = 0x0000, + GetHidDescriptor = 0x0001, + GetHardwareId = 0x0100, + I2cGetHidId = 0x0100, + GetModuleId = 0x0101, + I2cFwVersion = 0x0102, + I2cOsmVersion = 0x0103, + I2cForceTypeEnable = 0x0104, + I2cIapIcbody = 0x0110, + I2cIapVersion_2 = 0x0110, + I2cIapVersion = 0x0111, + I2cIapType = 0x0304, + I2cFwPw = 0x030e, + I2cFwChecksum = 0x030f, + I2cIapCtrl = 0x0310, + I2cIap = 0x0311, + I2cIapReset = 0x0314, + I2cIapChecksum = 0x0315, + I2cSetEepromCtrl = 0x0321, + I2cEepromSetting = 0x0322, + ForceAddr = 0x03ad, + I2cEepromWriteChecksum = 0x048B, + I2cHapticRestart = 0x0601, + I2cSetEepromLeaveIap = 0x0606, + I2cSetEepromEnterIap = 0x0607, + I2cCalcEepromChecksum = 0x060F, + I2cSetEepromDatatype = 0x0702, + I2cReadEepromChecksum = 0x070A, + I2cGetEepromFwVersion = 0x0710, + I2cGetEepromIapVersion = 0x0711, + I2cEepromLongTransEnable = 0x4607, + I2cEepromWriteInformation = 0x4600, +} diff -Nru fwupd-2.0.8/plugins/elantp/fu-self-test.c fwupd-2.0.20/plugins/elantp/fu-self-test.c --- fwupd-2.0.8/plugins/elantp/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elantp/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -31,7 +31,7 @@ g_assert_true(ret); csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); g_assert_no_error(error); - g_assert_cmpstr(csum1, ==, "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"); + g_assert_cmpstr(csum1, ==, "de53a29a438ff297202055151381433b86a2f64d"); /* ensure we can round-trip */ xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); diff -Nru fwupd-2.0.8/plugins/elantp/meson.build fwupd-2.0.20/plugins/elantp/meson.build --- fwupd-2.0.8/plugins/elantp/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/elantp/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginElantp"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -47,6 +48,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', @@ -54,4 +56,3 @@ ) test('elantp-self-test', e, env: env) endif -endif diff -Nru fwupd-2.0.8/plugins/emmc/emmc.quirk fwupd-2.0.20/plugins/emmc/emmc.quirk --- fwupd-2.0.8/plugins/emmc/emmc.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/emmc/emmc.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,4 @@ -#Western Digital +# Western Digital [EMMC\NAME_DI4064] EmmcBlockSize = 0x1000 [EMMC\NAME_DI4128] diff -Nru fwupd-2.0.8/plugins/emmc/fu-emmc-device.c fwupd-2.0.20/plugins/emmc/fu-emmc-device.c --- fwupd-2.0.8/plugins/emmc/fu-emmc-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/emmc/fu-emmc-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -270,7 +270,7 @@ FU_EMMC_DEVICE_IOCTL_TIMEOUT, FU_IOCTL_FLAG_NONE, error)) { - g_prefix_error(error, "failed to MMC_IOC_CMD: "); + g_prefix_error_literal(error, "failed to MMC_IOC_CMD: "); return FALSE; } @@ -333,12 +333,21 @@ fu_emmc_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuEmmcDevice *self = FU_EMMC_DEVICE(device); g_autoptr(FuFirmware) firmware = fu_firmware_new(); + /* sanity check */ + if (self->sect_size == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "sector size invalid"); + return NULL; + } + /* check alignment */ if (fu_firmware_parse_stream(firmware, stream, 0x0, flags, error)) return NULL; @@ -366,7 +375,7 @@ guint32 sector_size; gboolean check_sect_done = FALSE; gsize multi_cmdsz; - guint8 ext_csd[512]; + guint8 ext_csd[512] = {0}; guint failure_cnt = 0; g_autofree struct mmc_ioc_multi_cmd *multi_cmd = NULL; g_autoptr(GInputStream) stream = NULL; @@ -393,8 +402,7 @@ check_sect_done = (ext_csd[EXT_CSD_FFU_FEATURES] & 1) > 0; /* set CMD ARG */ - arg = ext_csd[EXT_CSD_FFU_ARG_0] | ext_csd[EXT_CSD_FFU_ARG_1] << 8 | - ext_csd[EXT_CSD_FFU_ARG_2] << 16 | ext_csd[EXT_CSD_FFU_ARG_3] << 24; + arg = fu_memread_uint32(ext_csd + EXT_CSD_FFU_ARG_0, G_LITTLE_ENDIAN); /* prepare multi_cmd to be sent */ multi_cmdsz = sizeof(struct mmc_ioc_multi_cmd) + 4 * sizeof(struct mmc_ioc_cmd); @@ -457,7 +465,7 @@ FU_IOCTL_FLAG_NONE, error)) { g_autoptr(GError) error_local = NULL; - g_prefix_error(error, "multi-cmd failed: "); + g_prefix_error_literal(error, "multi-cmd failed: "); /* multi-cmd ioctl failed before exiting from ffu mode */ if (!fu_ioctl_execute(ioctl, MMC_IOC_CMD, @@ -484,11 +492,8 @@ if (!fu_emmc_device_read_extcsd(self, ext_csd, sizeof(ext_csd), error)) return FALSE; - sect_done = ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_0] | - ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_1] << 8 | - ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_2] << 16 | - ext_csd[EXT_CSD_NUM_OF_FW_SEC_PROG_3] << 24; - + sect_done = + fu_memread_uint32(ext_csd + EXT_CSD_NUM_OF_FW_SEC_PROG_0, G_LITTLE_ENDIAN); if (sect_done != 0) break; @@ -546,8 +551,8 @@ FU_IOCTL_FLAG_NONE, error)) { g_autoptr(GError) error_local = NULL; - /* In case multi-cmd ioctl failed before exiting from ffu mode */ - g_prefix_error(error, "multi-cmd failed setting install mode: "); + /* in case multi-cmd ioctl failed before exiting from ffu mode */ + g_prefix_error_literal(error, "multi-cmd failed setting install mode: "); if (!fu_ioctl_execute(ioctl, MMC_IOC_CMD, (guint8 *)&multi_cmd->cmds[2], @@ -598,7 +603,7 @@ } static void -fu_emmc_device_set_progress(FuDevice *self, FuProgress *progress) +fu_emmc_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -613,7 +618,7 @@ fu_emmc_device_init(FuEmmcDevice *self) { fu_device_add_protocol(FU_DEVICE(self), "org.jedec.mmc"); - fu_device_add_icon(FU_DEVICE(self), "media-memory"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_MEMORY); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_SIGNED); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); diff -Nru fwupd-2.0.8/plugins/emmc/meson.build fwupd-2.0.20/plugins/emmc/meson.build --- fwupd-2.0.8/plugins/emmc/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/emmc/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginEmmc"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -16,4 +17,3 @@ enumeration_data += files('tests/sandisk-da4064-setup.json') device_tests += files('tests/sandisk-da4064.json') -endif diff -Nru fwupd-2.0.8/plugins/ep963x/fu-ep963x-common.h fwupd-2.0.20/plugins/ep963x/fu-ep963x-common.h --- fwupd-2.0.8/plugins/ep963x/fu-ep963x-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ep963x/fu-ep963x-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -15,43 +15,3 @@ #define FU_EP963_FEATURE_ID1_SIZE 0x08 #define FU_EP963_USB_CONTROL_ID 0x01 - -#define FU_EP963_ICP_ENTER 0x40 -#define FU_EP963_ICP_EXIT 0x82 -#define FU_EP963_ICP_BANK 0x83 -#define FU_EP963_ICP_ADDRESS 0x84 -#define FU_EP963_ICP_READBLOCK 0x85 -#define FU_EP963_ICP_WRITEBLOCK 0x86 -#define FU_EP963_ICP_MCUID 0x87 -#define FU_EP963_ICP_DONE 0x5A - -#define FU_EP963_OPCODE_SMBUS_READ 0x01 -#define FU_EP963_OPCODE_ERASE_SPI 0x02 -#define FU_EP963_OPCODE_RESET_BLOCK_INDEX 0x03 -#define FU_EP963_OPCODE_WRITE_BLOCK_DATA 0x04 -#define FU_EP963_OPCODE_PROGRAM_SPI_BLOCK 0x05 -#define FU_EP963_OPCODE_PROGRAM_SPI_FINISH 0x06 -#define FU_EP963_OPCODE_GET_SPI_CHECKSUM 0x07 -#define FU_EP963_OPCODE_PROGRAM_EP_FLASH 0x08 -#define FU_EP963_OPCODE_GET_EP_CHECKSUM 0x09 -#define FU_EP963_OPCODE_START_THROW_PAGE 0x0B -#define FU_EP963_OPCODE_GET_EP_SITE_TYPE 0x0C -#define FU_EP963_OPCODE_COMMAND_VERSION 0x10 -#define FU_EP963_OPCODE_COMMAND_STATUS 0x20 -#define FU_EP963_OPCODE_SUBMCU_ENTER_ICP 0x30 -#define FU_EP963_OPCODE_SUBMCU_RESET_BLOCK_IDX 0x31 -#define FU_EP963_OPCODE_SUBMCU_WRITE_BLOCK_DATA 0x32 -#define FU_EP963_OPCODE_SUBMCU_PROGRAM_BLOCK 0x33 -#define FU_EP963_OPCODE_SUBMCU_PROGRAM_FINISHED 0x34 - -#define FU_EP963_UF_CMD_VERSION 0x00 -#define FU_EP963_UF_CMD_ENTERISP 0x01 -#define FU_EP963_UF_CMD_PROGRAM 0x02 -#define FU_EP963_UF_CMD_READ 0x03 -#define FU_EP963_UF_CMD_MODE 0x04 - -/* byte 0x02 */ -#define FU_EP963_USB_STATE_READY 0x00 -#define FU_EP963_USB_STATE_BUSY 0x01 -#define FU_EP963_USB_STATE_FAIL 0x02 -#define FU_EP963_USB_STATE_UNKNOWN 0xff diff -Nru fwupd-2.0.8/plugins/ep963x/fu-ep963x-device.c fwupd-2.0.20/plugins/ep963x/fu-ep963x-device.c --- fwupd-2.0.8/plugins/ep963x/fu-ep963x-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ep963x/fu-ep963x-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -361,7 +361,7 @@ } static void -fu_ep963x_device_set_progress(FuDevice *self, FuProgress *progress) +fu_ep963x_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); diff -Nru fwupd-2.0.8/plugins/ep963x/fu-ep963x-firmware.c fwupd-2.0.20/plugins/ep963x/fu-ep963x-firmware.c --- fwupd-2.0.8/plugins/ep963x/fu-ep963x-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ep963x/fu-ep963x-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -30,7 +30,7 @@ static gboolean fu_ep963x_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize streamsz = 0; diff -Nru fwupd-2.0.8/plugins/ep963x/fu-ep963x-plugin.c fwupd-2.0.20/plugins/ep963x/fu-ep963x-plugin.c --- fwupd-2.0.8/plugins/ep963x/fu-ep963x-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ep963x/fu-ep963x-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,15 +10,16 @@ #include "fu-ep963x-firmware.h" #include "fu-ep963x-plugin.h" -struct _FuEp963XPlugin { +struct _FuEp963xPlugin { FuPlugin parent_instance; }; -G_DEFINE_TYPE(FuEp963XPlugin, fu_ep963x_plugin, FU_TYPE_PLUGIN) +G_DEFINE_TYPE(FuEp963xPlugin, fu_ep963x_plugin, FU_TYPE_PLUGIN) static void -fu_ep963x_plugin_init(FuEp963XPlugin *self) +fu_ep963x_plugin_init(FuEp963xPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void @@ -32,12 +33,13 @@ fu_ep963x_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_EP963X_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_EP963X_FIRMWARE); } static void -fu_ep963x_plugin_class_init(FuEp963XPluginClass *klass) +fu_ep963x_plugin_class_init(FuEp963xPluginClass *klass) { FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); GObjectClass *object_class = G_OBJECT_CLASS(klass); diff -Nru fwupd-2.0.8/plugins/ep963x/fu-ep963x-plugin.h fwupd-2.0.20/plugins/ep963x/fu-ep963x-plugin.h --- fwupd-2.0.8/plugins/ep963x/fu-ep963x-plugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ep963x/fu-ep963x-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,4 +8,4 @@ #include -G_DECLARE_FINAL_TYPE(FuEp963XPlugin, fu_ep963x_plugin, FU, EP963X_PLUGIN, FuPlugin) +G_DECLARE_FINAL_TYPE(FuEp963xPlugin, fu_ep963x_plugin, FU, EP963X_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/ep963x/fu-ep963x.rs fwupd-2.0.20/plugins/ep963x/fu-ep963x.rs --- fwupd-2.0.8/plugins/ep963x/fu-ep963x.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ep963x/fu-ep963x.rs 2026-02-26 11:36:18.000000000 +0000 @@ -19,3 +19,51 @@ Timeout = 0x10, Busy = 0x20, } + +enum FuEp963Icp { + Enter = 0x40, + Exit = 0x82, + Bank = 0x83, + Address = 0x84, + Readblock = 0x85, + Writeblock = 0x86, + Mcuid = 0x87, + Done = 0x5A, +} + +enum FuEp963Opcode { + SmbusRead = 0x01, + EraseSpi = 0x02, + ResetBlockIndex = 0x03, + WriteBlockData = 0x04, + ProgramSpiBlock = 0x05, + ProgramSpiFinish = 0x06, + GetSpiChecksum = 0x07, + ProgramEpFlash = 0x08, + GetEpChecksum = 0x09, + StartThrowPage = 0x0b, + GetEpSiteType = 0x0c, + CommandVersion = 0x10, + CommandStatus = 0x20, + SubmcuEnterIcp = 0x30, + SubmcuResetBlockIdx = 0x31, + SubmcuWriteBlockData = 0x32, + SubmcuProgramBlock = 0x33, + SubmcuProgramFinished = 0x34, +} + +enum FuEp963UfCmd { + Version = 0x00, + Enterisp = 0x01, + Program = 0x02, + Read = 0x03, + Mode = 0x04, +} + +/* byte 0x02 */ +enum FuEp963UsbState { + Ready = 0x00, + Busy = 0x01, + Fail = 0x02, + Unknown = 0xff, +} diff -Nru fwupd-2.0.8/plugins/ep963x/meson.build fwupd-2.0.20/plugins/ep963x/meson.build --- fwupd-2.0.8/plugins/ep963x/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ep963x/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginEp963x"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -15,4 +16,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/fastboot/ci.quirk fwupd-2.0.20/plugins/fastboot/ci.quirk --- fwupd-2.0.8/plugins/fastboot/ci.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/fastboot/ci.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,3 @@ +# Google Pixel 3a +[USB\VID_18D1&PID_4EE0] +Plugin = fastboot diff -Nru fwupd-2.0.8/plugins/fastboot/fastboot.quirk fwupd-2.0.20/plugins/fastboot/fastboot.quirk --- fwupd-2.0.8/plugins/fastboot/fastboot.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fastboot/fastboot.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -4,9 +4,6 @@ FastbootBlockSize = 16384 FastbootOperationDelay = 250 Summary = Quectel EG25-G modem (fastboot) -CounterpartGuid = USB\VID_2C7C&PID_0125 -Flags = detach-at-fastboot-has-no-response -ModemManagerBranchAtCommand = AT+GETFWBRANCH # Fibocom FM101 [USB\VID_2CB7&PID_D00D] @@ -39,7 +36,3 @@ Plugin = fastboot Summary = Foxconn T77w968/eSIM LTE modem (fastboot) CounterpartGuid = USB\VID_0489&PID_E0B5 - -# Google Pixel 3a -[USB\VID_18D1&PID_4EE0] -Plugin = fastboot diff -Nru fwupd-2.0.8/plugins/fastboot/fu-fastboot-device.c fwupd-2.0.20/plugins/fastboot/fu-fastboot-device.c --- fwupd-2.0.8/plugins/fastboot/fu-fastboot-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fastboot/fu-fastboot-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -47,9 +47,8 @@ } static gboolean -fu_fastboot_device_write(FuDevice *device, const guint8 *buf, gsize buflen, GError **error) +fu_fastboot_device_write(FuFastbootDevice *self, const guint8 *buf, gsize buflen, GError **error) { - FuFastbootDevice *self = FU_FASTBOOT_DEVICE(device); gboolean ret; gsize actual_len = 0; g_autofree guint8 *buf2 = NULL; @@ -69,10 +68,10 @@ error); /* give device some time to handle action */ - fu_device_sleep(device, self->operation_delay); + fu_device_sleep(FU_DEVICE(self), self->operation_delay); if (!ret) { - g_prefix_error(error, "failed to do bulk transfer: "); + g_prefix_error_literal(error, "failed to do bulk transfer: "); return FALSE; } if (actual_len != buflen) { @@ -87,7 +86,7 @@ } static gboolean -fu_fastboot_device_writestr(FuDevice *device, const gchar *str, GError **error) +fu_fastboot_device_writestr(FuFastbootDevice *self, const gchar *str, GError **error) { gsize buflen = strlen(str); if (buflen > FASTBOOT_CMD_BUFSZ - 4) { @@ -98,7 +97,7 @@ FASTBOOT_CMD_BUFSZ - 4); return FALSE; } - return fu_fastboot_device_write(device, (const guint8 *)str, buflen, error); + return fu_fastboot_device_write(self, (const guint8 *)str, buflen, error); } typedef enum { @@ -107,13 +106,12 @@ } FuFastbootDeviceReadFlags; static gboolean -fu_fastboot_device_read(FuDevice *device, +fu_fastboot_device_read(FuFastbootDevice *self, gchar **str, FuProgress *progress, FuFastbootDeviceReadFlags flags, GError **error) { - FuFastbootDevice *self = FU_FASTBOOT_DEVICE(device); guint retries = 1; /* these commands may return INFO or take some time to complete */ @@ -136,7 +134,7 @@ NULL, &error_local); /* give device some time to handle action */ - fu_device_sleep(device, self->operation_delay); + fu_device_sleep(FU_DEVICE(self), self->operation_delay); if (!ret) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT)) { @@ -159,7 +157,7 @@ } /* info */ - tmp = g_strndup((const gchar *)buf + 4, self->blocksz - 4); + tmp = fu_memstrsafe(buf, sizeof(buf), 4, sizeof(buf) - 4, NULL); if (memcmp(buf, "INFO", 4) == 0) { if (g_strcmp0(tmp, "erasing flash") == 0) fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_ERASE); @@ -201,45 +199,54 @@ } static gboolean -fu_fastboot_device_getvar(FuDevice *device, const gchar *key, gchar **str, GError **error) +fu_fastboot_device_getvar(FuFastbootDevice *self, const gchar *key, gchar **str, GError **error) { g_autofree gchar *tmp = g_strdup_printf("getvar:%s", key); g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); - if (!fu_fastboot_device_writestr(device, tmp, error)) + g_autoptr(GError) error_local = NULL; + + if (!fu_fastboot_device_writestr(self, tmp, error)) return FALSE; - if (!fu_fastboot_device_read(device, + if (!fu_fastboot_device_read(self, str, progress, FU_FASTBOOT_DEVICE_READ_FLAG_NONE, - error)) { - g_prefix_error(error, "failed to getvar %s: ", key); - return FALSE; + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_READ)) { + g_debug("ignoring: %s", error_local->message); + } else { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to getvar %s: ", + key); + return FALSE; + } } return TRUE; } static gboolean -fu_fastboot_device_cmd(FuDevice *device, +fu_fastboot_device_cmd(FuFastbootDevice *self, const gchar *cmd, FuProgress *progress, FuFastbootDeviceReadFlags flags, GError **error) { - if (!fu_fastboot_device_writestr(device, cmd, error)) + if (!fu_fastboot_device_writestr(self, cmd, error)) return FALSE; - if (!fu_fastboot_device_read(device, NULL, progress, flags, error)) + if (!fu_fastboot_device_read(self, NULL, progress, flags, error)) return FALSE; return TRUE; } static gboolean -fu_fastboot_device_flash(FuDevice *device, +fu_fastboot_device_flash(FuFastbootDevice *self, const gchar *partition, FuProgress *progress, GError **error) { g_autofree gchar *tmp = g_strdup_printf("flash:%s", partition); - return fu_fastboot_device_cmd(device, + return fu_fastboot_device_cmd(self, tmp, progress, FU_FASTBOOT_DEVICE_READ_FLAG_STATUS_POLL, @@ -247,15 +254,17 @@ } static gboolean -fu_fastboot_device_download(FuDevice *device, GBytes *fw, FuProgress *progress, GError **error) +fu_fastboot_device_download(FuFastbootDevice *self, + GBytes *fw, + FuProgress *progress, + GError **error) { - FuFastbootDevice *self = FU_FASTBOOT_DEVICE(device); gsize sz = g_bytes_get_size(fw); g_autofree gchar *tmp = g_strdup_printf("download:%08x", (guint)sz); g_autoptr(FuChunkArray) chunks = NULL; /* tell the client the size of data to expect */ - if (!fu_fastboot_device_cmd(device, + if (!fu_fastboot_device_cmd(self, tmp, progress, FU_FASTBOOT_DEVICE_READ_FLAG_STATUS_POLL, @@ -277,14 +286,14 @@ chk = fu_chunk_array_index(chunks, i, error); if (chk == NULL) return FALSE; - if (!fu_fastboot_device_write(device, + if (!fu_fastboot_device_write(self, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), error)) return FALSE; fu_progress_step_done(progress); } - if (!fu_fastboot_device_read(device, + if (!fu_fastboot_device_read(self, NULL, progress, FU_FASTBOOT_DEVICE_READ_FLAG_STATUS_POLL, @@ -307,7 +316,7 @@ return FALSE; /* product */ - if (!fu_fastboot_device_getvar(device, "product", &product, error)) + if (!fu_fastboot_device_getvar(self, "product", &product, error)) return FALSE; if (product != NULL && product[0] != '\0') { g_autofree gchar *tmp = g_strdup_printf("Fastboot %s", product); @@ -315,21 +324,19 @@ } /* bootloader version */ - if (!fu_fastboot_device_getvar(device, "version-bootloader", &version_bootloader, error)) + if (!fu_fastboot_device_getvar(self, "version-bootloader", &version_bootloader, error)) return FALSE; - if (version_bootloader != NULL && version_bootloader[0] != '\0') { - fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_PAIR); + if (version_bootloader != NULL && version_bootloader[0] != '\0') fu_device_set_version_bootloader(device, version_bootloader); - } /* serialno */ - if (!fu_fastboot_device_getvar(device, "serialno", &serialno, error)) + if (!fu_fastboot_device_getvar(self, "serialno", &serialno, error)) return FALSE; if (serialno != NULL && serialno[0] != '\0') fu_device_set_serial(device, serialno); /* secure */ - if (!fu_fastboot_device_getvar(device, "secure", &secure, error)) + if (!fu_fastboot_device_getvar(self, "secure", &secure, error)) return FALSE; if (secure != NULL && secure[0] != '\0') { fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); @@ -342,7 +349,7 @@ } static gboolean -fu_fastboot_device_write_qfil_part(FuDevice *device, +fu_fastboot_device_write_qfil_part(FuFastbootDevice *self, FuFirmware *firmware, XbNode *part, FuProgress *progress, @@ -370,13 +377,13 @@ partition += 2; /* flash the partition */ - if (!fu_fastboot_device_download(device, data, progress, error)) + if (!fu_fastboot_device_download(self, data, progress, error)) return FALSE; - return fu_fastboot_device_flash(device, partition, progress, error); + return fu_fastboot_device_flash(self, partition, progress, error); } static gboolean -fu_fastboot_device_write_motorola_part(FuDevice *device, +fu_fastboot_device_write_motorola_part(FuFastbootDevice *self, FuFirmware *firmware, XbNode *part, FuProgress *progress, @@ -410,7 +417,7 @@ } /* just has to be non-empty */ - if (!fu_fastboot_device_getvar(device, var, &tmp, error)) + if (!fu_fastboot_device_getvar(self, var, &tmp, error)) return FALSE; if (tmp == NULL || tmp[0] == '\0') { g_set_error(error, @@ -441,7 +448,7 @@ } /* erase the partition */ - return fu_fastboot_device_cmd(device, + return fu_fastboot_device_cmd(self, cmd, progress, FU_FASTBOOT_DEVICE_READ_FLAG_NONE, @@ -503,16 +510,16 @@ } /* flash the partition */ - if (!fu_fastboot_device_download(device, data, progress, error)) + if (!fu_fastboot_device_download(self, data, progress, error)) return FALSE; - return fu_fastboot_device_flash(device, partition, progress, error); + return fu_fastboot_device_flash(self, partition, progress, error); } /* dumb operation that doesn't expect a response */ if (g_strcmp0(op, "boot") == 0 || g_strcmp0(op, "continue") == 0 || g_strcmp0(op, "reboot") == 0 || g_strcmp0(op, "reboot-bootloader") == 0 || g_strcmp0(op, "powerdown") == 0) { - return fu_fastboot_device_cmd(device, + return fu_fastboot_device_cmd(self, op, progress, FU_FASTBOOT_DEVICE_READ_FLAG_NONE, @@ -525,7 +532,7 @@ } static gboolean -fu_fastboot_device_write_motorola(FuDevice *device, +fu_fastboot_device_write_motorola(FuFastbootDevice *self, FuFirmware *firmware, FuProgress *progress, GError **error) @@ -561,7 +568,7 @@ fu_progress_set_steps(progress, parts->len); for (guint i = 0; i < parts->len; i++) { XbNode *part = g_ptr_array_index(parts, i); - if (!fu_fastboot_device_write_motorola_part(device, + if (!fu_fastboot_device_write_motorola_part(self, firmware, part, fu_progress_get_child(progress), @@ -575,7 +582,7 @@ } static gboolean -fu_fastboot_device_write_qfil(FuDevice *device, +fu_fastboot_device_write_qfil(FuFastbootDevice *self, FuFirmware *firmware, FuProgress *progress, GError **error) @@ -611,7 +618,7 @@ fu_progress_set_steps(progress, parts->len); for (guint i = 0; i < parts->len; i++) { XbNode *part = g_ptr_array_index(parts, i); - if (!fu_fastboot_device_write_qfil_part(device, + if (!fu_fastboot_device_write_qfil_part(self, firmware, part, fu_progress_get_child(progress), @@ -631,15 +638,16 @@ FwupdInstallFlags flags, GError **error) { + FuFastbootDevice *self = FU_FASTBOOT_DEVICE(device); g_autoptr(FuFirmware) manifest = NULL; /* load the manifest of operations */ manifest = fu_firmware_get_image_by_id(firmware, "partition_nand.xml", NULL); if (manifest != NULL) - return fu_fastboot_device_write_qfil(device, firmware, progress, error); + return fu_fastboot_device_write_qfil(self, firmware, progress, error); manifest = fu_firmware_get_image_by_id(firmware, "flashfile.xml", NULL); if (manifest != NULL) - return fu_fastboot_device_write_motorola(device, firmware, progress, error); + return fu_fastboot_device_write_motorola(self, firmware, progress, error); /* not supported */ g_set_error_literal(error, @@ -683,7 +691,8 @@ static gboolean fu_fastboot_device_attach(FuDevice *device, FuProgress *progress, GError **error) { - if (!fu_fastboot_device_cmd(device, + FuFastbootDevice *self = FU_FASTBOOT_DEVICE(device); + if (!fu_fastboot_device_cmd(self, "reboot", progress, FU_FASTBOOT_DEVICE_READ_FLAG_NONE, @@ -694,7 +703,7 @@ } static void -fu_fastboot_device_set_progress(FuDevice *self, FuProgress *progress) +fu_fastboot_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/fastboot/fu-fastboot-plugin.c fwupd-2.0.20/plugins/fastboot/fu-fastboot-plugin.c --- fwupd-2.0.8/plugins/fastboot/fu-fastboot-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fastboot/fu-fastboot-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -27,6 +27,7 @@ FuContext *ctx = fu_plugin_get_context(plugin); fu_context_add_quirk_key(ctx, "FastbootBlockSize"); fu_context_add_quirk_key(ctx, "FastbootOperationDelay"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_FASTBOOT_DEVICE); } diff -Nru fwupd-2.0.8/plugins/fastboot/meson.build fwupd-2.0.20/plugins/fastboot/meson.build --- fwupd-2.0.8/plugins/fastboot/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fastboot/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -13,5 +13,8 @@ dependencies: plugin_deps, ) -enumeration_data += files('tests/fastboot-google-sargo-setup.json') -device_tests += files('tests/fastboot-google-sargo.json') +if not supported_build + plugin_quirks += files('ci.quirk') + enumeration_data += files('tests/fastboot-google-sargo-setup.json') + device_tests += files('tests/fastboot-google-sargo.json') +endif diff -Nru fwupd-2.0.8/plugins/flashrom/README.md fwupd-2.0.20/plugins/flashrom/README.md --- fwupd-2.0.8/plugins/flashrom/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/flashrom/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -69,6 +69,12 @@ This plugin uses the following plugin-specific quirks: +### `FlashromFmapRegions` + +A list of FMAP regions to be flashed, for instance `COREBOOT,EC,RW_MRC_CACHE` + +Since: 2.0.13 + ### `Flags=reset-cmos` Flag to determine if the CMOS checksum should be reset after the flash is reprogrammed. diff -Nru fwupd-2.0.8/plugins/flashrom/flashrom.quirk fwupd-2.0.20/plugins/flashrom/flashrom.quirk --- fwupd-2.0.8/plugins/flashrom/flashrom.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/flashrom/flashrom.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -22,6 +22,7 @@ # Star Labs generic (HwId - coreboot) [b8cf5af6-8a46-5deb-ac01-a35b1ea5fb48] Plugin = flashrom +FlashromFmapRegions = COREBOOT,EC # Star Labs generic (HwID - AMI) [29230df7-d1e5-5c38-859b-229e966b22e9] diff -Nru fwupd-2.0.8/plugins/flashrom/fu-flashrom-cmos.c fwupd-2.0.20/plugins/flashrom/fu-flashrom-cmos.c --- fwupd-2.0.8/plugins/flashrom/fu-flashrom-cmos.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/flashrom/fu-flashrom-cmos.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,19 +17,19 @@ { guint8 tmp; - /* Reject addresses in the second bank */ + /* reject addresses in the second bank */ if (addr >= 128) return FALSE; - /* Write the value to CMOS */ + /* write the value to CMOS */ outb(addr, RTC_BASE_PORT); outb(val, RTC_BASE_PORT + 1); - /* Read the value back from CMOS */ + /* read the value back from CMOS */ outb(addr, RTC_BASE_PORT); tmp = inb(RTC_BASE_PORT + 1); - /* Check the read value against the written */ + /* check the read value against the written */ return (tmp == val); } #endif @@ -38,7 +38,7 @@ fu_flashrom_cmos_reset(GError **error) { #ifdef HAVE_IO_H - /* Call ioperm() to grant us access to ports 0x70 and 0x71 */ + /* call ioperm() to grant us access to ports 0x70 and 0x71 */ if (ioperm(RTC_BASE_PORT, 2, TRUE) < 0) { g_set_error_literal(error, FWUPD_ERROR, @@ -47,7 +47,7 @@ return FALSE; } - /* Write a default value to the CMOS checksum */ + /* write a default value to the CMOS checksum */ if ((!fu_flashrom_cmos_write(CMOS_CHECKSUM_OFFSET, 0xff)) || (!fu_flashrom_cmos_write(CMOS_CHECKSUM_OFFSET + 1, 0xff))) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, "failed to reset CMOS"); diff -Nru fwupd-2.0.8/plugins/flashrom/fu-flashrom-device.c fwupd-2.0.20/plugins/flashrom/fu-flashrom-device.c --- fwupd-2.0.8/plugins/flashrom/fu-flashrom-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/flashrom/fu-flashrom-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,7 +17,8 @@ struct _FuFlashromDevice { FuUdevDevice parent_instance; - FuIfdRegion region; + FuIfdRegion ifd_region; + gchar *fmap_regions; struct flashrom_flashctx *flashctx; struct flashrom_layout *layout; }; @@ -26,12 +27,33 @@ enum { PROP_0, PROP_FLASHCTX, PROP_REGION, PROP_LAST }; +static void +fu_flashrom_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuFlashromDevice *self = FU_FLASHROM_DEVICE(device); + fwupd_codec_string_append(str, idt, "IfdRegion", fu_ifd_region_to_string(self->ifd_region)); + fwupd_codec_string_append(str, idt, "FmapRegions", self->fmap_regions); +} + static gboolean fu_flashrom_device_set_quirk_kv(FuDevice *device, const gchar *key, const gchar *value, GError **error) { + FuFlashromDevice *self = FU_FLASHROM_DEVICE(device); + if (g_strcmp0(key, "FlashromFmapRegions") == 0) { + if (g_strcmp0(value, "") == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "FMAP regions cannot be empty"); + return FALSE; + } + g_free(self->fmap_regions); + self->fmap_regions = g_strdup(value); + return TRUE; + } if (g_strcmp0(key, "PciBcrAddr") == 0) { guint64 tmp = 0; if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) @@ -64,6 +86,62 @@ return TRUE; } +typedef struct flashrom_layout _flashrom_layout; +G_DEFINE_AUTOPTR_CLEANUP_FUNC(_flashrom_layout, flashrom_layout_release) + +static gboolean +fu_flashrom_device_open_fmap(FuFlashromDevice *self, GError **error) +{ + g_auto(GStrv) fmap_regions = g_strsplit(self->fmap_regions, ",", 0); + g_autoptr(_flashrom_layout) layout = NULL; + + if (flashrom_layout_read_fmap_from_rom(&layout, self->flashctx, 0, 0)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to read layout from FMAP"); + return FALSE; + } + for (guint i = 0; fmap_regions[i] != NULL; i++) + flashrom_layout_include_region(layout, fmap_regions[i]); + + /* does not transfer ownership, so we must manage the lifetime of layout */ + self->layout = g_steal_pointer(&layout); + flashrom_layout_set(self->flashctx, self->layout); + + /* success */ + return TRUE; +} + +static gboolean +fu_flashrom_device_open_ifd(FuFlashromDevice *self, GError **error) +{ + g_autoptr(_flashrom_layout) layout = NULL; + + if (flashrom_layout_read_from_ifd(&layout, self->flashctx, NULL, 0)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "failed to read layout from Intel ICH descriptor"); + return FALSE; + } + if (flashrom_layout_include_region(layout, fu_ifd_region_to_string(self->ifd_region))) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "partition '%s' not found in IFD", + fu_ifd_region_to_string(self->ifd_region)); + return FALSE; + } + + /* does not transfer ownership, so we must manage the lifetime of layout */ + self->layout = g_steal_pointer(&layout); + flashrom_layout_set(self->flashctx, self->layout); + + /* success */ + return TRUE; +} + static gboolean fu_flashrom_device_open(FuDevice *device, GError **error) { @@ -88,31 +166,18 @@ fu_device_set_firmware_size_max(device, flash_size); } - /* update only one specific region of the flash and do not touch others */ + /* either use IFD or FMAP */ if (fu_cpu_get_vendor() == FU_CPU_VENDOR_INTEL) { - struct flashrom_layout *layout = NULL; - if (flashrom_layout_read_from_ifd(&layout, self->flashctx, NULL, 0)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "failed to read layout from Intel ICH descriptor"); - return FALSE; - } - if (flashrom_layout_include_region(layout, fu_ifd_region_to_string(self->region))) { - flashrom_layout_release(layout); - - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "invalid region name"); - return FALSE; + if (self->fmap_regions != NULL) { + if (!fu_flashrom_device_open_fmap(self, error)) + return FALSE; + } else { + if (!fu_flashrom_device_open_ifd(self, error)) + return FALSE; } - - /* does not transfer ownership, so we must manage the lifetime of layout */ - self->layout = layout; - flashrom_layout_set(self->flashctx, self->layout); } + /* success */ return TRUE; } @@ -184,13 +249,11 @@ { gboolean exists_orig = FALSE; g_autofree gchar *firmware_orig = NULL; - g_autofree gchar *localstatedir = NULL; g_autofree gchar *basename = NULL; /* if the original firmware doesn't exist, grab it now */ basename = g_strdup_printf("flashrom-%s.bin", fu_device_get_id(device)); - localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); - firmware_orig = g_build_filename(localstatedir, "builder", basename, NULL); + firmware_orig = fu_path_build(FU_PATH_KIND_LOCALSTATEDIR_PKG, "builder", basename, NULL); if (!fu_path_mkdir_parent(firmware_orig, error)) return FALSE; if (!fu_device_query_file_exists(device, firmware_orig, &exists_orig, error)) @@ -199,7 +262,7 @@ g_autoptr(GBytes) buf = NULL; buf = fu_flashrom_device_dump_firmware(device, progress, error); if (buf == NULL) { - g_prefix_error(error, "failed to back up original firmware: "); + g_prefix_error_literal(error, "failed to back up original firmware: "); return FALSE; } if (!fu_bytes_set_contents(firmware_orig, buf, error)) @@ -262,16 +325,16 @@ fu_progress_step_done(progress); if (flashrom_image_verify(self->flashctx, (void *)buf, sz)) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "image verify failed"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "image verify failed"); return FALSE; } fu_progress_step_done(progress); - /* Check if CMOS needs a reset */ + /* check if CMOS needs a reset */ if (fu_device_has_private_flag(device, FU_FLASHROM_DEVICE_FLAG_RESET_CMOS)) { g_debug("attempting CMOS reset"); if (!fu_flashrom_cmos_reset(error)) { - g_prefix_error(error, "failed CMOS reset: "); + g_prefix_error_literal(error, "failed CMOS reset: "); return FALSE; } } @@ -281,7 +344,7 @@ } static void -fu_flashrom_device_set_progress(FuDevice *self, FuProgress *progress) +fu_flashrom_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -307,7 +370,7 @@ fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE); fu_device_set_physical_id(FU_DEVICE(self), "flashrom"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); - fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); fu_device_register_private_flag(FU_DEVICE(self), FU_FLASHROM_DEVICE_FLAG_RESET_CMOS); fu_device_register_private_flag(FU_DEVICE(self), FU_FLASHROM_DEVICE_FLAG_FN_M_ME_UNLOCK); } @@ -321,7 +384,7 @@ g_value_set_pointer(value, self->flashctx); break; case PROP_REGION: - g_value_set_uint(value, self->region); + g_value_set_uint(value, self->ifd_region); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -341,8 +404,9 @@ self->flashctx = g_value_get_pointer(value); break; case PROP_REGION: - self->region = g_value_get_uint(value); - fu_device_set_logical_id(FU_DEVICE(self), fu_ifd_region_to_string(self->region)); + self->ifd_region = g_value_get_uint(value); + fu_device_set_logical_id(FU_DEVICE(self), + fu_ifd_region_to_string(self->ifd_region)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); @@ -356,6 +420,7 @@ FuFlashromDevice *self = FU_FLASHROM_DEVICE(object); if (self->layout != NULL) flashrom_layout_release(self->layout); + g_free(self->fmap_regions); G_OBJECT_CLASS(fu_flashrom_device_parent_class)->finalize(object); } @@ -397,6 +462,7 @@ g_object_class_install_property(object_class, PROP_FLASHCTX, pspec); object_class->finalize = fu_flashrom_device_finalize; + device_class->to_string = fu_flashrom_device_to_string; device_class->set_quirk_kv = fu_flashrom_device_set_quirk_kv; device_class->probe = fu_flashrom_device_probe; device_class->open = fu_flashrom_device_open; @@ -408,7 +474,7 @@ } FuDevice * -fu_flashrom_device_new(FuContext *ctx, struct flashrom_flashctx *flashctx, FuIfdRegion region) +fu_flashrom_device_new(FuContext *ctx, struct flashrom_flashctx *flashctx, FuIfdRegion ifd_region) { return FU_DEVICE(g_object_new(FU_TYPE_FLASHROM_DEVICE, "context", @@ -416,14 +482,14 @@ "flashctx", flashctx, "region", - region, + ifd_region, NULL)); } gboolean fu_flashrom_device_unlock(FuFlashromDevice *self, GError **error) { - if (self->region == FU_IFD_REGION_ME && + if (self->ifd_region == FU_IFD_REGION_ME && fu_device_has_private_flag(FU_DEVICE(self), FU_FLASHROM_DEVICE_FLAG_FN_M_ME_UNLOCK)) { g_set_error_literal(error, FWUPD_ERROR, diff -Nru fwupd-2.0.8/plugins/flashrom/fu-flashrom-plugin.c fwupd-2.0.20/plugins/flashrom/fu-flashrom-plugin.c --- fwupd-2.0.8/plugins/flashrom/fu-flashrom-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/flashrom/fu-flashrom-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -109,16 +109,11 @@ if (bios_tables == NULL) return FALSE; - /* ROM size if not already been quirked */ + /* get SMBIOS data */ bios_blob = g_ptr_array_index(bios_tables, 0); buf = g_bytes_get_data(bios_blob, &bufsz); - if (fu_device_get_firmware_size_max(device) == 0) { - guint8 bios_sz = 0x0; - if (fu_memread_uint8_safe(buf, bufsz, 0x9, &bios_sz, NULL)) { - guint64 firmware_size = (bios_sz + 1) * 64 * 1024; - fu_device_set_firmware_size_max(device, firmware_size); - } - } + if (bufsz == 0) + return FALSE; /* BIOS characteristics */ if (fu_memread_uint32_safe(buf, bufsz, 0xa, &bios_char, G_LITTLE_ENDIAN, NULL)) { @@ -128,6 +123,38 @@ "Not supported from SMBIOS"); } } + + /* ROM size if not already been quirked */ + if (fu_device_get_firmware_size_max(device) == 0) { + guint8 bios_sz = 0x0; + guint64 firmware_size = 0x0; + if (!fu_memread_uint8_safe(buf, bufsz, 0x9, &bios_sz, NULL)) + return FALSE; + + /* need to read extended ROM size */ + if (bios_sz == 0xff) { + guint16 bios_sz_ext = 0x0; + + /* Bits 15-14 scale, 13-0 size + * 00 scale -> MiB + * 01 scale -> GiB + * others reserved + */ + if (!fu_memread_uint16_safe(buf, + bufsz, + 0x18, + &bios_sz_ext, + G_LITTLE_ENDIAN, + error)) + return FALSE; + firmware_size = (bios_sz_ext & 0x3ff) * (1024 * 1024); + if (bios_sz_ext & 0xc000) + firmware_size *= 1024; + } else { + firmware_size = (bios_sz + 1) * 64 * 1024; + } + fu_device_set_firmware_size_max(device, firmware_size); + } return TRUE; } @@ -195,7 +222,7 @@ fu_flashrom_plugin_device_set_hwids(plugin, device); fu_flashrom_plugin_device_set_version(plugin, device); if (!fu_flashrom_plugin_device_set_bios_info(plugin, device, &error_local)) - g_warning("failed to set bios info: %s", error_local->message); + g_debug("failed to set bios info: %s", error_local->message); if (!fu_device_setup(device, error)) return NULL; @@ -352,7 +379,9 @@ fu_flashrom_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + fu_context_add_quirk_key(ctx, "FlashromFmapRegions"); (void)fu_plugin_alloc_data(plugin, sizeof(FuFlashromPlugin)); fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "linux_lockdown"); fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "coreboot"); /* obsoleted */ @@ -372,6 +401,7 @@ flashrom_programmer_shutdown(self->flashprog); g_free(self->guid); + /* nocheck:finalize */ /* G_OBJECT_CLASS(fu_flashrom_plugin_parent_class)->finalize() not required as modular */ } diff -Nru fwupd-2.0.8/plugins/flashrom/meson.build fwupd-2.0.20/plugins/flashrom/meson.build --- fwupd-2.0.8/plugins/flashrom/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/flashrom/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if allow_flashrom +allow_flashrom or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginFlashrom"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -24,4 +25,3 @@ libflashrom, ], ) -endif diff -Nru fwupd-2.0.8/plugins/focalfp/README.md fwupd-2.0.20/plugins/focalfp/README.md --- fwupd-2.0.8/plugins/focalfp/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/focalfp/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -48,10 +48,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.8.6`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Wayne Huang: @waynehuang2022 diff -Nru fwupd-2.0.8/plugins/focalfp/fu-focalfp-firmware.c fwupd-2.0.20/plugins/focalfp/fu-focalfp-firmware.c --- fwupd-2.0.8/plugins/focalfp/fu-focalfp-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/focalfp/fu-focalfp-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,7 @@ #include "fu-focalfp-firmware.h" struct _FuFocalfpFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; guint16 start_address; guint32 checksum; }; @@ -55,7 +55,7 @@ static gboolean fu_focalfp_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuFocalfpFirmware *self = FU_FOCALFP_FIRMWARE(firmware); diff -Nru fwupd-2.0.8/plugins/focalfp/fu-focalfp-hid-device.c fwupd-2.0.20/plugins/focalfp/fu-focalfp-hid-device.c --- fwupd-2.0.8/plugins/focalfp/fu-focalfp-hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/focalfp/fu-focalfp-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,6 +8,7 @@ #include "fu-focalfp-firmware.h" #include "fu-focalfp-hid-device.h" +#include "fu-focalfp-struct.h" struct _FuFocalfpHidDevice { FuHidrawDevice parent_instance; @@ -15,21 +16,6 @@ G_DEFINE_TYPE(FuFocalfpHidDevice, fu_focalfp_hid_device, FU_TYPE_HIDRAW_DEVICE) -#define CMD_ENTER_UPGRADE_MODE 0x40 -#define CMD_CHECK_CURRENT_STATE 0x41 -#define CMD_READY_FOR_UPGRADE 0x42 -#define CMD_SEND_DATA 0x43 -#define CMD_UPGRADE_CHECKSUM 0x44 -#define CMD_EXIT_UPGRADE_MODE 0x45 -#define CMD_USB_READ_UPGRADE_ID 0x46 -#define CMD_USB_ERASE_FLASH 0x47 -#define CMD_USB_BOOT_READ 0x48 -#define CMD_USB_BOOT_BOOTLOADERVERSION 0x49 -#define CMD_READ_REGISTER 0x50 -#define CMD_WRITE_REGISTER 0x51 -#define CMD_ACK 0xf0 -#define CMD_NACK 0xff - #define FIRST_PACKET 0x00 #define MID_PACKET 0x01 #define END_PACKET 0x02 @@ -164,7 +150,10 @@ return FALSE; /* check was correct response */ - if (!fu_focalfp_hid_device_check_cmd_crc(buf, sizeof(buf), CMD_READ_REGISTER, error)) + if (!fu_focalfp_hid_device_check_cmd_crc(buf, + sizeof(buf), + FU_FOCALFP_CMD_READ_REGISTER, + error)) return FALSE; /* success */ @@ -178,7 +167,7 @@ guint8 *val, /* out */ GError **error) { - guint8 buf[64] = {CMD_READ_REGISTER, reg_address}; + guint8 buf[64] = {FU_FOCALFP_CMD_READ_REGISTER, reg_address}; /* write */ if (!fu_focalfp_hid_device_io(self, buf, 2, NULL, 0, error)) @@ -197,23 +186,23 @@ static gboolean fu_focalfp_hid_device_enter_upgrade_mode(FuFocalfpHidDevice *self, GError **error) { - guint8 wbuf[64] = {CMD_ENTER_UPGRADE_MODE}; + guint8 wbuf[64] = {FU_FOCALFP_CMD_ENTER_UPGRADE_MODE}; guint8 rbuf[64] = {0x0}; if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 6, error)) { - g_prefix_error(error, "failed to CMD_ENTER_UPGRADE_MODE: "); + g_prefix_error_literal(error, "failed to FU_FOCALFP_CMD_ENTER_UPGRADE_MODE: "); return FALSE; } /* check was correct response */ - return fu_focalfp_hid_device_check_cmd_crc(rbuf, sizeof(rbuf), CMD_ACK, error); + return fu_focalfp_hid_device_check_cmd_crc(rbuf, sizeof(rbuf), FU_FOCALFP_CMD_ACK, error); } /* get bootloader current state */ static gboolean fu_focalfp_hid_device_check_current_state(FuFocalfpHidDevice *self, guint8 *val, GError **error) { - guint8 wbuf[64] = {CMD_CHECK_CURRENT_STATE}; + guint8 wbuf[64] = {FU_FOCALFP_CMD_CHECK_CURRENT_STATE}; guint8 rbuf[64] = {0x0}; if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 7, error)) @@ -222,7 +211,7 @@ /* check was correct response */ if (!fu_focalfp_hid_device_check_cmd_crc(rbuf, sizeof(rbuf), - CMD_CHECK_CURRENT_STATE, + FU_FOCALFP_CMD_CHECK_CURRENT_STATE, error)) return FALSE; @@ -237,7 +226,7 @@ GError **error) { FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); - guint8 wbuf[64] = {CMD_READY_FOR_UPGRADE}; + guint8 wbuf[64] = {FU_FOCALFP_CMD_READY_FOR_UPGRADE}; guint8 rbuf[64] = {0x0}; if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 7, error)) @@ -246,7 +235,7 @@ /* check was correct response */ return fu_focalfp_hid_device_check_cmd_crc(rbuf, sizeof(rbuf), - CMD_READY_FOR_UPGRADE, + FU_FOCALFP_CMD_READY_FOR_UPGRADE, error); } @@ -269,7 +258,7 @@ { FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); guint16 *us_ic_id = (guint16 *)user_data; - guint8 wbuf[64] = {CMD_USB_READ_UPGRADE_ID}; + guint8 wbuf[64] = {FU_FOCALFP_CMD_USB_READ_UPGRADE_ID}; guint8 rbuf[64] = {0x0}; if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 8, error)) @@ -278,7 +267,7 @@ /* check was correct response */ if (!fu_focalfp_hid_device_check_cmd_crc(rbuf, sizeof(rbuf), - CMD_USB_READ_UPGRADE_ID, + FU_FOCALFP_CMD_USB_READ_UPGRADE_ID, error)) return FALSE; @@ -303,14 +292,14 @@ static gboolean fu_focalfp_hid_device_erase_flash(FuFocalfpHidDevice *self, GError **error) { - guint8 wbuf[64] = {CMD_USB_ERASE_FLASH}; + guint8 wbuf[64] = {FU_FOCALFP_CMD_USB_ERASE_FLASH}; guint8 rbuf[64] = {0x0}; if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 6, error)) return FALSE; /* check was correct response */ - return fu_focalfp_hid_device_check_cmd_crc(rbuf, sizeof(rbuf), CMD_ACK, error); + return fu_focalfp_hid_device_check_cmd_crc(rbuf, sizeof(rbuf), FU_FOCALFP_CMD_ACK, error); } static gboolean @@ -323,7 +312,7 @@ return FALSE; /* check was correct response */ - return fu_focalfp_hid_device_check_cmd_crc(rbuf, sizeof(rbuf), CMD_ACK, error); + return fu_focalfp_hid_device_check_cmd_crc(rbuf, sizeof(rbuf), FU_FOCALFP_CMD_ACK, error); } /* send write data */ @@ -334,7 +323,7 @@ guint8 bufsz, GError **error) { - guint8 wbuf[64] = {CMD_SEND_DATA, packet_type}; + guint8 wbuf[64] = {FU_FOCALFP_CMD_SEND_DATA, packet_type}; /* sanity check */ if (bufsz > REPORT_SIZE - 8) { @@ -363,14 +352,17 @@ static gboolean fu_focalfp_hid_device_checksum_upgrade(FuFocalfpHidDevice *self, guint32 *val, GError **error) { - guint8 wbuf[64] = {CMD_UPGRADE_CHECKSUM}; + guint8 wbuf[64] = {FU_FOCALFP_CMD_UPGRADE_CHECKSUM}; guint8 rbuf[64] = {0x0}; if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 7 + 3, error)) return FALSE; /* check was correct response */ - if (!fu_focalfp_hid_device_check_cmd_crc(rbuf, sizeof(rbuf), CMD_UPGRADE_CHECKSUM, error)) + if (!fu_focalfp_hid_device_check_cmd_crc(rbuf, + sizeof(rbuf), + FU_FOCALFP_CMD_UPGRADE_CHECKSUM, + error)) return FALSE; /* success */ @@ -385,11 +377,11 @@ /* get current firmware version */ if (!fu_focalfp_hid_device_read_reg(self, 0xA6, buf, error)) { - g_prefix_error(error, "failed to read version1: "); + g_prefix_error_literal(error, "failed to read version1: "); return FALSE; } if (!fu_focalfp_hid_device_read_reg(self, 0xAD, buf + 1, error)) { - g_prefix_error(error, "failed to read version2: "); + g_prefix_error_literal(error, "failed to read version2: "); return FALSE; } fu_device_set_version_raw(device, fu_memread_uint16(buf, G_BIG_ENDIAN)); @@ -562,7 +554,7 @@ guint8 uc_mode = 0; if (!fu_focalfp_hid_device_enter_upgrade_mode(self, error)) { - g_prefix_error(error, "failed to enter upgrade mode: "); + g_prefix_error_literal(error, "failed to enter upgrade mode: "); return FALSE; } @@ -590,12 +582,12 @@ fu_focalfp_hid_device_detach(FuDevice *device, FuProgress *progress, GError **error) { FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); - guint8 wbuf[64] = {CMD_ENTER_UPGRADE_MODE}; + guint8 wbuf[64] = {FU_FOCALFP_CMD_ENTER_UPGRADE_MODE}; guint8 rbuf[64] = {0x0}; /* command to go from APP --> Bootloader -- but we do not check crc */ if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 6, error)) { - g_prefix_error(error, "failed to CMD_ENTER_UPGRADE_MODE: "); + g_prefix_error_literal(error, "failed to FU_FOCALFP_CMD_ENTER_UPGRADE_MODE: "); return FALSE; } fu_device_sleep(device, 200); @@ -619,14 +611,14 @@ fu_focalfp_hid_device_attach(FuDevice *device, FuProgress *progress, GError **error) { FuFocalfpHidDevice *self = FU_FOCALFP_HID_DEVICE(device); - guint8 wbuf[64] = {CMD_EXIT_UPGRADE_MODE}; + guint8 wbuf[64] = {FU_FOCALFP_CMD_EXIT_UPGRADE_MODE}; guint8 rbuf[64] = {0x0}; if (!fu_focalfp_hid_device_io(self, wbuf, 1, rbuf, 6, error)) return FALSE; /* check was correct response */ - if (!fu_focalfp_hid_device_check_cmd_crc(rbuf, sizeof(rbuf), CMD_ACK, error)) + if (!fu_focalfp_hid_device_check_cmd_crc(rbuf, sizeof(rbuf), FU_FOCALFP_CMD_ACK, error)) return FALSE; /* success */ @@ -635,7 +627,7 @@ } static void -fu_focalfp_hid_device_set_progress(FuDevice *self, FuProgress *progress) +fu_focalfp_hid_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -661,7 +653,7 @@ fu_device_set_firmware_size(FU_DEVICE(self), 0x1E000); fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_FOCALFP_FIRMWARE); fu_device_set_summary(FU_DEVICE(self), "Forcepad"); - fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_INPUT_TOUCHPAD); fu_device_add_protocol(FU_DEVICE(self), "tw.com.focalfp"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); diff -Nru fwupd-2.0.8/plugins/focalfp/fu-focalfp.rs fwupd-2.0.20/plugins/focalfp/fu-focalfp.rs --- fwupd-2.0.8/plugins/focalfp/fu-focalfp.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/focalfp/fu-focalfp.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +enum FuFocalfpCmd { + EnterUpgradeMode = 0x40, + CheckCurrentState = 0x41, + ReadyForUpgrade = 0x42, + SendData = 0x43, + UpgradeChecksum = 0x44, + ExitUpgradeMode = 0x45, + UsbReadUpgradeId = 0x46, + UsbEraseFlash = 0x47, + UsbBootRead = 0x48, + UsbBootBootloaderversion = 0x49, + ReadRegister = 0x50, + WriteRegister = 0x51, + Ack = 0xF0, + Nack = 0xFF, +} diff -Nru fwupd-2.0.8/plugins/focalfp/meson.build fwupd-2.0.20/plugins/focalfp/meson.build --- fwupd-2.0.8/plugins/focalfp/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/focalfp/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,9 +1,11 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginFocalfp"'] plugins += {meson.current_source_dir().split('/')[-1]: true} plugin_quirks += files('focalfp.quirk') plugin_builtins += static_library('fu_plugin_focalfp', + rustgen.process('fu-focalfp.rs'), sources: [ 'fu-focalfp-plugin.c', 'fu-focalfp-firmware.c', @@ -16,4 +18,3 @@ link_with: plugin_libs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/fpc/README.md fwupd-2.0.20/plugins/fpc/README.md --- fwupd-2.0.8/plugins/fpc/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fpc/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -57,10 +57,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.8.6`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Jim Zhang: @jimzhang2 diff -Nru fwupd-2.0.8/plugins/fpc/fu-fpc-device.c fwupd-2.0.20/plugins/fpc/fu-fpc-device.c --- fwupd-2.0.8/plugins/fpc/fu-fpc-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fpc/fu-fpc-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -54,7 +54,7 @@ fu_fpc_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { return fu_firmware_new_from_gtypes(stream, @@ -92,7 +92,7 @@ FPC_USB_TRANSFER_TIMEOUT, NULL, error)) { - fu_error_convert(error); + fwupd_error_convert(error); return FALSE; } if (actual_len != length) { @@ -131,7 +131,7 @@ FPC_USB_TRANSFER_TIMEOUT, NULL, error)) { - fu_error_convert(error); + fwupd_error_convert(error); return FALSE; } if (actual_len != length) { @@ -209,7 +209,8 @@ FALSE, FALSE, error)) { - g_prefix_error(error, "fail to clear status in setup version"); + g_prefix_error_literal(error, + "fail to clear status in setup version: "); return FALSE; } } @@ -223,7 +224,7 @@ TRUE, TRUE, error)) { - g_prefix_error(error, "fail to get fw status in setup version"); + g_prefix_error_literal(error, "fail to get fw status in setup version: "); return FALSE; } @@ -245,33 +246,33 @@ fu_fpc_device_check_dfu_status_cb(FuDevice *device, gpointer user_data, GError **error) { FuFpcDevice *self = FU_FPC_DEVICE(device); - g_autoptr(GByteArray) dfu_status = fu_struct_fpc_dfu_new(); + g_autoptr(FuStructFpcDfu) st = fu_struct_fpc_dfu_new(); if (!fu_fpc_device_dfu_cmd(self, FPC_CMD_DFU_GETSTATUS, 0x0000, - dfu_status->data, - dfu_status->len, + st->buf->data, + st->buf->len, TRUE, FALSE, error)) { - g_prefix_error(error, "failed to get status: "); + g_prefix_error_literal(error, "failed to get status: "); return FALSE; } - if (fu_struct_fpc_dfu_get_status(dfu_status) != 0 || - fu_struct_fpc_dfu_get_state(dfu_status) == FU_FPC_DFU_STATE_DNBUSY) { + if (fu_struct_fpc_dfu_get_status(st) != 0 || + fu_struct_fpc_dfu_get_state(st) == FU_FPC_DFU_STATE_DNBUSY) { /* device is not in correct status/state */ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "dfu status error [0x%x, 0x%x]", - fu_struct_fpc_dfu_get_status(dfu_status), - fu_struct_fpc_dfu_get_state(dfu_status)); + fu_struct_fpc_dfu_get_status(st), + fu_struct_fpc_dfu_get_state(st)); return FALSE; } - if (fu_struct_fpc_dfu_get_max_payload_size(dfu_status) > 0 || + if (fu_struct_fpc_dfu_get_max_payload_size(st) > 0 || fu_device_has_private_flag(FU_DEVICE(self), FU_FPC_DEVICE_FLAG_RTS_DEVICE)) self->max_block_size = FPC_FLASH_BLOCK_SIZE_4096; else @@ -292,7 +293,7 @@ FALSE, FALSE, error)) { - g_prefix_error(error, "failed to clear status: "); + g_prefix_error_literal(error, "failed to clear status: "); return FALSE; } } @@ -381,13 +382,13 @@ } if (!fu_fpc_device_setup_mode(self, error)) { - g_prefix_error(error, "failed to get device mode: "); + g_prefix_error_literal(error, "failed to get device mode: "); return FALSE; } /* ensure version */ if (!fu_fpc_device_setup_version(self, error)) { - g_prefix_error(error, "failed to get firmware version: "); + g_prefix_error_literal(error, "failed to get firmware version: "); return FALSE; } @@ -462,14 +463,14 @@ if (st_blkhdr == NULL) return FALSE; direction = fu_struct_fpc_ff2_block_hdr_get_dir(st_blkhdr); - offset += st_blkhdr->len; + offset += st_blkhdr->buf->len; /* validate dfu_sec_link_t and include the size in payload */ st_blksec = fu_struct_fpc_ff2_block_sec_parse_stream(stream, offset, error); if (st_blksec == NULL) return FALSE; payload_len = fu_struct_fpc_ff2_block_sec_get_payload_len(st_blksec); - payload_len += st_blksec->len; + payload_len += st_blksec->buf->len; if (direction == FU_FPC_FF2_BLOCK_DIR_OUT) { g_autoptr(GInputStream) partial_stream = NULL; @@ -491,7 +492,7 @@ FALSE, FALSE, error)) { - g_prefix_error(error, "failed to write sec-link: "); + g_prefix_error_literal(error, "failed to write sec-link: "); return FALSE; } @@ -639,7 +640,7 @@ FALSE, FALSE, error)) { - g_prefix_error(error, "fail to exit dnload loop: "); + g_prefix_error_literal(error, "fail to exit dnload loop: "); return FALSE; } } @@ -660,7 +661,7 @@ } static void -fu_fpc_device_set_progress(FuDevice *self, FuProgress *progress) +fu_fpc_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/fpc/fu-fpc-ff2-firmware.c fwupd-2.0.20/plugins/fpc/fu-fpc-ff2-firmware.c --- fwupd-2.0.8/plugins/fpc/fu-fpc-ff2-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fpc/fu-fpc-ff2-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -37,7 +37,7 @@ static gboolean fu_fpc_ff2_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuFpcFf2Firmware *self = FU_FPC_FF2_FIRMWARE(firmware); @@ -55,7 +55,7 @@ guint32 fu_fpc_ff2_firmware_get_blocks_num(FuFpcFf2Firmware *self) { - g_return_val_if_fail(FU_IS_FPC_FF2_FIRMWARE(self), G_MAXUINT16); + g_return_val_if_fail(FU_IS_FPC_FF2_FIRMWARE(self), G_MAXUINT32); return self->blocks_num; } diff -Nru fwupd-2.0.8/plugins/fpc/fu-fpc-plugin.c fwupd-2.0.20/plugins/fpc/fu-fpc-plugin.c --- fwupd-2.0.8/plugins/fpc/fu-fpc-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fpc/fu-fpc-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,6 +25,7 @@ fu_fpc_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_FPC_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_FPC_FF2_FIRMWARE); } diff -Nru fwupd-2.0.8/plugins/fpc/tests/fpc-lenfy-moh.json fwupd-2.0.20/plugins/fpc/tests/fpc-lenfy-moh.json --- fwupd-2.0.8/plugins/fpc/tests/fpc-lenfy-moh.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fpc/tests/fpc-lenfy-moh.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/0f69958d888545c4f99e09544ed81c982470f58723ad99c8b0b45997211371be-fpc-lenfy-moh-v27_26_23_19.cab", - "emulation-url": "https://fwupd.org/downloads/8ef3c5f6bf4d294bec4738eb6077652171939f8c5f7eee1e13fd9ca1e5c4ba9e-fpc-lenfy-moh-v27_26_23_19.zip", + "url": "0f69958d888545c4f99e09544ed81c982470f58723ad99c8b0b45997211371be-fpc-lenfy-moh-v27_26_23_19.cab", + "emulation-url": "8ef3c5f6bf4d294bec4738eb6077652171939f8c5f7eee1e13fd9ca1e5c4ba9e-fpc-lenfy-moh-v27_26_23_19.zip", "components": [ { "version": "27.26.23.19", diff -Nru fwupd-2.0.8/plugins/framework-qmk/README.md fwupd-2.0.20/plugins/framework-qmk/README.md --- fwupd-2.0.8/plugins/framework-qmk/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/framework-qmk/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,23 @@ +--- +title: Plugin: Framework QMK +--- + +## Introduction + +This plugin supports the [keyboards of the Framework 16 series](https://frame.work/tw/en/products/keyboard-module). + +Devices are updated by triggering a reset via HID, and then entering the Raspberry Pi Pico bootrom. + +The bootrom speaks the UF2 protocol (`com.microsoft.uf2`). + +## External Interface Access + +This plugin requires read/write access to `/dev/hidraw*`. + +## Version Considerations + +This plugin has been available since fwupd version `2.0.14`. + +## Vendor ID Security + +The vendor ID is set from the HID vendor ID. diff -Nru fwupd-2.0.8/plugins/framework-qmk/framework-qmk.quirk fwupd-2.0.20/plugins/framework-qmk/framework-qmk.quirk --- fwupd-2.0.8/plugins/framework-qmk/framework-qmk.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/framework-qmk/framework-qmk.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,29 @@ +# Laptop 16 Keyboard Module - ANSI +[HIDRAW\VEN_32AC&DEV_0012] +Plugin = framework_qmk +CounterpartGuid = BLOCK\VEN_2E8A&DEV_0003 + +# Laptop 16 RGB Macropad +[HIDRAW\VEN_32AC&DEV_0013] +Plugin = framework_qmk +CounterpartGuid = BLOCK\VEN_2E8A&DEV_0003 + +# Laptop 16 Numpad +[HIDRAW\VEN_32AC&DEV_0014] +Plugin = framework_qmk +CounterpartGuid = BLOCK\VEN_2E8A&DEV_0003 + +# Laptop 16 Keyboard Module - ISO +[HIDRAW\VEN_32AC&DEV_0018] +Plugin = framework_qmk +CounterpartGuid = BLOCK\VEN_2E8A&DEV_0003 + +# Laptop 16 Keyboard Module - JIS +[HIDRAW\VEN_32AC&DEV_0019] +Plugin = framework_qmk +CounterpartGuid = BLOCK\VEN_2E8A&DEV_0003 + +# Laptop 16 Keyboard Module - ANSI Copilot +[HIDRAW\VEN_32AC&DEV_0030] +Plugin = framework_qmk +CounterpartGuid = BLOCK\VEN_2E8A&DEV_0003 diff -Nru fwupd-2.0.8/plugins/framework-qmk/fu-framework-qmk-device.c fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk-device.c --- fwupd-2.0.8/plugins/framework-qmk/fu-framework-qmk-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,130 @@ +/* + * Copyright 2025 Framework Computer Inc + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-framework-qmk-device.h" +#include "fu-framework-qmk-struct.h" + +struct _FuFrameworkQmkDevice { + FuHidrawDevice parent_instance; + guint8 iface_reset; +}; + +G_DEFINE_TYPE(FuFrameworkQmkDevice, fu_framework_qmk_device, FU_TYPE_HIDRAW_DEVICE) + +#define FU_FRAMEWORK_QMK_RAW_USAGE_PAGE 0xFF60 +#define FU_FRAMEWORK_QMK_RAW_USAGE_ID 0x61 + +static gboolean +fu_framework_qmk_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuFrameworkQmkDevice *self = FU_FRAMEWORK_QMK_DEVICE(device); + g_autoptr(FuStructFrameworkQmkResetRequest) st_req = + fu_struct_framework_qmk_reset_request_new(); + + if (!fu_hidraw_device_set_report(FU_HIDRAW_DEVICE(self), + st_req->buf->data, + st_req->buf->len, + FU_IO_CHANNEL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to write packet: "); + return FALSE; + } + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static void +fu_framework_qmk_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 70, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 29, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_framework_qmk_device_init(FuFrameworkQmkDevice *self) +{ + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_INPUT_KEYBOARD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_set_remove_delay(FU_DEVICE(self), 15000); /* 15s */ + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_BCD); + fu_device_add_protocol(FU_DEVICE(self), "com.microsoft.uf2"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_ADD_COUNTERPART_GUIDS); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_RETRY_OPEN); + /* revisions indicate incompatible hardware */ + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); + fu_device_retry_set_delay(FU_DEVICE(self), 100); +} + +static gboolean +fu_framework_qmk_device_setup(FuDevice *device, GError **error) +{ + g_autoptr(FuHidDescriptor) descriptor = NULL; + g_autoptr(FuHidReport) report = NULL; + g_autoptr(FuDevice) device_usb = NULL; + guint16 version = 0x000; + + descriptor = fu_hidraw_device_parse_descriptor(FU_HIDRAW_DEVICE(device), error); + if (descriptor == NULL) { + g_prefix_error_literal(error, "failed to parse descriptor: "); + return FALSE; + } + report = fu_hid_descriptor_find_report(descriptor, + error, + "usage-page", + FU_FRAMEWORK_QMK_RAW_USAGE_PAGE, + "usage", + FU_FRAMEWORK_QMK_RAW_USAGE_ID, + "collection", + 0x01, + NULL); + if (report == NULL) { + /* Wrong usage page, this hidraw instance is not what we're looking for */ + return FALSE; + } + + device_usb = fu_device_get_backend_parent_with_subsystem(device, "usb:usb_device", error); + if (device_usb == NULL) { + g_prefix_error_literal(error, "no USB device: "); + return FALSE; + } + if (!fu_device_probe(device_usb, error)) { + g_prefix_error_literal(error, "USB probe failed: "); + return FALSE; + } + version = fu_usb_device_get_release(FU_USB_DEVICE(device_usb)); + + fu_device_set_version_raw(device, version); + + return TRUE; +} + +static gchar * +fu_framework_qmk_device_convert_version(FuDevice *device, guint64 version_raw) +{ + return fu_version_from_uint16(version_raw, fu_device_get_version_format(device)); +} + +static void +fu_framework_qmk_device_class_init(FuFrameworkQmkDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->setup = fu_framework_qmk_device_setup; + device_class->detach = fu_framework_qmk_device_detach; + device_class->convert_version = fu_framework_qmk_device_convert_version; + device_class->set_progress = fu_framework_qmk_device_set_progress; +} diff -Nru fwupd-2.0.8/plugins/framework-qmk/fu-framework-qmk-device.h fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk-device.h --- fwupd-2.0.8/plugins/framework-qmk/fu-framework-qmk-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Framework Computer Inc + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_FRAMEWORK_QMK_DEVICE (fu_framework_qmk_device_get_type()) +G_DECLARE_FINAL_TYPE(FuFrameworkQmkDevice, + fu_framework_qmk_device, + FU, + FRAMEWORK_QMK_DEVICE, + FuHidrawDevice) diff -Nru fwupd-2.0.8/plugins/framework-qmk/fu-framework-qmk-plugin.c fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk-plugin.c --- fwupd-2.0.8/plugins/framework-qmk/fu-framework-qmk-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright 2025 Framework Computer Inc + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-framework-qmk-device.h" +#include "fu-framework-qmk-plugin.h" + +struct _FuFrameworkQmkPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuFrameworkQmkPlugin, fu_framework_qmk_plugin, FU_TYPE_PLUGIN) + +static void +fu_framework_qmk_plugin_init(FuFrameworkQmkPlugin *self) +{ +} + +static void +fu_framework_qmk_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_device_gtype(plugin, FU_TYPE_FRAMEWORK_QMK_DEVICE); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); +} + +static void +fu_framework_qmk_plugin_class_init(FuFrameworkQmkPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->constructed = fu_framework_qmk_plugin_constructed; +} diff -Nru fwupd-2.0.8/plugins/framework-qmk/fu-framework-qmk-plugin.h fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk-plugin.h --- fwupd-2.0.8/plugins/framework-qmk/fu-framework-qmk-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,15 @@ +/* + * Copyright 2025 Framework Computer Inc + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuFrameworkQmkPlugin, + fu_framework_qmk_plugin, + FU, + FRAMEWORK_QMK_PLUGIN, + FuPlugin) diff -Nru fwupd-2.0.8/plugins/framework-qmk/fu-framework-qmk.rs fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk.rs --- fwupd-2.0.8/plugins/framework-qmk/fu-framework-qmk.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/framework-qmk/fu-framework-qmk.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,15 @@ +// Copyright 2025 Framework Computer Inc +// SPDX-License-Identifier: LGPL-2.1-or-later + +enum FuFrameworkQmkResetRequest { + BootloaderJump = 0x0B, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructFrameworkQmkResetRequest { + report_id: u8 == 0x00, + cmd: u8 == 0x0B, + reserved: [u8; 30] = [0xFE; 30], +} + diff -Nru fwupd-2.0.8/plugins/framework-qmk/meson.build fwupd-2.0.20/plugins/framework-qmk/meson.build --- fwupd-2.0.8/plugins/framework-qmk/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/framework-qmk/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,20 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginFrameworkQmk"'] + +plugins += {meson.current_source_dir().split('/')[-1]: true} +plugin_quirks += files('framework-qmk.quirk') +plugin_builtins += static_library('fu_plugin_framework_qmk', + rustgen.process('fu-framework-qmk.rs'), + sources: [ + 'fu-framework-qmk-device.c', + 'fu-framework-qmk-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +enumeration_data += files( + 'tests/framework16-macropad-setup.json', + 'tests/framework16-ansi-setup.json', +) +device_tests += files('tests/framework-qmk.json') diff -Nru fwupd-2.0.8/plugins/framework-qmk/tests/framework-qmk.json fwupd-2.0.20/plugins/framework-qmk/tests/framework-qmk.json --- fwupd-2.0.8/plugins/framework-qmk/tests/framework-qmk.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/framework-qmk/tests/framework-qmk.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,26 @@ +{ + "name": "Framework QMK", + "interactive": false, + "steps": [ + { + "emulation-file": "@enumeration_datadir@/framework16-macropad-setup.json", + "components": [ + { + "guids": [ + "aa1f48fb-ae6f-5f3f-96eb-1ce0002cdae3" + ] + } + ] + }, + { + "emulation-file": "@enumeration_datadir@/framework16-ansi-setup.json", + "components": [ + { + "guids": [ + "bf9a4319-494f-5c88-9637-72f97222cac9" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/framework-qmk/tests/framework16-ansi-setup.json fwupd-2.0.20/plugins/framework-qmk/tests/framework16-ansi-setup.json --- fwupd-2.0.8/plugins/framework-qmk/tests/framework16-ansi-setup.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/framework-qmk/tests/framework16-ansi-setup.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,267 @@ +{ + "FwupdVersion": "2.0.14", + "UsbDevices": [ + { + "Created": "2025-08-07T12:06:56.393594Z", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c2:00.3/usb1/1-4/1-4.2/1-4.2:1.1/0003:32AC:0012.0020/hidraw/hidraw5", + "DeviceFile": "/dev/hidraw5", + "Subsystem": "hidraw", + "Vendor": 12972, + "Model": 18, + "Events": [ + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "hidraw" + }, + { + "Id": "GetSymlinkTarget:Attr=driver" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=241\nMINOR=5\nDEVNAME=hidraw5" + }, + { + "Id": "ReadProp:Key=DEVNAME", + "Data": "hidraw5" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=hid", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c2:00.3/usb1/1-4/1-4.2/1-4.2:1.1/0003:32AC:0012.0020" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "hid" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "hid-generic" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=hid-generic\nHID_ID=0003:000032AC:00000012\nHID_NAME=Framework Laptop 16 Keyboard Module - ANSI\nHID_PHYS=usb-0000:c2:00.3-4.2/input1\nHID_UNIQ=FRAKDKEN0100000000\nMODALIAS=hid:b0003g0001v000032ACp00000012" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=HID_ID", + "Data": "0003:000032AC:00000012" + }, + { + "Id": "ReadProp:Key=HID_NAME", + "Data": "Framework Laptop 16 Keyboard Module - ANSI" + }, + { + "Id": "ReadProp:Key=HID_UNIQ", + "Data": "FRAKDKEN0100000000" + }, + { + "Id": "ReadProp:Key=HID_PHYS", + "Data": "usb-0000:c2:00.3-4.2/input1" + }, + { + "Id": "GetBackendParent:Subsystem=usb:usb_device", + "GType": "FuUsbDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c2:00.3/usb1/1-4/1-4.2", + "PhysicalId": "1-4.2" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "usb" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "usb" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=16\nDEVNAME=bus/usb/001/017\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=32ac/12/31\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=017" + }, + { + "Id": "ReadProp:Key=DEVNAME", + "Data": "bus/usb/001/017" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadProp:Key=BUSNUM", + "Data": "001" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=16\nDEVNAME=bus/usb/001/017\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=32ac/12/31\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=017" + }, + { + "Id": "ReadProp:Key=DEVNUM", + "Data": "017" + }, + { + "Id": "LoadDescriptor:basename=descriptors", + "Data": "EgEQAgAAAECsMhIAMQABAgMBCQJ7AAQBAKD6CQQAAAEDAQEACSERAQABIkAABwWBAwgAAQkEAQACAwAAAAkhEQEAASIiAAcFggMgAAEHBQMDIAABCQQCAAEDAAAACSERAQABIoYABwWEAyAAAQkEAwACAwAAAAkhEQEAASIiAAcFhQMgAAEHBQYDIAAB" + }, + { + "Id": "LoadDescriptor:basename=bos_descriptors" + }, + { + "Id": "ReadProp:Key=HID_FIRMWARE_VERSION" + }, + { + "Id": "GetBackendParent:Subsystem=hid", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c2:00.3/usb1/1-4/1-4.2/1-4.2:1.1/0003:32AC:0012.0020" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "hid" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "hid-generic" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=hid-generic\nHID_ID=0003:000032AC:00000012\nHID_NAME=Framework Laptop 16 Keyboard Module - ANSI\nHID_PHYS=usb-0000:c2:00.3-4.2/input1\nHID_UNIQ=FRAKDKEN0100000000\nMODALIAS=hid:b0003g0001v000032ACp00000012" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=HID_ID", + "Data": "0003:000032AC:00000012" + }, + { + "Id": "ReadProp:Key=HID_FIRMWARE_VERSION" + }, + { + "Id": "Ioctl:Request=0x80044801,Data=AAAAAA==,Length=0x4", + "DataOut": "IgAAAA==" + }, + { + "Id": "Ioctl:Request=0x90044802,Data=IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0x1004", + "DataOut": "IgAAAAZg/wlhoQEJYhUAJv8AlSB1CIECCWMVACb/AJUgdQiRAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + }, + { + "Id": "GetBackendParent:Subsystem=usb:usb_device", + "GType": "FuUsbDevice", + "DeviceId": null, + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c2:00.3/usb1/1-4/1-4.2", + "PhysicalId": "1-4.2" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "usb" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "usb" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=16\nDEVNAME=bus/usb/001/017\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=32ac/12/31\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=017" + }, + { + "Id": "ReadProp:Key=DEVNAME", + "Data": "bus/usb/001/017" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadProp:Key=BUSNUM", + "Data": "001" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=16\nDEVNAME=bus/usb/001/017\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=32ac/12/31\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=017" + }, + { + "Id": "ReadProp:Key=DEVNUM", + "Data": "017" + }, + { + "Id": "LoadDescriptor:basename=descriptors", + "Data": "EgEQAgAAAECsMhIAMQABAgMBCQJ7AAQBAKD6CQQAAAEDAQEACSERAQABIkAABwWBAwgAAQkEAQACAwAAAAkhEQEAASIiAAcFggMgAAEHBQMDIAABCQQCAAEDAAAACSERAQABIoYABwWEAyAAAQkEAwACAwAAAAkhEQEAASIiAAcFhQMgAAEHBQYDIAAB" + }, + { + "Id": "LoadDescriptor:basename=bos_descriptors" + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/framework-qmk/tests/framework16-macropad-setup.json fwupd-2.0.20/plugins/framework-qmk/tests/framework16-macropad-setup.json --- fwupd-2.0.8/plugins/framework-qmk/tests/framework16-macropad-setup.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/framework-qmk/tests/framework16-macropad-setup.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,267 @@ +{ + "FwupdVersion": "2.0.14", + "UsbDevices": [ + { + "Created": "2025-08-07T10:34:07.978512Z", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c2:00.3/usb1/1-3/1-3.2/1-3.2:1.1/0003:32AC:0013.005E/hidraw/hidraw3", + "DeviceFile": "/dev/hidraw3", + "Subsystem": "hidraw", + "Vendor": 12972, + "Model": 19, + "Events": [ + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "hidraw" + }, + { + "Id": "GetSymlinkTarget:Attr=driver" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=241\nMINOR=3\nDEVNAME=hidraw3" + }, + { + "Id": "ReadProp:Key=DEVNAME", + "Data": "hidraw3" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=hid", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c2:00.3/usb1/1-3/1-3.2/1-3.2:1.1/0003:32AC:0013.005E" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "hid" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "hid-generic" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=hid-generic\nHID_ID=0003:000032AC:00000013\nHID_NAME=Framework Laptop 16 RGB Macropad\nHID_PHYS=usb-0000:c2:00.3-3.2/input1\nHID_UNIQ=FRAKDKEN0100000000\nMODALIAS=hid:b0003g0001v000032ACp00000013" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=HID_ID", + "Data": "0003:000032AC:00000013" + }, + { + "Id": "ReadProp:Key=HID_NAME", + "Data": "Framework Laptop 16 RGB Macropad" + }, + { + "Id": "ReadProp:Key=HID_UNIQ", + "Data": "FRAKDKEN0100000000" + }, + { + "Id": "ReadProp:Key=HID_PHYS", + "Data": "usb-0000:c2:00.3-3.2/input1" + }, + { + "Id": "GetBackendParent:Subsystem=usb:usb_device", + "GType": "FuUsbDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c2:00.3/usb1/1-3/1-3.2", + "PhysicalId": "1-3.2" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "usb" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "usb" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=47\nDEVNAME=bus/usb/001/048\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=32ac/13/30\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=048" + }, + { + "Id": "ReadProp:Key=DEVNAME", + "Data": "bus/usb/001/048" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadProp:Key=BUSNUM", + "Data": "001" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=47\nDEVNAME=bus/usb/001/048\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=32ac/13/30\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=048" + }, + { + "Id": "ReadProp:Key=DEVNUM", + "Data": "048" + }, + { + "Id": "LoadDescriptor:basename=descriptors", + "Data": "EgEQAgAAAECsMhMAMAABAgMBCQJ7AAQBAKD6CQQAAAEDAQEACSERAQABIkAABwWBAwgAAQkEAQACAwAAAAkhEQEAASIiAAcFggMgAAEHBQMDIAABCQQCAAEDAAAACSERAQABIoYABwWEAyAAAQkEAwACAwAAAAkhEQEAASIiAAcFhQMgAAEHBQYDIAAB" + }, + { + "Id": "LoadDescriptor:basename=bos_descriptors" + }, + { + "Id": "ReadProp:Key=HID_FIRMWARE_VERSION" + }, + { + "Id": "GetBackendParent:Subsystem=hid", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c2:00.3/usb1/1-3/1-3.2/1-3.2:1.1/0003:32AC:0013.005E" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "hid" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "hid-generic" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=hid-generic\nHID_ID=0003:000032AC:00000013\nHID_NAME=Framework Laptop 16 RGB Macropad\nHID_PHYS=usb-0000:c2:00.3-3.2/input1\nHID_UNIQ=FRAKDKEN0100000000\nMODALIAS=hid:b0003g0001v000032ACp00000013" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=HID_ID", + "Data": "0003:000032AC:00000013" + }, + { + "Id": "ReadProp:Key=HID_FIRMWARE_VERSION" + }, + { + "Id": "Ioctl:Request=0x80044801,Data=AAAAAA==,Length=0x4", + "DataOut": "IgAAAA==" + }, + { + "Id": "Ioctl:Request=0x90044802,Data=IgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0x1004", + "DataOut": "IgAAAAZg/wlhoQEJYhUAJv8AlSB1CIECCWMVACb/AJUgdQiRAsAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + }, + { + "Id": "GetBackendParent:Subsystem=usb:usb_device", + "GType": "FuUsbDevice", + "DeviceId": null, + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c2:00.3/usb1/1-3/1-3.2", + "PhysicalId": "1-3.2" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "usb" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "usb" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=47\nDEVNAME=bus/usb/001/048\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=32ac/13/30\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=048" + }, + { + "Id": "ReadProp:Key=DEVNAME", + "Data": "bus/usb/001/048" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadProp:Key=BUSNUM", + "Data": "001" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=47\nDEVNAME=bus/usb/001/048\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=32ac/13/30\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=048" + }, + { + "Id": "ReadProp:Key=DEVNUM", + "Data": "048" + }, + { + "Id": "LoadDescriptor:basename=descriptors", + "Data": "EgEQAgAAAECsMhMAMAABAgMBCQJ7AAQBAKD6CQQAAAEDAQEACSERAQABIkAABwWBAwgAAQkEAQACAwAAAAkhEQEAASIiAAcFggMgAAEHBQMDIAABCQQCAAEDAAAACSERAQABIoYABwWEAyAAAQkEAwACAwAAAAkhEQEAASIiAAcFhQMgAAEHBQYDIAAB" + }, + { + "Id": "LoadDescriptor:basename=bos_descriptors" + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/fresco-pd/fu-fresco-pd-device.c fwupd-2.0.20/plugins/fresco-pd/fu-fresco-pd-device.c --- fwupd-2.0.8/plugins/fresco-pd/fu-fresco-pd-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fresco-pd/fu-fresco-pd-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -53,7 +53,7 @@ NULL, error)) { g_prefix_error(error, "failed to read from offset 0x%x: ", offset); - fu_error_convert(error); + fwupd_error_convert(error); return FALSE; } if (bufsz != actual_length) { @@ -190,7 +190,7 @@ fu_fresco_pd_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuFrescoPdDevice *self = FU_FRESCO_PD_DEVICE(device); @@ -280,15 +280,15 @@ * 0x6C04 = 0x08 */ g_debug("disable MCU, and enable mtp write"); if (!fu_fresco_pd_device_and_byte(self, 0xa001, ~(1 << 2), error)) { - g_prefix_error(error, "failed to disable MCU bit 2: "); + g_prefix_error_literal(error, "failed to disable MCU bit 2: "); return FALSE; } if (!fu_fresco_pd_device_and_byte(self, 0x6c00, ~(1 << 1), error)) { - g_prefix_error(error, "failed to disable MCU bit 1: "); + g_prefix_error_literal(error, "failed to disable MCU bit 1: "); return FALSE; } if (!fu_fresco_pd_device_write_byte(self, 0x6c04, 0x08, error)) { - g_prefix_error(error, "failed to disable MCU: "); + g_prefix_error_literal(error, "failed to disable MCU: "); return FALSE; } @@ -402,7 +402,7 @@ } static void -fu_fresco_pd_device_set_progress(FuDevice *self, FuProgress *progress) +fu_fresco_pd_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -416,7 +416,7 @@ static void fu_fresco_pd_device_init(FuFrescoPdDevice *self) { - fu_device_add_icon(FU_DEVICE(self), "usb-hub"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_USB_HUB); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_protocol(FU_DEVICE(self), "com.frescologic.pd"); diff -Nru fwupd-2.0.8/plugins/fresco-pd/fu-fresco-pd-firmware.c fwupd-2.0.20/plugins/fresco-pd/fu-fresco-pd-firmware.c --- fwupd-2.0.8/plugins/fresco-pd/fu-fresco-pd-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fresco-pd/fu-fresco-pd-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,7 @@ #include "fu-fresco-pd-firmware.h" struct _FuFrescoPdFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; guint8 customer_id; }; @@ -33,7 +33,7 @@ static gboolean fu_fresco_pd_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuFrescoPdFirmware *self = FU_FRESCO_PD_FIRMWARE(firmware); diff -Nru fwupd-2.0.8/plugins/fresco-pd/fu-fresco-pd-plugin.c fwupd-2.0.20/plugins/fresco-pd/fu-fresco-pd-plugin.c --- fwupd-2.0.8/plugins/fresco-pd/fu-fresco-pd-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fresco-pd/fu-fresco-pd-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,6 +25,7 @@ fu_fresco_pd_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_FRESCO_PD_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_FRESCO_PD_FIRMWARE); } diff -Nru fwupd-2.0.8/plugins/fresco-pd/tests/ugreen-cm260.json fwupd-2.0.20/plugins/fresco-pd/tests/ugreen-cm260.json --- fwupd-2.0.8/plugins/fresco-pd/tests/ugreen-cm260.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/fresco-pd/tests/ugreen-cm260.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/de152591660080ed5de8550b1b2dfcd738d1f5a68bd30e5f4c684b39b9ebeeb2-Ugreen-CM260-7.2.1.0.cab", + "url": "de152591660080ed5de8550b1b2dfcd738d1f5a68bd30e5f4c684b39b9ebeeb2-Ugreen-CM260-7.2.1.0.cab", "components": [ { "version": "7.2.1.0", @@ -14,7 +14,7 @@ ] }, { - "url": "https://fwupd.org/downloads/9480d06d1a3e524609660653fadc337d14d72f362bd2e80b15c5c3c1733f629b-Ugreen-CM260-7.2.2.0.cab", + "url": "9480d06d1a3e524609660653fadc337d14d72f362bd2e80b15c5c3c1733f629b-Ugreen-CM260-7.2.2.0.cab", "components": [ { "version": "7.2.2.0", diff -Nru fwupd-2.0.8/plugins/genesys/README.md fwupd-2.0.20/plugins/genesys/README.md --- fwupd-2.0.8/plugins/genesys/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -37,6 +37,7 @@ Additionally, some customized instance IDs are added. e.g. * `USB\VID_03F0&PID_0610&IC_352330&BONDING_0F` +* `USB\VID_03F0&PID_0610&IC_352330&BONDING_0F&RUNMODE_M` * `USB\VID_03F0&PID_0610&VENDOR_GENESYSLOGIC&IC_352330&BONDING_0F&PORTNUM_23&VENDORSUP_C09B5DD3-1A23-51D2-995A-F7366AAB3CA4` * `USB\VID_05E3&PID_0630&PROJECT_1885D34D-0418-5EF8-8E69-4CEF77B6B6E8` * `USB\VID_03F0&PID_0610&PUBKEY_AB859399-95B8-5817-B521-9AD8CC7F5BD6` @@ -99,18 +100,6 @@ Since 1.7.6. -### use-i2c-ch0 - -Scalar uses I²C channel 0. - -Since 1.7.6. - -### pause-r2-cpu - -Scalar pause R2 CPU. - -Since 1.7.6. - ### GenesysScalerDeviceTransferSize Scaler Block size to use for transfers. @@ -152,6 +141,78 @@ Since 1.8.2. +## Firmware Bank Quirk Use + +These parameters are used to configure and manage Genesys firmware banks. + +This plugin uses the following plugin-specific quirks to indicate firmware bank detail: + +### GenesysSupportDualBank + +Indicates if dual-bank support is enabled. + +Since 2.0.17 + +### GenesysSupportCodeSize + +Indicatess if code size support is enabled. + +Since 2.0.17 + +### GenesysHubBank1Address / GenesysHubBank2Address + +Memory addresses for Hub bank 1 and 2. + +Since 2.0.17 + +### GenesysHubBankCapacity + +Capacity of the Hub bank. + +Since 2.0.17 + +### GenesysDevBank1Address / GenesysDevBank2Address + +Memory addresses for Device bank 1 and 2. + +Since 2.0.17 + +### GenesysDevBankCapacity + +Capacity of the Device bank. + +Since 2.0.17 + +### GenesysPdBank1Address / GenesysPdBank2Address + +Memory addresses for PD bank 1 and 2. + +Since 2.0.17 + +### GenesysPdBankCapacity + +Capacity of the PD bank. + +Since 2.0.17 + +### GenesysCodesignBank1Address / GenesysCodesignBank2Address + +Memory addresses for Codesign bank 1 and 2. + +Since 2.0.17 + +### GenesysCodesignBankCapacity + +Capacity of the Codesign bank. + +Since 2.0.17 + +### GenesysFwDataMaxCount + +Maximum count of firmware data. + +Since 2.0.17 + ## Update Behavior The devices are independently updated at runtime using USB control transfers. @@ -171,10 +232,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.7.6`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Adam Chen: @adamgene diff -Nru fwupd-2.0.8/plugins/genesys/fu-genesys-common.h fwupd-2.0.20/plugins/genesys/fu-genesys-common.h --- fwupd-2.0.8/plugins/genesys/fu-genesys-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/fu-genesys-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -25,6 +25,7 @@ /* hub */ ISP_MODEL_HUB_GL3521, /* EOL */ ISP_MODEL_HUB_GL3523, + ISP_MODEL_HUB_GL3523PLUS, ISP_MODEL_HUB_GL3510, ISP_MODEL_HUB_GL3590, ISP_MODEL_HUB_GL7000, @@ -45,10 +46,11 @@ #define GENESYS_USBHUB_FW_CONFIGURATION_NEW_FORMAT 0xA5 #define GENESYS_USBHUB_FW_CONFIGURATION_NEW_FORMAT_V2 0xA6 -#define GENESYS_USBHUB_CODE_SIZE_OFFSET 0xFB -#define GENESYS_USBHUB_VERSION_OFFSET 0x10E -#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3521 0x221 -#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3523 0x221 -#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3590 0x241 -#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3525 0x251 -#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3525_V2 0x1E1 +#define GENESYS_USBHUB_CODE_SIZE_OFFSET 0xFB +#define GENESYS_USBHUB_VERSION_OFFSET 0x10E +#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3521 0x221 +#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3523 0x221 +#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3523PLUS 0x231 +#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3590 0x241 +#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3525 0x251 +#define GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3525_V2 0x1E1 diff -Nru fwupd-2.0.8/plugins/genesys/fu-genesys-hubhid-device.c fwupd-2.0.20/plugins/genesys/fu-genesys-hubhid-device.c --- fwupd-2.0.8/plugins/genesys/fu-genesys-hubhid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/fu-genesys-hubhid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -180,7 +180,7 @@ 0x0, /* src */ setup->length, error)) { - g_prefix_error(error, "error packing request data: "); + g_prefix_error_literal(error, "error packing request data: "); return FALSE; } } @@ -250,7 +250,7 @@ GENESYS_HUBHID_REPORT_TIMEOUT, GENESYS_HUBHID_REPORT_FLAGS, error)) { - g_prefix_error(error, "error finishing report: "); + g_prefix_error_literal(error, "error finishing report: "); return FALSE; } @@ -338,7 +338,7 @@ 0, buf_hid_token->len, error)) { - g_prefix_error(error, "wrong HID token string: "); + g_prefix_error_literal(error, "wrong HID token string: "); return FALSE; } @@ -372,7 +372,7 @@ /* FuHidDevice->setup */ if (!FU_DEVICE_CLASS(fu_genesys_hubhid_device_parent_class)->setup(device, error)) { - g_prefix_error(error, "error setting up device: "); + g_prefix_error_literal(error, "error setting up device: "); return FALSE; } diff -Nru fwupd-2.0.8/plugins/genesys/fu-genesys-plugin.c fwupd-2.0.20/plugins/genesys/fu-genesys-plugin.c --- fwupd-2.0.8/plugins/genesys/fu-genesys-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/fu-genesys-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,6 +21,7 @@ static void fu_genesys_plugin_init(FuGenesysPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void @@ -35,6 +36,22 @@ fu_context_add_quirk_key(ctx, "GenesysUsbhubReadRequest"); fu_context_add_quirk_key(ctx, "GenesysUsbhubSwitchRequest"); fu_context_add_quirk_key(ctx, "GenesysUsbhubWriteRequest"); + fu_context_add_quirk_key(ctx, "GenesysSupportDualBank"); + fu_context_add_quirk_key(ctx, "GenesysSupportCodeSize"); + fu_context_add_quirk_key(ctx, "GenesysHubBank1Address"); + fu_context_add_quirk_key(ctx, "GenesysHubBank2Address"); + fu_context_add_quirk_key(ctx, "GenesysHubBankCapacity"); + fu_context_add_quirk_key(ctx, "GenesysDevBank1Address"); + fu_context_add_quirk_key(ctx, "GenesysDevBank2Address"); + fu_context_add_quirk_key(ctx, "GenesysDevBankCapacity"); + fu_context_add_quirk_key(ctx, "GenesysPdBank1Address"); + fu_context_add_quirk_key(ctx, "GenesysPdBank2Address"); + fu_context_add_quirk_key(ctx, "GenesysPdBankCapacity"); + fu_context_add_quirk_key(ctx, "GenesysCodesignBank1Address"); + fu_context_add_quirk_key(ctx, "GenesysCodesignBank2Address"); + fu_context_add_quirk_key(ctx, "GenesysCodesignBankCapacity"); + fu_context_add_quirk_key(ctx, "GenesysFwDataMaxCount"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_GENESYS_USBHUB_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_GENESYS_HUBHID_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_GENESYS_USBHUB_FIRMWARE); @@ -75,7 +92,7 @@ fu_device_get_physical_id(usb_parent)); fu_plugin_device_remove(self, device); } else { - fu_genesys_usbhub_device_set_hid_channel(parent, device); + fu_genesys_usbhub_device_set_hid_channel(FU_GENESYS_USBHUB_DEVICE(parent), device); fu_device_add_child(parent, device); } } diff -Nru fwupd-2.0.8/plugins/genesys/fu-genesys-scaler-device.c fwupd-2.0.20/plugins/genesys/fu-genesys-scaler-device.c --- fwupd-2.0.8/plugins/genesys/fu-genesys-scaler-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/fu-genesys-scaler-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,6 +10,11 @@ #include "fu-genesys-scaler-device.h" #include "fu-genesys-scaler-firmware.h" +/* + * NOTE: DO NOT ALLOW ANY MORE MAGIC CONSTANTS IN THIS FILE + * nocheck:magic-inlines=119 + */ + #define GENESYS_SCALER_BANK_SIZE 0x200000U #define GENESYS_SCALER_MSTAR_READ 0x7a @@ -62,10 +67,13 @@ static gboolean fu_genesys_scaler_device_enter_serial_debug_mode(FuGenesysScalerDevice *self, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data[] = {0x53, 0x45, 0x52, 0x44, 0x42}; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -78,7 +86,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error entering Serial Debug Mode: "); + g_prefix_error_literal(error, "error entering Serial Debug Mode: "); return FALSE; } @@ -91,10 +99,13 @@ static gboolean fu_genesys_scaler_device_exit_serial_debug_mode(FuGenesysScalerDevice *self, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data[] = {0x45}; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -107,7 +118,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error exiting Serial Debug Mode: "); + g_prefix_error_literal(error, "error exiting Serial Debug Mode: "); return FALSE; } @@ -118,11 +129,14 @@ static gboolean fu_genesys_scaler_device_enter_single_step_mode(FuGenesysScalerDevice *self, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data1[] = {0x10, 0xc0, 0xc1, 0x53}; guint8 data2[] = {0x10, 0x1f, 0xc1, 0x53}; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -135,7 +149,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error entering Single Step Mode: "); + g_prefix_error_literal(error, "error entering Single Step Mode: "); return FALSE; } @@ -152,7 +166,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error entering Single Step Mode: "); + g_prefix_error_literal(error, "error entering Single Step Mode: "); return FALSE; } @@ -163,10 +177,13 @@ static gboolean fu_genesys_scaler_device_exit_single_step_mode(FuGenesysScalerDevice *self, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data[] = {0x10, 0xc0, 0xc1, 0xff}; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -179,7 +196,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error exiting Single Step Mode: "); + g_prefix_error_literal(error, "error exiting Single Step Mode: "); return FALSE; } @@ -190,10 +207,13 @@ static gboolean fu_genesys_scaler_device_enter_debug_mode(FuGenesysScalerDevice *self, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data[] = {0x10, 0x00, 0x00, 0x00}; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -206,7 +226,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error entering Debug Mode: "); + g_prefix_error_literal(error, "error entering Debug Mode: "); return FALSE; } @@ -217,11 +237,14 @@ static gboolean fu_genesys_scaler_device_mst_i2c_bus_ctrl(FuGenesysScalerDevice *self, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data[] = {0x35, 0x71}; + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; for (guint i = 0; i < sizeof(data); i++) { - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -246,11 +269,14 @@ static gboolean fu_genesys_scaler_device_mst_i2c_bus_switch_to_ch0(FuGenesysScalerDevice *self, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data[] = {0x80, 0x82, 0x84, 0x51, 0x7f, 0x37, 0x61}; + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; for (guint i = 0; i < sizeof(data); i++) { - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -275,11 +301,14 @@ static gboolean fu_genesys_scaler_device_mst_i2c_bus_switch_to_ch4(FuGenesysScalerDevice *self, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data[] = {0x80, 0x82, 0x85, 0x53, 0x7f}; + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; for (guint i = 0; i < sizeof(data); i++) { - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -304,7 +333,7 @@ static gboolean fu_genesys_scaler_device_disable_wp(FuGenesysScalerDevice *self, gboolean disable, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data_out[] = {0x10, 0x00 /* gpio_out_reg_h */, 0x00 /* gpio_out_reg_l */, @@ -320,7 +349,10 @@ data_out[2] = self->gpio_out_reg & 0x00ff; /* read gpio-out register */ - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -340,7 +372,7 @@ return FALSE; } - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -364,7 +396,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "error reading GPIO-Out Register 0x%02x%02x: ", + "error reading GPIO-Out Register 0x%02x%02x", data_out[1], data_out[2]); return FALSE; @@ -376,7 +408,7 @@ data_out[3] &= ~self->gpio_val; /* pull low */ /* write gpio-out register */ - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -403,7 +435,7 @@ data_en[2] = self->gpio_en_reg & 0x00ff; /* read gpio-enable register */ - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -423,7 +455,7 @@ return FALSE; } - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -447,7 +479,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "error reading GPIO-Enable Register 0x%02x%02x: ", + "error reading GPIO-Enable Register 0x%02x%02x", data_en[1], data_en[2]); return FALSE; @@ -456,7 +488,7 @@ data_en[3] &= ~self->gpio_val; /* write gpio-enable register */ - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -484,7 +516,7 @@ static gboolean fu_genesys_scaler_device_pause_r2_cpu(FuGenesysScalerDevice *self, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data[] = {0x10, 0x00, 0x10, 0x0F, 0xD7, 0x00}; /* @@ -493,7 +525,10 @@ * Pause R2 CPU for preventing Scaler entering Power Saving Mode also * need for Disable SPI Flash Write Protect Mode. */ - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -516,7 +551,7 @@ return FALSE; } - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -543,7 +578,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "error reading register 0x%02x%02x%02x%02x%02x: ", + "error reading register 0x%02x%02x%02x%02x%02x", data[0], data[1], data[2], @@ -553,7 +588,7 @@ } data[5] |= 0x80; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -583,13 +618,16 @@ } static gboolean -fu_genesys_scaler_device_set_isp_mode(FuDevice *device, gpointer user_data, GError **error) +fu_genesys_scaler_device_set_isp_mode_cb(FuDevice *device, gpointer user_data, GError **error) { - FuGenesysScalerDevice *self = user_data; - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device); + FuDevice *parent; guint8 data[] = {0x4d, 0x53, 0x54, 0x41, 0x52}; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -621,12 +659,12 @@ */ if (!fu_device_retry_full(FU_DEVICE(self), - fu_genesys_scaler_device_set_isp_mode, + fu_genesys_scaler_device_set_isp_mode_cb, 2, 1000 /* 1ms */, - self, + NULL, error)) { - g_prefix_error(error, "error entering ISP mode: "); + g_prefix_error_literal(error, "error entering ISP mode: "); return FALSE; } @@ -637,10 +675,13 @@ static gboolean fu_genesys_scaler_device_exit_isp_mode(FuGenesysScalerDevice *self, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data[] = {0x24}; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -653,7 +694,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error exiting ISP mode: "); + g_prefix_error_literal(error, "error exiting ISP mode: "); return FALSE; } @@ -729,9 +770,12 @@ static gboolean fu_genesys_scaler_device_get_level(FuGenesysScalerDevice *self, guint8 *level, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -744,7 +788,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error getting level: "); + g_prefix_error_literal(error, "error getting level: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), 100); /* ms */ @@ -759,9 +803,12 @@ guint bufsz, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -774,7 +821,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error getting version: "); + g_prefix_error_literal(error, "error getting version: "); return FALSE; } @@ -790,15 +837,18 @@ guint bufsz, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; const gsize data_size = 0x20; g_autoptr(GPtrArray) chunks = NULL; + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; chunks = fu_chunk_array_mutable_new(buf, bufsz, 0, 0, data_size); for (guint i = 0; i < chunks->len; i++) { FuChunk *chk = g_ptr_array_index(chunks, i); - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -811,7 +861,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error getting public key: "); + g_prefix_error_literal(error, "error getting public key: "); return FALSE; } @@ -830,10 +880,10 @@ FuProgress *progress, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data1[] = { GENESYS_SCALER_CMD_DATA_WRITE, - 0x00, /* Read Data command */ + 0x00, /* read data command */ (addr & 0x00ff0000) >> 16, (addr & 0x0000ff00) >> 8, (addr & 0x000000ff), @@ -849,7 +899,10 @@ if (!fu_cfi_device_get_cmd(self->cfi_device, FU_CFI_DEVICE_CMD_READ_DATA, &data1[1], error)) return FALSE; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -866,7 +919,7 @@ return FALSE; } - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -889,7 +942,7 @@ for (guint i = 0; i < chunks->len; i++) { FuChunk *chk = g_ptr_array_index(chunks, i); - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -910,7 +963,7 @@ fu_progress_step_done(progress); } - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -937,11 +990,14 @@ GError **error) { FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(dev); - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; FuGenesysWaitFlashRegisterHelper *helper = user_data; guint8 status = 0; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -954,7 +1010,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error reading flash control register: "); + g_prefix_error_literal(error, "error reading flash control register: "); return FALSE; } @@ -973,10 +1029,10 @@ static gboolean fu_genesys_scaler_device_flash_control_write_enable(FuGenesysScalerDevice *self, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data1[] = { GENESYS_SCALER_CMD_DATA_WRITE, - 0x00, /* Write Enable command */ + 0x00, /* write enable command */ }; guint8 data2[] = { GENESYS_SCALER_CMD_DATA_END, @@ -985,7 +1041,10 @@ if (!fu_cfi_device_get_cmd(self->cfi_device, FU_CFI_DEVICE_CMD_WRITE_EN, &data1[1], error)) return FALSE; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -998,11 +1057,11 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error sending flash control write enable: "); + g_prefix_error_literal(error, "error sending flash control write enable: "); return FALSE; } - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -1015,7 +1074,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error sending flash control write enable: "); + g_prefix_error_literal(error, "error sending flash control write enable: "); return FALSE; } @@ -1028,10 +1087,10 @@ guint8 status, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data1[] = { GENESYS_SCALER_CMD_DATA_WRITE, - 0x00, /* Write Status command */ + 0x00, /* write status command */ status, }; guint8 data2[] = { @@ -1044,7 +1103,10 @@ error)) return FALSE; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -1061,7 +1123,7 @@ return FALSE; } - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -1087,14 +1149,14 @@ guint addr, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; FuGenesysWaitFlashRegisterHelper helper = { - .reg = 0x00, /* Read Status command */ + .reg = 0x00, /* read status command */ .expected_val = 0, }; guint8 data1[] = { GENESYS_SCALER_CMD_DATA_WRITE, - 0x00, /* Sector Erase command */ + 0x00, /* sector erase command */ (addr & 0x00ff0000) >> 16, (addr & 0x0000ff00) >> 8, (addr & 0x000000ff), @@ -1128,14 +1190,18 @@ 50, /* 50ms */ &helper, error)) { - g_prefix_error(error, "error waiting for flash control read status register: "); + g_prefix_error_literal(error, + "error waiting for flash control read status register: "); return FALSE; } if (!fu_genesys_scaler_device_flash_control_write_enable(self, error)) return FALSE; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -1154,7 +1220,7 @@ return FALSE; } - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -1180,7 +1246,8 @@ 50, /* 50ms */ &helper, error)) { - g_prefix_error(error, "error waiting for flash control read status register: "); + g_prefix_error_literal(error, + "error waiting for flash control read status register: "); return FALSE; } @@ -1226,14 +1293,14 @@ FuProgress *progress, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; FuGenesysWaitFlashRegisterHelper helper = { - .reg = 0x00, /* Read Status command */ + .reg = 0x00, /* read status command */ .expected_val = 0, }; guint8 data1[] = { GENESYS_SCALER_CMD_DATA_WRITE, - 0x00, /* Page Program command */ + 0x00, /* page program command */ (addr & 0x00ff0000) >> 16, (addr & 0x0000ff00) >> 8, (addr & 0x000000ff), @@ -1272,6 +1339,10 @@ error)) return FALSE; + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + chunks = fu_chunk_array_mutable_new(data, datasz, addr + sizeof(data1), 0, self->transfer_size); fu_progress_set_id(progress, G_STRLOC); @@ -1283,7 +1354,7 @@ if ((i + 1) == chunks->len) index |= 0x0080; - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -1311,7 +1382,8 @@ 20, &helper, error)) { - g_prefix_error(error, "error waiting for flash control read status register: "); + g_prefix_error_literal(error, + "error waiting for flash control read status register: "); return FALSE; } @@ -1398,7 +1470,7 @@ guint bufsz, GError **error) { - FuDevice *parent_device = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent; guint8 data[] = {0x6e, 0x51, 0x83, 0xcd, 0x01, 0x00 /* command */, 0x00 /* checksum */}; data[5] = cmd; @@ -1417,13 +1489,16 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error setting dddci data: "); + g_prefix_error_literal(error, "error setting dddci data: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), 100); /* 1ms */ - if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent_device), + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, @@ -1436,7 +1511,7 @@ GENESYS_SCALER_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error getting dddci data: "); + g_prefix_error_literal(error, "error getting dddci data: "); return FALSE; } @@ -1451,7 +1526,7 @@ FuGenesysScalerFirmwarePacketVersion *ver, GError **error) { - guint8 buf[0x40]; + guint8 buf[0x40] = {0}; guint8 offset = 4; if (!fu_genesys_scaler_device_get_ddcci_data( @@ -1471,7 +1546,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "error dddci length too large, got 0x%x, expected <= 0x%zx: ", + "error dddci length too large, got 0x%x, expected <= 0x%zx", (guint)len, sizeof(buf)); return FALSE; @@ -1659,7 +1734,7 @@ fu_genesys_scaler_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuGenesysScalerDevice *self = FU_GENESYS_SCALER_DEVICE(device); @@ -1776,7 +1851,7 @@ } static void -fu_genesys_scaler_device_set_progress(FuDevice *self, FuProgress *progress) +fu_genesys_scaler_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -1892,7 +1967,7 @@ static void fu_genesys_scaler_device_init(FuGenesysScalerDevice *self) { - fu_device_set_vendor(FU_DEVICE(self), "MStar Semiconductor"); + fu_device_set_vendor(FU_DEVICE(self), "MStar"); fu_device_set_name(FU_DEVICE(self), "TSUMG"); fu_device_add_protocol(FU_DEVICE(self), "com.mstarsemi.scaler"); fu_device_retry_set_delay(FU_DEVICE(self), 10); /* 10ms */ diff -Nru fwupd-2.0.8/plugins/genesys/fu-genesys-scaler-firmware.c fwupd-2.0.20/plugins/genesys/fu-genesys-scaler-firmware.c --- fwupd-2.0.8/plugins/genesys/fu-genesys-scaler-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/fu-genesys-scaler-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,7 @@ #include "fu-genesys-scaler-firmware.h" struct _FuGenesysScalerFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; FuGenesysPublicKey public_key; }; @@ -19,7 +19,7 @@ static gboolean fu_genesys_scaler_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuGenesysScalerFirmware *self = FU_GENESYS_SCALER_FIRMWARE(firmware); @@ -64,13 +64,15 @@ if (!fu_firmware_parse_stream(firmware_payload, stream_payload, 0x0, flags, error)) return FALSE; fu_firmware_set_id(firmware_payload, FU_FIRMWARE_ID_PAYLOAD); - fu_firmware_add_image(firmware, firmware_payload); + if (!fu_firmware_add_image(firmware, firmware_payload, error)) + return FALSE; /* set public-key */ blob_public_key = g_bytes_new(&self->public_key, sizeof(self->public_key)); firmware_public_key = fu_firmware_new_from_bytes(blob_public_key); fu_firmware_set_id(firmware_public_key, FU_FIRMWARE_ID_SIGNATURE); - fu_firmware_add_image(firmware, firmware_public_key); + if (!fu_firmware_add_image(firmware, firmware_public_key, error)) + return FALSE; /* success */ return TRUE; diff -Nru fwupd-2.0.8/plugins/genesys/fu-genesys-usbhub-codesign-firmware.c fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-codesign-firmware.c --- fwupd-2.0.8/plugins/genesys/fu-genesys-usbhub-codesign-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-codesign-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,7 @@ #include "fu-genesys-usbhub-struct.h" struct _FuGenesysUsbhubCodesignFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; FuGenesysFwCodesign codesign; }; @@ -50,7 +50,7 @@ static gboolean fu_genesys_usbhub_codesign_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuGenesysUsbhubCodesignFirmware *self = FU_GENESYS_USBHUB_CODESIGN_FIRMWARE(firmware); @@ -62,13 +62,13 @@ code_size = streamsz; if (code_size == FU_STRUCT_GENESYS_FW_CODESIGN_INFO_RSA_SIZE) { if (!fu_struct_genesys_fw_codesign_info_rsa_validate_stream(stream, 0x0, error)) { - g_prefix_error(error, "not valid for codesign: "); + g_prefix_error_literal(error, "not valid for codesign: "); return FALSE; } self->codesign = FU_GENESYS_FW_CODESIGN_RSA; } else if (code_size == FU_STRUCT_GENESYS_FW_CODESIGN_INFO_ECDSA_SIZE) { if (!fu_struct_genesys_fw_codesign_info_ecdsa_validate_stream(stream, 0x0, error)) { - g_prefix_error(error, "not valid for codesign: "); + g_prefix_error_literal(error, "not valid for codesign: "); return FALSE; } self->codesign = FU_GENESYS_FW_CODESIGN_ECDSA; diff -Nru fwupd-2.0.8/plugins/genesys/fu-genesys-usbhub-dev-firmware.c fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-dev-firmware.c --- fwupd-2.0.8/plugins/genesys/fu-genesys-usbhub-dev-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-dev-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,7 +12,7 @@ #include "fu-genesys-usbhub-struct.h" struct _FuGenesysUsbhubDevFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; }; G_DEFINE_TYPE(FuGenesysUsbhubDevFirmware, fu_genesys_usbhub_dev_firmware, FU_TYPE_FIRMWARE) @@ -29,7 +29,7 @@ static gboolean fu_genesys_usbhub_dev_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize code_size = 0; @@ -41,7 +41,7 @@ /* truncate to correct size */ if (!fu_genesys_usbhub_firmware_calculate_size(stream, &code_size, error)) { - g_prefix_error(error, "not valid for dev: "); + g_prefix_error_literal(error, "not valid for dev: "); return FALSE; } stream_trunc = fu_partial_input_stream_new(stream, 0x0, code_size, error); @@ -51,16 +51,16 @@ return FALSE; /* calculate checksum */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { if (!fu_genesys_usbhub_firmware_verify_checksum(stream_trunc, error)) { - g_prefix_error(error, "not valid for dev: "); + g_prefix_error_literal(error, "not valid for dev: "); return FALSE; } } /* get firmware version */ if (!fu_genesys_usbhub_firmware_ensure_version(firmware, error)) { - g_prefix_error(error, "not valid for dev: "); + g_prefix_error_literal(error, "not valid for dev: "); return FALSE; } diff -Nru fwupd-2.0.8/plugins/genesys/fu-genesys-usbhub-device.c fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-device.c --- fwupd-2.0.8/plugins/genesys/fu-genesys-usbhub-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,12 @@ #include "fu-genesys-usbhub-firmware.h" #include "fu-genesys-usbhub-struct.h" +/* + * NOTE: DO NOT ALLOW ANY MORE MAGIC CONSTANTS IN THIS FILE + * nocheck:magic-inlines=85 + * nocheck:magic-defines=16 + */ + #define FU_GENESYS_USBHUB_FLAG_HAS_MSTAR_SCALER "has-mstar-scaler" #define FU_GENESYS_USBHUB_FLAG_HAS_PUBLIC_KEY "has-public-key" @@ -81,11 +87,10 @@ struct _FuGenesysUsbhubDevice { FuUsbDevice parent_instance; - GByteArray *st_static_ts; - GByteArray *st_dynamic_ts; - GByteArray *st_fwinfo_ts; - GByteArray *st_vendor_ts; - GByteArray *st_project_ts; + FuStructGenesysTsStatic *st_static_ts; + FuStructGenesysTsFirmwareInfo *st_fwinfo_ts; + FuStructGenesysTsVendorSupport *st_vendor_ts; + FuStructGenesysTsBrandProject *st_project_ts; FuGenesysVendorCommandSetting vcs; FuGenesysModelSpec spec; @@ -114,7 +119,7 @@ * Also, to fulfill dual bank mechanism, we shall keep at last one fw bank is right. * * For this purpose, we usually backup bank1 (right fw) to bank2, and update fw to bank1. - * In rare case - bootup on bank2, we can update fw to bank1 and skip backup. + * In rare case - boot-up on bank2, we can update fw to bank1 and skip backup. */ gboolean backup_hub_fw_bank1; GBytes *hub_fw_bank1_data; /* restore hub bank1 fw for backup */ @@ -125,6 +130,9 @@ FuGenesysVsCodesignCheck codesign_check; FuGenesysFwCodesign codesign; GByteArray *st_codesign; /* codesign info, may need to backup for GL352350 */ + FuStructGenesysFwRsaPublicKeyText *st_rsa_pubkey; + FuStructGenesysFwEcdsaPublicKey *st_ecdsa_pubkey; + FuStructGenesysFwCodesignInfoEcdsa *st_codesign_ecdsa; GByteArray *st_public_key; /* hid channel */ @@ -134,11 +142,9 @@ G_DEFINE_TYPE(FuGenesysUsbhubDevice, fu_genesys_usbhub_device, FU_TYPE_USB_DEVICE) void -fu_genesys_usbhub_device_set_hid_channel(FuDevice *device, FuDevice *channel) +fu_genesys_usbhub_device_set_hid_channel(FuGenesysUsbhubDevice *self, FuDevice *channel) { - FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); - - g_return_if_fail(self); + g_return_if_fail(FU_IS_GENESYS_USBHUB_DEVICE(self)); g_return_if_fail(FU_IS_GENESYS_HUBHID_DEVICE(channel)); if (self->hid_channel != NULL) { @@ -415,7 +421,7 @@ GENESYS_USBHUB_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error resetting device: "); + g_prefix_error_literal(error, "error resetting device: "); return FALSE; } @@ -454,7 +460,7 @@ GENESYS_USBHUB_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error reading flash chip: "); + g_prefix_error_literal(error, "error reading flash chip: "); return NULL; } @@ -550,7 +556,7 @@ error)) { g_prefix_error(error, "error setting isp mode - " - "control transfer error (reg 0x%02x) ", + "control transfer error (reg 0x%02x): ", self->vcs.req_switch); return FALSE; } @@ -564,7 +570,7 @@ 5, &helper, error)) { - g_prefix_error(error, "error setting isp mode: "); + g_prefix_error_literal(error, "error setting isp mode: "); return FALSE; } } @@ -660,14 +666,14 @@ offset_end = g_random_int_range(offset_start + 1, /* nocheck:blocked */ GENESYS_USBHUB_ENCRYPT_REGION_END); for (guint8 i = offset_start; i <= offset_end; i++) { - temp_byte ^= self->st_fwinfo_ts->data[i]; + temp_byte ^= self->st_fwinfo_ts->buf->data[i]; } if (!fu_genesys_usbhub_device_authentication_request(self, offset_start, offset_end, temp_byte, error)) { - g_prefix_error(error, "error authenticating device: "); + g_prefix_error_literal(error, "error authenticating device: "); return FALSE; } @@ -830,7 +836,7 @@ 1, NULL, error)) { - g_prefix_error(error, "error getting fw size from device: "); + g_prefix_error_literal(error, "error getting fw size from device: "); return FALSE; } self->fw_bank_code_sizes[bank_num][fw_type] = 1024 * kbs; @@ -909,7 +915,7 @@ &self->fw_bank_vers[bank_num][fw_type], G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to get version: "); + g_prefix_error_literal(error, "failed to get version: "); return FALSE; } @@ -923,7 +929,9 @@ /* read the public-key from the firmware stored in the device */ static gboolean -fu_genesys_usbhub_device_get_public_key(FuGenesysUsbhubDevice *self, int bank_num, GError **error) +fu_genesys_usbhub_device_ensure_public_key(FuGenesysUsbhubDevice *self, + int bank_num, + GError **error) { gsize bufsz = self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_CODESIGN]; g_autofree guint8 *buf = NULL; @@ -949,18 +957,21 @@ bufsz, NULL, error)) { - g_prefix_error(error, "error getting public-key from device: "); + g_prefix_error_literal(error, "error getting public-key from device: "); return FALSE; } /* validate and parse public-key */ if (self->has_hw_codesign) { - /* master device has ECDSA key and signature only */ + /* initiator device has ECDSA key and signature only */ if (fu_struct_genesys_fw_ecdsa_public_key_validate(buf, bufsz, 0, NULL)) { self->codesign = FU_GENESYS_FW_CODESIGN_ECDSA; - self->st_codesign = + self->st_ecdsa_pubkey = fu_struct_genesys_fw_ecdsa_public_key_parse(buf, bufsz, 0, error); - self->st_public_key = g_byte_array_ref(self->st_codesign); + if (self->st_ecdsa_pubkey == NULL) + return FALSE; + self->st_codesign = g_byte_array_ref(self->st_ecdsa_pubkey->buf); + self->st_public_key = g_byte_array_ref(self->st_ecdsa_pubkey->buf); } else { g_set_error_literal(error, FWUPD_ERROR, @@ -971,19 +982,26 @@ } else { if (fu_struct_genesys_fw_rsa_public_key_text_validate(buf, bufsz, 0, NULL)) { self->codesign = FU_GENESYS_FW_CODESIGN_RSA; - self->st_codesign = + self->st_rsa_pubkey = fu_struct_genesys_fw_rsa_public_key_text_parse(buf, bufsz, 0, error); - self->st_public_key = g_byte_array_ref(self->st_codesign); + if (self->st_rsa_pubkey == NULL) + return FALSE; + self->st_codesign = g_byte_array_ref(self->st_rsa_pubkey->buf); + self->st_public_key = g_byte_array_ref(self->st_rsa_pubkey->buf); } else if (fu_struct_genesys_fw_codesign_info_ecdsa_validate(buf, bufsz, 0, NULL)) { - /* slave device has completely ECDSA codesign info */ + /* target device has completely ECDSA codesign info */ gsize keysz = 0; const guint8 *key = NULL; self->codesign = FU_GENESYS_FW_CODESIGN_ECDSA; - self->st_codesign = + self->st_codesign_ecdsa = fu_struct_genesys_fw_codesign_info_ecdsa_parse(buf, bufsz, 0, error); - key = fu_struct_genesys_fw_codesign_info_ecdsa_get_key(self->st_codesign, - &keysz); + if (self->st_codesign_ecdsa == NULL) + return FALSE; + self->st_codesign = g_byte_array_ref(self->st_codesign_ecdsa->buf); + key = fu_struct_genesys_fw_codesign_info_ecdsa_get_key( + self->st_codesign_ecdsa, + &keysz); self->st_public_key = g_byte_array_new(); g_byte_array_append(self->st_public_key, key, keysz); } else { @@ -1067,16 +1085,19 @@ GError **error) { g_autofree gchar *project_ic_type = NULL; + g_autofree gchar *project_ic = NULL; self->st_static_ts = fu_struct_genesys_ts_static_parse(buf, bufsz, 0, error); if (self->st_static_ts == NULL) { - g_prefix_error(error, "failed to parse static tool info: "); + g_prefix_error_literal(error, "failed to parse static tool info: "); return FALSE; } project_ic_type = fu_struct_genesys_ts_static_get_mask_project_ic_type(self->st_static_ts); /* verify chip model and revision */ + self->spec.chip.revision = 10 * (project_ic_type[4] - '0') + (project_ic_type[5] - '0'); + if (memcmp(project_ic_type, "3521", 4) == 0) { g_set_error(error, FWUPD_ERROR, @@ -1085,7 +1106,12 @@ project_ic_type); return FALSE; } else if (memcmp(project_ic_type, "3523", 4) == 0) { - self->spec.chip.model = ISP_MODEL_HUB_GL3523; + if (self->spec.chip.revision >= 60) { + self->spec.chip.model = ISP_MODEL_HUB_GL3523PLUS; + } else { + self->spec.chip.model = ISP_MODEL_HUB_GL3523; + self->is_gl352350 = self->spec.chip.revision == 50; + } } else if (memcmp(project_ic_type, "3590", 4) == 0) { self->spec.chip.model = ISP_MODEL_HUB_GL3590; } else if (memcmp(project_ic_type, "3525", 4) == 0) { @@ -1099,78 +1125,29 @@ return FALSE; } - self->spec.chip.revision = 10 * (project_ic_type[4] - '0') + (project_ic_type[5] - '0'); - /* convert tool string version */ self->tool_string_version = fu_struct_genesys_ts_static_get_tool_string_version(self->st_static_ts); - /* setup firmware parameters */ - switch (self->spec.chip.model) { - case ISP_MODEL_HUB_GL3521: - self->spec.support_dual_bank = FALSE; - self->spec.support_code_size = FALSE; - self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_HUB] = 0x0000; - self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_HUB] = 0x5000; - self->spec.fw_data_max_count = 0x5000; - break; - case ISP_MODEL_HUB_GL3523: - self->spec.support_dual_bank = TRUE; - self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_HUB] = 0x0000; - self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_HUB] = 0x8000; - - if (self->spec.chip.revision == 50) { - self->is_gl352350 = TRUE; - self->spec.support_code_size = TRUE; - self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_CODESIGN] = 0x7C00; - self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_CODESIGN] = 0xFC00; - self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_HUB] = 0x8000; - } else { - self->spec.support_code_size = FALSE; - self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_CODESIGN] = 0x6000; - self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_CODESIGN] = 0xE000; - self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_HUB] = 0x6000; - } - self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_CODESIGN] = 0x400; - self->spec.fw_data_max_count = 0x10000; - break; - case ISP_MODEL_HUB_GL3590: - self->spec.support_dual_bank = TRUE; - self->spec.support_code_size = TRUE; - self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_HUB] = 0x0000; - self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_HUB] = 0x10000; - self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_DEV_BRIDGE] = 0x20000; - self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_DEV_BRIDGE] = 0x30000; - self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_CODESIGN] = 0xFF00; - self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_CODESIGN] = 0x1FF00; - self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_HUB] = 0x10000; - self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_DEV_BRIDGE] = 0x10000; - self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_CODESIGN] = 0x100; - self->spec.fw_data_max_count = 0x40000; - break; - case ISP_MODEL_HUB_GL3525: - self->spec.support_dual_bank = TRUE; - self->spec.support_code_size = TRUE; - self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_HUB] = 0x0000; - self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_HUB] = 0xB000; - self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_PD] = 0x16000; - self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_PD] = 0x23000; - self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_DEV_BRIDGE] = 0x30000; - self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_DEV_BRIDGE] = 0x38000; - self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_CODESIGN] = 0x16000; - self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_CODESIGN] = 0x17000; - self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_HUB] = 0xB000; - self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_PD] = 0xD000; - self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_DEV_BRIDGE] = 0x8000; - self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_CODESIGN] = 0x1000; - self->spec.fw_data_max_count = 0x40000; - break; - default: - break; - } + /* add IC product instance and load quirk kv */ + project_ic = g_strndup(project_ic_type, 4); + fu_device_add_instance_str(FU_DEVICE(self), "IC", project_ic); + if (!fu_device_build_instance_id_full(FU_DEVICE(self), + FU_DEVICE_INSTANCE_FLAG_QUIRKS, + error, + "GENESYS_USBHUB", + "IC", + NULL)) + return FALSE; - /* add IC product instance */ fu_device_add_instance_str(FU_DEVICE(self), "IC", project_ic_type); + if (!fu_device_build_instance_id_full(FU_DEVICE(self), + FU_DEVICE_INSTANCE_FLAG_QUIRKS, + error, + "GENESYS_USBHUB", + "IC", + NULL)) + return FALSE; /* success */ return TRUE; @@ -1206,17 +1183,18 @@ /* get running mode, portnum, bonding and flash dump location bit */ switch (self->spec.chip.model) { case ISP_MODEL_HUB_GL3523: - self->st_dynamic_ts = - fu_struct_genesys_ts_dynamic_gl3523_parse(buf, bufsz, 0, error); - if (self->st_dynamic_ts == NULL) { - g_prefix_error(error, "failed to parse dynamic tool info: "); + case ISP_MODEL_HUB_GL3523PLUS: { + g_autoptr(FuStructGenesysTsDynamicGl3523) st_dynamic_ts = NULL; + st_dynamic_ts = fu_struct_genesys_ts_dynamic_gl3523_parse(buf, bufsz, 0, error); + if (st_dynamic_ts == NULL) { + g_prefix_error_literal(error, "failed to parse dynamic tool info: "); return FALSE; } - rm_st = fu_struct_genesys_ts_dynamic_gl3523_get_running_mode(self->st_dynamic_ts); - ss_st = fu_struct_genesys_ts_dynamic_gl3523_get_ss_port_number(self->st_dynamic_ts); - hs_st = fu_struct_genesys_ts_dynamic_gl3523_get_hs_port_number(self->st_dynamic_ts); - bonding_st = fu_struct_genesys_ts_dynamic_gl3523_get_bonding(self->st_dynamic_ts); + rm_st = fu_struct_genesys_ts_dynamic_gl3523_get_running_mode(st_dynamic_ts); + ss_st = fu_struct_genesys_ts_dynamic_gl3523_get_ss_port_number(st_dynamic_ts); + hs_st = fu_struct_genesys_ts_dynamic_gl3523_get_hs_port_number(st_dynamic_ts); + bonding_st = fu_struct_genesys_ts_dynamic_gl3523_get_bonding(st_dynamic_ts); bonding = fu_genesys_usbhub_device_tsdigit_value(bonding_st[0]); if (self->tool_string_version < FU_GENESYS_TS_VERSION_BONDING_QC) @@ -1224,63 +1202,67 @@ self->bonding = bonding & GL3523_BONDING_VALID_BIT; flash_dump_location_bit = (bonding & GL3523_BONDING_FLASH_DUMP_LOCATION_BIT) > 0; break; - case ISP_MODEL_HUB_GL3590: + } + case ISP_MODEL_HUB_GL3590: { if (self->spec.chip.revision == 30) { - self->st_dynamic_ts = + g_autoptr(FuStructGenesysTsDynamicGl359030) st_dynamic_ts = NULL; + st_dynamic_ts = fu_struct_genesys_ts_dynamic_gl359030_parse(buf, bufsz, 0, error); - if (self->st_dynamic_ts == NULL) { - g_prefix_error(error, "failed to parse dynamic tool info: "); + if (st_dynamic_ts == NULL) { + g_prefix_error_literal(error, + "failed to parse dynamic tool info: "); return FALSE; } - rm_st = fu_struct_genesys_ts_dynamic_gl359030_get_running_mode( - self->st_dynamic_ts); - ss_st = fu_struct_genesys_ts_dynamic_gl359030_get_ss_port_number( - self->st_dynamic_ts); - hs_st = fu_struct_genesys_ts_dynamic_gl359030_get_hs_port_number( - self->st_dynamic_ts); + rm_st = + fu_struct_genesys_ts_dynamic_gl359030_get_running_mode(st_dynamic_ts); + ss_st = + fu_struct_genesys_ts_dynamic_gl359030_get_ss_port_number(st_dynamic_ts); + hs_st = + fu_struct_genesys_ts_dynamic_gl359030_get_hs_port_number(st_dynamic_ts); self->bonding = - fu_struct_genesys_ts_dynamic_gl359030_get_bonding(self->st_dynamic_ts); + fu_struct_genesys_ts_dynamic_gl359030_get_bonding(st_dynamic_ts); flash_dump_location_bit = fu_struct_genesys_ts_dynamic_gl359030_get_hub_fw_status( - self->st_dynamic_ts) == FU_GENESYS_FW_STATUS_BANK2; + st_dynamic_ts) == FU_GENESYS_FW_STATUS_BANK2; } else { - self->st_dynamic_ts = + g_autoptr(FuStructGenesysTsDynamicGl3590) st_dynamic_ts = NULL; + st_dynamic_ts = fu_struct_genesys_ts_dynamic_gl3590_parse(buf, bufsz, 0, error); - if (self->st_dynamic_ts == NULL) { - g_prefix_error(error, "failed to parse dynamic tool info: "); + if (st_dynamic_ts == NULL) { + g_prefix_error_literal(error, + "failed to parse dynamic tool info: "); return FALSE; } - rm_st = fu_struct_genesys_ts_dynamic_gl3590_get_running_mode( - self->st_dynamic_ts); - ss_st = fu_struct_genesys_ts_dynamic_gl3590_get_ss_port_number( - self->st_dynamic_ts); - hs_st = fu_struct_genesys_ts_dynamic_gl3590_get_hs_port_number( - self->st_dynamic_ts); - bonding = - fu_struct_genesys_ts_dynamic_gl3590_get_bonding(self->st_dynamic_ts); + rm_st = fu_struct_genesys_ts_dynamic_gl3590_get_running_mode(st_dynamic_ts); + ss_st = + fu_struct_genesys_ts_dynamic_gl3590_get_ss_port_number(st_dynamic_ts); + hs_st = + fu_struct_genesys_ts_dynamic_gl3590_get_hs_port_number(st_dynamic_ts); + bonding = fu_struct_genesys_ts_dynamic_gl3590_get_bonding(st_dynamic_ts); self->bonding = bonding & GL3590_BONDING_VALID_BIT; flash_dump_location_bit = (bonding & GL3590_BONDING_FLASH_DUMP_LOCATION_BIT) > 0; } break; - case ISP_MODEL_HUB_GL3525: - self->st_dynamic_ts = - fu_struct_genesys_ts_dynamic_gl3525_parse(buf, bufsz, 0, error); - if (self->st_dynamic_ts == NULL) { - g_prefix_error(error, "failed to parse dynamic tool info: "); + } + case ISP_MODEL_HUB_GL3525: { + g_autoptr(FuStructGenesysTsDynamicGl3525) st_dynamic_ts = NULL; + st_dynamic_ts = fu_struct_genesys_ts_dynamic_gl3525_parse(buf, bufsz, 0, error); + if (st_dynamic_ts == NULL) { + g_prefix_error_literal(error, "failed to parse dynamic tool info: "); return FALSE; } - rm_st = fu_struct_genesys_ts_dynamic_gl3525_get_running_mode(self->st_dynamic_ts); - ss_st = fu_struct_genesys_ts_dynamic_gl3525_get_ss_port_number(self->st_dynamic_ts); - hs_st = fu_struct_genesys_ts_dynamic_gl3525_get_hs_port_number(self->st_dynamic_ts); - self->bonding = - fu_struct_genesys_ts_dynamic_gl3525_get_bonding(self->st_dynamic_ts); + rm_st = fu_struct_genesys_ts_dynamic_gl3525_get_running_mode(st_dynamic_ts); + ss_st = fu_struct_genesys_ts_dynamic_gl3525_get_ss_port_number(st_dynamic_ts); + hs_st = fu_struct_genesys_ts_dynamic_gl3525_get_hs_port_number(st_dynamic_ts); + self->bonding = fu_struct_genesys_ts_dynamic_gl3525_get_bonding(st_dynamic_ts); flash_dump_location_bit = fu_struct_genesys_ts_dynamic_gl3525_get_hub_fw_status( - self->st_dynamic_ts) == FU_GENESYS_FW_STATUS_BANK2; + st_dynamic_ts) == FU_GENESYS_FW_STATUS_BANK2; break; + } default: g_set_error(error, FWUPD_ERROR, @@ -1305,6 +1287,7 @@ portnum = ss_port_number << 4 | hs_port_number; /* add specific product info */ + fu_device_add_instance_str(FU_DEVICE(self), "RUNMODE", rm_st); fu_device_add_instance_u8(FU_DEVICE(self), "PORTNUM", portnum); fu_device_add_instance_u8(FU_DEVICE(self), "BONDING", self->bonding); @@ -1320,7 +1303,7 @@ { self->st_vendor_ts = fu_struct_genesys_ts_vendor_support_parse(buf, bufsz, 0, error); if (self->st_vendor_ts == NULL) { - g_prefix_error(error, "failed to parse vendor support tool info: "); + g_prefix_error_literal(error, "failed to parse vendor support tool info: "); return FALSE; } @@ -1349,13 +1332,13 @@ self->st_project_ts = fu_struct_genesys_ts_brand_project_parse(buf, bufsz, 0, error); if (self->st_project_ts == NULL) { - g_prefix_error(error, "failed to parse brand project tool info: "); + g_prefix_error_literal(error, "failed to parse brand project tool info: "); return FALSE; } /* add specific product info */ - guid = fwupd_guid_hash_data(self->st_project_ts->data, - self->st_project_ts->len, + guid = fwupd_guid_hash_data(self->st_project_ts->buf->data, + self->st_project_ts->buf->len, FWUPD_GUID_FLAG_NONE); fu_device_add_instance_strup(FU_DEVICE(self), "PROJECT", guid); @@ -1501,7 +1484,7 @@ /* FuUsbDevice->setup */ if (!FU_DEVICE_CLASS(fu_genesys_usbhub_device_parent_class)->setup(device, error)) { - g_prefix_error(error, "error setting up device: "); + g_prefix_error_literal(error, "error setting up device: "); return FALSE; } @@ -1542,7 +1525,7 @@ return FALSE; } if (!fu_genesys_usbhub_device_get_descriptor_data(static_buf, buf, bufsz, error)) { - g_prefix_error(error, "failed to get static tool info from device: "); + g_prefix_error_literal(error, "failed to get static tool info from device: "); return FALSE; } if (!fu_genesys_usbhub_device_get_info_from_static_ts(self, buf, bufsz, error)) @@ -1562,7 +1545,7 @@ return FALSE; } if (!fu_genesys_usbhub_device_get_descriptor_data(dynamic_buf, buf, bufsz, error)) { - g_prefix_error(error, "failed to get dynamic tool info from device: "); + g_prefix_error_literal(error, "failed to get dynamic tool info from device: "); return FALSE; } if (!fu_genesys_usbhub_device_get_info_from_dynamic_ts(self, buf, bufsz, error)) @@ -1575,21 +1558,22 @@ 64, error); if (fw_buf == NULL) { - g_prefix_error(error, "failed to get firmware tool info from device: "); + g_prefix_error_literal(error, "failed to get firmware tool info from device: "); return FALSE; } if (!fu_genesys_usbhub_device_get_descriptor_data(fw_buf, buf, bufsz, error)) { - g_prefix_error(error, "failed to get firmware tool info from device: "); + g_prefix_error_literal(error, "failed to get firmware tool info from device: "); return FALSE; } self->st_fwinfo_ts = fu_struct_genesys_ts_firmware_info_parse(buf, bufsz, 0, error); if (self->st_fwinfo_ts == NULL) { - g_prefix_error(error, "failed to parse firmware tool info: "); + g_prefix_error_literal(error, "failed to parse firmware tool info: "); return FALSE; } /* parse vendor support tool string */ if (self->tool_string_version >= FU_GENESYS_TS_VERSION_VENDOR_SUPPORT) { + g_autoptr(GError) error_local = NULL; g_autoptr(GBytes) vendor_buf = fu_usb_device_get_string_descriptor_bytes_full( FU_USB_DEVICE(device), GENESYS_USBHUB_VENDOR_SUPPORT_DESC_IDX, @@ -1597,16 +1581,21 @@ 64, error); if (vendor_buf == NULL) { - g_prefix_error(error, - "failed to get vendor support tool info from device: "); - return FALSE; - } - if (!fu_genesys_usbhub_device_get_descriptor_data(vendor_buf, buf, bufsz, error)) { - g_prefix_error(error, - "failed to get vendor support tool info from device: "); + g_prefix_error_literal( + error, + "failed to get vendor support tool info from device: "); return FALSE; } - if (!fu_genesys_usbhub_device_get_info_from_vendor_ts(self, buf, bufsz, error)) + if (!fu_genesys_usbhub_device_get_descriptor_data(vendor_buf, + buf, + bufsz, + &error_local)) { + g_debug("ignoring vendor support tool info: %s", error_local->message); + self->st_vendor_ts = fu_struct_genesys_ts_vendor_support_new(); + } else if (!fu_genesys_usbhub_device_get_info_from_vendor_ts(self, + buf, + bufsz, + error)) return FALSE; } else { self->st_vendor_ts = fu_struct_genesys_ts_vendor_support_new(); @@ -1621,13 +1610,15 @@ 64, error); if (project_buf == NULL) { - g_prefix_error(error, - "failed to get brand project tool info from device: "); + g_prefix_error_literal( + error, + "failed to get brand project tool info from device: "); return FALSE; } if (!fu_genesys_usbhub_device_get_descriptor_data(project_buf, buf, bufsz, error)) { - g_prefix_error(error, - "failed to get brand project tool info from device: "); + g_prefix_error_literal( + error, + "failed to get brand project tool info from device: "); return FALSE; } if (!fu_genesys_usbhub_device_get_info_from_project_ts(self, buf, bufsz, error)) @@ -1675,26 +1666,52 @@ return FALSE; } - if (!fu_genesys_usbhub_device_get_public_key(self, bank, error)) + if (!fu_genesys_usbhub_device_ensure_public_key(self, bank, error)) return FALSE; } /* add specific product info */ if (self->running_bank != FU_GENESYS_FW_STATUS_MASK) { - const gchar *vendor = fwupd_device_get_vendor(FWUPD_DEVICE(device)); + const guint idx = fu_usb_device_get_manufacturer_index(FU_USB_DEVICE(device)); g_autofree gchar *guid = NULL; - guid = fwupd_guid_hash_data(self->st_vendor_ts->data, - self->st_vendor_ts->len, + if (idx != 0x00) { + /* get manufacturer */ + g_autofree gchar *tmp = NULL; + g_autoptr(GError) error_local = NULL; + tmp = fu_usb_device_get_string_descriptor(FU_USB_DEVICE(device), + idx, + &error_local); + if (tmp != NULL) + fu_device_add_instance_strup(device, "VENDOR", g_strchomp(tmp)); + else + g_debug("failed to load manufacturer string: %s", + error_local->message); + } else { + /* use default vendor */ + fu_device_add_instance_strup(device, + "VENDOR", + fu_device_get_vendor(device)); + } + + guid = fwupd_guid_hash_data(self->st_vendor_ts->buf->data, + self->st_vendor_ts->buf->len, FWUPD_GUID_FLAG_NONE); - fu_device_add_instance_strup(device, "VENDOR", vendor); fu_device_add_instance_strup(device, "VENDORSUP", guid); } - if (!fu_device_build_instance_id(device, error, "USB", "VID", "PID", "IC", NULL)) - return FALSE; if (!fu_device_build_instance_id(device, error, "USB", "VID", "PID", "IC", "BONDING", NULL)) return FALSE; + if (!fu_device_build_instance_id(device, + error, + "USB", + "VID", + "PID", + "IC", + "BONDING", + "RUNMODE", + NULL)) + return FALSE; fu_device_build_instance_id(device, NULL, "USB", @@ -1719,10 +1736,9 @@ } static void -fu_genesys_usbhub_device_codesign_to_string(FuDevice *device, guint idt, GString *str) +fu_genesys_usbhub_device_codesign_to_string(FuGenesysUsbhubDevice *self, guint idt, GString *str) { - FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); - guint64 fw_max_size = fu_device_get_firmware_size_max(device); + guint64 fw_max_size = fu_device_get_firmware_size_max(FU_DEVICE(self)); guint32 bank_addr1 = self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_CODESIGN]; guint32 bank_addr2 = self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_CODESIGN]; guint idt_detail = idt + 1; @@ -1758,6 +1774,10 @@ idt, "CFI", fu_device_get_name(FU_DEVICE(self->cfi_device))); + fwupd_codec_string_append_hex(str, + idt_detail, + "FlashCapacity", + fu_cfi_device_get_size(self->cfi_device)); } fwupd_codec_string_append_int(str, idt_detail, "FlashEraseDelay", self->flash_erase_delay); fwupd_codec_string_append_int(str, idt_detail, "FlashWriteDelay", self->flash_write_delay); @@ -1779,7 +1799,7 @@ if (i == FU_GENESYS_FW_TYPE_CODESIGN) { if (self->has_codesign) - fu_genesys_usbhub_device_codesign_to_string(device, idt + 1, str); + fu_genesys_usbhub_device_codesign_to_string(self, idt + 1, str); continue; } @@ -1947,7 +1967,6 @@ { FuGenesysFwCodesign codesign_type = FU_GENESYS_FW_CODESIGN_NONE; g_autoptr(GInputStream) stream = NULL; - g_autoptr(GByteArray) st_codesign = NULL; g_return_val_if_fail(FU_IS_GENESYS_USBHUB_CODESIGN_FIRMWARE(firmware), FALSE); @@ -1973,12 +1992,12 @@ } /* compare dev and fw public-key */ - switch (self->codesign) { - case FU_GENESYS_FW_CODESIGN_RSA: { + if (self->st_rsa_pubkey != NULL) { g_autofree gchar *fw_n = NULL; g_autofree gchar *fw_e = NULL; g_autofree gchar *dev_n = NULL; g_autofree gchar *dev_e = NULL; + g_autoptr(FuStructGenesysFwCodesignInfoRsa) st_codesign = NULL; /* parse and validate */ st_codesign = @@ -1991,11 +2010,11 @@ } fu_dump_raw(G_LOG_DOMAIN, "PublicKey", - st_codesign->data, - FU_STRUCT_GENESYS_FW_CODESIGN_INFO_RSA_SIZE); + st_codesign->buf->data, + st_codesign->buf->len); fw_n = fu_struct_genesys_fw_codesign_info_rsa_get_text_n(st_codesign); - dev_n = fu_struct_genesys_fw_rsa_public_key_text_get_text_n(self->st_public_key); + dev_n = fu_struct_genesys_fw_rsa_public_key_text_get_text_n(self->st_rsa_pubkey); if (!fu_memcmp_safe((const guint8 *)fw_n, FU_STRUCT_GENESYS_FW_CODESIGN_INFO_RSA_SIZE_TEXT_N, 0, @@ -2004,12 +2023,12 @@ 0, FU_STRUCT_GENESYS_FW_RSA_PUBLIC_KEY_TEXT_SIZE_TEXT_N, error)) { - g_prefix_error(error, "mismatch public-keyN: "); + g_prefix_error_literal(error, "mismatch public-keyN: "); return FALSE; } fw_e = fu_struct_genesys_fw_codesign_info_rsa_get_text_e(st_codesign); - dev_e = fu_struct_genesys_fw_rsa_public_key_text_get_text_e(self->st_public_key); + dev_e = fu_struct_genesys_fw_rsa_public_key_text_get_text_e(self->st_rsa_pubkey); if (!fu_memcmp_safe((const guint8 *)fw_e, FU_STRUCT_GENESYS_FW_CODESIGN_INFO_RSA_SIZE_TEXT_E, 0, @@ -2018,14 +2037,16 @@ 0, FU_STRUCT_GENESYS_FW_RSA_PUBLIC_KEY_TEXT_SIZE_TEXT_E, error)) { - g_prefix_error(error, "mismatch public-keyE: "); + g_prefix_error_literal(error, "mismatch public-keyE: "); return FALSE; } - break; + return TRUE; } - case FU_GENESYS_FW_CODESIGN_ECDSA: { + + if (self->codesign == FU_GENESYS_FW_CODESIGN_ECDSA) { gsize fw_keysz = 0; const guint8 *fw_key = NULL; + g_autoptr(FuStructGenesysFwCodesignInfoEcdsa) st_codesign = NULL; /* parse and validate */ st_codesign = @@ -2053,28 +2074,20 @@ 0, FU_STRUCT_GENESYS_FW_ECDSA_PUBLIC_KEY_SIZE, error)) { - g_prefix_error(error, "mismatch public-key: "); + g_prefix_error_literal(error, "mismatch public-key: "); fu_dump_raw(G_LOG_DOMAIN, "PublicKey", fw_key, fw_keysz); return FALSE; } - break; - } - default: - break; + return TRUE; } /* does not exist */ - if (st_codesign == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "unsupported codesign type %s", - fu_genesys_fw_codesign_to_string(codesign_type)); - return FALSE; - } - - /* success */ - return TRUE; + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "unsupported codesign type %s", + fu_genesys_fw_codesign_to_string(codesign_type)); + return FALSE; } static gboolean @@ -2148,7 +2161,7 @@ fu_genesys_usbhub_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(device); @@ -2228,7 +2241,7 @@ NULL, error)) { g_prefix_error(error, - "error erasing flash at sector 0x%02x in block 0x%02x", + "error erasing flash at sector 0x%02x in block 0x%02x: ", sectornum, blocknum); return FALSE; @@ -2240,7 +2253,7 @@ self->flash_erase_delay / 30, &helper, error)) { - g_prefix_error(error, "error erasing flash: "); + g_prefix_error_literal(error, "error erasing flash: "); return FALSE; } fu_progress_step_done(progress); @@ -2299,7 +2312,7 @@ self->flash_write_delay / 30, &helper, error)) { - g_prefix_error(error, "error writing flash: "); + g_prefix_error_literal(error, "error writing flash: "); return FALSE; } fu_progress_step_done(progress); @@ -2546,7 +2559,7 @@ GENESYS_USBHUB_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error setting up HW module: "); + g_prefix_error_literal(error, "error setting up HW module: "); return FALSE; } @@ -2585,7 +2598,7 @@ static gboolean fu_genesys_usbhub_device_send_hash_digest(FuGenesysUsbhubDevice *self, - GByteArray *st_codesign, + FuStructGenesysFwCodesignInfoEcdsa *st_codesign, GError **error) { gsize hash_bufsz = 0; @@ -2594,13 +2607,16 @@ hash_buf = fu_struct_genesys_fw_codesign_info_ecdsa_get_hash(st_codesign, &hash_bufsz); if (hash_buf == NULL) { - g_prefix_error(error, "failed to get hash digest: "); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_AUTH_FAILED, + "failed to get hash digest"); return FALSE; } buf_mut = fu_memdup_safe(hash_buf, hash_bufsz, error); if (buf_mut == NULL) { - g_prefix_error(error, "failed to dup hash digest: "); + g_prefix_error_literal(error, "failed to dup hash digest: "); return FALSE; } @@ -2618,7 +2634,7 @@ GENESYS_USBHUB_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error sending hash digest: "); + g_prefix_error_literal(error, "error sending hash digest: "); return FALSE; } @@ -2645,7 +2661,7 @@ GENESYS_USBHUB_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error getting hash digest verification: "); + g_prefix_error_literal(error, "error getting hash digest verification: "); return FALSE; } @@ -2691,7 +2707,7 @@ static gboolean fu_genesys_usbhub_device_send_signature(FuGenesysUsbhubDevice *self, - GByteArray *st_codesign, + FuStructGenesysFwCodesignInfoEcdsa *st_codesign, GError **error) { gsize sig_bufsz = 0; @@ -2700,13 +2716,16 @@ sig_buf = fu_struct_genesys_fw_codesign_info_ecdsa_get_signature(st_codesign, &sig_bufsz); if (sig_buf == NULL) { - g_prefix_error(error, "failed to get signature: "); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_AUTH_FAILED, + "failed to get signature"); return FALSE; } buf_mut = fu_memdup_safe(sig_buf, sig_bufsz, error); if (buf_mut == NULL) { - g_prefix_error(error, "failed to dup signature: "); + g_prefix_error_literal(error, "failed to dup signature: "); return FALSE; } @@ -2724,7 +2743,7 @@ GENESYS_USBHUB_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error sending signature: "); + g_prefix_error_literal(error, "error sending signature: "); return FALSE; } @@ -2751,7 +2770,7 @@ GENESYS_USBHUB_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "error getting signature verification: "); + g_prefix_error_literal(error, "error getting signature verification: "); return FALSE; } @@ -2809,7 +2828,7 @@ gsize codesize_to_hash = 0; g_autoptr(FuFirmware) codesign_img = NULL; g_autoptr(GInputStream) stream = NULL; - g_autoptr(GByteArray) st_codesign = NULL; + g_autoptr(FuStructGenesysFwCodesignInfoEcdsa) st_codesign = NULL; g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); /* get fw codesign info */ @@ -2972,7 +2991,7 @@ firmware, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to verify HW codesign: "); + g_prefix_error_literal(error, "failed to verify HW codesign: "); return FALSE; } fu_progress_step_done(progress); @@ -3044,6 +3063,125 @@ /* success */ return TRUE; } + /* fw bank */ + if (g_strcmp0(key, "GenesysSupportDualBank") == 0) { + if (!fu_strtobool(value, &self->spec.support_dual_bank, error)) + return FALSE; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysSupportCodeSize") == 0) { + if (!fu_strtobool(value, &self->spec.support_code_size, error)) + return FALSE; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysHubBank1Address") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_HUB] = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysHubBank2Address") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_HUB] = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysHubBankCapacity") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_HUB] = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysDevBank1Address") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_DEV_BRIDGE] = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysDevBank2Address") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_DEV_BRIDGE] = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysDevBankCapacity") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_DEV_BRIDGE] = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysPdBank1Address") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_PD] = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysPdBank2Address") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_PD] = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysPdBankCapacity") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_PD] = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysCodesignBank1Address") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_bank_addr[FW_BANK_1][FU_GENESYS_FW_TYPE_CODESIGN] = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysCodesignBank2Address") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_bank_addr[FW_BANK_2][FU_GENESYS_FW_TYPE_CODESIGN] = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysCodesignBankCapacity") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_bank_capacity[FU_GENESYS_FW_TYPE_CODESIGN] = tmp; + + /* success */ + return TRUE; + } + if (g_strcmp0(key, "GenesysFwDataMaxCount") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->spec.fw_data_max_count = tmp; + + /* success */ + return TRUE; + } /* failure */ g_set_error_literal(error, @@ -3088,14 +3226,18 @@ FuGenesysUsbhubDevice *self = FU_GENESYS_USBHUB_DEVICE(object); if (self->st_static_ts != NULL) fu_struct_genesys_ts_static_unref(self->st_static_ts); - if (self->st_dynamic_ts != NULL) - g_byte_array_unref(self->st_dynamic_ts); if (self->st_fwinfo_ts != NULL) - g_byte_array_unref(self->st_fwinfo_ts); + fu_struct_genesys_ts_firmware_info_unref(self->st_fwinfo_ts); if (self->st_vendor_ts != NULL) - g_byte_array_unref(self->st_vendor_ts); + fu_struct_genesys_ts_vendor_support_unref(self->st_vendor_ts); if (self->st_project_ts != NULL) - g_byte_array_unref(self->st_project_ts); + fu_struct_genesys_ts_brand_project_unref(self->st_project_ts); + if (self->st_rsa_pubkey != NULL) + fu_struct_genesys_fw_rsa_public_key_text_unref(self->st_rsa_pubkey); + if (self->st_ecdsa_pubkey != NULL) + fu_struct_genesys_fw_ecdsa_public_key_unref(self->st_ecdsa_pubkey); + if (self->st_codesign_ecdsa != NULL) + fu_struct_genesys_fw_codesign_info_ecdsa_unref(self->st_codesign_ecdsa); if (self->hub_fw_bank1_data != NULL) g_bytes_unref(self->hub_fw_bank1_data); if (self->st_codesign != NULL) diff -Nru fwupd-2.0.8/plugins/genesys/fu-genesys-usbhub-device.h fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-device.h --- fwupd-2.0.8/plugins/genesys/fu-genesys-usbhub-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -16,4 +16,4 @@ FuUsbDevice) void -fu_genesys_usbhub_device_set_hid_channel(FuDevice *device, FuDevice *channel); +fu_genesys_usbhub_device_set_hid_channel(FuGenesysUsbhubDevice *self, FuDevice *channel); diff -Nru fwupd-2.0.8/plugins/genesys/fu-genesys-usbhub-firmware.c fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-firmware.c --- fwupd-2.0.8/plugins/genesys/fu-genesys-usbhub-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,8 +15,8 @@ #include "fu-genesys-usbhub-struct.h" struct _FuGenesysUsbhubFirmware { - FuFirmwareClass parent_instance; - GByteArray *st_static_ts; + FuFirmware parent_instance; + FuStructGenesysTsStatic *st_static_ts; FuGenesysChip chip; }; @@ -27,7 +27,7 @@ GInputStream *stream, GError **error) { - guint8 project_ic_type[6]; + guint8 project_ic_type[6] = {0}; /* recognize GL3523 code base product */ if (!fu_input_stream_read_safe( @@ -53,6 +53,24 @@ return TRUE; } + /* recognize GL3523PLUS */ + if (!fu_input_stream_read_safe( + stream, + project_ic_type, + sizeof(project_ic_type), + 0, /* dst */ + GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3523PLUS + + FU_STRUCT_GENESYS_TS_STATIC_OFFSET_MASK_PROJECT_IC_TYPE, /* src */ + sizeof(project_ic_type), + error)) + return FALSE; + + if (memcmp(project_ic_type, "3523", 4) == 0) { + self->chip.model = ISP_MODEL_HUB_GL3523PLUS; + self->chip.revision = 10 * (project_ic_type[4] - '0') + (project_ic_type[5] - '0'); + return TRUE; + } + /* recognize GL3590 */ if (!fu_input_stream_read_safe( stream, @@ -134,7 +152,7 @@ &fw_checksum, G_BIG_ENDIAN, error)) { - g_prefix_error(error, "failed to get checksum: "); + g_prefix_error_literal(error, "failed to get checksum: "); return FALSE; } @@ -162,7 +180,7 @@ { guint8 kbs = 0; if (!fu_input_stream_read_u8(stream, GENESYS_USBHUB_CODE_SIZE_OFFSET, &kbs, error)) { - g_prefix_error(error, "failed to get codesize: "); + g_prefix_error_literal(error, "failed to get codesize: "); return FALSE; } if (kbs == 0) { @@ -177,6 +195,7 @@ return TRUE; } +/* nocheck:name */ gboolean fu_genesys_usbhub_firmware_ensure_version(FuFirmware *firmware, GError **error) { @@ -192,7 +211,7 @@ &version_raw, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to get version: "); + g_prefix_error_literal(error, "failed to get version: "); return FALSE; } fu_firmware_set_version_raw(firmware, version_raw); @@ -213,7 +232,7 @@ static gboolean fu_genesys_usbhub_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuGenesysUsbhubFirmware *self = FU_GENESYS_USBHUB_FIRMWARE(firmware); @@ -225,7 +244,7 @@ /* get chip */ if (!fu_genesys_usbhub_firmware_get_chip(self, stream, error)) { - g_prefix_error(error, "failed to get chip: "); + g_prefix_error_literal(error, "failed to get chip: "); return FALSE; } fu_firmware_set_id(firmware, fu_genesys_fw_type_to_string(FU_GENESYS_FW_TYPE_HUB)); @@ -240,6 +259,9 @@ case ISP_MODEL_HUB_GL3523: static_ts_offset = GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3523; break; + case ISP_MODEL_HUB_GL3523PLUS: + static_ts_offset = GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3523PLUS; + break; case ISP_MODEL_HUB_GL3590: static_ts_offset = GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3590; break; @@ -277,6 +299,7 @@ } break; } + case ISP_MODEL_HUB_GL3523PLUS: case ISP_MODEL_HUB_GL3590: case ISP_MODEL_HUB_GL3525: { if (!fu_genesys_usbhub_firmware_calculate_size(stream, &code_size, error)) @@ -295,7 +318,7 @@ return FALSE; /* calculate checksum */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { if (!fu_genesys_usbhub_firmware_verify_checksum(stream_trunc, error)) return FALSE; } @@ -312,18 +335,19 @@ g_autoptr(FuFirmware) firmware_sub = NULL; firmware_sub = fu_firmware_new_from_gtypes(stream, offset, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error, FU_TYPE_GENESYS_USBHUB_DEV_FIRMWARE, FU_TYPE_GENESYS_USBHUB_PD_FIRMWARE, FU_TYPE_GENESYS_USBHUB_CODESIGN_FIRMWARE, G_TYPE_INVALID); if (firmware_sub == NULL) { - g_prefix_error(error, "fw bytes have dual hub firmware: "); + g_prefix_error_literal(error, "fw bytes have dual hub firmware: "); return FALSE; } fu_firmware_set_offset(firmware_sub, offset); - fu_firmware_add_image(firmware, firmware_sub); + if (!fu_firmware_add_image(firmware, firmware_sub, error)) + return FALSE; offset += fu_firmware_get_size(firmware_sub); } @@ -358,10 +382,10 @@ if (!fu_memcpy_safe(buf->data, buf->len, GENESYS_USBHUB_STATIC_TOOL_STRING_OFFSET_GL3523, /* dst */ - self->st_static_ts->data, - self->st_static_ts->len, + self->st_static_ts->buf->data, + self->st_static_ts->buf->len, 0x0, /* src */ - self->st_static_ts->len, + self->st_static_ts->buf->len, error)) return NULL; } @@ -370,7 +394,7 @@ if (!fu_memwrite_uint16_safe(buf->data, buf->len, GENESYS_USBHUB_VERSION_OFFSET, - 0x1234, // TODO: parse from firmware version string + 0x1234, /* TODO: parse from firmware version string */ G_BIG_ENDIAN, error)) return NULL; diff -Nru fwupd-2.0.8/plugins/genesys/fu-genesys-usbhub-pd-firmware.c fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-pd-firmware.c --- fwupd-2.0.8/plugins/genesys/fu-genesys-usbhub-pd-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/fu-genesys-usbhub-pd-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,7 +12,7 @@ #include "fu-genesys-usbhub-struct.h" struct _FuGenesysUsbhubPdFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; }; G_DEFINE_TYPE(FuGenesysUsbhubPdFirmware, fu_genesys_usbhub_pd_firmware, FU_TYPE_FIRMWARE) @@ -29,7 +29,7 @@ static gboolean fu_genesys_usbhub_pd_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize code_size = 0; @@ -41,7 +41,7 @@ /* truncate to correct size */ if (!fu_genesys_usbhub_firmware_calculate_size(stream, &code_size, error)) { - g_prefix_error(error, "not valid for pd: "); + g_prefix_error_literal(error, "not valid for pd: "); return FALSE; } stream_trunc = fu_partial_input_stream_new(stream, 0x0, code_size, error); @@ -51,16 +51,16 @@ return FALSE; /* calculate checksum */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { if (!fu_genesys_usbhub_firmware_verify_checksum(stream_trunc, error)) { - g_prefix_error(error, "not valid for pd: "); + g_prefix_error_literal(error, "not valid for pd: "); return FALSE; } } /* get firmware version */ if (!fu_genesys_usbhub_firmware_ensure_version(firmware, error)) { - g_prefix_error(error, "not valid for pd: "); + g_prefix_error_literal(error, "not valid for pd: "); return FALSE; } diff -Nru fwupd-2.0.8/plugins/genesys/genesys-usbhub-fwbank.quirk fwupd-2.0.20/plugins/genesys/genesys-usbhub-fwbank.quirk --- fwupd-2.0.8/plugins/genesys/genesys-usbhub-fwbank.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/genesys-usbhub-fwbank.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,73 @@ +# GL3523 +[GENESYS_USBHUB\IC_3523] +GenesysSupportDualBank = true +GenesysHubBank2Address = 0x8000 +GenesysHubBankCapacity = 0x6000 +GenesysCodesignBank1Address = 0x6000 +GenesysCodesignBank2Address = 0xe000 +GenesysCodesignBankCapacity = 0x400 +GenesysFwDataMaxCount = 0x10000 + +# 352350 changes: +# hub supports flexible code size +# hub code capacity +# codesign bank addresses +[GENESYS_USBHUB\IC_352350] +GenesysSupportCodeSize = true +GenesysHubBankCapacity = 0x8000 +GenesysCodesignBank1Address = 0x7c00 +GenesysCodesignBank2Address = 0xfc00 + +# 352360(3523plus) changes: +# hub supports flexible code size +# hub code capacity and bank2 address +[GENESYS_USBHUB\IC_352360] +GenesysSupportCodeSize = true +GenesysHubBank2Address = 0x10000 +GenesysHubBankCapacity = 0x10000 +GenesysFwDataMaxCount = 0x20000 + +# GL3525 +[GENESYS_USBHUB\IC_3525] +GenesysSupportDualBank = true +GenesysSupportCodeSize = true +GenesysHubBank2Address = 0xb000 +GenesysHubBankCapacity = 0xb000 +GenesysPdBank1Address = 0x16000 +GenesysPdBank2Address = 0x23000 +GenesysPdBankCapacity = 0xd000 +GenesysDevBank1Address = 0x30000 +GenesysDevBank2Address = 0x38000 +GenesysDevBankCapacity = 0x8000 +GenesysCodesignBank1Address = 0x16000 +GenesysCodesignBank2Address = 0x17000 +GenesysCodesignBankCapacity = 0x1000 +GenesysFwDataMaxCount = 0x40000 + +# 352530 changes: +# hub code capacity and bank2 address +# dev bank addresses +# pd code capacity and bank addresses +[GENESYS_USBHUB\IC_352530] +GenesysHubBank2Address = 0x10000 +GenesysHubBankCapacity = 0x10000 +GenesysDevBank1Address = 0x20000 +GenesysDevBank2Address = 0x30000 +GenesysPdBank1Address = 0x40000 +GenesysPdBank2Address = 0x60000 +GenesysPdBankCapacity = 0x20000 +GenesyswDataMaxCount = 0x80000 + +# GL3590 +[GENESYS_USBHUB\IC_3590] +GenesysSupportDualBank = true +GenesysSupportCodeSize = true +GenesysHubBank2Address = 0x10000 +GenesysHubBankCapacity = 0x10000 +GenesysDevBank1Address = 0x20000 +GenesysDevBank2Address = 0x30000 +GenesysDevBankCapacity = 0x10000 +GenesysCodesignBank1Address = 0xff00 +GenesysCodesignBank2Address = 0x1ff00 +GenesysCodesignBankCapacity = 0x100 +GenesysFwDataMaxCount = 0x40000 diff -Nru fwupd-2.0.8/plugins/genesys/meson.build fwupd-2.0.20/plugins/genesys/meson.build --- fwupd-2.0.8/plugins/genesys/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,7 +1,7 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginGenesys"'] plugins += {meson.current_source_dir().split('/')[-1]: true} -plugin_quirks += files('genesys.quirk') +plugin_quirks += files('genesys.quirk', 'genesys-usbhub-fwbank.quirk') plugin_builtins += static_library('fu_plugin_genesys', rustgen.process( 'fu-genesys-usbhub.rs', # fuzzing @@ -22,3 +22,10 @@ c_args: cargs, dependencies: plugin_deps, ) + +device_tests += files( + 'tests/genesys-evb.json', +# 'tests/genesys-evb-failure.json', # cannot emulate two devices(usb hub + child hid). + 'tests/genesys-servo-dock.json', + 'tests/genesys-zdn-chromebook.json', +) diff -Nru fwupd-2.0.8/plugins/genesys/tests/genesys-evb-failure.json fwupd-2.0.20/plugins/genesys/tests/genesys-evb-failure.json --- fwupd-2.0.8/plugins/genesys/tests/genesys-evb-failure.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/tests/genesys-evb-failure.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,34 @@ +{ + "name": "EVB", + "interactive": false, + "steps": [ + { + "url": "793188b383dbdbe1ae074a3f095c35fef3779e1c3d0b6239b58fec9742f4769e-GenesysLogic_TestEVB_GL352360_89.19.cab", + "emulation-url": "676ebed5ab3bd6a8d9a3f0d4e7ad1672c9fe4a7fd44a386b3a62ae27965f96a5-TestEVB_GL352360_emu.zip", + "components": [ + { + "name": "GL352360-HID", + "version": "89.19", + "guids": [ + "9d544514-8380-5ac6-92bf-1565f106af5e", + "361571b4-7109-59c6-b776-1132ab45bd7b" + ] + } + ] + }, + { + "url": "b25e68b9f4203d6a1fccfe5f7f269fe78c8eb099248a3da4120de5bb6a757f1b-GenesysLogic_TestEVB_GL3525_89.06.cab", + "emulation-url": "da59f16b4b7fe32b794e3d3e72be35f3926a3f55c5134e28bfef0bc08431597d-TestEVB_GL3525_C.zip", + "components": [ + { + "name": "GL352510-HID", + "version": "89.6", + "guids": [ + "9aaeccaf-ac29-5104-8e63-5ebb8e2ce039", + "0adc4b01-85c8-5c04-a6bd-1317d9305e27" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/genesys/tests/genesys-evb.json fwupd-2.0.20/plugins/genesys/tests/genesys-evb.json --- fwupd-2.0.8/plugins/genesys/tests/genesys-evb.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/tests/genesys-evb.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +{ + "name": "EVB", + "interactive": false, + "steps": [ + { + "url": "b25e68b9f4203d6a1fccfe5f7f269fe78c8eb099248a3da4120de5bb6a757f1b-GenesysLogic_TestEVB_GL3525_89.06.cab", + "emulation-url": "38954ace3092f0dff12163e6daf0940dc67ddd3b27d6cd98c8fca6ed4e4d5c7e-TestEVB_GL3525_M.zip", + "components": [ + { + "name": "GL352510", + "version": "89.6", + "guids": [ + "9aaeccaf-ac29-5104-8e63-5ebb8e2ce039" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/genesys/tests/genesys-servo-dock.json fwupd-2.0.20/plugins/genesys/tests/genesys-servo-dock.json --- fwupd-2.0.8/plugins/genesys/tests/genesys-servo-dock.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/tests/genesys-servo-dock.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,32 @@ +{ + "name": "Google Servo Dock", + "interactive": false, + "steps": [ + { + "url": "93a82341d4668b64857ca1b90c344221eb72556818b2228a19d8eafc9a283ae1-GenesysLogic_GL3590_64.17.cab", + "emulation-url": "c1e1a58150362abcdcb13b50c555fe96c12cb5e65ed2995146cbc5a6f8a3a7a3-servo_gl359010_emu.zip", + "components": [ + { + "name": "GL359010", + "version": "64.17", + "guids": [ + "5e16db46-912c-54f6-84ce-73841f287847" + ] + } + ] + }, + { + "url": "93a82341d4668b64857ca1b90c344221eb72556818b2228a19d8eafc9a283ae1-GenesysLogic_GL3590_64.17.cab", + "emulation-url": "0dde236f88e129e7fd4b3de96cbaf4fd48558b17112236fa23e193a11345b3b3-servo_gl359020_emu.zip", + "components": [ + { + "name": "GL359020", + "version": "64.17", + "guids": [ + "5e16db46-912c-54f6-84ce-73841f287847" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/genesys/tests/genesys-zdn-chromebook.json fwupd-2.0.20/plugins/genesys/tests/genesys-zdn-chromebook.json --- fwupd-2.0.8/plugins/genesys/tests/genesys-zdn-chromebook.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys/tests/genesys-zdn-chromebook.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,20 @@ +{ + "name": "ZDN Chromebook", + "interactive": false, + "steps": [ + { + "url": "3a74af7efa88de7400208af472387c65fbddc1e924ba2ff8dc727117210e1981-GenesysLogic_ZDN_Chromebook_GL3523_65.08.cab", + "emulation-url": "6c710a5171528c39277648d9798ee96537057c416b17964fa2e9dfe1ea047323-zdn_gl352330_emu.zip", + "components": [ + { + "name": "GL352330", + "version": "65.8", + "guids": [ + "b4c454aa-eb70-5bdd-82c9-dde4577500b3", + "50ebd712-6f74-5df7-8881-0da89056c936" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/genesys-gl32xx/fu-genesys-gl32xx-device.c fwupd-2.0.20/plugins/genesys-gl32xx/fu-genesys-gl32xx-device.c --- fwupd-2.0.8/plugins/genesys-gl32xx/fu-genesys-gl32xx-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys-gl32xx/fu-genesys-gl32xx-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -53,7 +53,7 @@ { const guint8 cmd[] = {0xF3, 0x06, 0x00, 0x00, 0x00, 0x00}; if (!fu_block_device_sg_io_cmd_none(FU_BLOCK_DEVICE(self), cmd, sizeof(cmd), error)) { - g_prefix_error(error, "failed to switch into ROM mode: "); + g_prefix_error_literal(error, "failed to switch into ROM mode: "); return FALSE; } @@ -66,7 +66,7 @@ { const guint8 cmd[] = {0xE6, 0x00, 0x00, 0x00, 0x00, 0x00}; if (!fu_block_device_sg_io_cmd_none(FU_BLOCK_DEVICE(self), cmd, sizeof(cmd), error)) { - g_prefix_error(error, "failed to reset USB: "); + g_prefix_error_literal(error, "failed to reset USB: "); return FALSE; } @@ -158,7 +158,7 @@ buf = fu_genesys_gl32xx_device_cmd_get_version(self, error); if (buf == NULL) { - g_prefix_error(error, "failed to read version: "); + g_prefix_error_literal(error, "failed to read version: "); return FALSE; } if (buf->len < 0x24) { @@ -387,7 +387,7 @@ &mode, sizeof(mode), error)) { - g_prefix_error(error, "failed to read USB mode: "); + g_prefix_error_literal(error, "failed to read USB mode: "); return FALSE; } switch (mode) { @@ -418,33 +418,33 @@ { /* write enable */ if (!fu_genesys_gl32xx_device_cmd_write_enable(self, error)) { - g_prefix_error(error, "failed to write enable: "); + g_prefix_error_literal(error, "failed to write enable: "); return FALSE; } /* clear write protect */ if (!fu_genesys_gl32xx_device_cmd_clear_wp(self, error)) { - g_prefix_error(error, "failed to clear WP: "); + g_prefix_error_literal(error, "failed to clear WP: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), FU_GENESYS_GL32XX_CLEAR_WP_SLEEP_MS); /* write enable */ if (!fu_genesys_gl32xx_device_cmd_write_enable(self, error)) { - g_prefix_error(error, "failed to write enable: "); + g_prefix_error_literal(error, "failed to write enable: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), FU_GENESYS_GL32XX_CLEAR_WP_SLEEP_MS); /* chip erase */ if (!fu_genesys_gl32xx_device_cmd_chip_erase(self, error)) { - g_prefix_error(error, "failed to erase chip: "); + g_prefix_error_literal(error, "failed to erase chip: "); return FALSE; } /* wait WIP to reset back to 0 */ if (!fu_genesys_gl32xx_device_cmd_wait_wip(self, error)) { - g_prefix_error(error, "failed to wait WIP: "); + g_prefix_error_literal(error, "failed to wait WIP: "); return FALSE; } @@ -478,7 +478,7 @@ /* clear SR */ if (!fu_genesys_gl32xx_device_cmd_write_sr(self, error)) { - g_prefix_error(error, "failed to clear SR: "); + g_prefix_error_literal(error, "failed to clear SR: "); return FALSE; } @@ -504,7 +504,7 @@ return FALSE; if (!fu_genesys_gl32xx_device_ensure_rom_mode(self, error)) { - g_prefix_error(error, "failed to check ROM mode: "); + g_prefix_error_literal(error, "failed to check ROM mode: "); return FALSE; } @@ -527,10 +527,11 @@ } static GBytes * -fu_genesys_gl32xx_device_dump_bytes(FuDevice *device, FuProgress *progress, GError **error) +fu_genesys_gl32xx_device_dump_bytes(FuGenesysGl32xxDevice *self, + FuProgress *progress, + GError **error) { - FuGenesysGl32xxDevice *self = FU_GENESYS_GL32XX_DEVICE(device); - const gsize fwsz = fu_device_get_firmware_size_max(device); + const gsize fwsz = fu_device_get_firmware_size_max(FU_DEVICE(self)); g_autoptr(GPtrArray) chunks = NULL; g_autofree guint8 *buf = g_malloc0(fwsz); @@ -569,7 +570,7 @@ if (locker == NULL) return NULL; fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - fw = fu_genesys_gl32xx_device_dump_bytes(device, progress, error); + fw = fu_genesys_gl32xx_device_dump_bytes(self, progress, error); if (fw == NULL) return NULL; @@ -586,7 +587,7 @@ fw = fu_genesys_gl32xx_device_dump_firmware(device, progress, error); if (fw == NULL) return NULL; - if (!fu_firmware_parse_bytes(firmware, fw, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_bytes(firmware, fw, 0x0, FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, error)) return NULL; /* success */ @@ -597,7 +598,7 @@ fu_genesys_gl32xx_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) firmware = fu_genesys_gl32xx_firmware_new(); @@ -610,10 +611,9 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "firmware size is [%" G_GSIZE_FORMAT - "] bytes while expecting [%" G_GUINT64_FORMAT "] bytes", - fu_firmware_get_size(firmware), - fu_device_get_firmware_size_max(device)); + "firmware size 0x%x while expecting 0x%x bytes", + (guint)fu_firmware_get_size(firmware), + (guint)fu_device_get_firmware_size_max(device)); return NULL; } @@ -642,7 +642,7 @@ data, datasz, error)) { - g_prefix_error(error, "failed to write flash data: "); + g_prefix_error_literal(error, "failed to write flash data: "); return FALSE; } @@ -719,7 +719,7 @@ fu_progress_step_done(progress); /* verify written data */ - fw_read = fu_genesys_gl32xx_device_dump_bytes(device, progress, error); + fw_read = fu_genesys_gl32xx_device_dump_bytes(self, progress, error); if (fw_read == NULL) return FALSE; fu_progress_step_done(progress); @@ -734,7 +734,7 @@ /* write disable */ if (!fu_genesys_gl32xx_device_cmd_write_disable(self, error)) { - g_prefix_error(error, "failed to write disable: "); + g_prefix_error_literal(error, "failed to write disable: "); return FALSE; } @@ -743,7 +743,7 @@ } static void -fu_genesys_gl32xx_device_set_progress(FuDevice *self, FuProgress *progress) +fu_genesys_gl32xx_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/genesys-gl32xx/fu-genesys-gl32xx-firmware.c fwupd-2.0.20/plugins/genesys-gl32xx/fu-genesys-gl32xx-firmware.c --- fwupd-2.0.8/plugins/genesys-gl32xx/fu-genesys-gl32xx-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys-gl32xx/fu-genesys-gl32xx-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -22,7 +22,7 @@ static gboolean fu_genesys_gl32xx_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint8 ver[4] = {0}; @@ -41,7 +41,7 @@ fu_firmware_set_version(firmware, version); /* verify checksum */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { gsize streamsz = 0; guint8 chksum_actual = 0; guint8 chksum_expected = 0; diff -Nru fwupd-2.0.8/plugins/genesys-gl32xx/meson.build fwupd-2.0.20/plugins/genesys-gl32xx/meson.build --- fwupd-2.0.8/plugins/genesys-gl32xx/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/genesys-gl32xx/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginGenesysGl32xx"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -14,4 +15,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/goodix-moc/README.md fwupd-2.0.20/plugins/goodix-moc/README.md --- fwupd-2.0.8/plugins/goodix-moc/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -37,10 +37,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.5.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Boger Wang: @boger047 diff -Nru fwupd-2.0.8/plugins/goodix-moc/fu-goodix-moc-common.h fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-common.h --- fwupd-2.0.8/plugins/goodix-moc/fu-goodix-moc-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,66 @@ +/* + * Copyright 2016 Richard Hughes + * Copyright 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +/* protocol */ +#define FU_GOODIX_MOC_CMD_ACK 0xAA +#define FU_GOODIX_MOC_CMD_VERSION 0xD0 +#define FU_GOODIX_MOC_CMD_RESET 0xB4 +#define FU_GOODIX_MOC_CMD_UPGRADE 0x80 +#define FU_GOODIX_MOC_CMD_UPGRADE_INIT 0x00 +#define FU_GOODIX_MOC_CMD_UPGRADE_DATA 0x01 +#define FU_GOODIX_MOC_CMD1_DEFAULT 0x00 + +#define GX_SIZE_CRC32 4 + +/* type covert */ +#define MAKE_CMD_EX(cmd0, cmd1) ((guint16)(((cmd0) << 8) | (cmd1))) + +typedef struct { + guint8 format[2]; + guint8 fwtype[8]; + guint8 fwversion[8]; + guint8 customer[8]; + guint8 mcu[8]; + guint8 sensor[8]; + guint8 algversion[8]; + guint8 interface[8]; + guint8 protocol[8]; + guint8 flashVersion[8]; + guint8 reserved[62]; +} FuGoodixMocVersionInfo; + +typedef struct { + guint8 cmd; + gboolean configured; +} FuGoodixMocAckMsg; + +typedef struct { + guint8 result; + union { + FuGoodixMocAckMsg ack_msg; + FuGoodixMocVersionInfo version_info; + }; +} FuGoodixMocCmdResp; + +typedef enum { + GX_PKG_TYPE_NORMAL = 0x80, + GX_PKG_TYPE_EOP = 0, +} FuGoodixMocPkgType; + +typedef struct __attribute__((__packed__)) { /* nocheck:blocked */ + guint8 cmd0; + guint8 cmd1; + guint8 pkg_flag; + guint8 reserved; + guint16 len; + guint8 crc8; + guint8 rev_crc8; +} FuGoodixMocPkgHeader; diff -Nru fwupd-2.0.8/plugins/goodix-moc/fu-goodix-moc-device.c fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-device.c --- fwupd-2.0.8/plugins/goodix-moc/fu-goodix-moc-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,462 @@ +/* + * Copyright 2016 Richard Hughes + * Copyright 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-goodix-moc-common.h" +#include "fu-goodix-moc-device.h" + +struct _FuGoodixMocDevice { + FuUsbDevice parent_instance; + guint8 dummy_seq; +}; + +G_DEFINE_TYPE(FuGoodixMocDevice, fu_goodix_moc_device, FU_TYPE_USB_DEVICE) + +#define GX_USB_BULK_EP_IN (3 | 0x80) +#define GX_USB_BULK_EP_OUT (1 | 0x00) +#define GX_USB_INTERFACE 0 + +#define GX_USB_DATAIN_TIMEOUT 2000 /* ms */ +#define GX_USB_DATAOUT_TIMEOUT 200 /* ms */ +#define GX_FLASH_TRANSFER_BLOCK_SIZE 1000 /* 1000 */ + +static gboolean +fu_goodix_moc_device_cmd_send(FuGoodixMocDevice *self, + guint8 cmd0, + guint8 cmd1, + FuGoodixMocPkgType type, + GByteArray *req, + GError **error) +{ + guint32 crc_all = 0; + guint32 crc_hdr = 0; + gsize actual_len = 0; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* build header */ + fu_byte_array_append_uint8(buf, cmd0); + fu_byte_array_append_uint8(buf, cmd1); + fu_byte_array_append_uint8(buf, type); /* pkg_flag */ + fu_byte_array_append_uint8(buf, self->dummy_seq++); /* reserved */ + fu_byte_array_append_uint16(buf, req->len + GX_SIZE_CRC32, G_LITTLE_ENDIAN); + crc_hdr = ~fu_crc8(FU_CRC_KIND_B8_STANDARD, buf->data, buf->len); + fu_byte_array_append_uint8(buf, crc_hdr); + fu_byte_array_append_uint8(buf, ~crc_hdr); + g_byte_array_append(buf, req->data, req->len); + crc_all = fu_crc32(FU_CRC_KIND_B32_STANDARD, buf->data, buf->len); + fu_byte_array_append_uint32(buf, crc_all, G_LITTLE_ENDIAN); + + /* send zero length package */ + if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), + GX_USB_BULK_EP_OUT, + NULL, + 0, + NULL, + GX_USB_DATAOUT_TIMEOUT, + NULL, + error)) { + g_prefix_error_literal(error, "failed to req: "); + return FALSE; + } + fu_dump_full(G_LOG_DOMAIN, "REQST", buf->data, buf->len, 16, FU_DUMP_FLAGS_SHOW_ADDRESSES); + + /* send data */ + if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), + GX_USB_BULK_EP_OUT, + buf->data, + buf->len, + &actual_len, + GX_USB_DATAOUT_TIMEOUT, + NULL, + error)) { + g_prefix_error_literal(error, "failed to req: "); + return FALSE; + } + if (actual_len != buf->len) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid length"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_goodix_moc_device_cmd_recv(FuGoodixMocDevice *self, + FuGoodixMocCmdResp *presponse, + gboolean data_reply, + GError **error) +{ + guint32 crc_actual = 0; + guint32 crc_calculated = 0; + gsize actual_len = 0; + gsize offset = 0; + + g_return_val_if_fail(presponse != NULL, FALSE); + + /* + * package format + * | zlp | ack | zlp | data | + */ + while (1) { + guint16 header_len = 0x0; + guint8 header_cmd0 = 0x0; + g_autoptr(GByteArray) reply = g_byte_array_new(); + fu_byte_array_set_size(reply, GX_FLASH_TRANSFER_BLOCK_SIZE, 0x00); + if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), + GX_USB_BULK_EP_IN, + reply->data, + reply->len, + &actual_len, /* allowed to return short read */ + GX_USB_DATAIN_TIMEOUT, + NULL, + error)) { + g_prefix_error_literal(error, "failed to reply: "); + return FALSE; + } + + /* receive zero length package */ + if (actual_len == 0) + continue; + fu_dump_full(G_LOG_DOMAIN, + "REPLY", + reply->data, + actual_len, + 16, + FU_DUMP_FLAGS_SHOW_ADDRESSES); + + /* parse package header */ + if (!fu_memread_uint8_safe(reply->data, reply->len, 0x0, &header_cmd0, error)) + return FALSE; + if (!fu_memread_uint16_safe(reply->data, + reply->len, + 0x4, + &header_len, + G_LITTLE_ENDIAN, + error)) + return FALSE; + offset = sizeof(FuGoodixMocPkgHeader) + header_len - GX_SIZE_CRC32; + crc_actual = fu_crc32(FU_CRC_KIND_B32_STANDARD, reply->data, offset); + if (!fu_memread_uint32_safe(reply->data, + reply->len, + offset, + &crc_calculated, + G_LITTLE_ENDIAN, + error)) + return FALSE; + if (crc_actual != crc_calculated) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid checksum, got 0x%x, expected 0x%x", + crc_calculated, + crc_actual); + return FALSE; + } + + /* parse package data */ + if (!fu_memread_uint8_safe(reply->data, + reply->len, + sizeof(FuGoodixMocPkgHeader) + 0x00, + &presponse->result, + error)) + return FALSE; + if (header_cmd0 == FU_GOODIX_MOC_CMD_ACK) { + if (header_len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid bufsz"); + return FALSE; + } + if (!fu_memread_uint8_safe(reply->data, + reply->len, + sizeof(FuGoodixMocPkgHeader) + 0x01, + &presponse->ack_msg.cmd, + error)) + return FALSE; + } else if (header_cmd0 == FU_GOODIX_MOC_CMD_VERSION) { + if (!fu_memcpy_safe((guint8 *)&presponse->version_info, + sizeof(presponse->version_info), + 0x0, /* dst */ + reply->data, + reply->len, + sizeof(FuGoodixMocPkgHeader) + 0x01, /* src */ + sizeof(FuGoodixMocVersionInfo), + error)) + return FALSE; + } + + /* continue after ack received */ + if (header_cmd0 == FU_GOODIX_MOC_CMD_ACK && data_reply) + continue; + break; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_goodix_moc_device_cmd_xfer(FuGoodixMocDevice *device, + guint8 cmd0, + guint8 cmd1, + FuGoodixMocPkgType type, + GByteArray *req, + FuGoodixMocCmdResp *presponse, + gboolean data_reply, + GError **error) +{ + FuGoodixMocDevice *self = FU_GOODIX_MOC_DEVICE(device); + if (!fu_goodix_moc_device_cmd_send(self, cmd0, cmd1, type, req, error)) + return FALSE; + return fu_goodix_moc_device_cmd_recv(self, presponse, data_reply, error); +} + +static gboolean +fu_goodix_moc_device_setup_version(FuGoodixMocDevice *self, GError **error) +{ + FuGoodixMocCmdResp rsp = {0}; + g_autofree gchar *version = NULL; + g_autoptr(GByteArray) req = g_byte_array_new(); + + fu_byte_array_append_uint8(req, 0); /* dummy */ + if (!fu_goodix_moc_device_cmd_xfer(self, + FU_GOODIX_MOC_CMD_VERSION, + FU_GOODIX_MOC_CMD1_DEFAULT, + GX_PKG_TYPE_EOP, + req, + &rsp, + TRUE, + error)) + return FALSE; + version = g_strndup((const gchar *)rsp.version_info.fwversion, + sizeof(rsp.version_info.fwversion)); + fu_device_set_version(FU_DEVICE(self), version); + return TRUE; +} + +static gboolean +fu_goodix_moc_device_update_init(FuGoodixMocDevice *self, GError **error) +{ + FuGoodixMocCmdResp rsp = {0}; + g_autoptr(GByteArray) req = g_byte_array_new(); + + /* update initial */ + if (!fu_goodix_moc_device_cmd_xfer(self, + FU_GOODIX_MOC_CMD_UPGRADE, + FU_GOODIX_MOC_CMD_UPGRADE_INIT, + GX_PKG_TYPE_EOP, + req, + &rsp, + TRUE, + error)) { + g_prefix_error_literal(error, "failed to send initial update: "); + return FALSE; + } + + /* check result */ + if (rsp.result != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "initial update failed [0x%x]", + rsp.result); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_goodix_moc_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuGoodixMocDevice *self = FU_GOODIX_MOC_DEVICE(device); + FuGoodixMocCmdResp rsp = {0}; + g_autoptr(GByteArray) req = g_byte_array_new(); + + /* reset device */ + if (!fu_goodix_moc_device_cmd_xfer(self, + FU_GOODIX_MOC_CMD_RESET, + 0x03, + GX_PKG_TYPE_EOP, + req, + &rsp, + FALSE, + error)) { + g_prefix_error_literal(error, "failed to send reset device: "); + return FALSE; + } + + /* check result */ + if (rsp.result != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to reset device [0x%x]", + rsp.result); + return FALSE; + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_goodix_moc_device_setup(FuDevice *device, GError **error) +{ + FuGoodixMocDevice *self = FU_GOODIX_MOC_DEVICE(device); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_goodix_moc_device_parent_class)->setup(device, error)) + return FALSE; + + /* ensure version */ + if (!fu_goodix_moc_device_setup_version(self, error)) { + g_prefix_error_literal(error, "failed to get firmware version: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_goodix_moc_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuGoodixMocDevice *self = FU_GOODIX_MOC_DEVICE(device); + FuGoodixMocPkgType pkg_eop = GX_PKG_TYPE_NORMAL; + FuGoodixMocCmdResp rsp = {0}; + gboolean wait_data_reply = FALSE; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(FuChunkArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "init"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* build packets */ + chunks = fu_chunk_array_new_from_bytes(fw, + FU_CHUNK_ADDR_OFFSET_NONE, + FU_CHUNK_PAGESZ_NONE, + GX_FLASH_TRANSFER_BLOCK_SIZE); + + /* don't auto-boot firmware */ + if (!fu_goodix_moc_device_update_init(self, &error_local)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to initial update: %s", + error_local->message); + return FALSE; + } + fu_progress_step_done(progress); + + /* write each block */ + for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { + g_autoptr(FuChunk) chk = NULL; + g_autoptr(GByteArray) req = g_byte_array_new(); + g_autoptr(GError) error_block = NULL; + + /* prepare chunk */ + chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) + return FALSE; + g_byte_array_append(req, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); + + /* the last chunk */ + if (i == fu_chunk_array_length(chunks) - 1) { + wait_data_reply = TRUE; + pkg_eop = GX_PKG_TYPE_EOP; + } + if (!fu_goodix_moc_device_cmd_xfer(self, + FU_GOODIX_MOC_CMD_UPGRADE, + FU_GOODIX_MOC_CMD_UPGRADE_DATA, + pkg_eop, + req, + &rsp, + wait_data_reply, + &error_block)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to write: %s", + error_block->message); + return FALSE; + } + + /* check update status */ + if (wait_data_reply && rsp.result != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to verify firmware [0x%x]", + rsp.result); + return FALSE; + } + + /* update progress */ + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)fu_chunk_array_length(chunks)); + } + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static void +fu_goodix_moc_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_goodix_moc_device_init(FuGoodixMocDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_RUNTIME_VERSION); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_set_remove_delay(FU_DEVICE(self), 5000); + fu_device_add_protocol(FU_DEVICE(self), "com.goodix.goodixmoc"); + fu_device_set_name(FU_DEVICE(self), "Fingerprint Sensor"); + fu_device_set_summary(FU_DEVICE(self), "Match-On-Chip fingerprint sensor"); + fu_device_set_install_duration(FU_DEVICE(self), 10); + fu_device_set_firmware_size_min(FU_DEVICE(self), 0x20000); + fu_device_set_firmware_size_max(FU_DEVICE(self), 0x30000); + fu_usb_device_add_interface(FU_USB_DEVICE(self), GX_USB_INTERFACE); +} + +static void +fu_goodix_moc_device_class_init(FuGoodixMocDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->write_firmware = fu_goodix_moc_device_write_firmware; + device_class->setup = fu_goodix_moc_device_setup; + device_class->attach = fu_goodix_moc_device_attach; + device_class->set_progress = fu_goodix_moc_device_set_progress; +} diff -Nru fwupd-2.0.8/plugins/goodix-moc/fu-goodix-moc-device.h fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-device.h --- fwupd-2.0.8/plugins/goodix-moc/fu-goodix-moc-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2016 Richard Hughes + * Copyright 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_GOODIX_MOC_DEVICE (fu_goodix_moc_device_get_type()) +G_DECLARE_FINAL_TYPE(FuGoodixMocDevice, fu_goodix_moc_device, FU, GOODIX_MOC_DEVICE, FuUsbDevice) diff -Nru fwupd-2.0.8/plugins/goodix-moc/fu-goodix-moc-plugin.c fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-plugin.c --- fwupd-2.0.8/plugins/goodix-moc/fu-goodix-moc-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,37 @@ +/* + * Copyright 2015 Richard Hughes + * Copyright 2020 boger wang + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-goodix-moc-device.h" +#include "fu-goodix-moc-plugin.h" + +struct _FuGoodixMocPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuGoodixMocPlugin, fu_goodix_moc_plugin, FU_TYPE_PLUGIN) + +static void +fu_goodix_moc_plugin_init(FuGoodixMocPlugin *self) +{ +} + +static void +fu_goodix_moc_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_GOODIX_MOC_DEVICE); +} + +static void +fu_goodix_moc_plugin_class_init(FuGoodixMocPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->constructed = fu_goodix_moc_plugin_constructed; +} diff -Nru fwupd-2.0.8/plugins/goodix-moc/fu-goodix-moc-plugin.h fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-plugin.h --- fwupd-2.0.8/plugins/goodix-moc/fu-goodix-moc-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/fu-goodix-moc-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuGoodixMocPlugin, fu_goodix_moc_plugin, FU, GOODIX_MOC_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/goodix-moc/fu-goodixmoc-common.h fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-common.h --- fwupd-2.0.8/plugins/goodix-moc/fu-goodixmoc-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-common.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,66 +0,0 @@ -/* - * Copyright 2016 Richard Hughes - * Copyright 2020 boger wang - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -/* protocol */ -#define GX_CMD_ACK 0xAA -#define GX_CMD_VERSION 0xD0 -#define GX_CMD_RESET 0xB4 -#define GX_CMD_UPGRADE 0x80 -#define GX_CMD_UPGRADE_INIT 0x00 -#define GX_CMD_UPGRADE_DATA 0x01 -#define GX_CMD1_DEFAULT 0x00 - -#define GX_SIZE_CRC32 4 - -/* type covert */ -#define MAKE_CMD_EX(cmd0, cmd1) ((guint16)(((cmd0) << 8) | (cmd1))) - -typedef struct { - guint8 format[2]; - guint8 fwtype[8]; - guint8 fwversion[8]; - guint8 customer[8]; - guint8 mcu[8]; - guint8 sensor[8]; - guint8 algversion[8]; - guint8 interface[8]; - guint8 protocol[8]; - guint8 flashVersion[8]; - guint8 reserved[62]; -} GxfpVersionInfo; - -typedef struct { - guint8 cmd; - gboolean configured; -} GxfpAckMsg; - -typedef struct { - guint8 result; - union { - GxfpAckMsg ack_msg; - GxfpVersionInfo version_info; - }; -} GxfpCmdResp; - -typedef enum { - GX_PKG_TYPE_NORMAL = 0x80, - GX_PKG_TYPE_EOP = 0, -} GxPkgType; - -typedef struct __attribute__((__packed__)) { /* nocheck:blocked */ - guint8 cmd0; - guint8 cmd1; - guint8 pkg_flag; - guint8 reserved; - guint16 len; - guint8 crc8; - guint8 rev_crc8; -} GxfpPkgHeader; diff -Nru fwupd-2.0.8/plugins/goodix-moc/fu-goodixmoc-device.c fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-device.c --- fwupd-2.0.8/plugins/goodix-moc/fu-goodixmoc-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,463 +0,0 @@ -/* - * Copyright 2016 Richard Hughes - * Copyright 2020 boger wang - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-goodixmoc-common.h" -#include "fu-goodixmoc-device.h" - -struct _FuGoodixMocDevice { - FuUsbDevice parent_instance; - guint8 dummy_seq; -}; - -G_DEFINE_TYPE(FuGoodixMocDevice, fu_goodixmoc_device, FU_TYPE_USB_DEVICE) - -#define GX_USB_BULK_EP_IN (3 | 0x80) -#define GX_USB_BULK_EP_OUT (1 | 0x00) -#define GX_USB_INTERFACE 0 - -#define GX_USB_DATAIN_TIMEOUT 2000 /* ms */ -#define GX_USB_DATAOUT_TIMEOUT 200 /* ms */ -#define GX_FLASH_TRANSFER_BLOCK_SIZE 1000 /* 1000 */ - -static gboolean -fu_goodixmoc_device_cmd_send(FuGoodixMocDevice *self, - guint8 cmd0, - guint8 cmd1, - GxPkgType type, - GByteArray *req, - GError **error) -{ - guint32 crc_all = 0; - guint32 crc_hdr = 0; - gsize actual_len = 0; - g_autoptr(GByteArray) buf = g_byte_array_new(); - - /* build header */ - fu_byte_array_append_uint8(buf, cmd0); - fu_byte_array_append_uint8(buf, cmd1); - fu_byte_array_append_uint8(buf, type); /* pkg_flag */ - fu_byte_array_append_uint8(buf, self->dummy_seq++); /* reserved */ - fu_byte_array_append_uint16(buf, req->len + GX_SIZE_CRC32, G_LITTLE_ENDIAN); - crc_hdr = ~fu_crc8(FU_CRC_KIND_B8_STANDARD, buf->data, buf->len); - fu_byte_array_append_uint8(buf, crc_hdr); - fu_byte_array_append_uint8(buf, ~crc_hdr); - g_byte_array_append(buf, req->data, req->len); - crc_all = fu_crc32(FU_CRC_KIND_B32_STANDARD, buf->data, buf->len); - fu_byte_array_append_uint32(buf, crc_all, G_LITTLE_ENDIAN); - - /* send zero length package */ - if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), - GX_USB_BULK_EP_OUT, - NULL, - 0, - NULL, - GX_USB_DATAOUT_TIMEOUT, - NULL, - error)) { - g_prefix_error(error, "failed to req: "); - return FALSE; - } - fu_dump_full(G_LOG_DOMAIN, "REQST", buf->data, buf->len, 16, FU_DUMP_FLAGS_SHOW_ADDRESSES); - - /* send data */ - if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), - GX_USB_BULK_EP_OUT, - buf->data, - buf->len, - &actual_len, - GX_USB_DATAOUT_TIMEOUT, - NULL, - error)) { - g_prefix_error(error, "failed to req: "); - return FALSE; - } - if (actual_len != buf->len) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid length"); - return FALSE; - } - - /* success */ - return TRUE; -} - -static gboolean -fu_goodixmoc_device_cmd_recv(FuGoodixMocDevice *self, - GxfpCmdResp *presponse, - gboolean data_reply, - GError **error) -{ - guint32 crc_actual = 0; - guint32 crc_calculated = 0; - gsize actual_len = 0; - gsize offset = 0; - - g_return_val_if_fail(presponse != NULL, FALSE); - - /* - * package format - * | zlp | ack | zlp | data | - */ - while (1) { - guint16 header_len = 0x0; - guint8 header_cmd0 = 0x0; - g_autoptr(GByteArray) reply = g_byte_array_new(); - fu_byte_array_set_size(reply, GX_FLASH_TRANSFER_BLOCK_SIZE, 0x00); - if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), - GX_USB_BULK_EP_IN, - reply->data, - reply->len, - &actual_len, /* allowed to return short read */ - GX_USB_DATAIN_TIMEOUT, - NULL, - error)) { - g_prefix_error(error, "failed to reply: "); - return FALSE; - } - - /* receive zero length package */ - if (actual_len == 0) - continue; - fu_dump_full(G_LOG_DOMAIN, - "REPLY", - reply->data, - actual_len, - 16, - FU_DUMP_FLAGS_SHOW_ADDRESSES); - - /* parse package header */ - if (!fu_memread_uint8_safe(reply->data, reply->len, 0x0, &header_cmd0, error)) - return FALSE; - if (!fu_memread_uint16_safe(reply->data, - reply->len, - 0x4, - &header_len, - G_LITTLE_ENDIAN, - error)) - return FALSE; - offset = sizeof(GxfpPkgHeader) + header_len - GX_SIZE_CRC32; - crc_actual = fu_crc32(FU_CRC_KIND_B32_STANDARD, reply->data, offset); - if (!fu_memread_uint32_safe(reply->data, - reply->len, - offset, - &crc_calculated, - G_LITTLE_ENDIAN, - error)) - return FALSE; - if (crc_actual != crc_calculated) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "invalid checksum, got 0x%x, expected 0x%x", - crc_calculated, - crc_actual); - return FALSE; - } - - /* parse package data */ - if (!fu_memread_uint8_safe(reply->data, - reply->len, - sizeof(GxfpPkgHeader) + 0x00, - &presponse->result, - error)) - return FALSE; - if (header_cmd0 == GX_CMD_ACK) { - if (header_len == 0) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "invalid bufsz"); - return FALSE; - } - if (!fu_memread_uint8_safe(reply->data, - reply->len, - sizeof(GxfpPkgHeader) + 0x01, - &presponse->ack_msg.cmd, - error)) - return FALSE; - } else if (header_cmd0 == GX_CMD_VERSION) { - if (!fu_memcpy_safe((guint8 *)&presponse->version_info, - sizeof(presponse->version_info), - 0x0, /* dst */ - reply->data, - reply->len, - sizeof(GxfpPkgHeader) + 0x01, /* src */ - sizeof(GxfpVersionInfo), - error)) - return FALSE; - } - - /* continue after ack received */ - if (header_cmd0 == GX_CMD_ACK && data_reply) - continue; - break; - } - - /* success */ - return TRUE; -} - -static gboolean -fu_goodixmoc_device_cmd_xfer(FuGoodixMocDevice *device, - guint8 cmd0, - guint8 cmd1, - GxPkgType type, - GByteArray *req, - GxfpCmdResp *presponse, - gboolean data_reply, - GError **error) -{ - FuGoodixMocDevice *self = FU_GOODIXMOC_DEVICE(device); - if (!fu_goodixmoc_device_cmd_send(self, cmd0, cmd1, type, req, error)) - return FALSE; - return fu_goodixmoc_device_cmd_recv(self, presponse, data_reply, error); -} - -static gboolean -fu_goodixmoc_device_setup_version(FuGoodixMocDevice *self, GError **error) -{ - GxfpCmdResp rsp = {0}; - g_autofree gchar *version = NULL; - g_autoptr(GByteArray) req = g_byte_array_new(); - - fu_byte_array_append_uint8(req, 0); /* dummy */ - if (!fu_goodixmoc_device_cmd_xfer(self, - GX_CMD_VERSION, - GX_CMD1_DEFAULT, - GX_PKG_TYPE_EOP, - req, - &rsp, - TRUE, - error)) - return FALSE; - version = g_strndup((const gchar *)rsp.version_info.fwversion, - sizeof(rsp.version_info.fwversion)); - fu_device_set_version(FU_DEVICE(self), version); - return TRUE; -} - -static gboolean -fu_goodixmoc_device_update_init(FuGoodixMocDevice *self, GError **error) -{ - GxfpCmdResp rsp = {0}; - g_autoptr(GByteArray) req = g_byte_array_new(); - - /* update initial */ - if (!fu_goodixmoc_device_cmd_xfer(self, - GX_CMD_UPGRADE, - GX_CMD_UPGRADE_INIT, - GX_PKG_TYPE_EOP, - req, - &rsp, - TRUE, - error)) { - g_prefix_error(error, "failed to send initial update: "); - return FALSE; - } - - /* check result */ - if (rsp.result != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "initial update failed [0x%x]", - rsp.result); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_goodixmoc_device_attach(FuDevice *device, FuProgress *progress, GError **error) -{ - FuGoodixMocDevice *self = FU_GOODIXMOC_DEVICE(device); - GxfpCmdResp rsp = {0}; - g_autoptr(GByteArray) req = g_byte_array_new(); - - /* reset device */ - if (!fu_goodixmoc_device_cmd_xfer(self, - GX_CMD_RESET, - 0x03, - GX_PKG_TYPE_EOP, - req, - &rsp, - FALSE, - error)) { - g_prefix_error(error, "failed to send reset device: "); - return FALSE; - } - - /* check result */ - if (rsp.result != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "failed to reset device [0x%x]", - rsp.result); - return FALSE; - } - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - return TRUE; -} - -static gboolean -fu_goodixmoc_device_setup(FuDevice *device, GError **error) -{ - FuGoodixMocDevice *self = FU_GOODIXMOC_DEVICE(device); - - /* FuUsbDevice->setup */ - if (!FU_DEVICE_CLASS(fu_goodixmoc_device_parent_class)->setup(device, error)) - return FALSE; - - /* ensure version */ - if (!fu_goodixmoc_device_setup_version(self, error)) { - g_prefix_error(error, "failed to get firmware version: "); - return FALSE; - } - - /* success */ - return TRUE; -} - -static gboolean -fu_goodixmoc_device_write_firmware(FuDevice *device, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - FuGoodixMocDevice *self = FU_GOODIXMOC_DEVICE(device); - GxPkgType pkg_eop = GX_PKG_TYPE_NORMAL; - GxfpCmdResp rsp = {0}; - gboolean wait_data_reply = FALSE; - g_autoptr(GBytes) fw = NULL; - g_autoptr(GError) error_local = NULL; - g_autoptr(FuChunkArray) chunks = NULL; - - /* progress */ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "init"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 99, NULL); - - /* get default image */ - fw = fu_firmware_get_bytes(firmware, error); - if (fw == NULL) - return FALSE; - - /* build packets */ - chunks = fu_chunk_array_new_from_bytes(fw, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - GX_FLASH_TRANSFER_BLOCK_SIZE); - - /* don't auto-boot firmware */ - if (!fu_goodixmoc_device_update_init(self, &error_local)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "failed to initial update: %s", - error_local->message); - return FALSE; - } - fu_progress_step_done(progress); - - /* write each block */ - for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - g_autoptr(FuChunk) chk = NULL; - g_autoptr(GByteArray) req = g_byte_array_new(); - g_autoptr(GError) error_block = NULL; - - /* prepare chunk */ - chk = fu_chunk_array_index(chunks, i, error); - if (chk == NULL) - return FALSE; - g_byte_array_append(req, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); - - /* the last chunk */ - if (i == fu_chunk_array_length(chunks) - 1) { - wait_data_reply = TRUE; - pkg_eop = GX_PKG_TYPE_EOP; - } - if (!fu_goodixmoc_device_cmd_xfer(self, - GX_CMD_UPGRADE, - GX_CMD_UPGRADE_DATA, - pkg_eop, - req, - &rsp, - wait_data_reply, - &error_block)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "failed to write: %s", - error_block->message); - return FALSE; - } - - /* check update status */ - if (wait_data_reply && rsp.result != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "failed to verify firmware [0x%x]", - rsp.result); - return FALSE; - } - - /* update progress */ - fu_progress_set_percentage_full(fu_progress_get_child(progress), - (gsize)i + 1, - (gsize)fu_chunk_array_length(chunks)); - } - fu_progress_step_done(progress); - - /* success! */ - return TRUE; -} - -static void -fu_goodixmoc_device_set_progress(FuDevice *self, FuProgress *progress) -{ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); -} - -static void -fu_goodixmoc_device_init(FuGoodixMocDevice *self) -{ - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_RUNTIME_VERSION); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); - fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); - fu_device_set_remove_delay(FU_DEVICE(self), 5000); - fu_device_add_protocol(FU_DEVICE(self), "com.goodix.goodixmoc"); - fu_device_set_name(FU_DEVICE(self), "Fingerprint Sensor"); - fu_device_set_summary(FU_DEVICE(self), "Match-On-Chip fingerprint sensor"); - fu_device_set_vendor(FU_DEVICE(self), "Goodix"); - fu_device_set_install_duration(FU_DEVICE(self), 10); - fu_device_set_firmware_size_min(FU_DEVICE(self), 0x20000); - fu_device_set_firmware_size_max(FU_DEVICE(self), 0x30000); - fu_usb_device_add_interface(FU_USB_DEVICE(self), GX_USB_INTERFACE); -} - -static void -fu_goodixmoc_device_class_init(FuGoodixMocDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->write_firmware = fu_goodixmoc_device_write_firmware; - device_class->setup = fu_goodixmoc_device_setup; - device_class->attach = fu_goodixmoc_device_attach; - device_class->set_progress = fu_goodixmoc_device_set_progress; -} diff -Nru fwupd-2.0.8/plugins/goodix-moc/fu-goodixmoc-device.h fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-device.h --- fwupd-2.0.8/plugins/goodix-moc/fu-goodixmoc-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -/* - * Copyright 2016 Richard Hughes - * Copyright 2020 boger wang - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_GOODIXMOC_DEVICE (fu_goodixmoc_device_get_type()) -G_DECLARE_FINAL_TYPE(FuGoodixMocDevice, fu_goodixmoc_device, FU, GOODIXMOC_DEVICE, FuUsbDevice) diff -Nru fwupd-2.0.8/plugins/goodix-moc/fu-goodixmoc-plugin.c fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-plugin.c --- fwupd-2.0.8/plugins/goodix-moc/fu-goodixmoc-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-plugin.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,45 +0,0 @@ -/* - * Copyright 2015 Richard Hughes - * Copyright 2020 boger wang - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-goodixmoc-device.h" -#include "fu-goodixmoc-plugin.h" - -struct _FuGoodixMocPlugin { - FuPlugin parent_instance; -}; - -G_DEFINE_TYPE(FuGoodixMocPlugin, fu_goodixmoc_plugin, FU_TYPE_PLUGIN) - -static void -fu_goodixmoc_plugin_init(FuGoodixMocPlugin *self) -{ -} - -static void -fu_goodixmoc_plugin_object_constructed(GObject *obj) -{ - FuPlugin *plugin = FU_PLUGIN(obj); - fu_plugin_set_name(plugin, "goodixmoc"); -} - -static void -fu_goodixmoc_plugin_constructed(GObject *obj) -{ - FuPlugin *plugin = FU_PLUGIN(obj); - fu_plugin_add_device_gtype(plugin, FU_TYPE_GOODIXMOC_DEVICE); -} - -static void -fu_goodixmoc_plugin_class_init(FuGoodixMocPluginClass *klass) -{ - FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); - GObjectClass *object_class = G_OBJECT_CLASS(klass); - object_class->constructed = fu_goodixmoc_plugin_object_constructed; - plugin_class->constructed = fu_goodixmoc_plugin_constructed; -} diff -Nru fwupd-2.0.8/plugins/goodix-moc/fu-goodixmoc-plugin.h fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-plugin.h --- fwupd-2.0.8/plugins/goodix-moc/fu-goodixmoc-plugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/fu-goodixmoc-plugin.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -G_DECLARE_FINAL_TYPE(FuGoodixMocPlugin, fu_goodixmoc_plugin, FU, GOODIXMOC_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/goodix-moc/goodix-moc.quirk fwupd-2.0.20/plugins/goodix-moc/goodix-moc.quirk --- fwupd-2.0.8/plugins/goodix-moc/goodix-moc.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/goodix-moc.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,26 @@ +# Goodix Fingerprint sensor +[USB\VID_27C6&PID_60A2] +Plugin = goodix_moc +Flags = enforce-requires +[USB\VID_27C6&PID_6384] +Plugin = goodix_moc +Flags = enforce-requires +[USB\VID_27C6&PID_639C] +Plugin = goodix_moc +Flags = enforce-requires +[USB\VID_27C6&PID_63AC] +Plugin = goodix_moc +Flags = enforce-requires +[USB\VID_27C6&PID_6594] +Plugin = goodix_moc +Flags = enforce-requires +[USB\VID_27C6&PID_6496] +Plugin = goodix_moc +Flags = enforce-requires +[USB\VID_27C6&PID_659A] +Plugin = goodix_moc +Flags = enforce-requires +[USB\VID_27C6&PID_609C] +Plugin = goodix_moc +Flags = enforce-requires,dual-image +RemoveDelay = 10000 diff -Nru fwupd-2.0.8/plugins/goodix-moc/goodixmoc.quirk fwupd-2.0.20/plugins/goodix-moc/goodixmoc.quirk --- fwupd-2.0.8/plugins/goodix-moc/goodixmoc.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/goodixmoc.quirk 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ -# Goodix Fingerprint sensor -[USB\VID_27C6&PID_60A2] -Plugin = goodixmoc -Flags = enforce-requires -[USB\VID_27C6&PID_6384] -Plugin = goodixmoc -Flags = enforce-requires -[USB\VID_27C6&PID_639C] -Plugin = goodixmoc -Flags = enforce-requires -[USB\VID_27C6&PID_63AC] -Plugin = goodixmoc -Flags = enforce-requires -[USB\VID_27C6&PID_6594] -Plugin = goodixmoc -Flags = enforce-requires -[USB\VID_27C6&PID_6496] -Plugin = goodixmoc -Flags = enforce-requires -[USB\VID_27C6&PID_659A] -Plugin = goodixmoc -Flags = enforce-requires -[USB\VID_27C6&PID_609C] -Plugin = goodixmoc -Flags = enforce-requires,dual-image -RemoveDelay = 10000 diff -Nru fwupd-2.0.8/plugins/goodix-moc/meson.build fwupd-2.0.20/plugins/goodix-moc/meson.build --- fwupd-2.0.8/plugins/goodix-moc/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-moc/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,11 +1,11 @@ cargs = ['-DG_LOG_DOMAIN="FuPluginGoodixMoc"'] plugins += {meson.current_source_dir().split('/')[-1]: true} -plugin_quirks += files('goodixmoc.quirk') -plugin_builtins += static_library('fu_plugin_goodixmoc', +plugin_quirks += files('goodix-moc.quirk') +plugin_builtins += static_library('fu_plugin_goodix_moc', sources: [ - 'fu-goodixmoc-device.c', - 'fu-goodixmoc-plugin.c', + 'fu-goodix-moc-device.c', + 'fu-goodix-moc-plugin.c', ], include_directories: plugin_incdirs, link_with: plugin_libs, diff -Nru fwupd-2.0.8/plugins/goodix-tp/README.md fwupd-2.0.20/plugins/goodix-tp/README.md --- fwupd-2.0.8/plugins/goodix-tp/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-tp/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -39,10 +39,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.9.2`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Goodix: @xulinkun diff -Nru fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-brlb-device.c fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-brlb-device.c --- fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-brlb-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-brlb-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -157,7 +157,7 @@ g_autofree gchar *patch_pid = NULL; if (!fu_goodixtp_brlb_device_hid_read(self, 0x1001E, hidbuf, sizeof(hidbuf), error)) { - g_prefix_error(error, "failed read PID/VID: "); + g_prefix_error_literal(error, "failed read PID/VID: "); return FALSE; } vice_ver = hidbuf[10]; @@ -175,7 +175,7 @@ fu_goodixtp_hid_device_set_sensor_id(FU_GOODIXTP_HID_DEVICE(self), hidbuf[13]); if (!fu_goodixtp_brlb_device_hid_read(self, 0x10076, hidbuf, 5, error)) { - g_prefix_error(error, "Failed read config id/version: "); + g_prefix_error_literal(error, "failed read config id/version: "); return FALSE; } @@ -204,7 +204,7 @@ fu_goodixtp_brlb_device_wait_erase_cb(FuDevice *device, gpointer user_data, GError **error) { FuGoodixtpBrlbDevice *self = FU_GOODIXTP_BRLB_DEVICE(device); - guint8 hidbuf[5]; + guint8 hidbuf[5]; /* nocheck:zero-init */ guint8 recvBuf[5] = {0x0}; memset(hidbuf, 0x55, sizeof(hidbuf)); @@ -223,12 +223,12 @@ static gboolean fu_goodixtp_brlb_device_update_prepare(FuGoodixtpBrlbDevice *self, GError **error) { - guint8 cmdbuf[1]; + guint8 cmdbuf[1] = {0}; /* step 1. switch mini system */ cmdbuf[0] = 0x01; if (!fu_goodixtp_brlb_device_send_cmd(self, 0x10, cmdbuf, sizeof(cmdbuf), error)) { - g_prefix_error(error, "failed send minisystem cmd: "); + g_prefix_error_literal(error, "failed send minisystem cmd: "); return FALSE; } @@ -239,7 +239,7 @@ 30, NULL, error)) { - g_prefix_error(error, "wait brlb minisystem status failed: "); + g_prefix_error_literal(error, "wait brlb minisystem status failed: "); return FALSE; } g_debug("switch mini system successfully"); @@ -247,7 +247,7 @@ /* step 2. erase flash */ cmdbuf[0] = 0x01; if (!fu_goodixtp_brlb_device_send_cmd(self, 0x11, cmdbuf, sizeof(cmdbuf), error)) { - g_prefix_error(error, "Failed send erase flash cmd: "); + g_prefix_error_literal(error, "failed send erase flash cmd: "); return FALSE; } @@ -258,7 +258,7 @@ 20, NULL, error)) { - g_prefix_error(error, "wait brlb erase status failed: "); + g_prefix_error_literal(error, "wait brlb erase status failed: "); return FALSE; } @@ -273,7 +273,7 @@ guint8 hidbuf[1] = {0}; if (!fu_goodixtp_brlb_device_hid_read(self, 0x10011, hidbuf, 1, error)) { - g_prefix_error(error, "Failed to read 0x10011"); + g_prefix_error_literal(error, "failed to read 0x10011: "); return FALSE; } if (hidbuf[0] != 0xAA) { @@ -308,7 +308,7 @@ buf_align4k, sizeof(buf_align4k), error)) { - g_prefix_error(error, "write fw data failed: "); + g_prefix_error_literal(error, "write fw data failed: "); return FALSE; } @@ -329,7 +329,7 @@ fu_memwrite_uint32(cmdbuf + 2, fu_chunk_get_address(chk), G_BIG_ENDIAN); fu_memwrite_uint32(cmdbuf + 6, checksum, G_BIG_ENDIAN); if (!fu_goodixtp_brlb_device_send_cmd(self, 0x12, cmdbuf, sizeof(cmdbuf), error)) { - g_prefix_error(error, "failed send start update cmd: "); + g_prefix_error_literal(error, "failed send start update cmd: "); return FALSE; } @@ -342,7 +342,7 @@ 20, NULL, error)) { - g_prefix_error(error, "wait flash status failed: "); + g_prefix_error_literal(error, "wait flash status failed: "); return FALSE; } return TRUE; @@ -372,7 +372,7 @@ /* reset IC */ if (!fu_goodixtp_brlb_device_send_cmd(self, 0x13, cmdbuf, sizeof(cmdbuf), error)) { - g_prefix_error(error, "failed reset IC: "); + g_prefix_error_literal(error, "failed reset IC: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), 100); @@ -384,7 +384,7 @@ { FuGoodixtpBrlbDevice *self = FU_GOODIXTP_BRLB_DEVICE(device); if (!fu_goodixtp_brlb_device_ensure_version(self, error)) { - g_prefix_error(error, "brlb read version failed: "); + g_prefix_error_literal(error, "brlb read version failed: "); return FALSE; } return TRUE; @@ -394,7 +394,7 @@ fu_goodixtp_brlb_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuGoodixtpBrlbDevice *self = FU_GOODIXTP_BRLB_DEVICE(device); @@ -473,7 +473,7 @@ GError **error) { FuGoodixtpBrlbDevice *self = FU_GOODIXTP_BRLB_DEVICE(device); - guint32 fw_ver = fu_goodixtp_firmware_get_version(FU_GOODIXTP_FIRMWARE(firmware)); + guint32 fw_ver = fu_firmware_get_version_raw(firmware); g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); /* progress */ diff -Nru fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-brlb-firmware.c fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-brlb-firmware.c --- fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-brlb-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-brlb-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -33,7 +33,7 @@ guint8 cfg_ver = 0; gsize bufsz = 0; const guint8 *buf; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructGoodixBrlbHdr) st = NULL; g_autoptr(GBytes) fw = NULL; st = fu_struct_goodix_brlb_hdr_parse_stream(stream, 0x0, error); @@ -61,7 +61,8 @@ if (fw_img == NULL) return FALSE; fu_firmware_set_bytes(img, fw_img); - fu_firmware_add_image(FU_FIRMWARE(self), img); + if (!fu_firmware_add_image(FU_FIRMWARE(self), img, error)) + return FALSE; if (!fu_memread_uint8_safe(buf, bufsz, firmware_size + 64 + 34, &cfg_ver, error)) return FALSE; g_debug("config size:0x%x, config ver:0x%02x", @@ -93,10 +94,10 @@ "invalid subsys_num"); return FALSE; } - offset_hdr = st->len; + offset_hdr = st->buf->len; for (guint i = 0; i < subsys_num; i++) { guint32 img_size; - g_autoptr(GByteArray) st_img = NULL; + g_autoptr(FuStructGoodixBrlbImg) st_img = NULL; st_img = fu_struct_goodix_brlb_img_parse_stream(stream, offset_hdr, error); if (st_img == NULL) @@ -113,15 +114,15 @@ if (fw_img == NULL) return FALSE; fu_firmware_set_bytes(img, fw_img); - if (!fu_firmware_add_image_full(FU_FIRMWARE(self), img, error)) + if (!fu_firmware_add_image(FU_FIRMWARE(self), img, error)) return FALSE; } - offset_hdr += st_img->len; + offset_hdr += st_img->buf->len; offset_payload += img_size; } version = (fu_struct_goodix_brlb_hdr_get_vid(st) << 8) | cfg_ver; - fu_goodixtp_firmware_set_version(self, version); + fu_firmware_set_version_raw(FU_FIRMWARE(self), version); return TRUE; } diff -Nru fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-firmware.c fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-firmware.c --- fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,26 +10,11 @@ typedef struct { FuFirmwareClass parent_instance; - guint32 version; } FuGoodixtpFirmwarePrivate; G_DEFINE_TYPE_WITH_PRIVATE(FuGoodixtpFirmware, fu_goodixtp_firmware, FU_TYPE_FIRMWARE) #define GET_PRIVATE(o) (fu_goodixtp_firmware_get_instance_private(o)) -guint32 -fu_goodixtp_firmware_get_version(FuGoodixtpFirmware *self) -{ - FuGoodixtpFirmwarePrivate *priv = GET_PRIVATE(self); - return priv->version; -} - -void -fu_goodixtp_firmware_set_version(FuGoodixtpFirmware *self, guint32 version) -{ - FuGoodixtpFirmwarePrivate *priv = GET_PRIVATE(self); - priv->version = version; -} - static void fu_goodixtp_firmware_init(FuGoodixtpFirmware *self) { diff -Nru fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-firmware.h fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-firmware.h --- fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-firmware.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -18,8 +18,3 @@ struct _FuGoodixtpFirmwareClass { FuFirmwareClass parent_class; }; - -guint32 -fu_goodixtp_firmware_get_version(FuGoodixtpFirmware *self); -void -fu_goodixtp_firmware_set_version(FuGoodixtpFirmware *self, guint32 version); diff -Nru fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-gtx8-device.c fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-gtx8-device.c --- fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-gtx8-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-gtx8-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -144,7 +144,7 @@ hidbuf, bufsz, error)) { - g_prefix_error(error, "failed to send cmd: "); + g_prefix_error_literal(error, "failed to send cmd: "); return FALSE; } @@ -163,11 +163,11 @@ g_autofree gchar *patch_pid = NULL; if (!fu_goodixtp_gtx8_device_hid_read(self, 0x60DC, &cfg_ver, 1, error)) { - g_prefix_error(error, "failed to read cfg version: "); + g_prefix_error_literal(error, "failed to read cfg version: "); return FALSE; } if (!fu_goodixtp_gtx8_device_hid_read(self, 0x452C, fw_info, sizeof(fw_info), error)) { - g_prefix_error(error, "failed to read firmware version: "); + g_prefix_error_literal(error, "failed to read firmware version: "); return FALSE; } @@ -212,7 +212,7 @@ buf_disable, sizeof(buf_disable), error)) { - g_prefix_error(error, "send close report cmd failed: "); + g_prefix_error_literal(error, "send close report cmd failed: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), 10); @@ -223,12 +223,12 @@ buf_confirm, sizeof(buf_confirm), error)) { - g_prefix_error(error, "send confirm cmd failed: "); + g_prefix_error_literal(error, "send confirm cmd failed: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), 30); if (!fu_goodixtp_gtx8_device_hid_read(self, CMD_ADDR, buf, sizeof(buf), error)) { - g_prefix_error(error, "read confirm flag failed: "); + g_prefix_error_literal(error, "read confirm flag failed: "); return FALSE; } if (buf[1] != 1) { @@ -267,7 +267,7 @@ /* close report */ if (!fu_goodixtp_gtx8_device_disable_report(self, error)) { - g_prefix_error(error, "disable report failed: "); + g_prefix_error_literal(error, "disable report failed: "); return FALSE; } @@ -275,7 +275,7 @@ cmd_switch_to_patch, sizeof(cmd_switch_to_patch), error)) { - g_prefix_error(error, "failed switch to patch: "); + g_prefix_error_literal(error, "failed switch to patch: "); return FALSE; } @@ -287,12 +287,12 @@ 30, NULL, error)) { - g_prefix_error(error, "wait gtx8 BL status failed: "); + g_prefix_error_literal(error, "wait gtx8 BL status failed: "); return FALSE; } if (!fu_goodixtp_gtx8_device_disable_report(self, error)) { - g_prefix_error(error, "disable report failed: "); + g_prefix_error_literal(error, "disable report failed: "); return FALSE; } @@ -301,7 +301,7 @@ cmd_start_update, sizeof(cmd_start_update), error)) { - g_prefix_error(error, "failed to start update: "); + g_prefix_error_literal(error, "failed to start update: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), 100); @@ -316,7 +316,7 @@ guint8 cmd_switch_ptp_mode[] = {0x03, 0x03, 0x00, 0x00, 0x01, 0x01}; if (!fu_goodixtp_gtx8_device_send_cmd(self, cmd_reset, sizeof(cmd_reset), error)) { - g_prefix_error(error, "failed write reset command: "); + g_prefix_error_literal(error, "failed write reset command: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), 100); @@ -324,7 +324,7 @@ cmd_switch_ptp_mode, sizeof(cmd_switch_ptp_mode), error)) { - g_prefix_error(error, "failed switch to ptp mode: "); + g_prefix_error_literal(error, "failed switch to ptp mode: "); return FALSE; } return TRUE; @@ -383,7 +383,7 @@ fu_memwrite_uint16(buf_load_flash + 7, fu_chunk_get_address(chk) >> 8, G_BIG_ENDIAN); fu_memwrite_uint16(buf_load_flash + 9, check_sum, G_BIG_ENDIAN); if (!fu_goodixtp_gtx8_device_send_cmd(self, buf_load_flash, 11, error)) { - g_prefix_error(error, "failed write load flash command: "); + g_prefix_error_literal(error, "failed write load flash command: "); return FALSE; } @@ -395,7 +395,7 @@ 20, NULL, error)) { - g_prefix_error(error, "wait flash status failed: "); + g_prefix_error_literal(error, "wait flash status failed: "); return FALSE; } @@ -431,7 +431,7 @@ { FuGoodixtpGtx8Device *self = FU_GOODIXTP_GTX8_DEVICE(device); if (!fu_goodixtp_gtx8_device_ensure_version(self, error)) { - g_prefix_error(error, "gtx8 read version failed: "); + g_prefix_error_literal(error, "gtx8 read version failed: "); return FALSE; } return TRUE; @@ -441,7 +441,7 @@ fu_goodixtp_gtx8_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuGoodixtpGtx8Device *self = FU_GOODIXTP_GTX8_DEVICE(device); @@ -520,8 +520,7 @@ GError **error) { FuGoodixtpGtx8Device *self = FU_GOODIXTP_GTX8_DEVICE(device); - FuGoodixtpFirmware *firmware_goodixtp = FU_GOODIXTP_FIRMWARE(firmware); - guint32 fw_ver = fu_goodixtp_firmware_get_version(firmware_goodixtp); + guint32 fw_ver = fu_firmware_get_version_raw(firmware); g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); /* progress */ diff -Nru fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-gtx8-firmware.c fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-gtx8-firmware.c --- fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-gtx8-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-gtx8-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -35,7 +35,7 @@ guint32 offset_hdr; guint32 offset_payload = GTX8_FW_DATA_OFFSET; const guint8 *buf; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructGoodixGtx8Hdr) st = NULL; g_autoptr(GBytes) fw = NULL; st = fu_struct_goodix_gtx8_hdr_parse_stream(stream, 0x0, error); @@ -92,10 +92,10 @@ error)) return FALSE; if ((gint)(bufsz - firmware_size - 6) != (gint)cfg_packlen + 6) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "config pack len error"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "config pack len error"); return FALSE; } @@ -114,19 +114,19 @@ error)) return FALSE; if (checksum != read_cksum) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "config pack checksum error"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "config pack checksum error"); return FALSE; } if (!fu_memread_uint8_safe(buf, bufsz, firmware_size + 9, &sub_cfg_num, error)) return FALSE; if (sub_cfg_num == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "sub_cfg_num is 0"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "sub_cfg_num is 0"); return FALSE; } sub_cfg_info_pos = firmware_size + 12; @@ -156,11 +156,11 @@ if (fw_img == NULL) return FALSE; fu_firmware_set_bytes(img, fw_img); - if (!fu_firmware_add_image_full(FU_FIRMWARE(self), img, error)) + if (!fu_firmware_add_image(FU_FIRMWARE(self), img, error)) return FALSE; if (!fu_memread_uint8_safe(buf, bufsz, cfg_offset, &cfg_ver, error)) return FALSE; - g_debug("Find a cfg match sensorID:ID=%d, cfg version=%d", + g_debug("find a cfg match sensorID:ID=%d, cfg version=%d", sensor_id, cfg_ver); break; @@ -173,13 +173,16 @@ /* parse each image */ subsys_num = fu_struct_goodix_gtx8_hdr_get_subsys_num(st); if (subsys_num == 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "subsys_num is 0, exit"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "subsys_num is 0"); return FALSE; } - offset_hdr = st->len; + offset_hdr = st->buf->len; for (guint i = 0; i < subsys_num; i++) { guint32 img_size; - g_autoptr(GByteArray) st_img = NULL; + g_autoptr(FuStructGoodixGtx8Img) st_img = NULL; st_img = fu_struct_goodix_gtx8_img_parse_stream(stream, offset_hdr, error); if (st_img == NULL) @@ -194,15 +197,15 @@ if (fw_img == NULL) return FALSE; fu_firmware_set_bytes(img, fw_img); - if (!fu_firmware_add_image_full(FU_FIRMWARE(self), img, error)) + if (!fu_firmware_add_image(FU_FIRMWARE(self), img, error)) return FALSE; } - offset_hdr += st_img->len; + offset_hdr += st_img->buf->len; offset_payload += img_size; } version = (fu_struct_goodix_gtx8_hdr_get_vid(st) << 8) | cfg_ver; - fu_goodixtp_firmware_set_version(self, version); + fu_firmware_set_version_raw(FU_FIRMWARE(self), version); return TRUE; } diff -Nru fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-hid-device.c fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-hid-device.c --- fwupd-2.0.8/plugins/goodix-tp/fu-goodixtp-hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/goodix-tp/fu-goodixtp-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -80,7 +80,7 @@ sizeof(rcv_buf), FU_IOCTL_FLAG_NONE, error)) { - g_prefix_error(error, "failed get report: "); + g_prefix_error_literal(error, "failed get report: "); return FALSE; } if (rcv_buf[0] != REPORT_ID) { @@ -108,14 +108,14 @@ bufsz, FU_IOCTL_FLAG_NONE, error)) { - g_prefix_error(error, "failed set report: "); + g_prefix_error_literal(error, "failed set report: "); return FALSE; } return TRUE; } static void -fu_goodixtp_hid_device_set_progress(FuDevice *self, FuProgress *progress) +fu_goodixtp_hid_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -139,10 +139,10 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_set_summary(FU_DEVICE(self), "Touchpad"); - fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_INPUT_TOUCHPAD); fu_device_add_protocol(FU_DEVICE(self), "com.goodix.goodixtp"); fu_device_set_name(FU_DEVICE(self), "Touch Controller Sensor"); - fu_device_set_vendor(FU_DEVICE(self), "Goodix inc."); + fu_device_set_vendor(FU_DEVICE(self), "Goodix"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); fu_device_set_priority(FU_DEVICE(self), 1); /* better than i2c */ fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); diff -Nru fwupd-2.0.8/plugins/gpio/fu-gpio-device.c fwupd-2.0.20/plugins/gpio/fu-gpio-device.c --- fwupd-2.0.8/plugins/gpio/fu-gpio-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/gpio/fu-gpio-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -61,7 +61,7 @@ FU_GPIO_DEVICE_IOCTL_TIMEOUT, FU_IOCTL_FLAG_NONE, error)) { - g_prefix_error(error, "failed to get chipinfo: "); + g_prefix_error_literal(error, "failed to get chipinfo: "); return FALSE; } @@ -140,7 +140,7 @@ FU_GPIO_DEVICE_IOCTL_TIMEOUT, FU_IOCTL_FLAG_NONE, error)) { - g_prefix_error(error, "failed to assign: "); + g_prefix_error_literal(error, "failed to assign: "); return FALSE; } @@ -177,7 +177,7 @@ FU_GPIO_DEVICE_IOCTL_TIMEOUT, FU_IOCTL_FLAG_NONE, error)) { - g_prefix_error(error, "failed to get lineinfo: "); + g_prefix_error_literal(error, "failed to get lineinfo: "); return FALSE; } } else { @@ -193,7 +193,7 @@ FU_GPIO_DEVICE_IOCTL_TIMEOUT, FU_IOCTL_FLAG_NONE, error)) { - g_prefix_error(error, "failed to get lineinfo: "); + g_prefix_error_literal(error, "failed to get lineinfo: "); return FALSE; } name = fu_strsafe(info.name, sizeof(info.name)); diff -Nru fwupd-2.0.8/plugins/gpio/meson.build fwupd-2.0.20/plugins/gpio/meson.build --- fwupd-2.0.8/plugins/gpio/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/gpio/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,6 @@ -gpio_header = cc.has_header_symbol('linux/gpio.h', 'GPIO_V2_LINE_FLAG_OUTPUT', required: false) +host_machine.system() == 'linux' or subdir_done() +cc.has_header_symbol('linux/gpio.h', 'GPIO_V2_LINE_FLAG_OUTPUT', required: false) or subdir_done() -if gpio_header and host_machine.system() == 'linux' cargs = ['-DG_LOG_DOMAIN="FuPluginGpio"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -18,4 +18,3 @@ link_with: plugin_libs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/hailuck/README.md fwupd-2.0.20/plugins/hailuck/README.md --- fwupd-2.0.8/plugins/hailuck/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,48 +0,0 @@ ---- -title: Plugin: Hailuck ---- - -## Introduction - -Hailuck produce the firmware used on the keyboard and trackpad used in the -Pinebook Pro laptops. - -## Firmware Format - -The daemon will decompress the cabinet archive and extract a firmware blob in -a packed binary file format. - -This plugin supports the following protocol ID: - -* `com.hailuck.kbd` -* `com.hailuck.tp` - -## GUID Generation - -These devices use the standard USB DeviceInstanceId values, e.g. - -* `USB\VID_0603&PID_1020` - -## Update Behavior - -The keyboard device usually presents in runtime mode, but on detach it -re-enumerates with a different USB VID and PID in bootloader mode. On attach -the device again re-enumerates back to the runtime mode. - -For this reason the `REPLUG_MATCH_GUID` internal device flag is used so that -the bootloader and runtime modes are treated as the same device. - -The touchpad firmware is deployed when the device is in normal runtime mode, -and the device will reset when the new firmware has been written. - -## Vendor ID Security - -The vendor ID is set from the USB vendor, in this instance set to `USB:0x0603` - -## External Interface Access - -This plugin requires read/write access to `/dev/bus/usb`. - -## Version Considerations - -This plugin has been available since fwupd version `1.5.2`. diff -Nru fwupd-2.0.8/plugins/hailuck/data/lspci-bl.txt fwupd-2.0.20/plugins/hailuck/data/lspci-bl.txt --- fwupd-2.0.8/plugins/hailuck/data/lspci-bl.txt 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/data/lspci-bl.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,47 +0,0 @@ -Bus 003 Device 003: ID 0603:1020 Novatek Microelectronics Corp. -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.10 - bDeviceClass 0 - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 8 - idVendor 0x0603 Novatek Microelectronics Corp. - idProduct 0x1020 - bcdDevice 3.01 - iManufacturer 0 - iProduct 0 - iSerial 0 - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 0x0022 - bNumInterfaces 1 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0xa0 - (Bus Powered) - Remote Wakeup - MaxPower 100mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 1 - bInterfaceClass 3 Human Interface Device - bInterfaceSubClass 1 Boot Interface Subclass - bInterfaceProtocol 1 Keyboard - iInterface 0 - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x81 EP 1 IN - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0008 1x 8 bytes - bInterval 10 diff -Nru fwupd-2.0.8/plugins/hailuck/data/lspci.txt fwupd-2.0.20/plugins/hailuck/data/lspci.txt --- fwupd-2.0.8/plugins/hailuck/data/lspci.txt 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/data/lspci.txt 1970-01-01 00:00:00.000000000 +0000 @@ -1,89 +0,0 @@ -Bus 003 Device 008: ID 258a:001e HAILUCK CO.,LTD USB KEYBOARD -Device Descriptor: - bLength 18 - bDescriptorType 1 - bcdUSB 1.10 - bDeviceClass 0 - bDeviceSubClass 0 - bDeviceProtocol 0 - bMaxPacketSize0 8 - idVendor 0x258a - idProduct 0x001e - bcdDevice 1.00 - iManufacturer 1 HAILUCK CO.,LTD - iProduct 2 USB KEYBOARD - iSerial 0 - bNumConfigurations 1 - Configuration Descriptor: - bLength 9 - bDescriptorType 2 - wTotalLength 0x003b - bNumInterfaces 2 - bConfigurationValue 1 - iConfiguration 0 - bmAttributes 0xa0 - (Bus Powered) - Remote Wakeup - MaxPower 100mA - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 0 - bAlternateSetting 0 - bNumEndpoints 1 - bInterfaceClass 3 Human Interface Device - bInterfaceSubClass 1 Boot Interface Subclass - bInterfaceProtocol 1 Keyboard - iInterface 0 - HID Device Descriptor: - bLength 9 - bDescriptorType 33 - bcdHID 1.10 - bCountryCode 0 Not supported - bNumDescriptors 1 - bDescriptorType 34 Report - wDescriptorLength 65 - 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 0x0008 1x 8 bytes - bInterval 10 - Interface Descriptor: - bLength 9 - bDescriptorType 4 - bInterfaceNumber 1 - bAlternateSetting 0 - bNumEndpoints 1 - bInterfaceClass 3 Human Interface Device - bInterfaceSubClass 0 - bInterfaceProtocol 0 - iInterface 0 - HID Device Descriptor: - bLength 9 - bDescriptorType 33 - bcdHID 1.10 - bCountryCode 0 Not supported - bNumDescriptors 1 - bDescriptorType 34 Report - wDescriptorLength 487 - Report Descriptors: - ** UNAVAILABLE ** - Endpoint Descriptor: - bLength 7 - bDescriptorType 5 - bEndpointAddress 0x82 EP 2 IN - bmAttributes 3 - Transfer Type Interrupt - Synch Type None - Usage Type Data - wMaxPacketSize 0x0008 1x 8 bytes - bInterval 10 -Device Status: 0x0000 - (Bus Powered) diff -Nru fwupd-2.0.8/plugins/hailuck/fu-hailuck-bl-device.c fwupd-2.0.20/plugins/hailuck/fu-hailuck-bl-device.c --- fwupd-2.0.8/plugins/hailuck/fu-hailuck-bl-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/fu-hailuck-bl-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,315 +0,0 @@ -/* - * Copyright 2020 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-hailuck-bl-device.h" -#include "fu-hailuck-common.h" -#include "fu-hailuck-kbd-firmware.h" -#include "fu-hailuck-struct.h" - -struct _FuHailuckBlDevice { - FuHidDevice parent_instance; -}; - -G_DEFINE_TYPE(FuHailuckBlDevice, fu_hailuck_bl_device, FU_TYPE_HID_DEVICE) - -static gboolean -fu_hailuck_bl_device_attach(FuDevice *device, FuProgress *progress, GError **error) -{ - guint8 buf[6] = { - FU_HAILUCK_REPORT_ID_SHORT, - FU_HAILUCK_CMD_ATTACH, - }; - if (!fu_hid_device_set_report(FU_HID_DEVICE(device), - buf[0], - buf, - sizeof(buf), - 1000, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error)) - return FALSE; - if (!fu_usb_device_reset(FU_USB_DEVICE(device), error)) - return FALSE; - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - return TRUE; -} - -static gboolean -fu_hailuck_bl_device_probe(FuDevice *device, GError **error) -{ - /* add instance ID */ - fu_device_add_instance_str(device, "MODE", "KBD"); - return fu_device_build_instance_id(device, error, "USB", "VID", "PID", "MODE", NULL); -} - -static gboolean -fu_hailuck_bl_device_read_block_start(FuHailuckBlDevice *self, guint32 length, GError **error) -{ - guint8 buf[6] = { - FU_HAILUCK_REPORT_ID_SHORT, - FU_HAILUCK_CMD_READ_BLOCK_START, - }; - fu_memwrite_uint16(buf + 4, length, G_LITTLE_ENDIAN); - return fu_hid_device_set_report(FU_HID_DEVICE(self), - buf[0], - buf, - sizeof(buf), - 100, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error); -} - -static gboolean -fu_hailuck_bl_device_read_block(FuHailuckBlDevice *self, - guint8 *data, - gsize data_sz, - GError **error) -{ - gsize bufsz = data_sz + 2; - g_autofree guint8 *buf = g_malloc0(bufsz); - - buf[0] = FU_HAILUCK_REPORT_ID_LONG; - buf[1] = FU_HAILUCK_CMD_READ_BLOCK; - if (!fu_hid_device_get_report(FU_HID_DEVICE(self), - buf[0], - buf, - bufsz, - 2000, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error)) - return FALSE; - if (!fu_memcpy_safe(data, - data_sz, - 0x0, /* dst */ - buf, - bufsz, - 0x02, /* src */ - data_sz, - error)) - return FALSE; - - /* success */ - fu_device_sleep(FU_DEVICE(self), 10); - return TRUE; -} - -static GBytes * -fu_hailuck_bl_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) -{ - FuHailuckBlDevice *self = FU_HAILUCK_BL_DEVICE(device); - gsize fwsz = fu_device_get_firmware_size_max(device); - g_autoptr(GByteArray) fwbuf = g_byte_array_new(); - g_autoptr(GPtrArray) chunks = NULL; - - /* tell device amount of data to send */ - fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); - if (!fu_hailuck_bl_device_read_block_start(self, fwsz, error)) - return NULL; - - /* receive data back */ - fu_byte_array_set_size(fwbuf, fwsz, 0x00); - chunks = fu_chunk_array_mutable_new(fwbuf->data, fwbuf->len, 0x0, 0x0, 2048); - fu_progress_set_id(progress, G_STRLOC); - fu_progress_set_steps(progress, chunks->len); - for (guint i = 0; i < chunks->len; i++) { - FuChunk *chk = g_ptr_array_index(chunks, i); - if (!fu_hailuck_bl_device_read_block(self, - fu_chunk_get_data_out(chk), - fu_chunk_get_data_sz(chk), - error)) - return NULL; - fu_progress_step_done(progress); - } - - /* success */ - return g_bytes_new(fwbuf->data, fwbuf->len); -} - -static gboolean -fu_hailuck_bl_device_erase(FuHailuckBlDevice *self, FuProgress *progress, GError **error) -{ - guint8 buf[6] = { - FU_HAILUCK_REPORT_ID_SHORT, - FU_HAILUCK_CMD_ERASE, - }; - if (!fu_hid_device_set_report(FU_HID_DEVICE(self), - buf[0], - buf, - sizeof(buf), - 100, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error)) - return FALSE; - fu_device_sleep_full(FU_DEVICE(self), 2000, progress); - return TRUE; -} - -static gboolean -fu_hailuck_bl_device_write_block_start(FuHailuckBlDevice *self, guint32 length, GError **error) -{ - guint8 buf[6] = { - FU_HAILUCK_REPORT_ID_SHORT, - FU_HAILUCK_CMD_WRITE_BLOCK_START, - }; - fu_memwrite_uint16(buf + 4, length, G_LITTLE_ENDIAN); - return fu_hid_device_set_report(FU_HID_DEVICE(self), - buf[0], - buf, - sizeof(buf), - 100, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error); -} - -static gboolean -fu_hailuck_bl_device_write_block(FuHailuckBlDevice *self, - const guint8 *data, - gsize data_sz, - GError **error) -{ - gsize bufsz = data_sz + 2; - g_autofree guint8 *buf = g_malloc0(bufsz); - - buf[0] = FU_HAILUCK_REPORT_ID_LONG; - buf[1] = FU_HAILUCK_CMD_WRITE_BLOCK; - if (!fu_memcpy_safe(buf, - bufsz, - 0x02, /* dst */ - data, - data_sz, - 0x0, /* src */ - data_sz, - error)) - return FALSE; - if (!fu_hid_device_set_report(FU_HID_DEVICE(self), - buf[0], - buf, - bufsz, - 2000, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error)) - return FALSE; - - /* success */ - fu_device_sleep(FU_DEVICE(self), 10); - return TRUE; -} - -static gboolean -fu_hailuck_bl_device_write_firmware(FuDevice *device, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - FuHailuckBlDevice *self = FU_HAILUCK_BL_DEVICE(device); - g_autoptr(GBytes) fw = NULL; - g_autoptr(GBytes) fw_new = NULL; - g_autoptr(FuChunk) chk0 = NULL; - g_autoptr(FuChunkArray) chunks = NULL; - g_autofree guint8 *chk0_data = NULL; - - /* progress */ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 10, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 80, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "device-write-blk0"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 9, NULL); - - /* get default image */ - fw = fu_firmware_get_bytes(firmware, error); - if (fw == NULL) - return FALSE; - - /* erase all contents */ - if (!fu_hailuck_bl_device_erase(self, fu_progress_get_child(progress), error)) - return FALSE; - fu_progress_step_done(progress); - - /* tell device amount of data to expect */ - if (!fu_hailuck_bl_device_write_block_start(self, g_bytes_get_size(fw), error)) - return FALSE; - - /* build packets */ - chunks = fu_chunk_array_new_from_bytes(fw, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - 2048); - - /* intentionally corrupt first chunk so that CRC fails */ - chk0 = fu_chunk_array_index(chunks, 0, error); - if (chk0 == NULL) - return FALSE; - chk0_data = fu_memdup_safe(fu_chunk_get_data(chk0), fu_chunk_get_data_sz(chk0), error); - if (chk0_data == NULL) - return FALSE; - chk0_data[0] = 0x00; - if (!fu_hailuck_bl_device_write_block(self, chk0_data, fu_chunk_get_data_sz(chk0), error)) - return FALSE; - - /* send the rest of the chunks */ - for (guint i = 1; i < fu_chunk_array_length(chunks); i++) { - g_autoptr(FuChunk) chk = NULL; - - /* prepare chunk */ - chk = fu_chunk_array_index(chunks, i, error); - if (chk == NULL) - return FALSE; - if (!fu_hailuck_bl_device_write_block(self, - fu_chunk_get_data(chk), - fu_chunk_get_data_sz(chk), - error)) - return FALSE; - fu_progress_set_percentage_full(fu_progress_get_child(progress), - i + 1, - fu_chunk_array_length(chunks)); - } - fu_progress_step_done(progress); - - /* retry write of first block */ - if (!fu_hailuck_bl_device_write_block_start(self, g_bytes_get_size(fw), error)) - return FALSE; - if (!fu_hailuck_bl_device_write_block(self, - fu_chunk_get_data(chk0), - fu_chunk_get_data_sz(chk0), - error)) - return FALSE; - fu_progress_step_done(progress); - - /* verify */ - fw_new = fu_hailuck_bl_device_dump_firmware(device, fu_progress_get_child(progress), error); - fu_progress_step_done(progress); - return fu_bytes_compare(fw, fw_new, error); -} - -static void -fu_hailuck_bl_device_init(FuHailuckBlDevice *self) -{ - fu_device_set_firmware_size(FU_DEVICE(self), 0x4000); - fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_HAILUCK_KBD_FIRMWARE); - fu_device_add_protocol(FU_DEVICE(self), "com.hailuck.kbd"); - fu_device_set_name(FU_DEVICE(self), "Keyboard [bootloader]"); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); - fu_device_add_icon(FU_DEVICE(self), "input-keyboard"); - fu_hid_device_add_flag(FU_HID_DEVICE(self), FU_HID_DEVICE_FLAG_NO_KERNEL_REBIND); - fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); -} - -static void -fu_hailuck_bl_device_class_init(FuHailuckBlDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->dump_firmware = fu_hailuck_bl_device_dump_firmware; - device_class->write_firmware = fu_hailuck_bl_device_write_firmware; - device_class->attach = fu_hailuck_bl_device_attach; - device_class->probe = fu_hailuck_bl_device_probe; -} diff -Nru fwupd-2.0.8/plugins/hailuck/fu-hailuck-bl-device.h fwupd-2.0.20/plugins/hailuck/fu-hailuck-bl-device.h --- fwupd-2.0.8/plugins/hailuck/fu-hailuck-bl-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/fu-hailuck-bl-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -/* - * Copyright 2020 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_HAILUCK_BL_DEVICE (fu_hailuck_bl_device_get_type()) -G_DECLARE_FINAL_TYPE(FuHailuckBlDevice, fu_hailuck_bl_device, FU, HAILUCK_BL_DEVICE, FuHidDevice) diff -Nru fwupd-2.0.8/plugins/hailuck/fu-hailuck-common.h fwupd-2.0.20/plugins/hailuck/fu-hailuck-common.h --- fwupd-2.0.8/plugins/hailuck/fu-hailuck-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/fu-hailuck-common.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -/* - * Copyright 2020 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_HAILUCK_REPORT_ID_SHORT 0x05 -#define FU_HAILUCK_REPORT_ID_LONG 0x06 diff -Nru fwupd-2.0.8/plugins/hailuck/fu-hailuck-kbd-device.c fwupd-2.0.20/plugins/hailuck/fu-hailuck-kbd-device.c --- fwupd-2.0.8/plugins/hailuck/fu-hailuck-kbd-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/fu-hailuck-kbd-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,92 +0,0 @@ -/* - * Copyright 2020 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-hailuck-common.h" -#include "fu-hailuck-kbd-device.h" -#include "fu-hailuck-struct.h" -#include "fu-hailuck-tp-device.h" - -struct _FuHailuckKbdDevice { - FuHidDevice parent_instance; -}; - -G_DEFINE_TYPE(FuHailuckKbdDevice, fu_hailuck_kbd_device, FU_TYPE_HID_DEVICE) - -static gboolean -fu_hailuck_kbd_device_detach(FuDevice *device, FuProgress *progress, GError **error) -{ - guint8 buf[6] = {FU_HAILUCK_REPORT_ID_SHORT, FU_HAILUCK_CMD_DETACH}; - if (!fu_hid_device_set_report(FU_HID_DEVICE(device), - buf[0], - buf, - sizeof(buf), - 1000, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error)) - return FALSE; - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - return TRUE; -} - -static gboolean -fu_hailuck_kbd_device_probe(FuDevice *device, GError **error) -{ - g_autoptr(FuHailuckTpDevice) tp_device = fu_hailuck_tp_device_new(FU_DEVICE(device)); - - /* add extra keyboard-specific GUID */ - fu_device_add_instance_str(device, "MODE", "KBD"); - if (!fu_device_build_instance_id(device, error, "USB", "VID", "PID", "MODE", NULL)) - return FALSE; - - /* add touchpad */ - if (!fu_device_probe(FU_DEVICE(tp_device), error)) - return FALSE; - - /* assume the TP has the same version as the keyboard */ - fu_device_set_version(FU_DEVICE(tp_device), fu_device_get_version(device)); - fu_device_set_version_format(FU_DEVICE(tp_device), fu_device_get_version_format(device)); - fu_device_add_child(device, FU_DEVICE(tp_device)); - - /* success */ - return TRUE; -} - -static void -fu_hailuck_kbd_device_set_progress(FuDevice *self, FuProgress *progress) -{ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); -} - -static void -fu_hailuck_kbd_device_init(FuHailuckKbdDevice *self) -{ - fu_device_set_firmware_size(FU_DEVICE(self), 0x4000); - fu_device_add_protocol(FU_DEVICE(self), "com.hailuck.kbd"); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); - fu_device_add_icon(FU_DEVICE(self), "input-keyboard"); - fu_hid_device_set_interface(FU_HID_DEVICE(self), 0x1); - fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); -} - -static void -fu_hailuck_kbd_device_class_init(FuHailuckKbdDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->detach = fu_hailuck_kbd_device_detach; - device_class->probe = fu_hailuck_kbd_device_probe; - device_class->set_progress = fu_hailuck_kbd_device_set_progress; -} diff -Nru fwupd-2.0.8/plugins/hailuck/fu-hailuck-kbd-device.h fwupd-2.0.20/plugins/hailuck/fu-hailuck-kbd-device.h --- fwupd-2.0.8/plugins/hailuck/fu-hailuck-kbd-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/fu-hailuck-kbd-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -/* - * Copyright 2020 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_HAILUCK_KBD_DEVICE (fu_hailuck_kbd_device_get_type()) -G_DECLARE_FINAL_TYPE(FuHailuckKbdDevice, fu_hailuck_kbd_device, FU, HAILUCK_KBD_DEVICE, FuHidDevice) diff -Nru fwupd-2.0.8/plugins/hailuck/fu-hailuck-kbd-firmware.c fwupd-2.0.20/plugins/hailuck/fu-hailuck-kbd-firmware.c --- fwupd-2.0.8/plugins/hailuck/fu-hailuck-kbd-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/fu-hailuck-kbd-firmware.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -/* - * Copyright 2020 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-hailuck-kbd-firmware.h" - -struct _FuHailuckKbdFirmware { - FuIhexFirmwareClass parent_instance; -}; - -G_DEFINE_TYPE(FuHailuckKbdFirmware, fu_hailuck_kbd_firmware, FU_TYPE_IHEX_FIRMWARE) - -static gboolean -fu_hailuck_kbd_firmware_parse(FuFirmware *firmware, - GInputStream *stream, - FwupdInstallFlags flags, - GError **error) -{ - GPtrArray *records = fu_ihex_firmware_get_records(FU_IHEX_FIRMWARE(firmware)); - g_autoptr(GByteArray) buf = g_byte_array_new(); - g_autoptr(GBytes) fw_new = NULL; - - for (guint j = 0; j < records->len; j++) { - FuIhexFirmwareRecord *rcd = g_ptr_array_index(records, j); - if (rcd->record_type == FU_IHEX_FIRMWARE_RECORD_TYPE_EOF) - break; - if (rcd->record_type != FU_IHEX_FIRMWARE_RECORD_TYPE_DATA) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "only record 0x0 supported, got 0x%02x", - rcd->record_type); - return FALSE; - } - if (rcd->data->len == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "record 0x%x had zero size", - j); - return FALSE; - } - if (rcd->addr + rcd->data->len > buf->len) { - if (rcd->addr + rcd->data->len == 0) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "buffer would have zero size"); - return FALSE; - } - fu_byte_array_set_size(buf, rcd->addr + rcd->data->len, 0x00); - } - if (!fu_memcpy_safe(buf->data, - buf->len, - rcd->addr, - rcd->data->data, - rcd->data->len, - 0x0, - rcd->data->len, - error)) - return FALSE; - } - - /* set the main function executed on system init */ - if (buf->len > 0x37FD && buf->data[1] == 0x38 && buf->data[2] == 0x00) { - buf->data[0] = buf->data[0x37FB]; - buf->data[1] = buf->data[0x37FC]; - buf->data[2] = buf->data[0x37FD]; - buf->data[0x37FB] = 0x00; - buf->data[0x37FC] = 0x00; - buf->data[0x37FD] = 0x00; - } - - /* whole image */ - fw_new = g_bytes_new(buf->data, buf->len); - fu_firmware_set_bytes(firmware, fw_new); - return TRUE; -} - -static void -fu_hailuck_kbd_firmware_init(FuHailuckKbdFirmware *self) -{ - fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION); -} - -static void -fu_hailuck_kbd_firmware_class_init(FuHailuckKbdFirmwareClass *klass) -{ - FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); - firmware_class->parse = fu_hailuck_kbd_firmware_parse; -} diff -Nru fwupd-2.0.8/plugins/hailuck/fu-hailuck-kbd-firmware.h fwupd-2.0.20/plugins/hailuck/fu-hailuck-kbd-firmware.h --- fwupd-2.0.8/plugins/hailuck/fu-hailuck-kbd-firmware.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/fu-hailuck-kbd-firmware.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -/* - * Copyright 2020 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_HAILUCK_KBD_FIRMWARE (fu_hailuck_kbd_firmware_get_type()) -G_DECLARE_FINAL_TYPE(FuHailuckKbdFirmware, - fu_hailuck_kbd_firmware, - FU, - HAILUCK_KBD_FIRMWARE, - FuIhexFirmware) diff -Nru fwupd-2.0.8/plugins/hailuck/fu-hailuck-plugin.c fwupd-2.0.20/plugins/hailuck/fu-hailuck-plugin.c --- fwupd-2.0.8/plugins/hailuck/fu-hailuck-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/fu-hailuck-plugin.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,39 +0,0 @@ -/* - * Copyright 2020 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-hailuck-bl-device.h" -#include "fu-hailuck-kbd-device.h" -#include "fu-hailuck-kbd-firmware.h" -#include "fu-hailuck-plugin.h" - -struct _FuHailuckPlugin { - FuPlugin parent_instance; -}; - -G_DEFINE_TYPE(FuHailuckPlugin, fu_hailuck_plugin, FU_TYPE_PLUGIN) - -static void -fu_hailuck_plugin_init(FuHailuckPlugin *self) -{ -} - -static void -fu_hailuck_plugin_constructed(GObject *obj) -{ - FuPlugin *plugin = FU_PLUGIN(obj); - fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_HAILUCK_KBD_FIRMWARE); - fu_plugin_add_device_gtype(plugin, FU_TYPE_HAILUCK_BL_DEVICE); - fu_plugin_add_device_gtype(plugin, FU_TYPE_HAILUCK_KBD_DEVICE); -} - -static void -fu_hailuck_plugin_class_init(FuHailuckPluginClass *klass) -{ - FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); - plugin_class->constructed = fu_hailuck_plugin_constructed; -} diff -Nru fwupd-2.0.8/plugins/hailuck/fu-hailuck-plugin.h fwupd-2.0.20/plugins/hailuck/fu-hailuck-plugin.h --- fwupd-2.0.8/plugins/hailuck/fu-hailuck-plugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/fu-hailuck-plugin.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -G_DECLARE_FINAL_TYPE(FuHailuckPlugin, fu_hailuck_plugin, FU, HAILUCK_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/hailuck/fu-hailuck-tp-device.c fwupd-2.0.20/plugins/hailuck/fu-hailuck-tp-device.c --- fwupd-2.0.8/plugins/hailuck/fu-hailuck-tp-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/fu-hailuck-tp-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,255 +0,0 @@ -/* - * Copyright 2020 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-hailuck-common.h" -#include "fu-hailuck-struct.h" -#include "fu-hailuck-tp-device.h" - -struct _FuHailuckTpDevice { - FuDevice parent_instance; -}; - -G_DEFINE_TYPE(FuHailuckTpDevice, fu_hailuck_tp_device, FU_TYPE_DEVICE) - -static gboolean -fu_hailuck_tp_device_probe(FuDevice *device, GError **error) -{ - /* add extra touchpad-specific GUID */ - fu_device_add_instance_str(device, "MODE", "TP"); - return fu_device_build_instance_id(device, error, "USB", "VID", "PID", "MODE", NULL); -} - -typedef struct { - guint8 type; - guint8 success; /* if 0xff, then cmd-0x10 */ -} FuHailuckTpDeviceReq; - -static gboolean -fu_hailuck_tp_device_cmd_cb(FuDevice *device, gpointer user_data, GError **error) -{ - FuDevice *parent = fu_device_get_parent(device); - FuHailuckTpDeviceReq *req = (FuHailuckTpDeviceReq *)user_data; - guint8 buf[6] = { - FU_HAILUCK_REPORT_ID_SHORT, - FU_HAILUCK_CMD_GET_STATUS, - req->type, - }; - guint8 success_tmp = req->success; - if (!fu_hid_device_set_report(FU_HID_DEVICE(parent), - buf[0], - buf, - sizeof(buf), - 1000, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error)) - return FALSE; - if (!fu_hid_device_get_report(FU_HID_DEVICE(parent), - buf[0], - buf, - sizeof(buf), - 2000, - FU_HID_DEVICE_FLAG_IS_FEATURE | - FU_HID_DEVICE_FLAG_ALLOW_TRUNC, - error)) - return FALSE; - if (success_tmp == 0xff) - success_tmp = req->type - 0x10; - if (buf[0] != FU_HAILUCK_REPORT_ID_SHORT || buf[1] != success_tmp) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "report mismatch for type=0x%02x[%s]: " - "expected=0x%02x, received=0x%02x", - req->type, - fu_hailuck_cmd_to_string(req->type), - success_tmp, - buf[1]); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_hailuck_tp_device_write_firmware(FuDevice *device, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - FuDevice *parent = fu_device_get_parent(device); - const guint block_size = 1024; - g_autoptr(GInputStream) stream = NULL; - g_autoptr(FuChunkArray) chunks = NULL; - FuHailuckTpDeviceReq req = { - .type = 0xff, - .success = 0xff, - }; - - /* progress */ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 10, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 85, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "end-program"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 3, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "pass"); - - /* get default image */ - stream = fu_firmware_get_stream(firmware, error); - if (stream == NULL) - return FALSE; - - /* erase */ - req.type = FU_HAILUCK_CMD_I2C_ERASE; - if (!fu_device_retry(device, fu_hailuck_tp_device_cmd_cb, 100, &req, error)) { - g_prefix_error(error, "failed to erase: "); - return FALSE; - } - fu_device_sleep(device, 10); - fu_progress_step_done(progress); - - /* write */ - chunks = fu_chunk_array_new_from_stream(stream, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - block_size, - error); - if (chunks == NULL) - return FALSE; - for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - g_autoptr(FuChunk) chk = NULL; - g_autoptr(GByteArray) buf = g_byte_array_new(); - - /* prepare chunk */ - chk = fu_chunk_array_index(chunks, i, error); - if (chk == NULL) - return FALSE; - - /* write block */ - fu_byte_array_append_uint8(buf, FU_HAILUCK_REPORT_ID_LONG); - fu_byte_array_append_uint8(buf, FU_HAILUCK_CMD_WRITE_TP); - fu_byte_array_append_uint16(buf, 0xCCCC, G_LITTLE_ENDIAN); - fu_byte_array_append_uint16(buf, fu_chunk_get_address(chk), G_LITTLE_ENDIAN); - fu_byte_array_append_uint16(buf, 0xCCCC, G_LITTLE_ENDIAN); - g_byte_array_append(buf, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); - fu_byte_array_append_uint8(buf, 0xEE); - fu_byte_array_append_uint8(buf, 0xD2); - fu_byte_array_append_uint16(buf, 0xCCCC, G_LITTLE_ENDIAN); - fu_byte_array_append_uint16(buf, 0xCCCC, G_LITTLE_ENDIAN); - fu_byte_array_append_uint16(buf, 0xCCCC, G_LITTLE_ENDIAN); - if (buf->len != block_size + 16) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "packet mismatch: len=0x%04x, expected=0x%04x", - buf->len, - block_size + 16); - return FALSE; - } - if (!fu_hid_device_set_report(FU_HID_DEVICE(parent), - buf->data[0], - buf->data, - buf->len, - 1000, - FU_HID_DEVICE_FLAG_IS_FEATURE, - error)) { - g_prefix_error(error, "failed to write block 0x%x: ", i); - return FALSE; - } - fu_device_sleep(device, 150); - - /* verify block */ - req.type = FU_HAILUCK_CMD_I2C_VERIFY_BLOCK; - if (!fu_device_retry(device, fu_hailuck_tp_device_cmd_cb, 100, &req, error)) { - g_prefix_error(error, "failed to verify block 0x%x: ", i); - return FALSE; - } - - /* update progress */ - fu_progress_set_percentage_full(fu_progress_get_child(progress), - i + 1, - fu_chunk_array_length(chunks)); - } - fu_device_sleep(device, 50); - fu_progress_step_done(progress); - - /* end-program */ - req.type = FU_HAILUCK_CMD_I2C_END_PROGRAM; - if (!fu_device_retry(device, fu_hailuck_tp_device_cmd_cb, 100, &req, error)) { - g_prefix_error(error, "failed to end program: "); - return FALSE; - } - fu_device_sleep(device, 50); - fu_progress_step_done(progress); - - /* verify checksum */ - req.type = FU_HAILUCK_CMD_I2C_VERIFY_CHECKSUM; - if (!fu_device_retry(device, fu_hailuck_tp_device_cmd_cb, 100, &req, error)) { - g_prefix_error(error, "failed to verify: "); - return FALSE; - } - fu_device_sleep(device, 50); - fu_progress_step_done(progress); - - /* signal that programming has completed */ - req.type = FU_HAILUCK_CMD_I2C_PROGRAMPASS; - req.success = 0x0; - if (!fu_device_retry(device, fu_hailuck_tp_device_cmd_cb, 100, &req, error)) { - g_prefix_error(error, "failed to program: "); - return FALSE; - } - fu_progress_step_done(progress); - - /* success! */ - return TRUE; -} - -static void -fu_hailuck_tp_device_set_progress(FuDevice *self, FuProgress *progress) -{ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); -} - -static void -fu_hailuck_tp_device_init(FuHailuckTpDevice *self) -{ - fu_device_retry_set_delay(FU_DEVICE(self), 50); /* ms */ - fu_device_set_firmware_size(FU_DEVICE(self), 0x6018); - fu_device_add_protocol(FU_DEVICE(self), "com.hailuck.tp"); - fu_device_set_logical_id(FU_DEVICE(self), "TP"); - fu_device_set_name(FU_DEVICE(self), "Touchpad"); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN); - fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); - fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); -} - -static void -fu_hailuck_tp_device_class_init(FuHailuckTpDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->write_firmware = fu_hailuck_tp_device_write_firmware; - device_class->probe = fu_hailuck_tp_device_probe; - device_class->set_progress = fu_hailuck_tp_device_set_progress; -} - -FuHailuckTpDevice * -fu_hailuck_tp_device_new(FuDevice *parent) -{ - FuHailuckTpDevice *self; - self = g_object_new(FU_TYPE_HAILUCK_TP_DEVICE, "parent", parent, NULL); - return FU_HAILUCK_TP_DEVICE(self); -} diff -Nru fwupd-2.0.8/plugins/hailuck/fu-hailuck-tp-device.h fwupd-2.0.20/plugins/hailuck/fu-hailuck-tp-device.h --- fwupd-2.0.8/plugins/hailuck/fu-hailuck-tp-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/fu-hailuck-tp-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,15 +0,0 @@ -/* - * Copyright 2020 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_HAILUCK_TP_DEVICE (fu_hailuck_tp_device_get_type()) -G_DECLARE_FINAL_TYPE(FuHailuckTpDevice, fu_hailuck_tp_device, FU, HAILUCK_TP_DEVICE, FuDevice) - -FuHailuckTpDevice * -fu_hailuck_tp_device_new(FuDevice *parent); diff -Nru fwupd-2.0.8/plugins/hailuck/fu-hailuck.rs fwupd-2.0.20/plugins/hailuck/fu-hailuck.rs --- fwupd-2.0.8/plugins/hailuck/fu-hailuck.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/fu-hailuck.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,23 +0,0 @@ -// Copyright 2023 Richard Hughes -// SPDX-License-Identifier: LGPL-2.1-or-later - -#[derive(ToString)] -enum FuHailuckCmd { - Erase = 0x45, - ReadBlockStart = 0x52, - Attach = 0x55, // guessed - WriteBlockStart = 0x57, - ReadBlock = 0x72, - Detach = 0x75, // guessed - WriteBlock = 0x77, - GetStatus = 0xA1, - WriteTp = 0xD0, // guessed - I2cCheckChecksum = 0xF0, - I2cEnterBl = 0xF1, - I2cErase = 0xF2, - I2cProgram = 0xF3, - I2cVerifyBlock = 0xF4, - I2cVerifyChecksum = 0xF5, - I2cProgrampass = 0xF6, - I2cEndProgram = 0xF7, -} diff -Nru fwupd-2.0.8/plugins/hailuck/hailuck.quirk fwupd-2.0.20/plugins/hailuck/hailuck.quirk --- fwupd-2.0.8/plugins/hailuck/hailuck.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/hailuck.quirk 1970-01-01 00:00:00.000000000 +0000 @@ -1,25 +0,0 @@ -# bootloader - -[USB\VID_0603&PID_1020] -Plugin = hailuck -GType = FuHailuckBlDevice -#Flags = is-bootloader - -[USB\VID_258A&PID_001E] -Plugin = hailuck -GType = FuHailuckKbdDevice -Vendor = PINE64 -CounterpartGuid = USB\VID_0603&PID_1020 - -[USB\VID_258A&PID_001E&MODE_KBD] -Name = Keyboard - -[USB\VID_258A&PID_001F] -Plugin = hailuck -GType = FuHailuckKbdDevice -CounterpartGuid = USB\VID_0603&PID_1020 - -[USB\VID_258A&PID_000D] -Plugin = hailuck -GType = FuHailuckKbdDevice -CounterpartGuid = USB\VID_0603&PID_1020 diff -Nru fwupd-2.0.8/plugins/hailuck/meson.build fwupd-2.0.20/plugins/hailuck/meson.build --- fwupd-2.0.8/plugins/hailuck/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/meson.build 1970-01-01 00:00:00.000000000 +0000 @@ -1,21 +0,0 @@ -cargs = ['-DG_LOG_DOMAIN="FuPluginHailuck"'] -plugins += {meson.current_source_dir().split('/')[-1]: true} - -plugin_quirks += files('hailuck.quirk') -plugin_builtins += static_library('fu_plugin_hailuck', - rustgen.process('fu-hailuck.rs'), - sources: [ - 'fu-hailuck-bl-device.c', - 'fu-hailuck-kbd-device.c', - 'fu-hailuck-kbd-firmware.c', # fuzzing - 'fu-hailuck-tp-device.c', - 'fu-hailuck-plugin.c', - ], - include_directories: plugin_incdirs, - link_with: plugin_libs, - c_args: cargs, - dependencies: plugin_deps, -) - -enumeration_data += files('tests/hailuck-setup.json') -device_tests += files('tests/hailuck.json') diff -Nru fwupd-2.0.8/plugins/hailuck/tests/hailuck-setup.json fwupd-2.0.20/plugins/hailuck/tests/hailuck-setup.json --- fwupd-2.0.8/plugins/hailuck/tests/hailuck-setup.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/tests/hailuck-setup.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,98 +0,0 @@ -{ - "UsbDevices": [ - { - "GType": "FuUsbDevice", - "PlatformId": "1-1", - "Created": "2024-10-29T16:25:20.797574Z", - "IdVendor": 9610, - "IdProduct": 30, - "Device": 256, - "USB": 272, - "Manufacturer": 1, - "Product": 2, - "UsbConfigDescriptors": [ - { - "ConfigurationValue": 1 - } - ], - "UsbInterfaces": [ - { - "Length": 9, - "DescriptorType": 4, - "InterfaceClass": 3, - "InterfaceSubClass": 1, - "InterfaceProtocol": 1, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 129, - "Interval": 10, - "MaxPacketSize": 8 - } - ] - }, - { - "Length": 9, - "DescriptorType": 4, - "InterfaceNumber": 1, - "InterfaceClass": 3, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 130, - "Interval": 10, - "MaxPacketSize": 8 - } - ] - } - ], - "UsbEvents": [ - { - "Id": "#d5a801ad", - "Data": "usb" - }, - { - "Id": "#ad8c58d3", - "Data": "usb" - }, - { - "Id": "#1075ed5c", - "Data": "usb_device" - }, - { - "Id": "#bddbca22", - "Data": "MAJOR=189\nMINOR=1\nDEVNAME=bus/usb/001/002\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=258a/1e/100\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=002" - }, - { - "Id": "#d432c663", - "Data": "bus/usb/001/002" - }, - { - "Id": "#9b895db2" - }, - { - "Id": "#66f3e150" - }, - { - "Id": "#d410b6c7" - }, - { - "Id": "#1075ed5c", - "Data": "usb_device" - }, - { - "Id": "#4693935e", - "Data": "001" - }, - { - "Id": "#bddbca22", - "Data": "MAJOR=189\nMINOR=1\nDEVNAME=bus/usb/001/002\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=258a/1e/100\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=002" - }, - { - "Id": "#1ab3ae0a", - "Data": "002" - } - ] - } - ] -} diff -Nru fwupd-2.0.8/plugins/hailuck/tests/hailuck.json fwupd-2.0.20/plugins/hailuck/tests/hailuck.json --- fwupd-2.0.8/plugins/hailuck/tests/hailuck.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hailuck/tests/hailuck.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -{ - "name": "Hailuck Keyboard (PineBook Pro)", - "interactive": false, - "steps": [ - { - "emulation-file": "@enumeration_datadir@/hailuck-setup.json", - "components": [ - { - "version": "1.0", - "guids": [ - "e997e24f-850a-51ba-b2be-b7c9e07b794b" - ] - } - ] - } - ] -} diff -Nru fwupd-2.0.8/plugins/hpi-cfu/README.md fwupd-2.0.20/plugins/hpi-cfu/README.md --- fwupd-2.0.8/plugins/hpi-cfu/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hpi-cfu/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,7 @@ +--- +title: Plugin: HPI CFU +--- + ## Introduction The plugin used for updating firmware on HP USB 4 100W G6 Dock and HP Thunderbolt 4 Ultra 180W/280W G6 Dock. @@ -42,10 +46,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Pena Christian diff -Nru fwupd-2.0.8/plugins/hpi-cfu/fu-hpi-cfu-device.c fwupd-2.0.20/plugins/hpi-cfu/fu-hpi-cfu-device.c --- fwupd-2.0.8/plugins/hpi-cfu/fu-hpi-cfu-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hpi-cfu/fu-hpi-cfu-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -24,8 +24,9 @@ #define OUT_REPORT_TYPE 0x0200 #define FEATURE_REPORT_TYPE 0x0300 -#define FU_HPI_CFU_PAYLOAD_LENGTH 52 -#define FU_HPI_CFU_DEVICE_TIMEOUT 0 /* ms */ +#define FU_HPI_CFU_PAYLOAD_LENGTH 52 +#define FU_HPI_CFU_DEVICE_TIMEOUT 0 /* ms */ +#define FU_HPI_CFU_DEVICE_FLAG_UPDATE_ON_REBOOT "update-on-reboot" const guint8 report_data[15] = {0x00, 0xff, 0xa0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; @@ -42,6 +43,7 @@ gboolean last_packet_sent; guint8 bulk_opt; gboolean firmware_status; + gboolean update_on_reboot; gboolean exit_state_machine_framework; }; @@ -68,14 +70,14 @@ fu_hpi_cfu_device_start_entire_transaction(FuHpiCfuDevice *self, GError **error) { g_autoptr(GError) error_local = NULL; - g_autoptr(GByteArray) st_req = fu_struct_hpi_cfu_buf_new(); + g_autoptr(FuStructHpiCfuBuf) st_req = fu_struct_hpi_cfu_buf_new(); fu_struct_hpi_cfu_buf_set_report_id(st_req, OFFER_REPORT_ID); fu_struct_hpi_cfu_buf_set_command(st_req, FU_CFU_OFFER_INFO_CODE_START_ENTIRE_TRANSACTION); if (!fu_struct_hpi_cfu_buf_set_report_data(st_req, report_data, sizeof(report_data), error)) return FALSE; - fu_dump_raw(G_LOG_DOMAIN, "StartEntireTransaction", st_req->data, st_req->len); + fu_dump_raw(G_LOG_DOMAIN, "StartEntireTransaction", st_req->buf->data, st_req->buf->len); if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, @@ -83,8 +85,8 @@ SET_REPORT, OUT_REPORT_TYPE | OFFER_REPORT_ID, FU_HPI_CFU_INTERFACE, - st_req->data, - st_req->len, + st_req->buf->data, + st_req->buf->len, NULL, FU_HPI_CFU_DEVICE_TIMEOUT, NULL, @@ -134,14 +136,14 @@ fu_hpi_cfu_device_send_start_offer_list(FuHpiCfuDevice *self, GError **error) { g_autoptr(GError) error_local = NULL; - g_autoptr(GByteArray) st_req = fu_struct_hpi_cfu_buf_new(); + g_autoptr(FuStructHpiCfuBuf) st_req = fu_struct_hpi_cfu_buf_new(); fu_struct_hpi_cfu_buf_set_report_id(st_req, OFFER_REPORT_ID); fu_struct_hpi_cfu_buf_set_command(st_req, FU_CFU_OFFER_INFO_CODE_START_OFFER_LIST); if (!fu_struct_hpi_cfu_buf_set_report_data(st_req, report_data, sizeof(report_data), error)) return FALSE; - fu_dump_raw(G_LOG_DOMAIN, "SendStartOfferList", st_req->data, st_req->len); + fu_dump_raw(G_LOG_DOMAIN, "SendStartOfferList", st_req->buf->data, st_req->buf->len); if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, @@ -149,8 +151,8 @@ SET_REPORT, OUT_REPORT_TYPE | OFFER_REPORT_ID, FU_HPI_CFU_INTERFACE, - st_req->data, - st_req->len, + st_req->buf->data, + st_req->buf->len, NULL, FU_HPI_CFU_DEVICE_TIMEOUT, NULL, @@ -214,7 +216,7 @@ const guint8 *buf; gint8 flag_value = 0; gsize bufsz = 0; - g_autoptr(GByteArray) st_req = fu_struct_hpi_cfu_offer_cmd_new(); + g_autoptr(FuStructHpiCfuOfferCmd) st_req = fu_struct_hpi_cfu_offer_cmd_new(); g_autoptr(GError) error_local = NULL; g_autoptr(GBytes) blob_offer = NULL; @@ -223,15 +225,15 @@ return FALSE; buf = g_bytes_get_data(blob_offer, &bufsz); - fu_struct_hpi_cfu_payload_cmd_set_report_id(st_req, OFFER_REPORT_ID); - if (!fu_memcpy_safe(st_req->data, st_req->len, 0x1, buf, bufsz, 0x0, 16, error)) + fu_struct_hpi_cfu_offer_cmd_set_report_id(st_req, OFFER_REPORT_ID); + if (!fu_memcpy_safe(st_req->buf->data, st_req->buf->len, 0x1, buf, bufsz, 0x0, 16, error)) return FALSE; FU_BIT_SET(flag_value, 7); /* (update now) */ FU_BIT_SET(flag_value, 6); /* (force update version) */ fu_struct_hpi_cfu_offer_cmd_set_flags(st_req, flag_value); - fu_dump_raw(G_LOG_DOMAIN, "SendOfferUpdateCommand", st_req->data, st_req->len); + fu_dump_raw(G_LOG_DOMAIN, "SendOfferUpdateCommand", st_req->buf->data, st_req->buf->len); if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, @@ -239,8 +241,8 @@ SET_REPORT, OUT_REPORT_TYPE | FIRMWARE_REPORT_ID, FU_HPI_CFU_INTERFACE, - st_req->data, - st_req->len, + st_req->buf->data, + st_req->buf->len, NULL, FU_HPI_CFU_DEVICE_TIMEOUT, NULL, @@ -373,14 +375,14 @@ fu_hpi_cfu_device_send_end_offer_list(FuHpiCfuDevice *self, GError **error) { g_autoptr(GError) error_local = NULL; - g_autoptr(GByteArray) st_req = fu_struct_hpi_cfu_buf_new(); + g_autoptr(FuStructHpiCfuBuf) st_req = fu_struct_hpi_cfu_buf_new(); fu_struct_hpi_cfu_buf_set_report_id(st_req, OFFER_REPORT_ID); fu_struct_hpi_cfu_buf_set_command(st_req, FU_CFU_OFFER_INFO_CODE_END_OFFER_LIST); if (!fu_struct_hpi_cfu_buf_set_report_data(st_req, report_data, sizeof(report_data), error)) return FALSE; - fu_dump_raw(G_LOG_DOMAIN, "SendEndOfferListCommand", st_req->data, st_req->len); + fu_dump_raw(G_LOG_DOMAIN, "SendEndOfferListCommand", st_req->buf->data, st_req->buf->len); if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, @@ -388,8 +390,8 @@ SET_REPORT, OUT_REPORT_TYPE | OFFER_REPORT_ID, FU_HPI_CFU_INTERFACE, - st_req->data, - st_req->len, + st_req->buf->data, + st_req->buf->len, NULL, FU_HPI_CFU_DEVICE_TIMEOUT, NULL, @@ -454,7 +456,7 @@ { if (!fu_hpi_cfu_device_start_entire_transaction(self, error)) { self->state = FU_HPI_CFU_STATE_ERROR; - g_prefix_error(error, "start_entire_transaction: "); + g_prefix_error_literal(error, "start_entire_transaction: "); return FALSE; } self->state = FU_HPI_CFU_STATE_START_ENTIRE_TRANSACTION_ACCEPTED; @@ -469,7 +471,7 @@ { if (!fu_hpi_cfu_device_start_entire_transaction_accepted(self, error)) { self->state = FU_HPI_CFU_STATE_ERROR; - g_prefix_error(error, "start_entire_transaction_accept: "); + g_prefix_error_literal(error, "start_entire_transaction_accept: "); return FALSE; } @@ -486,7 +488,7 @@ { if (!fu_hpi_cfu_device_send_start_offer_list(self, error)) { self->state = FU_HPI_CFU_STATE_ERROR; - g_prefix_error(error, "start_offer_list: "); + g_prefix_error_literal(error, "start_offer_list: "); return FALSE; } self->state = FU_HPI_CFU_STATE_START_OFFER_LIST_ACCEPTED; @@ -505,7 +507,7 @@ if (!fu_hpi_cfu_device_send_offer_list_accepted(self, &status, error)) { self->state = FU_HPI_CFU_STATE_UPDATE_STOP; - g_prefix_error(error, "start_offer_list_accept: "); + g_prefix_error_literal(error, "start_offer_list_accept: "); return FALSE; } @@ -526,7 +528,7 @@ { if (!fu_hpi_cfu_device_send_offer_update_command(self, options->fw_offer, error)) { self->state = FU_HPI_CFU_STATE_ERROR; - g_prefix_error(error, "send_offer_update_command: "); + g_prefix_error_literal(error, "send_offer_update_command: "); return FALSE; } self->state = FU_HPI_CFU_STATE_UPDATE_OFFER_ACCEPTED; @@ -544,7 +546,7 @@ if (!fu_hpi_cfu_device_firmware_update_offer_accepted(self, &reply, &reason, error)) { self->state = FU_HPI_CFU_STATE_ERROR; - g_prefix_error(error, "send_offer_accepted: "); + g_prefix_error_literal(error, "send_offer_accepted: "); return FALSE; } if (reply == FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_ACCEPT) { @@ -586,7 +588,7 @@ static gboolean fu_hpi_cfu_device_send_payload(FuHpiCfuDevice *self, GByteArray *cfu_buf, GError **error) { - g_autoptr(GByteArray) st_req = fu_struct_hpi_cfu_payload_cmd_new(); + g_autoptr(FuStructHpiCfuPayloadCmd) st_req = fu_struct_hpi_cfu_payload_cmd_new(); g_autoptr(GError) error_local = NULL; fu_struct_hpi_cfu_payload_cmd_set_report_id(st_req, FIRMWARE_REPORT_ID); @@ -606,7 +608,7 @@ self->currentaddress += cfu_buf->len; - fu_dump_raw(G_LOG_DOMAIN, "ToDevice", st_req->data, st_req->len); + fu_dump_raw(G_LOG_DOMAIN, "ToDevice", st_req->buf->data, st_req->buf->len); if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, @@ -614,8 +616,8 @@ SET_REPORT, OUT_REPORT_TYPE | FIRMWARE_REPORT_ID, FU_HPI_CFU_INTERFACE, - st_req->data, - st_req->len, + st_req->buf->data, + st_req->buf->len, NULL, FU_HPI_CFU_DEVICE_TIMEOUT, NULL, @@ -714,10 +716,10 @@ untransmitted_data->len, fill_from_position, error)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "to set untransmitted_data"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "to set untransmitted_data"); return FALSE; } } @@ -753,7 +755,7 @@ payload_header_length, fill_from_position, error)) { - g_prefix_error(error, "failed to set untransmitted_data: "); + g_prefix_error_literal(error, "failed to set untransmitted_data: "); return FALSE; } } @@ -762,6 +764,83 @@ return TRUE; } +static void +fu_hpi_cfu_device_handler_set_status_report_25(FuHpiCfuDevice *self, + guint8 status, + gboolean lastpacket) +{ + switch (status) { + case FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_SKIP: + case FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_REJECT: + case FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_COMMAND_READY: + case FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_CMD_NOT_SUPPORTED: + g_warning("check_update_content: reason: %s", + fu_hpi_cfu_firmware_update_offer_to_string(status)); + self->state = FU_HPI_CFU_STATE_UPDATE_MORE_OFFERS; + break; + + case FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_ACCEPT: + g_debug("check_update_content: reason: %s", + fu_hpi_cfu_firmware_update_offer_to_string(status)); + if (lastpacket) { + g_debug("check_update_content: reason: %s for last_packet_sent", + fu_hpi_cfu_firmware_update_offer_to_string(status)); + self->state = FU_HPI_CFU_STATE_UPDATE_SUCCESS; + } else + self->state = FU_HPI_CFU_STATE_UPDATE_CONTENT; + break; + + case FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_BUSY: + g_warning("check_update_content: reason:%s", + fu_hpi_cfu_firmware_update_offer_to_string(status)); + self->state = FU_HPI_CFU_STATE_NOTIFY_ON_READY; + break; + + default: + g_warning("check_update_content: FU_HPI_CFU_STATE_ERROR"); + self->state = FU_HPI_CFU_STATE_ERROR; + break; + } +} + +static void +fu_hpi_cfu_device_handler_set_status_report_22(FuHpiCfuDevice *self, + guint8 status, + gboolean lastpacket) +{ + switch (status) { + case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_PREPARE: + case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_WRITE: + case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_COMPLETE: + case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_VERIFY: + case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_CRC: + case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_SIGNATURE: + case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_VERSION: + case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_SWAP_PENDING: + case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_INVALID_ADDR: + case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_NO_OFFER: + case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_INVALID: + self->state = FU_HPI_CFU_STATE_ERROR; + g_warning("check_update_content: reason:%s", + fu_cfu_content_status_to_string(status)); + g_debug("check_update_content: %s", + fu_hpi_cfu_firmware_update_status_to_string(status)); + break; + + case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_SUCCESS: + g_debug("check_update_content: SUCCESS"); + if (lastpacket) + self->state = FU_HPI_CFU_STATE_UPDATE_SUCCESS; + else + self->state = FU_HPI_CFU_STATE_UPDATE_CONTENT; + break; + default: + g_warning("check_update_content: status none"); + self->state = FU_HPI_CFU_STATE_ERROR; + break; + } +} + static gboolean fu_hpi_cfu_device_handler_check_update_content(FuHpiCfuDevice *self, FuProgress *progress, @@ -850,73 +929,11 @@ self->state = FU_HPI_CFU_STATE_UPDATE_CONTENT; if (report_id == 0x25) { - g_debug("check_update_content: report_id:%d", report_id == FIRMWARE_REPORT_ID); - switch (status) { - case FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_SKIP: - case FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_REJECT: - case FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_COMMAND_READY: - case FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_CMD_NOT_SUPPORTED: - g_warning("check_update_content: reason: %s", - fu_hpi_cfu_firmware_update_offer_to_string(status)); - self->state = FU_HPI_CFU_STATE_UPDATE_MORE_OFFERS; - break; - - case FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_ACCEPT: - g_debug("check_update_content: reason: %s", - fu_hpi_cfu_firmware_update_offer_to_string(status)); - if (lastpacket) { - g_debug("check_update_content: reason: %s for last_packet_sent", - fu_hpi_cfu_firmware_update_offer_to_string(status)); - self->state = FU_HPI_CFU_STATE_UPDATE_SUCCESS; - } else - self->state = FU_HPI_CFU_STATE_UPDATE_CONTENT; - break; - - case FU_HPI_CFU_FIRMWARE_UPDATE_OFFER_BUSY: - g_warning("check_update_content: reason:%s", - fu_hpi_cfu_firmware_update_offer_to_string(status)); - self->state = FU_HPI_CFU_STATE_NOTIFY_ON_READY; - break; - - default: - g_warning("check_update_content: FU_HPI_CFU_STATE_ERROR"); - self->state = FU_HPI_CFU_STATE_ERROR; - break; - } + g_debug("check_update_content: report_id:0x%x", report_id); + fu_hpi_cfu_device_handler_set_status_report_25(self, status, lastpacket); } else if (report_id == 0x22) { - g_debug("check_update_content: report_id:0x22"); - switch (status) { - case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_PREPARE: - case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_WRITE: - case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_COMPLETE: - case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_VERIFY: - case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_CRC: - case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_SIGNATURE: - case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_VERSION: - case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_SWAP_PENDING: - case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_INVALID_ADDR: - case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_NO_OFFER: - case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_ERROR_INVALID: - self->state = FU_HPI_CFU_STATE_ERROR; - g_warning("check_update_content: reason:%s", - fu_cfu_content_status_to_string(status)); - g_debug("check_update_content: %s", - fu_hpi_cfu_firmware_update_status_to_string(status)); - break; - - case FU_HPI_CFU_FIRMWARE_UPDATE_STATUS_SUCCESS: - g_debug("check_update_content: SUCCESS"); - if (lastpacket) { - self->state = FU_HPI_CFU_STATE_UPDATE_SUCCESS; - } else - self->state = FU_HPI_CFU_STATE_UPDATE_CONTENT; - break; - - default: - g_warning("check_update_content: status none."); - self->state = FU_HPI_CFU_STATE_ERROR; - break; - } + g_debug("check_update_content: report_id:0x%x", report_id); + fu_hpi_cfu_device_handler_set_status_report_22(self, status, lastpacket); } /* success */ @@ -946,10 +963,10 @@ payload_buf, read_index, error)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "to get payload header"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "to get payload header"); return FALSE; } @@ -961,10 +978,10 @@ payload_header_length, read_index, error)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "to get payload data"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "to get payload data"); return FALSE; } @@ -1006,10 +1023,10 @@ payload_header_length, fill_from_position, error)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "to set untransmitted_data"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "to set untransmitted_data"); return FALSE; } } else { @@ -1070,7 +1087,7 @@ chunks = fu_firmware_get_chunks(options->fw_payload, error); if (chunks == NULL) { - g_prefix_error(error, "null chunks"); + g_prefix_error_literal(error, "null chunks: "); return FALSE; } for (guint32 i = 0; i < chunks->len; i++) { @@ -1080,7 +1097,7 @@ progress, options, error)) { - g_prefix_error(error, "send_payload: "); + g_prefix_error_literal(error, "send_payload: "); return FALSE; } } @@ -1137,7 +1154,7 @@ { if (!fu_hpi_cfu_device_send_end_offer_list(self, error)) { self->state = FU_HPI_CFU_STATE_ERROR; - g_prefix_error(error, "send_end_offer_list: "); + g_prefix_error_literal(error, "send_end_offer_list: "); return FALSE; } self->state = FU_HPI_CFU_STATE_END_OFFER_LIST_ACCEPTED; @@ -1151,7 +1168,7 @@ GError **error) { if (!fu_hpi_cfu_device_end_offer_list_accepted(self, error)) { - g_prefix_error(error, "end_offer_list_accept: "); + g_prefix_error_literal(error, "end_offer_list_accept: "); return FALSE; } self->state = FU_HPI_CFU_STATE_VERIFY_CHECK_SWAP_PENDING_BY_SENDING_OFFER_LIST_AGAIN; @@ -1208,7 +1225,7 @@ { if (!fu_hpi_cfu_device_send_start_offer_list(self, error)) { self->state = FU_HPI_CFU_STATE_UPDATE_VERIFY_ERROR; - g_prefix_error(error, "swap_pending_send_offer_list_again: "); + g_prefix_error_literal(error, "swap_pending_send_offer_list_again: "); return FALSE; } @@ -1227,7 +1244,7 @@ if (!fu_hpi_cfu_device_send_offer_list_accepted(self, &status, error)) { self->state = FU_HPI_CFU_STATE_ERROR; - g_prefix_error(error, "swap_pending_offer_list_accept: "); + g_prefix_error_literal(error, "swap_pending_offer_list_accept: "); return FALSE; } @@ -1247,7 +1264,7 @@ { if (!fu_hpi_cfu_device_send_offer_update_command(self, options->fw_offer, error)) { self->state = FU_HPI_CFU_STATE_ERROR; - g_prefix_error(error, "swap_pending_send_offer_again: "); + g_prefix_error_literal(error, "swap_pending_send_offer_again: "); return FALSE; } self->state = FU_HPI_CFU_STATE_VERIFY_CHECK_SWAP_PENDING_OFFER_ACCEPTED; @@ -1265,7 +1282,7 @@ /* reply status must be SWAP_PENDING */ if (!fu_hpi_cfu_device_firmware_update_offer_accepted(self, &reply, &reason, error)) { - g_prefix_error(error, "swap_pending_send_offer_accept: "); + g_prefix_error_literal(error, "swap_pending_send_offer_accept: "); return FALSE; } @@ -1313,7 +1330,7 @@ GError **error) { if (!fu_hpi_cfu_device_send_end_offer_list(self, error)) { - g_prefix_error(error, "swap_pending_send_end_offer_list: "); + g_prefix_error_literal(error, "swap_pending_send_end_offer_list: "); return FALSE; } @@ -1330,7 +1347,7 @@ GError **error) { if (!fu_hpi_cfu_device_end_offer_list_accepted(self, error)) { - g_prefix_error(error, "swap_pending_end_offer_list_accept: "); + g_prefix_error_literal(error, "swap_pending_end_offer_list_accept: "); return FALSE; } @@ -1418,6 +1435,14 @@ if (!FU_DEVICE_CLASS(fu_hpi_cfu_device_parent_class)->setup(device, error)) return FALSE; + /* sets update_on_reboot flag if device needs reboot for update */ + if (fu_device_has_private_flag(device, FU_HPI_CFU_DEVICE_FLAG_UPDATE_ON_REBOOT)) { + self->update_on_reboot = TRUE; + g_debug("update_on_reboot flag is set for device: %s", + fu_device_get_name(FU_DEVICE(self))); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + } + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, @@ -1431,7 +1456,7 @@ FU_HPI_CFU_DEVICE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to do device setup"); + g_prefix_error_literal(error, "failed to do device setup: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, "VersionResponse", buf, actual_length); @@ -1442,7 +1467,7 @@ bulk_opt_index = version_table_offset + component_index * component_data_size + component_id_offset; - /* Get Bulk optimization value */ + /* get bulk optimization value */ if (!fu_memcpy_safe((guint8 *)&self->bulk_opt, sizeof(self->bulk_opt), 0x0, @@ -1459,7 +1484,7 @@ } static void -fu_hpi_cfu_device_set_progress(FuDevice *self, FuProgress *progress) +fu_hpi_cfu_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -1517,13 +1542,13 @@ progress, hpi_cfu_states[self->state].options, error)) { - g_prefix_error(error, "state: "); + g_prefix_error_literal(error, "state: "); return FALSE; } } /* the device automatically reboots */ - if (self->firmware_status) + if (self->firmware_status && !self->update_on_reboot) fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); return TRUE; } @@ -1552,6 +1577,7 @@ fu_device_set_install_duration(FU_DEVICE(self), 720); /* 12 min */ fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV); fu_usb_device_add_interface(FU_USB_DEVICE(self), FU_HPI_CFU_INTERFACE); + fu_device_register_private_flag(FU_DEVICE(self), FU_HPI_CFU_DEVICE_FLAG_UPDATE_ON_REBOOT); /* reboot takes down the entire hub for ~12 minutes */ fu_device_set_remove_delay(FU_DEVICE(self), 720 * 1000); diff -Nru fwupd-2.0.8/plugins/hpi-cfu/fu-hpi-cfu-plugin.c fwupd-2.0.20/plugins/hpi-cfu/fu-hpi-cfu-plugin.c --- fwupd-2.0.8/plugins/hpi-cfu/fu-hpi-cfu-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hpi-cfu/fu-hpi-cfu-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,12 +18,14 @@ static void fu_hpi_cfu_plugin_init(FuHpiCfuPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void fu_hpi_cfu_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_HPI_CFU_DEVICE); } diff -Nru fwupd-2.0.8/plugins/hpi-cfu/hpi-cfu.quirk fwupd-2.0.20/plugins/hpi-cfu/hpi-cfu.quirk --- fwupd-2.0.8/plugins/hpi-cfu/hpi-cfu.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hpi-cfu/hpi-cfu.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -5,3 +5,12 @@ # HP Thunderbolt 4 Ultra 180W/280W G6 Dock [USB\VID_03F0&PID_03B7] Plugin = hpi_cfu + +# HP USB-C 100W G6 Dock +[USB\VID_03F0&PID_04B7] +Plugin = hpi_cfu + +# HP Engage One G2 Advanced Hub +[USB\VID_03F0&PID_07C8] +Plugin = hpi_cfu +Flags = update-on-reboot diff -Nru fwupd-2.0.8/plugins/hpi-cfu/tests/hp-dock-g6-ultra.json fwupd-2.0.20/plugins/hpi-cfu/tests/hp-dock-g6-ultra.json --- fwupd-2.0.8/plugins/hpi-cfu/tests/hp-dock-g6-ultra.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hpi-cfu/tests/hp-dock-g6-ultra.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/30adee4fdd507f00bce9c884718321cbe95b5fb2fca93f330d32b49d9ed10a87-HPTBT4UltraG6_HDX_00.21.22.00.cab", - "emulation-url": "https://fwupd.org/downloads/140f40be8950b95dd2b1132c432f0b762272e1176816f40fd6edf02a92b587e2-HPTBT4UltraG6_HDX_00.21.22.00.zip", + "url": "30adee4fdd507f00bce9c884718321cbe95b5fb2fca93f330d32b49d9ed10a87-HPTBT4UltraG6_HDX_00.21.22.00.cab", + "emulation-url": "140f40be8950b95dd2b1132c432f0b762272e1176816f40fd6edf02a92b587e2-HPTBT4UltraG6_HDX_00.21.22.00.zip", "components": [ { "version": "00.21.22.00", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/2eaf001e5b8b44ee363dc9d1e58a3608b3adcf1fe3e6ffb5a40cfcebb90253f9-HPTBT4UltraG6_HDX_00.21.28.00.cab", - "emulation-url": "https://fwupd.org/downloads/bc92e78d8bc95ad69ce8d7a41bfff41ae23d0153234b7fd1dd9e8c069dd2fbd1-HPTBT4UltraG6_HDX_00.21.28.00.zip", + "url": "2eaf001e5b8b44ee363dc9d1e58a3608b3adcf1fe3e6ffb5a40cfcebb90253f9-HPTBT4UltraG6_HDX_00.21.28.00.cab", + "emulation-url": "bc92e78d8bc95ad69ce8d7a41bfff41ae23d0153234b7fd1dd9e8c069dd2fbd1-HPTBT4UltraG6_HDX_00.21.28.00.zip", "components": [ { "version": "00.21.28.00", diff -Nru fwupd-2.0.8/plugins/huddly-usb/README.md fwupd-2.0.20/plugins/huddly-usb/README.md --- fwupd-2.0.8/plugins/huddly-usb/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/huddly-usb/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -5,7 +5,7 @@ ## Introduction This plugin supports performing firmware upgrades on Huddly L1 and S1 video conferencing cameras -connected via the Huddly USB adapter. +connected via the Huddly USB adapter, as well as the Huddly C1 video bar. ## Firmware Format @@ -39,10 +39,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Huddly: @LarsStensen diff -Nru fwupd-2.0.8/plugins/huddly-usb/fu-huddly-usb-common.c fwupd-2.0.20/plugins/huddly-usb/fu-huddly-usb-common.c --- fwupd-2.0.8/plugins/huddly-usb/fu-huddly-usb-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/huddly-usb/fu-huddly-usb-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,7 @@ { g_free(msg->msg_name); if (msg->header != NULL) - g_byte_array_unref(msg->header); + fu_struct_h_link_header_unref(msg->header); if (msg->payload != NULL) g_byte_array_unref(msg->payload); g_free(msg); @@ -59,7 +59,7 @@ g_return_val_if_fail(msg != NULL, NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); - g_byte_array_append(packet, msg->header->data, msg->header->len); + fu_byte_array_append_array(packet, msg->header->buf); g_byte_array_append(packet, (const guint8 *)msg->msg_name, strlen(msg->msg_name)); if (msg->payload != NULL) g_byte_array_append(packet, msg->payload->data, msg->payload->len); diff -Nru fwupd-2.0.8/plugins/huddly-usb/fu-huddly-usb-device.c fwupd-2.0.20/plugins/huddly-usb/fu-huddly-usb-device.c --- fwupd-2.0.8/plugins/huddly-usb/fu-huddly-usb-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/huddly-usb/fu-huddly-usb-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -44,7 +44,7 @@ intfs = fu_usb_device_get_interfaces(FU_USB_DEVICE(self), error); if (intfs == NULL) { - g_prefix_error(error, "could not find interface"); + g_prefix_error_literal(error, "could not find interface: "); return FALSE; } for (guint i = 0; i < intfs->len; i++) { @@ -63,10 +63,10 @@ } } if (self->bulk_ep[EP_OUT] == 0 || self->bulk_ep[EP_IN] == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "could not find usb endpoints"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "could not find usb endpoints"); return FALSE; } return TRUE; @@ -93,7 +93,7 @@ src->data + offset, chunk_size, &transmitted, - 2000, + 5000, NULL, error)) { return FALSE; @@ -140,12 +140,12 @@ fu_byte_array_set_size(msg_res, HUDDLY_USB_RECEIVE_BUFFER_SIZE, 0u); if (!fu_huddly_usb_device_bulk_read(self, msg_res, &received_length, error)) { - g_prefix_error(error, "HLink receive failed: "); + g_prefix_error_literal(error, "HLink receive failed: "); return NULL; } msg = fu_huddly_usb_hlink_msg_parse(msg_res->data, received_length, error); if (msg == NULL) { - g_prefix_error(error, "HLink receive failed: "); + g_prefix_error_literal(error, "HLink receive failed: "); return NULL; } return g_steal_pointer(&msg); @@ -179,7 +179,7 @@ { g_autoptr(GByteArray) packet = g_byte_array_new(); if (!fu_huddly_usb_device_bulk_write(self, packet, NULL, error)) { - g_prefix_error(error, "reset device failed: "); + g_prefix_error_literal(error, "reset device failed: "); return FALSE; } return TRUE; @@ -194,17 +194,17 @@ g_autoptr(GByteArray) response = g_byte_array_new(); g_autofree gchar *str = NULL; - g_debug("send salute..."); + g_debug("send salute…"); fu_byte_array_append_uint8(salutation, 0x00); if (!fu_huddly_usb_device_bulk_write(self, salutation, NULL, error)) { - g_prefix_error(error, "send salute send message failed: "); + g_prefix_error_literal(error, "send salute send message failed: "); return FALSE; } fu_byte_array_set_size(response, 100, 0x0); if (!fu_huddly_usb_device_bulk_read(self, response, &received_length, error)) { - g_prefix_error(error, "send salute read response failed: "); + g_prefix_error_literal(error, "send salute read response failed: "); return FALSE; } str = fu_strsafe((const gchar *)response->data, received_length); @@ -223,17 +223,17 @@ g_autoptr(GPtrArray) items = NULL; if (!fu_huddly_usb_device_hlink_subscribe(self, "prodinfo/get_msgpack_reply", error)) { - g_prefix_error(error, "failed to read product info: "); + g_prefix_error_literal(error, "failed to read product info: "); return FALSE; } msg_req = fu_huddly_usb_hlink_msg_new("prodinfo/get_msgpack", NULL); if (!fu_huddly_usb_device_hlink_send(self, msg_req, error)) { - g_prefix_error(error, "failed to read product info: "); + g_prefix_error_literal(error, "failed to read product info: "); return FALSE; } msg_res = fu_huddly_usb_device_hlink_receive(self, error); if (msg_res == NULL) { - g_prefix_error(error, "failed to read product info: "); + g_prefix_error_literal(error, "failed to read product info: "); return FALSE; } g_debug("receive data %s", msg_res->msg_name); @@ -244,7 +244,7 @@ /* version */ item_version = fu_msgpack_map_lookup(items, 0, "app_version", error); if (item_version == NULL) { - g_prefix_error(error, "failed to read product info: "); + g_prefix_error_literal(error, "failed to read product info: "); return FALSE; } version_split = g_regex_split_simple("[-+]", @@ -256,7 +256,7 @@ /* state */ item_state = fu_msgpack_map_lookup(items, 0, "state", error); if (item_state == NULL) { - g_prefix_error(error, "failed to read product info: "); + g_prefix_error_literal(error, "failed to read product info: "); return FALSE; } fu_huddly_usb_device_set_state(self, fu_msgpack_item_get_string(item_state)->str); @@ -267,7 +267,25 @@ fu_huddly_usb_device_reboot(FuHuddlyUsbDevice *self, GError **error) { g_autoptr(FuHuddlyUsbHLinkMsg) msg = fu_huddly_usb_hlink_msg_new("camctrl/reboot", NULL); - return fu_huddly_usb_device_hlink_send(self, msg, error); + g_autoptr(GError) error_local = NULL; + + /* set flag to indicate disconnection is expected before sending reboot command */ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* send reboot command - device may disconnect during or after this call */ + if (!fu_huddly_usb_device_hlink_send(self, msg, &error_local)) { + /* if error is due to device disconnection (expected after reboot), ignore it */ + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("device disconnected during reboot as expected: %s", + error_local->message); + return TRUE; + } + /* propagate other errors */ + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + return TRUE; } static gboolean @@ -348,11 +366,26 @@ g_autoptr(FuMsgpackItem) item_operation = NULL; g_autoptr(FuMsgpackItem) item_error = NULL; g_autoptr(FuMsgpackItem) item_reboot = NULL; + g_autoptr(GError) error_local = NULL; g_autoptr(GPtrArray) items = NULL; - - msg_res = fu_huddly_usb_device_hlink_receive(self, error); - if (msg_res == NULL) + msg_res = fu_huddly_usb_device_hlink_receive(self, &error_local); + if (msg_res == NULL) { + /* For dual-image devices, disconnection during hpk execution might be expected */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE) && + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("device disconnected during hpk execution (expected for " + "dual-image): %s", + error_local->message); + /* assume reboot is needed since device disconnected during firmware + * execution */ + self->need_reboot = TRUE; + /* set flag to indicate device has disconnected */ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; + } items = fu_msgpack_parse(msg_res->payload, error); if (items == NULL) return FALSE; @@ -368,7 +401,7 @@ return FALSE; err = fu_msgpack_item_get_integer(item_error); if (err != 0) { - g_prefix_error(error, "received error %s", operation->str); + g_prefix_error(error, "received error %s: ", operation->str); return FALSE; } @@ -421,6 +454,12 @@ error)) return FALSE; + /* if device has disconnected (dual-image case), skip unsubscribe since device is gone */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)) { + g_debug("skipping unsubscribe as device has disconnected"); + return TRUE; + } + return fu_huddly_usb_device_hlink_unsubscribe(self, "upgrader/status", error); } @@ -465,8 +504,31 @@ { FuHuddlyUsbDevice *self = FU_HUDDLY_USB_DEVICE(device); + /* check if device is in usb adapter mode*/ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("device in USB Adapter mode (PID BA01), running verification cycle"); + + /* in usb adapter mode, device needs verification to complete the update process.*/ + if (!fu_huddly_usb_device_verify(self, progress, error)) { + g_prefix_error_literal(error, "verification failed in USB Adapter mode: "); + return FALSE; + } + + /* after verification, explicitly reboot to return to normal camera mode.*/ + g_debug("verification completed, sending final reboot to return to normal mode"); + if (!fu_huddly_usb_device_reboot(self, error)) { + g_prefix_error_literal(error, + "failed final reboot from USB Adapter mode: "); + return FALSE; + } + + g_debug("final reboot sent, device should return to normal mode"); + return TRUE; + } + + /* normal attach process for regular operation (PID A032) */ if (!fu_huddly_usb_device_ensure_product_info(self, error)) { - g_prefix_error(error, "failed to read product info: "); + g_prefix_error_literal(error, "failed to read product info: "); return FALSE; } @@ -576,7 +638,7 @@ fu_progress_step_done(progress); if (!self->need_reboot) { - /* The device not requesting reboot could occur if the device was in an unverified + /* the device not requesting reboot could occur if the device was in an unverified state due to an aborted previous upgrade attempt, in which case this download will complete the upgrade */ g_warning("expected device to request reboot after download"); @@ -590,7 +652,7 @@ /* success */ self->pending_verify = TRUE; - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + /* note: FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG is already set in reboot function */ return TRUE; } @@ -615,7 +677,7 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); - fu_device_add_icon(FU_DEVICE(self), "camera-web"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_WEB_CAMERA); } static void diff -Nru fwupd-2.0.8/plugins/huddly-usb/fu-huddly-usb-plugin.c fwupd-2.0.20/plugins/huddly-usb/fu-huddly-usb-plugin.c --- fwupd-2.0.8/plugins/huddly-usb/fu-huddly-usb-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/huddly-usb/fu-huddly-usb-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -24,6 +24,7 @@ fu_huddly_usb_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_HUDDLY_USB_DEVICE); } diff -Nru fwupd-2.0.8/plugins/huddly-usb/huddly-usb.quirk fwupd-2.0.20/plugins/huddly-usb/huddly-usb.quirk --- fwupd-2.0.8/plugins/huddly-usb/huddly-usb.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/huddly-usb/huddly-usb.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -6,6 +6,10 @@ [USB\VID_2BD9&PID_A031] Plugin = huddly_usb +# Huddly C1 +[USB\VID_2BD9&PID_1CEF] +Plugin = huddly_usb + # Huddly Crew [USB\VID_2BD9&PID_A033] Plugin = huddly_usb diff -Nru fwupd-2.0.8/plugins/huddly-usb/tests/huddly-s1.json fwupd-2.0.20/plugins/huddly-usb/tests/huddly-s1.json --- fwupd-2.0.8/plugins/huddly-usb/tests/huddly-s1.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/huddly-usb/tests/huddly-s1.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/4af1947aa24b5a6cc484537b42ca16e0f79bc241fc720ea2fa72611b5274137f-huddly-s1-1.8.4.cab", + "url": "4af1947aa24b5a6cc484537b42ca16e0f79bc241fc720ea2fa72611b5274137f-huddly-s1-1.8.4.cab", "components": [ { "version": "1.8.4", @@ -14,7 +14,7 @@ ] }, { - "url": "https://fwupd.org/downloads/57c29008fcfe98fd3ba74d0ee519321961a27c338e56faa33d74737f9bbf9f20-huddly-s1-1.8.6.cab", + "url": "57c29008fcfe98fd3ba74d0ee519321961a27c338e56faa33d74737f9bbf9f20-huddly-s1-1.8.6.cab", "components": [ { "version": "1.8.6", diff -Nru fwupd-2.0.8/plugins/hughski-colorhug/README.md fwupd-2.0.20/plugins/hughski-colorhug/README.md --- fwupd-2.0.8/plugins/hughski-colorhug/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hughski-colorhug/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -52,10 +52,3 @@ ## Version Considerations This plugin has been available since fwupd version `0.8.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Richard Hughes: @hughsie diff -Nru fwupd-2.0.8/plugins/hughski-colorhug/fu-hughski-colorhug-device.c fwupd-2.0.20/plugins/hughski-colorhug/fu-hughski-colorhug-device.c --- fwupd-2.0.8/plugins/hughski-colorhug/fu-hughski-colorhug-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hughski-colorhug/fu-hughski-colorhug-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -20,14 +20,6 @@ G_DEFINE_TYPE(FuHughskiColorhugDevice, fu_hughski_colorhug_device, FU_TYPE_USB_DEVICE) -#define CH_CMD_GET_FIRMWARE_VERSION 0x07 -#define CH_CMD_RESET 0x24 -#define CH_CMD_READ_FLASH 0x25 -#define CH_CMD_WRITE_FLASH 0x26 -#define CH_CMD_BOOT_FLASH 0x27 -#define CH_CMD_SET_FLASH_SUCCESS 0x28 -#define CH_CMD_ERASE_FLASH 0x29 - #define CH_USB_HID_EP 0x0001 #define CH_USB_HID_EP_IN (CH_USB_HID_EP | 0x80) #define CH_USB_HID_EP_OUT (CH_USB_HID_EP | 0x00) @@ -94,7 +86,7 @@ CH_DEVICE_USB_TIMEOUT, NULL, /* cancellable */ &error_local)) { - if (cmd == CH_CMD_RESET && + if (cmd == FU_HUGHSKI_COLORHUG_CMD_RESET && g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { g_debug("ignoring '%s' on reset", error_local->message); return TRUE; @@ -122,7 +114,7 @@ CH_DEVICE_USB_TIMEOUT, NULL, /* cancellable */ &error_local)) { - if (cmd == CH_CMD_RESET && + if (cmd == FU_HUGHSKI_COLORHUG_CMD_RESET && g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { g_debug("ignoring '%s' on reset", error_local->message); return TRUE; @@ -192,7 +184,7 @@ return TRUE; } if (!fu_hughski_colorhug_device_msg(self, - CH_CMD_RESET, + FU_HUGHSKI_COLORHUG_CMD_RESET, NULL, 0, /* in */ NULL, @@ -221,7 +213,7 @@ return TRUE; } if (!fu_hughski_colorhug_device_msg(self, - CH_CMD_BOOT_FLASH, + FU_HUGHSKI_COLORHUG_CMD_BOOT_FLASH, NULL, 0, /* in */ NULL, @@ -248,7 +240,7 @@ g_debug("setting flash success %s", val ? "true" : "false"); if (!fu_hughski_colorhug_device_msg(self, - CH_CMD_SET_FLASH_SUCCESS, + FU_HUGHSKI_COLORHUG_CMD_SET_FLASH_SUCCESS, buf, sizeof(buf), /* in */ NULL, @@ -277,13 +269,13 @@ gsize sz, GError **error) { - guint8 buf[4]; + guint8 buf[4]; /* nocheck:zero-init */ g_autoptr(GError) error_local = NULL; fu_memwrite_uint16(buf + 0, addr, G_LITTLE_ENDIAN); fu_memwrite_uint16(buf + 2, sz, G_LITTLE_ENDIAN); if (!fu_hughski_colorhug_device_msg(self, - CH_CMD_ERASE_FLASH, + FU_HUGHSKI_COLORHUG_CMD_ERASE_FLASH, buf, sizeof(buf), /* in */ NULL, @@ -302,9 +294,9 @@ static gchar * fu_hughski_colorhug_device_get_version(FuHughskiColorhugDevice *self, GError **error) { - guint8 buf[6]; + guint8 buf[6] = {0}; if (!fu_hughski_colorhug_device_msg(self, - CH_CMD_GET_FIRMWARE_VERSION, + FU_HUGHSKI_COLORHUG_CMD_GET_FIRMWARE_VERSION, NULL, 0, /* in */ buf, @@ -411,7 +403,7 @@ fu_progress_set_id(progress, G_STRLOC); fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - guint8 buf[CH_FLASH_TRANSFER_BLOCK_SIZE + 4]; + guint8 buf[CH_FLASH_TRANSFER_BLOCK_SIZE + 4] = {0}; g_autoptr(FuChunk) chk = NULL; g_autoptr(GError) error_local = NULL; @@ -435,7 +427,7 @@ error)) return FALSE; if (!fu_hughski_colorhug_device_msg(self, - CH_CMD_WRITE_FLASH, + FU_HUGHSKI_COLORHUG_CMD_WRITE_FLASH, buf, sizeof(buf), /* in */ NULL, @@ -468,8 +460,8 @@ fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { g_autoptr(FuChunk) chk = NULL; - guint8 buf[3]; - guint8 buf_out[CH_FLASH_TRANSFER_BLOCK_SIZE + 1]; + guint8 buf[3] = {0}; + guint8 buf_out[CH_FLASH_TRANSFER_BLOCK_SIZE + 1] = {0}; g_autoptr(GError) error_local = NULL; /* prepare chunk */ @@ -481,7 +473,7 @@ fu_memwrite_uint16(buf + 0, fu_chunk_get_address(chk), G_LITTLE_ENDIAN); buf[2] = fu_chunk_get_data_sz(chk); if (!fu_hughski_colorhug_device_msg(self, - CH_CMD_READ_FLASH, + FU_HUGHSKI_COLORHUG_CMD_READ_FLASH, buf, sizeof(buf), /* in */ buf_out, @@ -580,7 +572,7 @@ } static void -fu_hughski_colorhug_device_set_progress(FuDevice *self, FuProgress *progress) +fu_hughski_colorhug_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/hughski-colorhug/fu-hughski-colorhug-plugin.c fwupd-2.0.20/plugins/hughski-colorhug/fu-hughski-colorhug-plugin.c --- fwupd-2.0.8/plugins/hughski-colorhug/fu-hughski-colorhug-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hughski-colorhug/fu-hughski-colorhug-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -24,6 +24,7 @@ fu_hughski_colorhug_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_HUGHSKI_COLORHUG_DEVICE); } diff -Nru fwupd-2.0.8/plugins/hughski-colorhug/fu-hughski-colorhug.rs fwupd-2.0.20/plugins/hughski-colorhug/fu-hughski-colorhug.rs --- fwupd-2.0.8/plugins/hughski-colorhug/fu-hughski-colorhug.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hughski-colorhug/fu-hughski-colorhug.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,16 @@ // Copyright 2024 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later +enum FuHughskiColorhugCmd { + GetFirmwareVersion = 0x07, + Reset = 0x24, + ReadFlash = 0x25, + WriteFlash = 0x26, + BootFlash = 0x27, + SetFlashSuccess = 0x28, + EraseFlash = 0x29, +} + #[derive(ToString)] enum FuHughskiColorhugError { None, diff -Nru fwupd-2.0.8/plugins/hughski-colorhug/tests/hughski-colorhug-plus.json fwupd-2.0.20/plugins/hughski-colorhug/tests/hughski-colorhug-plus.json --- fwupd-2.0.8/plugins/hughski-colorhug/tests/hughski-colorhug-plus.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hughski-colorhug/tests/hughski-colorhug-plus.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/5cbff92158331aeb10008ca36fa918a9637dde7bfe31de3e0523d14090be8977-fakedevice01_dfu.cab", - "emulation-url": "https://fwupd.org/downloads/8be5c7f3fe5c399a8699768da3f4ddd2582df19c70e197b852efaa027f42688b-fakedevice01_dfu.zip", + "url": "5cbff92158331aeb10008ca36fa918a9637dde7bfe31de3e0523d14090be8977-fakedevice01_dfu.cab", + "emulation-url": "8be5c7f3fe5c399a8699768da3f4ddd2582df19c70e197b852efaa027f42688b-fakedevice01_dfu.zip", "components": [ { "version": "0.1", @@ -16,8 +16,8 @@ ] }, { - "url": "https://fwupd.org/downloads/8bc3afd07a0af3baaab8b19893791dd3972e8305-fakedevice02_dfu.cab", - "emulation-url": "https://fwupd.org/downloads/72c580400020f22929510e9fec43d6781d56af697e72e8d560b45daa57e1817c-fakedevice02_dfu.zip", + "url": "8bc3afd07a0af3baaab8b19893791dd3972e8305-fakedevice02_dfu.cab", + "emulation-url": "72c580400020f22929510e9fec43d6781d56af697e72e8d560b45daa57e1817c-fakedevice02_dfu.zip", "components": [ { "version": "0.2", diff -Nru fwupd-2.0.8/plugins/hughski-colorhug/tests/hughski-colorhug.json fwupd-2.0.20/plugins/hughski-colorhug/tests/hughski-colorhug.json --- fwupd-2.0.8/plugins/hughski-colorhug/tests/hughski-colorhug.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hughski-colorhug/tests/hughski-colorhug.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/42a0a07a978018af235833c9b861461e923163dffd1fb177a9caa59a0c36995e-hughski-colorhug2-1.2.5.cab", - "emulation-url": "https://fwupd.org/downloads/08ddc01a50fb866398d01cc4829531640e651f0233306e10a54117f88474b153-hughski-colorhug-1.2.5.zip", + "url": "42a0a07a978018af235833c9b861461e923163dffd1fb177a9caa59a0c36995e-hughski-colorhug2-1.2.5.cab", + "emulation-url": "08ddc01a50fb866398d01cc4829531640e651f0233306e10a54117f88474b153-hughski-colorhug-1.2.5.zip", "components": [ { "version": "1.2.5", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/2a066c8a1bfbd99f161c867b4dbe7e51ac36fc2b16ef37b11d18419874fbcb6c-hughski-colorhug-1.2.6.cab", - "emulation-url": "https://fwupd.org/downloads/b25122f17912467c1488aaadb59483efc3fe773cb7d002f64289177ca2f3285c-hughski-colorhug-1.2.6.zip", + "url": "2a066c8a1bfbd99f161c867b4dbe7e51ac36fc2b16ef37b11d18419874fbcb6c-hughski-colorhug-1.2.6.cab", + "emulation-url": "b25122f17912467c1488aaadb59483efc3fe773cb7d002f64289177ca2f3285c-hughski-colorhug-1.2.6.zip", "components": [ { "version": "1.2.6", diff -Nru fwupd-2.0.8/plugins/hughski-colorhug/tests/hughski-colorhug2.json fwupd-2.0.20/plugins/hughski-colorhug/tests/hughski-colorhug2.json --- fwupd-2.0.8/plugins/hughski-colorhug/tests/hughski-colorhug2.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/hughski-colorhug/tests/hughski-colorhug2.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/170f2c19f17b7819644d3fcc7617621cc3350a04-hughski-colorhug2-2.0.6.cab", - "emulation-url": "https://fwupd.org/downloads/5ddb5d8e9ac85ea05b8dafcad638befdf3e70e5e1db0ca47489fa43d93f33a57-hughski-colorhug2-2.0.6.zip", + "url": "170f2c19f17b7819644d3fcc7617621cc3350a04-hughski-colorhug2-2.0.6.cab", + "emulation-url": "5ddb5d8e9ac85ea05b8dafcad638befdf3e70e5e1db0ca47489fa43d93f33a57-hughski-colorhug2-2.0.6.zip", "components": [ { "version": "2.0.6", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/e5ad222bdbd3d3d48d8613e67c7e0a0e194f8cd828e33c554d9f05d933e482c7-hughski-colorhug2-2.0.7.cab", - "emulation-url": "https://fwupd.org/downloads/29ee025e65b6a469556bbdd819cba665b043244c90c65b9f659933a347f8cf2e-hughski-colorhug2-2.0.7.zip", + "url": "e5ad222bdbd3d3d48d8613e67c7e0a0e194f8cd828e33c554d9f05d933e482c7-hughski-colorhug2-2.0.7.cab", + "emulation-url": "29ee025e65b6a469556bbdd819cba665b043244c90c65b9f659933a347f8cf2e-hughski-colorhug2-2.0.7.zip", "components": [ { "version": "2.0.7", diff -Nru fwupd-2.0.8/plugins/ilitek-its/README.md fwupd-2.0.20/plugins/ilitek-its/README.md --- fwupd-2.0.8/plugins/ilitek-its/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,62 @@ +--- +title: Plugin: Ilitek Its +--- + +## Introduction + +This plugin can update ILITEK touch controller's firmware. +Devices are enumerated via HID-over-I2C, USB and intel-quicki2c driver. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +a packed binary file format. + +This plugin supports the following protocol ID: + +* `tw.com.ilitek.its` + +## GUID Generation + +These devices use the standard DeviceInstanceId values, e.g. + +* `HIDRAW\VEN_222A&DEV_1234` +* `HIDRAW\VEN_222A&DEV_FF29` + +Additional instance ID are added which corresponds to the FWID and/or sensor ID: + +* `HIDRAW\VEN_222A&DEV_1234&FWID_1234` +* `HIDRAW\VEN_222A&DEV_1234&SENSORID_12` + +Additional instance IDs are added which corresponds to the EDID PNP ID and product code: + +* `HIDRAW\VEN_222A&DEV_1234&PNPID_ABC` +* `HIDRAW\VEN_222A&DEV_1234&PNPID_ABC&PCODE_1234` +* `HIDRAW\VEN_222A&DEV_1234&SENSORID_12&PNPID_ABC&PCODE_1234` + +## Update Behavior + +The device is updated after switching into bootloader mode, where touchscreen is nonfunctional. +After a successful firmware update, device will switch back to normal runtime mode. + +## Vendor ID Security + +The vendor ID is set from the HID vendor, e.g. `HIDRAW:0x222A` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +### IlitekItsSensorIdMask + +The sensor ID mask to use when constructing the instance ID. + +Since: 2.0.14 + +## External Interface Access + +This plugin requires ioctl `HIDIOCSFEATURE` and read access to `/dev/hidraw`. + +## Version Considerations + +This plugin has been available since fwupd version `2.0.14`. diff -Nru fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-block.c fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-block.c --- fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-block.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-block.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,74 @@ +/* + * Copyright 2025 Joe Hong + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-ilitek-its-block.h" + +struct _FuIlitekItsBlock { + FuFirmware parent_instance; + guint16 crc; +}; + +G_DEFINE_TYPE(FuIlitekItsBlock, fu_ilitek_its_block, FU_TYPE_FIRMWARE) + +static void +fu_ilitek_its_block_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuIlitekItsBlock *self = FU_ILITEK_ITS_BLOCK(firmware); + fu_xmlb_builder_insert_kx(bn, "crc", self->crc); +} + +static gboolean +fu_ilitek_its_block_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ + FuIlitekItsBlock *self = FU_ILITEK_ITS_BLOCK(firmware); + gsize streamsz = 0; + g_autoptr(GInputStream) partial_stream = NULL; + + /* calculate CRC of block minus the CRC16 itself */ + if (!fu_input_stream_size(stream, &streamsz, error)) + return FALSE; + partial_stream = fu_partial_input_stream_new(stream, 0x0, streamsz - 2, error); + if (partial_stream == NULL) + return FALSE; + if (!fu_input_stream_compute_crc16(partial_stream, + FU_CRC_KIND_B16_KERMIT, + &self->crc, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +guint16 +fu_ilitek_its_block_get_crc(FuIlitekItsBlock *self) +{ + return self->crc; +} + +static void +fu_ilitek_its_block_init(FuIlitekItsBlock *self) +{ +} + +static void +fu_ilitek_its_block_class_init(FuIlitekItsBlockClass *klass) +{ + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + firmware_class->export = fu_ilitek_its_block_export; + firmware_class->parse = fu_ilitek_its_block_parse; +} + +FuFirmware * +fu_ilitek_its_block_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_ILITEK_ITS_BLOCK, NULL)); +} diff -Nru fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-block.h fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-block.h --- fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-block.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-block.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Copyright 2025 Joe Hong + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_ILITEK_ITS_BLOCK (fu_ilitek_its_block_get_type()) +G_DECLARE_FINAL_TYPE(FuIlitekItsBlock, fu_ilitek_its_block, FU, ILITEK_ITS_BLOCK, FuFirmware) + +FuFirmware * +fu_ilitek_its_block_new(void); +guint16 +fu_ilitek_its_block_get_crc(FuIlitekItsBlock *self) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-common.c fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-common.c --- fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,23 @@ +/* + * Copyright 2025 Joe Hong + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-ilitek-its-common.h" + +gchar * +fu_ilitek_its_convert_version(guint64 version_raw) +{ + /* + * convert 8 byte version in to human readable format. + * e.g. convert 0x0700000101020304 into 700.1.102.304 + */ + return g_strdup_printf("%x.%x.%x.%x", + (guint)((version_raw >> 48) & 0xFFFF), + (guint)((version_raw >> 32) & 0xFFFF), + (guint)((version_raw >> 16) & 0xFFFF), + (guint)(version_raw & 0xFFFF)); +} diff -Nru fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-common.h fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-common.h --- fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * Copyright 2025 Joe Hong + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +gchar * +fu_ilitek_its_convert_version(guint64 version_raw); diff -Nru fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-device.c fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-device.c --- fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,1049 @@ +/* + * Copyright 2025 Joe Hong + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-ilitek-its-block.h" +#include "fu-ilitek-its-common.h" +#include "fu-ilitek-its-device.h" +#include "fu-ilitek-its-firmware.h" +#include "fu-ilitek-its-struct.h" + +#define FU_ILITEK_ITS_HID_ACK_BYTE 0xAC + +#define FU_ILITEK_ITS_LOOKUP_TYPE_EDID 0x1 +#define FU_ILITEK_ITS_LOOKUP_TYPE_SENSOR_ID 0x2 + +#define FU_ILITEK_ITS_CRC_RECALCULATE 0x0 +#define FU_ILITEK_ITS_CRC_GET 0x1 + +#define FU_ILITEK_ITS_WRITE_ENABLE_KEY 0x5AA5 +#define FU_ILITEK_ITS_WRITE_ENABLE_START 0x5000 +#define FU_ILITEK_ITS_WRITE_ENABLE_END 0x5001 + +#define FU_ILITEK_ITS_AP_MODE 0x5A /* device in Application mode */ +#define FU_ILITEK_ITS_BL_MODE 0x55 /* device in Bootloader mode */ + +struct _FuIlitekItsDevice { + FuHidrawDevice parent_instance; + gchar *ic_name; + guint32 protocol_ver; + guint8 sensor_id_mask; +}; + +G_DEFINE_TYPE(FuIlitekItsDevice, fu_ilitek_its_device, FU_TYPE_HIDRAW_DEVICE) + +static void +fu_ilitek_its_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuIlitekItsDevice *self = FU_ILITEK_ITS_DEVICE(device); + fwupd_codec_string_append(str, idt, "IcName", self->ic_name); + fwupd_codec_string_append_hex(str, idt, "ProtocolVer", self->protocol_ver); + fwupd_codec_string_append_hex(str, idt, "SensorIdMask", self->sensor_id_mask); +} + +typedef struct { + FuIlitekItsCmd cmd; + gboolean is_ack; + GByteArray *rbuf; +} FuIlitekItsHidCmdHelper; + +static gboolean +fu_ilitek_its_device_read_cb(FuDevice *device, gpointer data, GError **error) +{ + FuIlitekItsDevice *self = FU_ILITEK_ITS_DEVICE(device); + FuIlitekItsHidCmdHelper *helper = (FuIlitekItsHidCmdHelper *)data; + const guint8 *buf_data; + gsize bufsz_data = 0; + gsize bufsz = FU_STRUCT_ILITEK_ITS_HID_RES_SIZE; + g_autofree guint8 *buf = g_new0(guint8, bufsz); + g_autoptr(FuStructIlitekItsHidRes) st_res = NULL; + + if (!fu_udev_device_read(FU_UDEV_DEVICE(self), + buf, + bufsz, + NULL, + 200, + FU_IO_CHANNEL_FLAG_NONE, + error)) + return FALSE; + st_res = fu_struct_ilitek_its_hid_res_parse(buf, bufsz, 0, error); + if (st_res == NULL) + return FALSE; + if (fu_struct_ilitek_its_hid_res_get_cmd(st_res) != helper->cmd) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid hid response header"); + return FALSE; + } + + buf_data = fu_struct_ilitek_its_hid_res_get_data(st_res, &bufsz_data); + if (helper->is_ack && (bufsz_data == 0 || buf_data[0] != FU_ILITEK_ITS_HID_ACK_BYTE)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid ack response"); + return FALSE; + } + + fu_dump_raw(G_LOG_DOMAIN, "HidReadReport", st_res->buf->data, st_res->buf->len); + + if (helper->rbuf != NULL) + g_byte_array_append(helper->rbuf, buf_data, bufsz_data); + + /* success */ + return TRUE; +} + +static gboolean +fu_ilitek_its_device_send_cmd(FuIlitekItsDevice *self, + FuStructIlitekItsHidCmd *st_cmd, + GByteArray *rbuf, + GError **error) +{ + FuIlitekItsHidCmdHelper helper = { + .cmd = fu_struct_ilitek_its_hid_cmd_get_cmd(st_cmd), + .rbuf = rbuf, + .is_ack = FALSE, + }; + + if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), + st_cmd->buf->data, + st_cmd->buf->len, + FU_IOCTL_FLAG_RETRY, + error)) { + g_prefix_error_literal(error, "failed to send HID cmd: "); + return FALSE; + } + if (rbuf != NULL) { + fu_device_sleep(FU_DEVICE(self), 100); + if (!fu_device_retry_full(FU_DEVICE(self), + fu_ilitek_its_device_read_cb, + 50, + 100, + &helper, + error)) { + g_prefix_error_literal(error, "failed to recv HID packet: "); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ilitek_its_device_send_cmd_then_wake_ack(FuIlitekItsDevice *self, + FuStructIlitekItsHidCmd *st_cmd, + GError **error) +{ + FuIlitekItsHidCmdHelper helper = { + .cmd = fu_struct_ilitek_its_hid_cmd_get_cmd(st_cmd), + .rbuf = NULL, + .is_ack = TRUE, + }; + + if (!fu_ilitek_its_device_send_cmd(self, st_cmd, NULL, error)) + return FALSE; + + return fu_device_retry_full(FU_DEVICE(self), + fu_ilitek_its_device_read_cb, + 50, + 100, + &helper, + error); +} + +static gboolean +fu_ilitek_its_device_send_long_cmd_then_wake_ack(FuIlitekItsDevice *self, + FuStructIlitekItsLongHidCmd *st_cmd, + GError **error) +{ + FuIlitekItsHidCmdHelper helper = { + .cmd = fu_struct_ilitek_its_long_hid_cmd_get_cmd(st_cmd), + .rbuf = NULL, + .is_ack = TRUE, + }; + + if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), + st_cmd->buf->data, + st_cmd->buf->len, + FU_IOCTL_FLAG_RETRY, + error)) { + g_prefix_error_literal(error, "failed to send long HID cmd: "); + return FALSE; + } + + return fu_device_retry_full(FU_DEVICE(self), + fu_ilitek_its_device_read_cb, + 50, + 100, + &helper, + error); +} + +static gboolean +fu_ilitek_its_device_recalculate_crc(FuIlitekItsDevice *self, + guint32 start, + guint32 end, + GError **error) +{ + g_autoptr(FuStructIlitekItsHidCmd) st_cmd = fu_struct_ilitek_its_hid_cmd_new(); + + fu_struct_ilitek_its_hid_cmd_set_write_len(st_cmd, 8); + fu_struct_ilitek_its_hid_cmd_set_cmd(st_cmd, FU_ILITEK_ITS_CMD_GET_BLOCK_CRC); + st_cmd->buf->data[FU_STRUCT_ILITEK_ITS_HID_CMD_OFFSET_DATA] = FU_ILITEK_ITS_CRC_RECALCULATE; + fu_memwrite_uint24(st_cmd->buf->data + FU_STRUCT_ILITEK_ITS_HID_CMD_OFFSET_DATA + 1, + start, + G_LITTLE_ENDIAN); + fu_memwrite_uint24(st_cmd->buf->data + FU_STRUCT_ILITEK_ITS_HID_CMD_OFFSET_DATA + 4, + end, + G_LITTLE_ENDIAN); + + return fu_ilitek_its_device_send_cmd_then_wake_ack(self, st_cmd, error); +} + +static gboolean +fu_ilitek_its_device_get_block_crc(FuIlitekItsDevice *self, guint16 *crc, GError **error) +{ + g_autoptr(FuStructIlitekItsHidCmd) st_cmd = fu_struct_ilitek_its_hid_cmd_new(); + g_autoptr(GByteArray) rbuf = g_byte_array_new(); + + fu_struct_ilitek_its_hid_cmd_set_write_len(st_cmd, 2); + fu_struct_ilitek_its_hid_cmd_set_read_len(st_cmd, 2); + fu_struct_ilitek_its_hid_cmd_set_cmd(st_cmd, FU_ILITEK_ITS_CMD_GET_BLOCK_CRC); + st_cmd->buf->data[FU_STRUCT_ILITEK_ITS_HID_CMD_OFFSET_DATA] = FU_ILITEK_ITS_CRC_GET; + + if (!fu_ilitek_its_device_send_cmd(self, st_cmd, rbuf, error)) + return FALSE; + + return fu_memread_uint16_safe(rbuf->data, rbuf->len, 0, crc, G_LITTLE_ENDIAN, error); +} + +static gboolean +fu_ilitek_its_device_flash_enable(FuIlitekItsDevice *self, + gboolean in_ap, + guint32 start, + guint32 end, + GError **error) +{ + g_autoptr(FuStructIlitekItsHidCmd) st_cmd = fu_struct_ilitek_its_hid_cmd_new(); + + fu_struct_ilitek_its_hid_cmd_set_write_len(st_cmd, in_ap ? 3 : 9); + fu_struct_ilitek_its_hid_cmd_set_cmd(st_cmd, FU_ILITEK_ITS_CMD_FLASH_ENABLE); + fu_memwrite_uint16(st_cmd->buf->data + FU_STRUCT_ILITEK_ITS_HID_CMD_OFFSET_DATA + 0, + FU_ILITEK_ITS_WRITE_ENABLE_KEY, + G_BIG_ENDIAN); + if (!in_ap) { + fu_memwrite_uint24(st_cmd->buf->data + FU_STRUCT_ILITEK_ITS_HID_CMD_OFFSET_DATA + 2, + start, + G_LITTLE_ENDIAN); + fu_memwrite_uint24(st_cmd->buf->data + FU_STRUCT_ILITEK_ITS_HID_CMD_OFFSET_DATA + 5, + end, + G_LITTLE_ENDIAN); + } + + return fu_ilitek_its_device_send_cmd(self, st_cmd, NULL, error); +} + +static gboolean +fu_ilitek_its_device_set_ctrl_mode(FuIlitekItsDevice *self, + FuIlitekItsCtrlMode mode, + GError **error) +{ + g_autoptr(FuStructIlitekItsHidCmd) st_cmd = fu_struct_ilitek_its_hid_cmd_new(); + + fu_struct_ilitek_its_hid_cmd_set_write_len(st_cmd, 3); + fu_struct_ilitek_its_hid_cmd_set_cmd(st_cmd, FU_ILITEK_ITS_CMD_SET_CTRL_MODE); + st_cmd->buf->data[FU_STRUCT_ILITEK_ITS_HID_CMD_OFFSET_DATA] = mode; + st_cmd->buf->data[FU_STRUCT_ILITEK_ITS_HID_CMD_OFFSET_DATA + 1] = 0x0; + + if (!fu_ilitek_its_device_send_cmd(self, st_cmd, NULL, error)) + return FALSE; + + /* success */ + fu_device_sleep(FU_DEVICE(self), 100); + return TRUE; +} + +static gboolean +fu_ilitek_its_device_enable_tde(FuIlitekItsDevice *self, GError **error) +{ + return fu_ilitek_its_device_set_ctrl_mode(self, FU_ILITEK_ITS_CTRL_MODE_SUSPEND, error); +} + +static gboolean +fu_ilitek_its_device_disable_tde(FuIlitekItsDevice *self, GError **error) +{ + return fu_ilitek_its_device_set_ctrl_mode(self, FU_ILITEK_ITS_CTRL_MODE_NORMAL, error); +} + +static gboolean +fu_ilitek_its_device_get_fwid(FuIlitekItsDevice *self, guint16 *fwid, GError **error) +{ + g_autoptr(FuStructIlitekItsHidCmd) st_cmd = fu_struct_ilitek_its_hid_cmd_new(); + g_autoptr(FuStructIlitekItsFwid) st_resp = NULL; + g_autoptr(GByteArray) rbuf = g_byte_array_new(); + + /* check fwid protocol is supported */ + if ((fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER) && + self->protocol_ver < 0x010802) || + (!fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER) && + self->protocol_ver < 0x060007)) { + *fwid = 0xFFFF; + return TRUE; + } + + fu_struct_ilitek_its_hid_cmd_set_write_len(st_cmd, 1); + fu_struct_ilitek_its_hid_cmd_set_read_len(st_cmd, 4); + fu_struct_ilitek_its_hid_cmd_set_cmd(st_cmd, FU_ILITEK_ITS_CMD_GET_FIRMWARE_ID); + if (!fu_ilitek_its_device_send_cmd(self, st_cmd, rbuf, error)) + return FALSE; + + /* success */ + st_resp = fu_struct_ilitek_its_fwid_parse(rbuf->data, rbuf->len, 0, error); + if (st_resp == NULL) + return FALSE; + *fwid = fu_struct_ilitek_its_fwid_get_fwid(st_resp); + return TRUE; +} + +static gboolean +fu_ilitek_its_device_get_sensor_id(FuIlitekItsDevice *self, guint8 *sensor_id, GError **error) +{ + g_autoptr(FuStructIlitekItsHidCmd) st_cmd = fu_struct_ilitek_its_hid_cmd_new(); + g_autoptr(FuStructIlitekItsSensorId) st_resp = NULL; + g_autoptr(GByteArray) rbuf = g_byte_array_new(); + + /* check sensor-id protocol is supported */ + if ((fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER) && + self->protocol_ver < 0x010803) || + (!fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER) && + self->protocol_ver < 0x060004)) { + *sensor_id = 0xFF; + return TRUE; + } + + fu_struct_ilitek_its_hid_cmd_set_write_len(st_cmd, 1); + fu_struct_ilitek_its_hid_cmd_set_read_len(st_cmd, 4); + fu_struct_ilitek_its_hid_cmd_set_cmd(st_cmd, FU_ILITEK_ITS_CMD_GET_SENSOR_ID); + if (!fu_ilitek_its_device_send_cmd(self, st_cmd, rbuf, error)) + return FALSE; + + /* success */ + st_resp = fu_struct_ilitek_its_sensor_id_parse(rbuf->data, rbuf->len, 0, error); + if (st_resp == NULL) + return FALSE; + *sensor_id = fu_struct_ilitek_its_sensor_id_get_sensor_id(st_resp); + return TRUE; +} + +static gboolean +fu_ilitek_its_device_ensure_protocol_version(FuIlitekItsDevice *self, GError **error) +{ + g_autoptr(FuStructIlitekItsHidCmd) st_cmd = fu_struct_ilitek_its_hid_cmd_new(); + g_autoptr(GByteArray) rbuf = g_byte_array_new(); + + fu_struct_ilitek_its_hid_cmd_set_write_len(st_cmd, 1); + fu_struct_ilitek_its_hid_cmd_set_read_len(st_cmd, 3); + fu_struct_ilitek_its_hid_cmd_set_cmd(st_cmd, FU_ILITEK_ITS_CMD_GET_PROTOCOL_VERSION); + + if (!fu_ilitek_its_device_send_cmd(self, st_cmd, rbuf, error)) + return FALSE; + return fu_memread_uint24_safe(rbuf->data, + rbuf->len, + 0, + &self->protocol_ver, + G_BIG_ENDIAN, + error); +} + +static gboolean +fu_ilitek_its_device_ensure_fw_version(FuIlitekItsDevice *self, GError **error) +{ + guint64 version; + g_autoptr(FuStructIlitekItsHidCmd) st_cmd = fu_struct_ilitek_its_hid_cmd_new(); + g_autoptr(GByteArray) rbuf = g_byte_array_new(); + + fu_struct_ilitek_its_hid_cmd_set_write_len(st_cmd, 1); + fu_struct_ilitek_its_hid_cmd_set_read_len(st_cmd, 8); + fu_struct_ilitek_its_hid_cmd_set_cmd(st_cmd, FU_ILITEK_ITS_CMD_GET_FIRMWARE_VERSION); + + if (!fu_ilitek_its_device_send_cmd(self, st_cmd, rbuf, error)) + return FALSE; + + if (!fu_memread_uint64_safe(rbuf->data, rbuf->len, 0, &version, G_BIG_ENDIAN, error)) + return FALSE; + + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + fu_device_set_version_bootloader_raw(FU_DEVICE(self), version); + + /* fw update forcely */ + fu_device_set_version_raw(FU_DEVICE(self), 0); + } else { + fu_device_set_version_raw(FU_DEVICE(self), version); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ilitek_its_device_ensure_ic_mode(FuIlitekItsDevice *self, GError **error) +{ + guint8 ic_mode; + g_autoptr(FuStructIlitekItsHidCmd) st_cmd = fu_struct_ilitek_its_hid_cmd_new(); + g_autoptr(GByteArray) rbuf = g_byte_array_new(); + + fu_struct_ilitek_its_hid_cmd_set_write_len(st_cmd, 1); + fu_struct_ilitek_its_hid_cmd_set_read_len(st_cmd, 2); + fu_struct_ilitek_its_hid_cmd_set_cmd(st_cmd, FU_ILITEK_ITS_CMD_GET_IC_MODE); + if (!fu_ilitek_its_device_send_cmd(self, st_cmd, rbuf, error)) + return FALSE; + + if (!fu_memread_uint8_safe(rbuf->data, rbuf->len, 0, &ic_mode, error)) + return FALSE; + if (ic_mode == FU_ILITEK_ITS_BL_MODE) + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + else + fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + + /* success */ + return TRUE; +} + +static gboolean +fu_ilitek_its_device_ensure_ic_name_old(FuIlitekItsDevice *self, GError **error) +{ + g_autofree gchar *name = NULL; + g_autoptr(FuStructIlitekItsHidCmd) st_cmd = fu_struct_ilitek_its_hid_cmd_new(); + g_autoptr(FuStructIlitekItsMcuVersion) st_resp = NULL; + g_autoptr(GByteArray) rbuf = g_byte_array_new(); + + fu_struct_ilitek_its_hid_cmd_set_write_len(st_cmd, 1); + fu_struct_ilitek_its_hid_cmd_set_read_len(st_cmd, 32); + fu_struct_ilitek_its_hid_cmd_set_cmd(st_cmd, FU_ILITEK_ITS_CMD_GET_MCU_VERSION); + if (!fu_ilitek_its_device_send_cmd(self, st_cmd, rbuf, error)) + return FALSE; + + st_resp = fu_struct_ilitek_its_mcu_version_parse(rbuf->data, rbuf->len, 0, error); + if (st_resp == NULL) + return FALSE; + self->ic_name = + g_strdup_printf("%04x", fu_struct_ilitek_its_mcu_version_get_ic_name(st_resp)); + name = g_strdup_printf("Touchscreen ILI%s", self->ic_name); + fu_device_set_name(FU_DEVICE(self), name); + + /* success */ + return TRUE; +} + +static gboolean +fu_ilitek_its_device_ensure_ic_name(FuIlitekItsDevice *self, GError **error) +{ + g_autofree gchar *name = NULL; + g_autoptr(FuStructIlitekItsHidCmd) st_cmd = fu_struct_ilitek_its_hid_cmd_new(); + g_autoptr(FuStructIlitekItsMcuInfo) st_resp = NULL; + g_autoptr(GByteArray) rbuf = g_byte_array_new(); + + /* check new protocol is supported */ + if ((fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER) && + self->protocol_ver < 0x010803) || + (!fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER) && + self->protocol_ver < 0x060009)) + return fu_ilitek_its_device_ensure_ic_name_old(self, error); + + fu_struct_ilitek_its_hid_cmd_set_write_len(st_cmd, 1); + fu_struct_ilitek_its_hid_cmd_set_read_len(st_cmd, 32); + fu_struct_ilitek_its_hid_cmd_set_cmd(st_cmd, FU_ILITEK_ITS_CMD_GET_MCU_INFO); + if (!fu_ilitek_its_device_send_cmd(self, st_cmd, rbuf, error)) + return FALSE; + + st_resp = fu_struct_ilitek_its_mcu_info_parse(rbuf->data, rbuf->len, 0, error); + if (st_resp == NULL) + return FALSE; + self->ic_name = fu_struct_ilitek_its_mcu_info_get_ic_name(st_resp); + name = g_strdup_printf("Touchscreen ILI%s", self->ic_name); + fu_device_set_name(FU_DEVICE(self), name); + + /* success */ + return TRUE; +} + +static gboolean +fu_ilitek_its_device_io_channel_write(const gchar *fn, const gchar *buf, GError **error) +{ + g_autoptr(FuIOChannel) io = NULL; + + io = fu_io_channel_new_file(fn, FU_IO_CHANNEL_OPEN_FLAG_WRITE, error); + if (io == NULL) + return FALSE; + return fu_io_channel_write_raw(io, + (const guint8 *)buf, + strlen(buf), + 1000, + FU_IO_CHANNEL_FLAG_NONE, + error); +} + +static FuDevice * +fu_ilitek_its_device_get_backend_parent(FuIlitekItsDevice *self, GError **error) +{ + switch (fu_hidraw_device_get_bus_type(FU_HIDRAW_DEVICE(self))) { + case FU_HIDRAW_BUS_TYPE_I2C: + return fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), "i2c", error); + case FU_HIDRAW_BUS_TYPE_PCI: + return fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), "pci", error); + case FU_HIDRAW_BUS_TYPE_USB: + return fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), "usb", error); + default: + break; + } + + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unexpected bus type: 0x%x", + fu_hidraw_device_get_bus_type(FU_HIDRAW_DEVICE(self))); + return NULL; +} + +static gboolean +fu_ilitek_its_device_rebind_driver(FuIlitekItsDevice *self, GError **error) +{ + const gchar *hid_id; + const gchar *driver; + const gchar *subsystem; + g_autofree gchar *fn_bind = NULL; + g_autofree gchar *fn_unbind = NULL; + g_auto(GStrv) hid_strs = NULL; + g_autoptr(FuUdevDevice) parent = NULL; + + /* skip */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) + return TRUE; + + parent = FU_UDEV_DEVICE(fu_ilitek_its_device_get_backend_parent(self, error)); + if (parent == NULL) + return FALSE; + + /* find the physical ID to use for the rebind */ + hid_strs = g_strsplit(fu_udev_device_get_sysfs_path(parent), "/", -1); + hid_id = hid_strs[g_strv_length(hid_strs) - 1]; + if (hid_id == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no HID_PHYS in %s", + fu_udev_device_get_sysfs_path(parent)); + return FALSE; + } + + driver = fu_udev_device_get_driver(parent); + subsystem = fu_udev_device_get_subsystem(parent); + fn_bind = g_build_filename("/sys/bus/", subsystem, "drivers", driver, "bind", NULL); + fn_unbind = g_build_filename("/sys/bus/", subsystem, "drivers", driver, "unbind", NULL); + + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + if (!fu_ilitek_its_device_io_channel_write(fn_unbind, hid_id, error)) + return FALSE; + if (!fu_ilitek_its_device_io_channel_write(fn_bind, hid_id, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_ilitek_its_device_switch_mode(FuIlitekItsDevice *self, gboolean to_bootloader, GError **error) +{ + g_autoptr(FuStructIlitekItsHidCmd) st_cmd = fu_struct_ilitek_its_hid_cmd_new(); + + if (to_bootloader && fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + if (!to_bootloader && !fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + if (!fu_ilitek_its_device_flash_enable(self, + to_bootloader, + FU_ILITEK_ITS_WRITE_ENABLE_START, + FU_ILITEK_ITS_WRITE_ENABLE_END, + error)) + return FALSE; + + fu_struct_ilitek_its_hid_cmd_set_write_len(st_cmd, 1); + fu_struct_ilitek_its_hid_cmd_set_cmd(st_cmd, + to_bootloader ? FU_ILITEK_ITS_CMD_SET_BL_MODE + : FU_ILITEK_ITS_CMD_SET_AP_MODE); + if (!fu_ilitek_its_device_send_cmd(self, st_cmd, NULL, error)) + return FALSE; + + fu_device_sleep(FU_DEVICE(self), 1000); + + /* success */ + return TRUE; +} + +static gboolean +fu_ilitek_its_device_switch_mode_cb(FuDevice *device, gpointer data, GError **error) +{ + FuIlitekItsDevice *self = FU_ILITEK_ITS_DEVICE(device); + gboolean to_bootloader = *(gboolean *)data; + if (!fu_ilitek_its_device_switch_mode(self, to_bootloader, error)) + return FALSE; + + if (!fu_ilitek_its_device_ensure_ic_mode(self, error)) + return FALSE; + + /* check it changed state */ + if ((to_bootloader && + !fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) || + (!to_bootloader && + fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER))) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "switch mode failed"); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_ilitek_its_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuIlitekItsDevice *self = FU_ILITEK_ITS_DEVICE(device); + gboolean to_bootloader = TRUE; + + /* go to suspend mode before switch to bootloader mode */ + if (!fu_ilitek_its_device_enable_tde(self, error)) + return FALSE; + + switch (fu_hidraw_device_get_bus_type(FU_HIDRAW_DEVICE(self))) { + case FU_HIDRAW_BUS_TYPE_I2C: + case FU_HIDRAW_BUS_TYPE_PCI: + if (!fu_device_retry_full(device, + fu_ilitek_its_device_switch_mode_cb, + 5, + 100, + &to_bootloader, + error)) { + g_prefix_error_literal(error, "failed to switch mode: "); + return FALSE; + } + break; + + case FU_HIDRAW_BUS_TYPE_USB: + if (!fu_ilitek_its_device_switch_mode(self, to_bootloader, error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + break; + default: + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unexpected bus type: 0x%x", + fu_hidraw_device_get_bus_type(FU_HIDRAW_DEVICE(self))); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ilitek_its_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuIlitekItsDevice *self = FU_ILITEK_ITS_DEVICE(device); + gboolean to_bootloader = FALSE; + + switch (fu_hidraw_device_get_bus_type(FU_HIDRAW_DEVICE(self))) { + case FU_HIDRAW_BUS_TYPE_I2C: + case FU_HIDRAW_BUS_TYPE_PCI: + if (!fu_device_retry_full(device, + fu_ilitek_its_device_switch_mode_cb, + 5, + 100, + &to_bootloader, + error)) { + g_prefix_error_literal(error, "failed to switch mode: "); + return FALSE; + } + + /* rebind driver to update report descriptor */ + if (!fu_ilitek_its_device_rebind_driver(self, error)) + return FALSE; + + break; + + case FU_HIDRAW_BUS_TYPE_USB: + if (!fu_ilitek_its_device_switch_mode(self, to_bootloader, error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + break; + default: + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unexpected bus type: 0x%x", + fu_hidraw_device_get_bus_type(FU_HIDRAW_DEVICE(self))); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ilitek_its_device_probe(FuDevice *device, GError **error) +{ + /* ignore unsupported subsystems */ + if (g_strcmp0(fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)), "hidraw") != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "is not correct subsystem: %s, expected hidraw", + fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device))); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_ilitek_its_device_register_drm_device(FuIlitekItsDevice *self, + FuDrmDevice *drm_device, + GError **error) +{ + FuEdid *edid = fu_drm_device_get_edid(drm_device); + + if (edid == NULL) + return TRUE; + if (fu_edid_get_pnp_id(edid) == NULL) + return TRUE; + + fu_device_add_instance_str(FU_DEVICE(self), "PNPID", fu_edid_get_pnp_id(edid)); + fu_device_add_instance_u16(FU_DEVICE(self), "PCODE", fu_edid_get_product_code(edid)); + if (!fu_device_build_instance_id(FU_DEVICE(self), + error, + "HIDRAW", + "VEN", + "DEV", + "PNPID", + NULL)) + return FALSE; + + return fu_device_build_instance_id(FU_DEVICE(self), + error, + "HIDRAW", + "VEN", + "DEV", + "PNPID", + "PCODE", + NULL); +} + +static gboolean +fu_ilitek_its_device_setup(FuDevice *device, GError **error) +{ + FuIlitekItsDevice *self = FU_ILITEK_ITS_DEVICE(device); + guint16 fwid; + guint8 sensor_id; + g_autoptr(FuDeviceLocker) locker = NULL; + + locker = fu_device_locker_new_full(FU_DEVICE(self), + (FuDeviceLockerFunc)fu_ilitek_its_device_enable_tde, + (FuDeviceLockerFunc)fu_ilitek_its_device_disable_tde, + error); + if (locker == NULL) + return FALSE; + + if (!fu_ilitek_its_device_ensure_ic_mode(self, error)) + return FALSE; + if (!fu_ilitek_its_device_ensure_protocol_version(self, error)) + return FALSE; + + if (!fu_ilitek_its_device_ensure_ic_name(self, error)) + return FALSE; + if (!fu_ilitek_its_device_ensure_fw_version(self, error)) + return FALSE; + + if (!fu_ilitek_its_device_get_fwid(self, &fwid, error)) + return FALSE; + if (!fu_ilitek_its_device_get_sensor_id(self, &sensor_id, error)) + return FALSE; + + fu_device_add_instance_u16(device, "FWID", fwid); + if (!fu_device_build_instance_id(device, error, "HIDRAW", "VEN", "DEV", "FWID", NULL)) + return FALSE; + + fu_device_add_instance_u8(device, "SENSORID", sensor_id & self->sensor_id_mask); + if (!fu_device_build_instance_id(device, error, "HIDRAW", "VEN", "DEV", "SENSORID", NULL)) + return FALSE; + + /* some SKU needs both EDID and sensor-id */ + fu_device_build_instance_id(device, + NULL, + "HIDRAW", + "VEN", + "DEV", + "SENSORID", + "PNPID", + "PCODE", + NULL); + + /* FuHidrawDevice->setup */ + return FU_DEVICE_CLASS(fu_ilitek_its_device_parent_class)->setup(device, error); +} + +static FuFirmware * +fu_ilitek_its_device_prepare_firmware(FuDevice *device, + GInputStream *stream, + FuProgress *progress, + FuFirmwareParseFlags flags, + GError **error) +{ + FuIlitekItsDevice *self = FU_ILITEK_ITS_DEVICE(device); + const gchar *fw_ic_name; + g_autoptr(FuFirmware) firmware = fu_ilitek_its_firmware_new(); + + if (!fu_firmware_parse_stream(firmware, stream, 0x0, flags, error)) + return NULL; + fw_ic_name = fu_ilitek_its_firmware_get_ic_name(FU_ILITEK_ITS_FIRMWARE(firmware)); + if (g_strcmp0(self->ic_name, fw_ic_name) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware ic name %s does not match device ic name %s", + fw_ic_name, + fu_device_get_name(device)); + return NULL; + } + + /* success */ + return g_steal_pointer(&firmware); +} + +static gboolean +fu_ilitek_its_device_write_block(FuIlitekItsDevice *self, + FuFirmware *block_img, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + guint16 crc; + guint16 fw_crc; + guint32 end; + guint32 idx = fu_firmware_get_idx(block_img); + guint32 start; + g_autoptr(FuChunkArray) chunks = NULL; + g_autoptr(GBytes) blob = NULL; + + start = fu_firmware_get_addr(block_img); + end = start + fu_firmware_get_size(block_img) - 1; + + fw_crc = fu_ilitek_its_block_get_crc(FU_ILITEK_ITS_BLOCK(block_img)); + if (!fu_ilitek_its_device_recalculate_crc(self, start, end, error)) + return FALSE; + if (!fu_ilitek_its_device_get_block_crc(self, &crc, error)) + return FALSE; + + g_debug("block[%u]: start/end addr: 0x%x/0x%x, ic/file crc: 0x%x/0x%x, need update: %s", + idx, + start, + end, + crc, + fw_crc, + crc == fw_crc ? "no" : "yes"); + + /* no need to upgrade block if crc matched */ + if (crc == fw_crc && (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) + return TRUE; + + blob = fu_firmware_get_bytes(block_img, error); + if (blob == NULL) + return FALSE; + chunks = + fu_chunk_array_new_from_bytes(blob, 0, 0, FU_STRUCT_ILITEK_ITS_LONG_HID_CMD_SIZE_DATA); + if (chunks == NULL) + return FALSE; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); + + if (!fu_ilitek_its_device_flash_enable(self, FALSE, start, end, error)) + return FALSE; + + for (guint32 i = 0; i < fu_chunk_array_length(chunks); i++) { + g_autoptr(FuChunk) chunk = NULL; + g_autoptr(FuStructIlitekItsLongHidCmd) st_cmd = + fu_struct_ilitek_its_long_hid_cmd_new(); + g_autoptr(GBytes) data = NULL; + + chunk = fu_chunk_array_index(chunks, i, error); + if (chunk == NULL) + return FALSE; + + fu_struct_ilitek_its_long_hid_cmd_set_write_len( + st_cmd, + FU_STRUCT_ILITEK_ITS_LONG_HID_CMD_SIZE_DATA + 1); + fu_struct_ilitek_its_long_hid_cmd_set_cmd(st_cmd, FU_ILITEK_ITS_CMD_WRITE_DATA); + + data = fu_bytes_pad(fu_chunk_get_bytes(chunk), + FU_STRUCT_ILITEK_ITS_LONG_HID_CMD_SIZE_DATA, + 0xff); + + if (!fu_struct_ilitek_its_long_hid_cmd_set_data(st_cmd, + g_bytes_get_data(data, NULL), + g_bytes_get_size(data), + error)) + return FALSE; + + if (!fu_ilitek_its_device_send_long_cmd_then_wake_ack(self, st_cmd, error)) + return FALSE; + + fu_progress_step_done(progress); + } + + if (!fu_ilitek_its_device_get_block_crc(self, &crc, error)) + return FALSE; + + g_debug("block[%u]: start/end addr: 0x%x/0x%x, ic/file crc: 0x%x/0x%x %s", + idx, + start, + end, + crc, + fw_crc, + crc == fw_crc ? "matched" : "not matched"); + + if (crc != fw_crc) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "block crc mismatch: device 0x%04x, firmware 0x%04x", + crc, + fw_crc); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_ilitek_its_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuIlitekItsDevice *self = FU_ILITEK_ITS_DEVICE(device); + g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, imgs->len); + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + if (!fu_ilitek_its_device_write_block(self, + img, + fu_progress_get_child(progress), + flags, + error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* success! */ + return TRUE; +} + +static gboolean +fu_ilitek_its_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuIlitekItsDevice *self = FU_ILITEK_ITS_DEVICE(device); + + if (g_strcmp0(key, "IlitekItsSensorIdMask") == 0) { + guint64 tmp = 0; + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->sensor_id_mask = (guint8)tmp; + return TRUE; + } + + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_ilitek_its_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 10, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 80, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 10, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static gchar * +fu_ilitek_its_device_convert_version(FuDevice *device, guint64 version_raw) +{ + return fu_ilitek_its_convert_version(version_raw); +} + +static void +fu_ilitek_its_device_init(FuIlitekItsDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_VIDEO_DISPLAY); + fu_device_add_protocol(FU_DEVICE(self), "tw.com.ilitek.its"); + fu_device_set_summary(FU_DEVICE(self), "Touch controller"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_NONBLOCK); +} + +static void +fu_ilitek_its_device_finalize(GObject *object) +{ + FuIlitekItsDevice *self = FU_ILITEK_ITS_DEVICE(object); + g_free(self->ic_name); + G_OBJECT_CLASS(fu_ilitek_its_device_parent_class)->finalize(object); +} + +static void +fu_ilitek_its_device_class_init(FuIlitekItsDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_ilitek_its_device_finalize; + device_class->to_string = fu_ilitek_its_device_to_string; + device_class->probe = fu_ilitek_its_device_probe; + device_class->setup = fu_ilitek_its_device_setup; + device_class->attach = fu_ilitek_its_device_attach; + device_class->detach = fu_ilitek_its_device_detach; + device_class->set_quirk_kv = fu_ilitek_its_device_set_quirk_kv; + device_class->prepare_firmware = fu_ilitek_its_device_prepare_firmware; + device_class->write_firmware = fu_ilitek_its_device_write_firmware; + device_class->set_progress = fu_ilitek_its_device_set_progress; + device_class->convert_version = fu_ilitek_its_device_convert_version; +} diff -Nru fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-device.h fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-device.h --- fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Copyright 2025 Joe Hong + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_ILITEK_ITS_DEVICE (fu_ilitek_its_device_get_type()) +G_DECLARE_FINAL_TYPE(FuIlitekItsDevice, fu_ilitek_its_device, FU, ILITEK_ITS_DEVICE, FuHidrawDevice) + +gboolean +fu_ilitek_its_device_register_drm_device(FuIlitekItsDevice *self, + FuDrmDevice *drm_device, + GError **error) G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-firmware.c fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-firmware.c --- fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-firmware.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,228 @@ +/* + * Copyright 2025 Joe Hong + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-ilitek-its-block.h" +#include "fu-ilitek-its-common.h" +#include "fu-ilitek-its-firmware.h" +#include "fu-ilitek-its-struct.h" + +#define FU_ILITEK_ITS_FIRMWARE_MAX_BLOB_SIZE (256 * 1024) + +struct _FuIlitekItsFirmware { + FuIhexFirmware parent_instance; + guint32 mm_addr; + gchar *fw_ic_name; +}; + +G_DEFINE_TYPE(FuIlitekItsFirmware, fu_ilitek_its_firmware, FU_TYPE_IHEX_FIRMWARE) + +static void +fu_ilitek_its_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuIlitekItsFirmware *self = FU_ILITEK_ITS_FIRMWARE(firmware); + fu_xmlb_builder_insert_kv(bn, "fw_ic_name", self->fw_ic_name); + fu_xmlb_builder_insert_kx(bn, "mm_addr", self->mm_addr); +} + +static gboolean +fu_ilitek_its_firmware_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ + FuFirmwareClass *klass = FU_FIRMWARE_CLASS(fu_ilitek_its_firmware_parent_class); + FuIlitekItsFirmware *self = FU_ILITEK_ITS_FIRMWARE(firmware); + GPtrArray *records = fu_ihex_firmware_get_records(FU_IHEX_FIRMWARE(firmware)); + FuIhexFirmwareRecord *rcd = NULL; + guint32 mm_ver; + guint32 start_addr; + guint8 block_num; + const guint8 *ic_name = NULL; + g_autoptr(FuStructIlitekItsMmInfo) st_mm = NULL; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) hex_blob = NULL; + const gchar *ap_end_tag = + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFILITek AP CRC "; + const gchar *block_end_tag = + "\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFF\xFFILITek END TAG "; + + /* first line is ILITEK-specific record type as memory mapping addr. */ + if (records->len == 0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "no records"); + return FALSE; + } + rcd = g_ptr_array_index(records, 0); + if (!fu_memread_uint24_safe(rcd->data->data, + rcd->data->len, + 0, + &self->mm_addr, + G_BIG_ENDIAN, + error)) + return FALSE; + g_ptr_array_remove_index(records, 0); + + if (!klass->parse(firmware, stream, flags, error)) + return FALSE; + + hex_blob = fu_firmware_get_bytes(firmware, error); + if (hex_blob == NULL) + return FALSE; + + start_addr = fu_firmware_get_addr(firmware); + /* fill 0xFF data before start address and after end address */ + fu_byte_array_set_size(buf, start_addr - buf->len, 0xFF); + fu_byte_array_append_bytes(buf, hex_blob); + fu_byte_array_set_size(buf, FU_ILITEK_ITS_FIRMWARE_MAX_BLOB_SIZE, 0xFF); + blob = g_byte_array_free_to_bytes(g_steal_pointer(&buf)); /* nocheck:blocked */ + st_mm = fu_struct_ilitek_its_mm_info_parse_bytes(blob, self->mm_addr, error); + if (st_mm == NULL) + return FALSE; + + mm_ver = fu_struct_ilitek_its_mm_info_get_mapping_ver(st_mm); + ic_name = fu_struct_ilitek_its_mm_info_get_ic_name(st_mm, NULL); + g_debug("mm ver: 0x%06x, protocol ver: 0x%06x", + mm_ver, + fu_struct_ilitek_its_mm_info_get_protocol_ver(st_mm)); + + g_free(self->fw_ic_name); + switch ((mm_ver >> 16) & 0xFF) { + case 0x02: + self->fw_ic_name = g_strdup_printf("%s", (gchar *)ic_name); + break; + case 0x01: + default: + self->fw_ic_name = g_strdup_printf("%02x%02x", ic_name[1], ic_name[0]); + break; + } + block_num = fu_struct_ilitek_its_mm_info_get_block_num(st_mm); + if (block_num == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "block_num was zero"); + return FALSE; + } + for (guint8 i = 0; i < block_num; i++) { + const gchar *tag = (i == 0) ? ap_end_tag : block_end_tag; + guint32 start; + guint32 end; + gsize offset; + g_autoptr(FuFirmware) block_img = fu_ilitek_its_block_new(); + g_autoptr(FuStructIlitekItsBlockInfo) st_block = NULL; + g_autoptr(GBytes) block_fw_fixed = NULL; + g_autoptr(GBytes) block_fw = NULL; + + st_block = fu_struct_ilitek_its_mm_info_get_blocks(st_mm, i); + start = fu_struct_ilitek_its_block_info_get_addr(st_block); + end = fu_struct_ilitek_its_mm_info_get_end_addr(st_mm); + + if (i != block_num - 1) { + g_autoptr(FuStructIlitekItsBlockInfo) st_block_next = + fu_struct_ilitek_its_mm_info_get_blocks(st_mm, i + 1); + end = fu_struct_ilitek_its_block_info_get_addr(st_block_next); + } + + /* sanity check */ + if (end < start) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "start 0x%x > end 0x%x", + start, + end); + return FALSE; + } + + block_fw = fu_bytes_new_offset(blob, start, end - start + 1, error); + if (block_fw == NULL) + return FALSE; + if (fu_memmem_safe(g_bytes_get_data(block_fw, NULL), + g_bytes_get_size(block_fw), + (guint8 *)tag, + strlen(tag), + &offset, + NULL)) { + offset = offset + strlen(tag) + 2; + end = start + offset - 1; + block_fw_fixed = fu_bytes_new_offset(blob, start, offset, error); + if (block_fw_fixed == NULL) + return FALSE; + } else { + block_fw_fixed = g_bytes_ref(block_fw); + } + if (!fu_firmware_parse_bytes(block_img, + block_fw_fixed, + 0x0, + flags | FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, + error)) { + g_prefix_error(error, "failed to parse block %u: ", i); + return FALSE; + } + g_debug("block %u: start 0x%08x, end 0x%08x, crc: 0x%x", + i, + start, + end, + fu_ilitek_its_block_get_crc(FU_ILITEK_ITS_BLOCK(block_img))); + + fu_firmware_set_offset(block_img, offset); + fu_firmware_set_idx(block_img, i); + fu_firmware_set_parent(block_img, firmware); + fu_firmware_set_addr(block_img, start); + if (!fu_firmware_add_image(firmware, block_img, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gchar * +fu_ilitek_its_firmware_convert_version(FuFirmware *firmware, guint64 version_raw) +{ + return fu_ilitek_its_convert_version(version_raw); +} + +const gchar * +fu_ilitek_its_firmware_get_ic_name(FuIlitekItsFirmware *self) +{ + return self->fw_ic_name; +} + +static void +fu_ilitek_its_firmware_init(FuIlitekItsFirmware *self) +{ + fu_ihex_firmware_set_padding_value(FU_IHEX_FIRMWARE(self), 0xFF); + fu_firmware_set_images_max(FU_FIRMWARE(self), 100); + fu_firmware_set_version_format(FU_FIRMWARE(self), FWUPD_VERSION_FORMAT_QUAD); +} + +static void +fu_ilitek_its_firmware_finalize(GObject *object) +{ + FuIlitekItsFirmware *self = FU_ILITEK_ITS_FIRMWARE(object); + g_free(self->fw_ic_name); + G_OBJECT_CLASS(fu_ilitek_its_firmware_parent_class)->finalize(object); +} + +static void +fu_ilitek_its_firmware_class_init(FuIlitekItsFirmwareClass *klass) +{ + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_ilitek_its_firmware_finalize; + firmware_class->convert_version = fu_ilitek_its_firmware_convert_version; + firmware_class->parse = fu_ilitek_its_firmware_parse; + firmware_class->export = fu_ilitek_its_firmware_export; +} + +FuFirmware * +fu_ilitek_its_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_ILITEK_ITS_FIRMWARE, NULL)); +} diff -Nru fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-firmware.h fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-firmware.h --- fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-firmware.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * Copyright 2025 Joe Hong + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_ILITEK_ITS_FIRMWARE (fu_ilitek_its_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuIlitekItsFirmware, + fu_ilitek_its_firmware, + FU, + ILITEK_ITS_FIRMWARE, + FuIhexFirmware) + +FuFirmware * +fu_ilitek_its_firmware_new(void); + +const gchar * +fu_ilitek_its_firmware_get_ic_name(FuIlitekItsFirmware *self) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-plugin.c fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-plugin.c --- fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,93 @@ +/* + * Copyright 2025 Joe Hong + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-ilitek-its-device.h" +#include "fu-ilitek-its-firmware.h" +#include "fu-ilitek-its-plugin.h" + +struct _FuIlitekItsPlugin { + FuPlugin parent_instance; + GPtrArray *drm_devices; +}; + +G_DEFINE_TYPE(FuIlitekItsPlugin, fu_ilitek_its_plugin, FU_TYPE_PLUGIN) + +static void +fu_ilitek_its_plugin_device_registered(FuPlugin *plugin, FuDevice *device) +{ + FuIlitekItsPlugin *self = FU_ILITEK_ITS_PLUGIN(plugin); + + /* new DRM device, so register for any added FuIlitekItsDevices */ + if (FU_IS_DRM_DEVICE(device)) { + GPtrArray *its_devices = fu_plugin_get_devices(plugin); + for (guint i = 0; i < its_devices->len; i++) { + FuIlitekItsDevice *its_device = g_ptr_array_index(its_devices, i); + FuDrmDevice *drm_device = FU_DRM_DEVICE(device); + g_autoptr(GError) error_local = NULL; + + if (!fu_ilitek_its_device_register_drm_device(its_device, + drm_device, + &error_local)) { + g_warning("ignoring: %s", error_local->message); + continue; + } + } + g_ptr_array_add(self->drm_devices, g_object_ref(device)); + } +} + +static gboolean +fu_ilitek_its_plugin_device_created(FuPlugin *plugin, FuDevice *device, GError **error) +{ + FuIlitekItsPlugin *self = FU_ILITEK_ITS_PLUGIN(plugin); + FuIlitekItsDevice *its_device = FU_ILITEK_ITS_DEVICE(device); + + /* any DRM devices added before ITS devices */ + for (guint i = 0; i < self->drm_devices->len; i++) { + FuDrmDevice *drm_device = g_ptr_array_index(self->drm_devices, i); + if (!fu_ilitek_its_device_register_drm_device(its_device, drm_device, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_ilitek_its_plugin_init(FuIlitekItsPlugin *self) +{ + self->drm_devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); +} + +static void +fu_ilitek_its_plugin_finalize(GObject *object) +{ + FuIlitekItsPlugin *self = FU_ILITEK_ITS_PLUGIN(object); + g_ptr_array_unref(self->drm_devices); + G_OBJECT_CLASS(fu_ilitek_its_plugin_parent_class)->finalize(object); +} + +static void +fu_ilitek_its_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_ILITEK_ITS_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_ILITEK_ITS_FIRMWARE); +} + +static void +fu_ilitek_its_plugin_class_init(FuIlitekItsPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_ilitek_its_plugin_finalize; + plugin_class->constructed = fu_ilitek_its_plugin_constructed; + plugin_class->device_created = fu_ilitek_its_plugin_device_created; + plugin_class->device_registered = fu_ilitek_its_plugin_device_registered; +} diff -Nru fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-plugin.h fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-plugin.h --- fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * Copyright 2025 Joe Hong + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuIlitekItsPlugin, fu_ilitek_its_plugin, FU, ILITEK_ITS_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its.rs fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its.rs --- fwupd-2.0.8/plugins/ilitek-its/fu-ilitek-its.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/fu-ilitek-its.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,115 @@ +// Copyright 2025 Joe Hong +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[repr(u8)] +enum FuIlitekItsCtrlMode { + Normal = 0x0, + Test = 0x1, + Debug = 0x2, + Suspend = 0x3, +} + +#[repr(u8)] +enum FuIlitekItsCmd { + GetSensorId = 0x27, + GetFirmwareVersion = 0x40, + GetProtocolVersion = 0x42, + GetFirmwareId = 0x46, + GetMcuVersion = 0x61, + GetMcuInfo = 0x62, + GetIcMode = 0xC0, + SetApMode = 0xC1, + SetBlMode = 0xC2, + WriteData = 0xC3, + FlashEnable = 0xCC, + GetBlockCrc = 0xCD, + SetCtrlMode = 0xF0, +} + +#[derive(Parse, Default)] +struct FuStructIlitekItsHidRes { + report_id: u8 == 0x03, + res_size_supported_id: u8 == 0xA3, + cmd: u8, + read_len: u8, + data: [u8; 60], +} + +#[derive(New, Getters, Default)] +#[repr(C, packed)] +struct FuStructIlitekItsHidCmd { + report_id: u8 == 0x03, + res_size_supported_id: u8 == 0xA3, + write_len: u8 = 0, + read_len: u8 = 0, + cmd: FuIlitekItsCmd, + data: [u8; 59], +} + +#[derive(New, Getters, Default)] +#[repr(C, packed)] +struct FuStructIlitekItsLongHidCmd { + report_id: u8 == 0x08, + res_size_supported_id: u8 == 0xA3, + write_len: u16le = 0, + read_len: u16le = 0, + cmd: FuIlitekItsCmd, + data: [u8; 1024], +} + +#[derive(Parse)] +#[repr(C, packed)] +struct FuStructIlitekItsFwid { + customer_id: u16le, + fwid: u16le, +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuStructIlitekItsSensorId { + header: u16le == 0xa55a, + sensor_id: u8, +} + +#[derive(Parse)] +#[repr(C, packed)] +struct FuStructIlitekItsMcuVersion { + ic_name: u16le, + data_flash_addr: u24le, + data_flash_size: u8, + module_name: [char; 26], +} + +#[derive(Parse)] +#[repr(C, packed)] +struct FuStructIlitekItsMcuInfo { + ic_name: [char; 5], + _reserve_1: [u8; 2], + mm_addr: u24le, + module_name: [char; 18], +} + +#[repr(C, packed)] +struct FuStructIlitekItsBlockInfo { + addr: u24le, +} + +#[derive(ParseBytes, Getters)] +#[repr(C, packed)] +struct FuStructIlitekItsMmInfo { + mapping_ver: u24le, + protocol_ver: u24le, + ic_name: [u8; 6], + + tuning_ver: u32le, + fw_ver: u32le, + _reserve_1: [u8; 24], + fwid: u16le, + _reserve_2: [u8; 34], + block_num: u8, + _reserve_3: [u8; 3], + blocks: [FuStructIlitekItsBlockInfo; 10], + _reserve_4: [u8; 9], + end_addr: u24le, + _reserve_5: [u8; 2], +} diff -Nru fwupd-2.0.8/plugins/ilitek-its/ilitek-its.quirk fwupd-2.0.20/plugins/ilitek-its/ilitek-its.quirk --- fwupd-2.0.8/plugins/ilitek-its/ilitek-its.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/ilitek-its.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,20 @@ +# ILI2901 Bootloader +[HIDRAW\VEN_222A&DEV_FF29] +Plugin = ilitek_its +Flags = is-bootloader + +# Framework Laptop 12 (13th Gen Intel Core) (FWID: 0x0049) +[HIDRAW\VEN_222A&DEV_5539] +Plugin = ilitek_its + +# Lenovo X1 (FWID: 0x0035, EDID: AUO-6DB4) +[HIDRAW\VEN_222A&DEV_5520] +Plugin = ilitek_its + +# Lenovo X1 (FWID: 0x0037, EDID: CMN-1490) +[HIDRAW\VEN_222A&DEV_54D7] +Plugin = ilitek_its + +# ASUS CX9406 (FWID: 0x00AE) +[HIDRAW\VEN_222A&DEV_55A3] +Plugin = ilitek_its diff -Nru fwupd-2.0.8/plugins/ilitek-its/meson.build fwupd-2.0.20/plugins/ilitek-its/meson.build --- fwupd-2.0.8/plugins/ilitek-its/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,24 @@ +host_machine.system() == 'linux' or subdir_done() + +cargs = ['-DG_LOG_DOMAIN="FuPluginIlitekIts"'] +plugins += {meson.current_source_dir().split('/')[-1]: true} + +plugin_quirks += files('ilitek-its.quirk') +plugin_builtins += static_library('fu_plugin_ilitek_its', + rustgen.process('fu-ilitek-its.rs'), + sources: [ + 'fu-ilitek-its-common.c', + 'fu-ilitek-its-device.c', + 'fu-ilitek-its-block.c', + 'fu-ilitek-its-firmware.c', + 'fu-ilitek-its-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) + +device_tests += files( + 'tests/ilitek-touchscreen.json', +) diff -Nru fwupd-2.0.8/plugins/ilitek-its/tests/ilitek-touchscreen.json fwupd-2.0.20/plugins/ilitek-its/tests/ilitek-touchscreen.json --- fwupd-2.0.8/plugins/ilitek-its/tests/ilitek-touchscreen.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/ilitek-its/tests/ilitek-touchscreen.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +{ + "name": "ILITEK Touchscreen", + "interactive": false, + "steps": [ + { + "url": "151d089e777f2c47642a3c8d6d6a6c65b688a659adb42316e051b02ae89b0485-ilitek-touchscreen-for-emulation.cab", + "emulation-url": "76333e48882e6363d6aa3ea58a574490206e21b4309f78040b4e710ea636b716-ilitek_its.zip", + "components": [ + { + "version": "700.7.0.0", + "guids": [ + "1cf4c326-2fa3-5229-b982-df7416351a82" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/intel-amt/README.md fwupd-2.0.20/plugins/intel-amt/README.md --- fwupd-2.0.8/plugins/intel-amt/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-amt/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,34 @@ +--- +title: Plugin: Intel AMT +--- + +## Introduction + +This plugin is used to version number for the Intel AMT, typically CSME. + +If AMT is enabled and provisioned and the AMT version is between 6.0 and 11.2, +and you have not upgraded your firmware, you are vulnerable to CVE-2017-5689 and +you should disable AMT in your system firmware. + +This code is inspired by [AMT status checker for Linux](https://github.com/mjg59/mei-amt-check) +by Matthew Garrett. + +That tool in turn is heavily based on mei-amt-version from samples/mei in the +Linux source tree and copyright Intel Corporation. + +## GUID Generation + +These devices use the existing GUIDs provided by the ME host interfaces. + +## Vendor ID Security + +The devices are not upgradable and thus require no vendor ID set. + +## External Interface Access + +This plugin requires `ioctl(IOCTL_MEI_CONNECT_CLIENT)` to `/dev/mei0`. + +## Version Considerations + +This plugin has been available since fwupd version `2.0.9`, although the functionality was +previously introduced in the `intel-me` plugin released with fwupd version `1.8.7`. diff -Nru fwupd-2.0.8/plugins/intel-amt/fu-intel-amt-device.c fwupd-2.0.20/plugins/intel-amt/fu-intel-amt-device.c --- fwupd-2.0.8/plugins/intel-amt/fu-intel-amt-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-amt/fu-intel-amt-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,252 @@ +/* + * Copyright 2012 Intel Corporation. + * Copyright 2017 Google, Inc. + * Copyright 2017 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-intel-amt-device.h" +#include "fu-intel-amt-struct.h" + +struct _FuIntelAmtDevice { + FuMeiDevice parent_instance; +}; + +G_DEFINE_TYPE(FuIntelAmtDevice, fu_intel_amt_device, FU_TYPE_MEI_DEVICE) + +#define FU_AMT_STATUS_HOST_IF_EMPTY_RESPONSE 0x4000 + +#define FU_INTEL_AMT_DEVICE_UUID "12f80028-b4b7-4b2d-aca8-46e0ff65814c" + +static gboolean +fu_intel_amt_device_status_set_error(guint32 status, GError **error) +{ + if (status == FU_AMT_STATUS_SUCCESS) + return TRUE; + if (status == FU_AMT_STATUS_INTERNAL_ERROR) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "internal error"); + return FALSE; + } + if (status == FU_AMT_STATUS_NOT_READY) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "not ready"); + return FALSE; + } + if (status == FU_AMT_STATUS_INVALID_AMT_MODE) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid AMT mode"); + return FALSE; + } + if (status == FU_AMT_STATUS_INVALID_MESSAGE_LENGTH) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid message length"); + return FALSE; + } + if (status == FU_AMT_STATUS_HOST_IF_EMPTY_RESPONSE) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Intel AMT is disabled"); + return FALSE; + } + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unknown error"); + return FALSE; +} + +static GByteArray * +fu_intel_amt_device_host_if_call(FuIntelAmtDevice *self, GByteArray *inbuf, GError **error) +{ + gsize outbufsz; + g_autoptr(GByteArray) outbuf = g_byte_array_new(); + + fu_byte_array_set_size(outbuf, fu_mei_device_get_max_msg_length(FU_MEI_DEVICE(self)), 0x0); + if (!fu_mei_device_write(FU_MEI_DEVICE(self), inbuf->data, inbuf->len, 5000, error)) + return NULL; + if (!fu_mei_device_read(FU_MEI_DEVICE(self), + outbuf->data, + outbuf->len, + &outbufsz, + 2000, + error)) + return NULL; + if (outbufsz <= 0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, "empty response"); + return NULL; + } + g_byte_array_set_size(outbuf, outbufsz); + return g_steal_pointer(&outbuf); +} + +static gboolean +fu_intel_amt_device_get_provisioning_state(FuIntelAmtDevice *self, + FuAmtProvisioningState *provisioning_state, + GError **error) +{ + g_autoptr(GByteArray) data = NULL; + g_autoptr(FuAmtHostIfMsgProvisioningStateRequest) st_req = + fu_amt_host_if_msg_provisioning_state_request_new(); + g_autoptr(FuAmtHostIfMsgProvisioningStateResponse) st_res = NULL; + + data = fu_intel_amt_device_host_if_call(self, st_req->buf, error); + if (data == NULL) + return FALSE; + + /* parse response */ + st_res = + fu_amt_host_if_msg_provisioning_state_response_parse(data->data, data->len, 0x0, error); + if (st_res == NULL) + return FALSE; + if (!fu_intel_amt_device_status_set_error( + fu_amt_host_if_msg_provisioning_state_response_get_status(st_res), + error)) + return FALSE; + *provisioning_state = + fu_amt_host_if_msg_provisioning_state_response_get_provisioning_state(st_res); + + /* success */ + return TRUE; +} + +static gboolean +fu_intel_amt_device_ensure_version(FuIntelAmtDevice *self, GError **error) +{ + guint32 version_count; + g_autoptr(FuAmtHostIfMsgCodeVersionRequest) st_req = + fu_amt_host_if_msg_code_version_request_new(); + g_autoptr(FuAmtHostIfMsgCodeVersionResponse) st_res = NULL; + g_autoptr(GByteArray) data = NULL; + g_autoptr(GString) version_bl = g_string_new(NULL); + g_autoptr(GString) version_fw = g_string_new(NULL); + + data = fu_intel_amt_device_host_if_call(self, st_req->buf, error); + if (data == NULL) + return FALSE; + + /* parse response */ + st_res = fu_amt_host_if_msg_code_version_response_parse(data->data, data->len, 0x0, error); + if (st_res == NULL) + return FALSE; + if (!fu_intel_amt_device_status_set_error( + fu_amt_host_if_msg_code_version_response_get_status(st_res), + error)) + return FALSE; + + /* parse each version */ + version_count = fu_amt_host_if_msg_code_version_response_get_version_count(st_res); + for (guint i = 0; i < version_count; i++) { + g_autofree gchar *description = NULL; + g_autofree gchar *version = NULL; + g_autoptr(FuAmtUnicodeString) st_str = NULL; + + st_str = + fu_amt_unicode_string_parse(data->data, + data->len, + st_res->buf->len + (i * FU_AMT_UNICODE_STRING_SIZE), + error); + if (st_str == NULL) + return FALSE; + + /* get description */ + if (fu_amt_unicode_string_get_description_length(st_str) > + FU_AMT_UNICODE_STRING_SIZE_DESCRIPTION_STRING) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "description string too large"); + return FALSE; + } + description = fu_amt_unicode_string_get_description_string(st_str); + + /* get version */ + if (fu_amt_unicode_string_get_version_length(st_str) > + FU_AMT_UNICODE_STRING_SIZE_VERSION_STRING) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "version string too large"); + return FALSE; + } + version = fu_amt_unicode_string_get_version_string(st_str); + + /* build something suitable for fwupd */ + if (g_strcmp0(description, "AMT") == 0) { + g_string_append(version_fw, version); + continue; + } + if (g_strcmp0(description, "Recovery Version") == 0) { + g_string_append(version_bl, version); + continue; + } + if (g_strcmp0(description, "Build Number") == 0) { + g_string_append_printf(version_fw, ".%s", version); + continue; + } + if (g_strcmp0(description, "Recovery Build Num") == 0) { + g_string_append_printf(version_bl, ".%s", version); + continue; + } + } + + /* success */ + if (version_fw->len > 0) + fu_device_set_version(FU_DEVICE(self), version_fw->str); + if (version_bl->len > 0) + fu_device_set_version_bootloader(FU_DEVICE(self), version_bl->str); + return TRUE; +} + +static gboolean +fu_intel_amt_device_setup(FuDevice *device, GError **error) +{ + FuIntelAmtDevice *self = FU_INTEL_AMT_DEVICE(device); + FuAmtProvisioningState provisioning_state = FU_AMT_PROVISIONING_STATE_UNPROVISIONED; + + /* get versions */ + if (!fu_mei_device_connect(FU_MEI_DEVICE(device), FU_INTEL_AMT_DEVICE_UUID, 0, error)) { + g_prefix_error_literal(error, "failed to connect: "); + return FALSE; + } + if (!fu_intel_amt_device_ensure_version(self, error)) { + g_prefix_error_literal(error, "failed to check version: "); + return FALSE; + } + + /* get provisioning state */ + if (!fu_intel_amt_device_get_provisioning_state(self, &provisioning_state, error)) { + g_prefix_error_literal(error, "failed to get provisioning state: "); + return FALSE; + } + if (provisioning_state < FU_AMT_PROVISIONING_STATE_LAST) { + g_autofree gchar *name = + g_strdup_printf("AMT [%s]", + fu_amt_provisioning_state_to_string(provisioning_state)); + fu_device_set_name(device, name); + } + + /* success */ + return TRUE; +} + +static void +fu_intel_amt_device_init(FuIntelAmtDevice *self) +{ + fu_device_set_logical_id(FU_DEVICE(self), "AMT"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_INTEL_ME); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); + fu_device_set_name(FU_DEVICE(self), "AMT"); + fu_device_set_summary(FU_DEVICE(self), + "Hardware and firmware technology for remote " + "out-of-band management"); +} + +static void +fu_intel_amt_device_class_init(FuIntelAmtDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->setup = fu_intel_amt_device_setup; +} diff -Nru fwupd-2.0.8/plugins/intel-amt/fu-intel-amt-device.h fwupd-2.0.20/plugins/intel-amt/fu-intel-amt-device.h --- fwupd-2.0.8/plugins/intel-amt/fu-intel-amt-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-amt/fu-intel-amt-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_INTEL_AMT_DEVICE (fu_intel_amt_device_get_type()) +G_DECLARE_FINAL_TYPE(FuIntelAmtDevice, fu_intel_amt_device, FU, INTEL_AMT_DEVICE, FuMeiDevice) diff -Nru fwupd-2.0.8/plugins/intel-amt/fu-intel-amt-plugin.c fwupd-2.0.20/plugins/intel-amt/fu-intel-amt-plugin.c --- fwupd-2.0.8/plugins/intel-amt/fu-intel-amt-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-amt/fu-intel-amt-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-intel-amt-device.h" +#include "fu-intel-amt-plugin.h" + +struct _FuIntelAmtPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuIntelAmtPlugin, fu_intel_amt_plugin, FU_TYPE_PLUGIN) + +static void +fu_intel_amt_plugin_init(FuIntelAmtPlugin *self) +{ +} + +static void +fu_intel_amt_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "mei"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_INTEL_AMT_DEVICE); +} + +static void +fu_intel_amt_plugin_class_init(FuIntelAmtPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->constructed = fu_intel_amt_plugin_constructed; +} diff -Nru fwupd-2.0.8/plugins/intel-amt/fu-intel-amt-plugin.h fwupd-2.0.20/plugins/intel-amt/fu-intel-amt-plugin.h --- fwupd-2.0.8/plugins/intel-amt/fu-intel-amt-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-amt/fu-intel-amt-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuIntelAmtPlugin, fu_intel_amt_plugin, FU, INTEL_AMT_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/intel-amt/fu-intel-amt.rs fwupd-2.0.20/plugins/intel-amt/fu-intel-amt.rs --- fwupd-2.0.8/plugins/intel-amt/fu-intel-amt.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-amt/fu-intel-amt.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,83 @@ +// Copyright 2024 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +enum FuAmtStatus { + Success, + InternalError, + NotReady, + InvalidAmtMode, + InvalidMessageLength, +} + +#[derive(ToString)] +#[repr(u8)] +enum FuAmtProvisioningState { + Unprovisioned, + BeingProvisioned, + Provisioned, +} + +#[repr(u32le)] +enum FuAmtHostIfCommand { + ProvisioningModeRequest = 0x04000008, + ProvisioningStateRequest = 0x04000011, + CodeVersionsRequest = 0x0400001A, + ProvisioningModeResponse = 0x04800008, + ProvisioningStateResponse = 0x04800011, + CodeVersionsResponse = 0x0480001A, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuAmtHostIfMsgCodeVersionRequest { + version_major: u8 == 0x1, + version_minor: u8 == 0x1, + _reserved: u16le, + command: FuAmtHostIfCommand == CodeVersionsRequest, + length: u32le == 0x0, +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuAmtHostIfMsgCodeVersionResponse { + version_major: u8 == 0x1, + version_minor: u8 == 0x1, + _reserved: u16le, + command: FuAmtHostIfCommand == CodeVersionsResponse, + _length: u32le, + status: u32le, + _bios: [char; 65], + version_count: u32le, + // now variable length of FuAmtUnicodeString +} + +#[derive(Parse)] +#[repr(C, packed)] +struct FuAmtUnicodeString { + description_length: u16le, + description_string: [char; 20], + version_length: u16le, + version_string: [char; 20], +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuAmtHostIfMsgProvisioningStateRequest { + version_major: u8 == 0x1, + version_minor: u8 == 0x1, + _reserved: u16le, + command: FuAmtHostIfCommand == ProvisioningStateRequest, + length: u32le == 0x0, +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuAmtHostIfMsgProvisioningStateResponse { + version_major: u8 == 0x1, + version_minor: u8 == 0x1, + _reserved: u16le, + command: FuAmtHostIfCommand == ProvisioningStateResponse, + length: u32le == 0x8, + status: u32le, + provisioning_state: FuAmtProvisioningState, +} diff -Nru fwupd-2.0.8/plugins/intel-amt/intel-amt.quirk fwupd-2.0.20/plugins/intel-amt/intel-amt.quirk --- fwupd-2.0.8/plugins/intel-amt/intel-amt.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-amt/intel-amt.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,3 @@ +# PTHI client (via the HECI device) +[12f80028-b4b7-4b2d-aca8-46e0ff65814c] +Plugin = intel_amt diff -Nru fwupd-2.0.8/plugins/intel-amt/meson.build fwupd-2.0.20/plugins/intel-amt/meson.build --- fwupd-2.0.8/plugins/intel-amt/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-amt/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,22 @@ +host_machine.system() == 'linux' or subdir_done() + +cargs = ['-DG_LOG_DOMAIN="FuPluginIntelAmt"'] +plugins += {meson.current_source_dir().split('/')[-1]: true} + +plugin_quirks += files('intel-amt.quirk') +plugin_builtins += static_library('fu_plugin_intel_amt', + rustgen.process( + 'fu-intel-amt.rs', + ), + sources: [ + 'fu-intel-amt-plugin.c', + 'fu-intel-amt-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) + +enumeration_data += files('tests/intel-amt-setup.json') +device_tests += files('tests/intel-amt.json') diff -Nru fwupd-2.0.8/plugins/intel-amt/tests/intel-amt-setup.json fwupd-2.0.20/plugins/intel-amt/tests/intel-amt-setup.json --- fwupd-2.0.8/plugins/intel-amt/tests/intel-amt-setup.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-amt/tests/intel-amt-setup.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,428 @@ +{ + "FwupdVersion": "2.0.9", + "UsbDevices": [ + { + "Created": "2025-04-28T11:16:00.979005Z", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:16.0/mei/mei0", + "DeviceFile": "/dev/mei0", + "Subsystem": "mei", + "Vendor": 32902, + "Model": 41274, + "Events": [ + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "mei" + }, + { + "Id": "GetSymlinkTarget:Attr=driver" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=235\nMINOR=0\nDEVNAME=mei0" + }, + { + "Id": "ReadProp:Key=DEVNAME", + "Data": "mei0" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=pci", + "GType": "FuPciDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:16.0", + "PhysicalId": "PCI_SLOT_NAME=0000:00:16.0" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "pci" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "mei_me" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A13A\nPCI_SUBSYS_ID=17AA:222E\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A13Asv000017AAsd0000222Ebc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa13a" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A13A\nPCI_SUBSYS_ID=17AA:222E\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A13Asv000017AAsd0000222Ebc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa13a" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=i2c", + "Error": 8, + "ErrorMsg": "no parent with subsystem i2c" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadAttr:Attr=revision", + "Data": "0x31" + }, + { + "Id": "ReadAttr:Attr=subsystem_vendor", + "Data": "0x17aa" + }, + { + "Id": "ReadAttr:Attr=subsystem_device", + "Data": "0x222e" + }, + { + "Id": "ReadProp:Key=PCI_SLOT_NAME", + "Data": "0000:00:16.0" + }, + { + "Id": "GetBackendParent:Subsystem=(null)", + "GType": "FuPciDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:16.0", + "PhysicalId": "PCI_SLOT_NAME=0000:00:16.0" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "pci" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "mei_me" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A13A\nPCI_SUBSYS_ID=17AA:222E\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A13Asv000017AAsd0000222Ebc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa13a" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A13A\nPCI_SUBSYS_ID=17AA:222E\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A13Asv000017AAsd0000222Ebc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa13a" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=i2c", + "Error": 8, + "ErrorMsg": "no parent with subsystem i2c" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadAttr:Attr=revision", + "Data": "0x31" + }, + { + "Id": "ReadAttr:Attr=subsystem_vendor", + "Data": "0x17aa" + }, + { + "Id": "ReadAttr:Attr=subsystem_device", + "Data": "0x222e" + }, + { + "Id": "ReadProp:Key=PCI_SLOT_NAME", + "Data": "0000:00:16.0" + }, + { + "Id": "ListAttr", + "Data": "uevent\npower_state\nbroken_parity_status\nsubsystem_device\ndma_mask_bits\n0000:00:16.0-3c4852d6-d47b-4f46-b05e-b5edc1aa440e\nvendor\nlocal_cpus\npower\n0000:00:16.0-dba4d603-d7ed-4931-8823-17ad585705d5\nclass\nnuma_node\nresource\nrescan\n0000:00:16.0-05b79a6f-4628-4d7f-899d-a91514cb32ab\nmei\nmsi_bus\ndevice\n0000:00:16.0-082ee5a7-7c25-470a-9643-0c06f0466ea1\n0000:00:16.0-01e88543-8050-4380-9d6f-4f9cec704917\n0000:00:16.0-bb875e12-cb58-4d14-ae93-8566183c66c7\n0000:00:16.0-42b3ce2f-bd9f-485a-96ae-26406230b1ff\n0000:00:16.0-309dcde8-ccb1-4062-8f78-600115a34327\n0000:00:16.0-55213584-9a29-4916-badf-0fb7ed682aeb\ndriver\n0000:00:16.0-8e6a6715-9abc-4043-88ef-9e39c6f63e0f\nlocal_cpulist\n0000:00:16.0-8c2f4425-77d6-4755-aca3-891fdbc66a58\n0000:00:16.0-cea154ea-8ff5-4f94-9290-0bb7355a34db\ndriver_override\nsubsystem\nd3cold_allowed\nirq\nrevision\n0000:00:16.0-fbf6fcf1-96cf-4e2e-a6a6-1bab8cbe36b1\nconsistent_dma_mask_bits\nresource0\nconfig\nari_enabled\nmsi_irqs\nremove\n0000:00:16.0-b638ab7e-94e2-4ea2-a552-d1c54b627f04\nenable\nlink\n0000:00:16.0-f908627d-13bf-4a04-b91f-a64e9245323d\n0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c\nmodalias\nsubsystem_vendor\n0000:00:16.0-5565a099-7fe2-45c1-a22b-d7e9dfea9a2e" + }, + { + "Id": "GetBackendParent:Subsystem=pci", + "GType": "FuPciDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:16.0", + "PhysicalId": "PCI_SLOT_NAME=0000:00:16.0" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "pci" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "mei_me" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A13A\nPCI_SUBSYS_ID=17AA:222E\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A13Asv000017AAsd0000222Ebc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa13a" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A13A\nPCI_SUBSYS_ID=17AA:222E\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A13Asv000017AAsd0000222Ebc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa13a" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=i2c", + "Error": 8, + "ErrorMsg": "no parent with subsystem i2c" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadAttr:Attr=revision", + "Data": "0x31" + }, + { + "Id": "ReadAttr:Attr=subsystem_vendor", + "Data": "0x17aa" + }, + { + "Id": "ReadAttr:Attr=subsystem_device", + "Data": "0x222e" + }, + { + "Id": "ReadProp:Key=PCI_SLOT_NAME", + "Data": "0000:00:16.0" + }, + { + "Id": "GetBackendParent:Subsystem=(null)", + "GType": "FuPciDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:16.0", + "PhysicalId": "PCI_SLOT_NAME=0000:00:16.0" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "pci" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "mei_me" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A13A\nPCI_SUBSYS_ID=17AA:222E\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A13Asv000017AAsd0000222Ebc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa13a" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A13A\nPCI_SUBSYS_ID=17AA:222E\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A13Asv000017AAsd0000222Ebc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa13a" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=i2c", + "Error": 8, + "ErrorMsg": "no parent with subsystem i2c" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadAttr:Attr=revision", + "Data": "0x31" + }, + { + "Id": "ReadAttr:Attr=subsystem_vendor", + "Data": "0x17aa" + }, + { + "Id": "ReadAttr:Attr=subsystem_device", + "Data": "0x222e" + }, + { + "Id": "ReadProp:Key=PCI_SLOT_NAME", + "Data": "0000:00:16.0" + }, + { + "Id": "ListAttr", + "Data": "uevent\npower_state\nbroken_parity_status\nsubsystem_device\ndma_mask_bits\n0000:00:16.0-3c4852d6-d47b-4f46-b05e-b5edc1aa440e\nvendor\nlocal_cpus\npower\n0000:00:16.0-dba4d603-d7ed-4931-8823-17ad585705d5\nclass\nnuma_node\nresource\nrescan\n0000:00:16.0-05b79a6f-4628-4d7f-899d-a91514cb32ab\nmei\nmsi_bus\ndevice\n0000:00:16.0-082ee5a7-7c25-470a-9643-0c06f0466ea1\n0000:00:16.0-01e88543-8050-4380-9d6f-4f9cec704917\n0000:00:16.0-bb875e12-cb58-4d14-ae93-8566183c66c7\n0000:00:16.0-42b3ce2f-bd9f-485a-96ae-26406230b1ff\n0000:00:16.0-309dcde8-ccb1-4062-8f78-600115a34327\n0000:00:16.0-55213584-9a29-4916-badf-0fb7ed682aeb\ndriver\n0000:00:16.0-8e6a6715-9abc-4043-88ef-9e39c6f63e0f\nlocal_cpulist\n0000:00:16.0-8c2f4425-77d6-4755-aca3-891fdbc66a58\n0000:00:16.0-cea154ea-8ff5-4f94-9290-0bb7355a34db\ndriver_override\nsubsystem\nd3cold_allowed\nirq\nrevision\n0000:00:16.0-fbf6fcf1-96cf-4e2e-a6a6-1bab8cbe36b1\nconsistent_dma_mask_bits\nresource0\nconfig\nari_enabled\nmsi_irqs\nremove\n0000:00:16.0-b638ab7e-94e2-4ea2-a552-d1c54b627f04\nenable\nlink\n0000:00:16.0-f908627d-13bf-4a04-b91f-a64e9245323d\n0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c\nmodalias\nsubsystem_vendor\n0000:00:16.0-5565a099-7fe2-45c1-a22b-d7e9dfea9a2e" + }, + { + "Id": "Ioctl:Request=0xc0104801,Data=KAD4Ere0LUusqEbg/2WBTA==,Length=0x10", + "DataOut": "ABQAAAG0LUusqEbg/2WBTA==" + }, + { + "Id": "Write:Data=AQEAABoAAAQAAAAA,Length=0xc" + }, + { + "Id": "Read:Length=0x1400", + "Data": "AQEAABoAgAQBAgAAAAAAAE4xRUVUQTJXICgxLjc1ICkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACgAAAAUARmxhc2gAAAAAAAAAAAAAAAAAAAAHADExLjguOTMAAAAAAAAAAAAAAAAACABOZXRzdGFjawAAAAAAAAAAAAAAAAcAMTEuOC45MwAAAAAAAAAAAAAAAAAHAEFNVEFwcHMAAAAAAAAAAAAAAAAABwAxMS44LjkzAAAAAAAAAAAAAAAAAAMAQU1UAAAAAAAAAAAAAAAAAAAAAAAHADExLjguOTMAAAAAAAAAAAAAAAAAAwBTa3UAAAAAAAAAAAAAAAAAAAAAAAEAOAAAAAAAAAAAAAAAAAAAAAAAAAAIAFZlbmRvcklEAAAAAAAAAAAAAAAABAA4MDg2AAAAAAAAAAAAAAAAAAAAAAwAQnVpbGQgTnVtYmVyAAAAAAAAAAAEADQzMjMAAAAAAAAAAAAAAAAAAAAAEABSZWNvdmVyeSBWZXJzaW9uAAAAAAcAMTEuOC45MwAAAAAAAAAAAAAAAAASAFJlY292ZXJ5IEJ1aWxkIE51bQAABAA0MzIzAAAAAAAAAAAAAAAAAAAAAAsATGVnYWN5IE1vZGUAAAAAAAAAAAAFAEZhbHNlAAAAAAAAAAAAAAAAAAAA" + }, + { + "Id": "Write:Data=AQEAABEAAAQAAAAA,Length=0xc" + }, + { + "Id": "Read:Length=0x1400", + "Data": "AQEAABEAgAQIAAAAAAAAAAAAAAA=" + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/intel-amt/tests/intel-amt.json fwupd-2.0.20/plugins/intel-amt/tests/intel-amt.json --- fwupd-2.0.8/plugins/intel-amt/tests/intel-amt.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-amt/tests/intel-amt.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +{ + "name": "Intel AMT", + "interactive": false, + "steps": [ + { + "emulation-file": "@enumeration_datadir@/intel-amt-setup.json", + "components": [ + { + "version": "11.8.93.4323", + "guids": [ + "d39310dc-e5d7-5eb2-945d-22011cbd3157" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/intel-cvs/README.md fwupd-2.0.20/plugins/intel-cvs/README.md --- fwupd-2.0.8/plugins/intel-cvs/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-cvs/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -63,10 +63,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.7`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Richard Hughes: @hughsie diff -Nru fwupd-2.0.8/plugins/intel-cvs/fu-intel-cvs-device.c fwupd-2.0.20/plugins/intel-cvs/fu-intel-cvs-device.c --- fwupd-2.0.8/plugins/intel-cvs/fu-intel-cvs-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-cvs/fu-intel-cvs-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -100,7 +100,7 @@ fu_intel_cvs_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIntelCvsDevice *self = FU_INTEL_CVS_DEVICE(device); @@ -189,7 +189,7 @@ FU_INTEL_CVS_DEVICE_SYSFS_TIMEOUT, FU_IO_CHANNEL_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write payload to virtual stream: "); + g_prefix_error_literal(error, "failed to write payload to virtual stream: "); return FALSE; } if (!fu_io_channel_seek(io_payload, 0x0, error)) @@ -202,7 +202,7 @@ fu_struct_intel_cvs_write_set_fw_bin_fd(st_write, fu_io_channel_unix_get_fd(io_payload)); if (!fu_udev_device_write_sysfs_byte_array(FU_UDEV_DEVICE(self), "cvs_ctrl_data_pre", - st_write, + st_write->buf, FU_INTEL_CVS_DEVICE_SYSFS_TIMEOUT, error)) return FALSE; @@ -222,8 +222,7 @@ } } - /* wait for hardware to re-appear */ - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + /* success */ return TRUE; } @@ -260,7 +259,7 @@ } static void -fu_intel_cvs_device_set_progress(FuDevice *self, FuProgress *progress) +fu_intel_cvs_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -286,7 +285,8 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); - fu_device_add_icon(FU_DEVICE(self), "camera-video"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_VIDEO_CAMERA); fu_device_set_name(FU_DEVICE(self), "Camera"); fu_device_set_summary(FU_DEVICE(self), "Computer Vision Sensing Camera"); fu_device_retry_add_recovery(FU_DEVICE(self), FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, NULL); diff -Nru fwupd-2.0.8/plugins/intel-cvs/fu-intel-cvs-firmware.c fwupd-2.0.20/plugins/intel-cvs/fu-intel-cvs-firmware.c --- fwupd-2.0.8/plugins/intel-cvs/fu-intel-cvs-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-cvs/fu-intel-cvs-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -39,7 +39,7 @@ static gboolean fu_intel_cvs_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIntelCvsFirmware *self = FU_INTEL_CVS_FIRMWARE(firmware); @@ -55,7 +55,7 @@ return FALSE; /* verify checksum of header */ - checksum_new = fu_sum32w(st_hdr->data, st_hdr->len, G_LITTLE_ENDIAN); + checksum_new = fu_sum32w(st_hdr->buf->data, st_hdr->buf->len, G_LITTLE_ENDIAN); if (checksum_new != 0) { g_set_error(error, FWUPD_ERROR, diff -Nru fwupd-2.0.8/plugins/intel-cvs/fu-intel-cvs.rs fwupd-2.0.20/plugins/intel-cvs/fu-intel-cvs.rs --- fwupd-2.0.8/plugins/intel-cvs/fu-intel-cvs.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-cvs/fu-intel-cvs.rs 2026-02-26 11:36:18.000000000 +0000 @@ -23,7 +23,7 @@ header_checksum: u32le, } -// CV SoC device status */ +// CV SoC device status #[repr(u8)] enum FuIntelCvsDeviceState { DeviceOff = 0, @@ -35,7 +35,7 @@ DeviceDwnldBusy = 1 << 7, }; -// CVS sensor Status */ +// CVS sensor Status #[repr(u8)] enum FuIntelCvsSensorState { Released = 0, @@ -43,7 +43,7 @@ IpuAcquired = 1 << 1, }; -// CVS driver Status */ +// CVS driver Status #[repr(u8)] enum FuIntelCvsState { Init = 0, diff -Nru fwupd-2.0.8/plugins/intel-gsc/README.md fwupd-2.0.20/plugins/intel-gsc/README.md --- fwupd-2.0.8/plugins/intel-gsc/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -8,12 +8,175 @@ ## Firmware Format -There are two firmware formats in use: +### FWCODE -* `$FPT` with children `FuIfwiFptFirmware`, where the `FW_DATA_IMAGE` is a `FuIfwiCpdFirmware` -* A linear array of `FuOpromFirmware` images, each with a `FuIfwiCpdFirmware` +* This is a superset of `FuIfwiFptFirmware` +* The `INFO` partition is a `FuStructIgscFwuImageMetadataV1`, which gives the `id` and `version`, + and the `FuStructIgscFwuFwImageData` is also part of that. This then gives the `arb_svn`. +* The `IMGI` partition is a `FuStructIgscFwuGwsImageInfo`, which gives us `hw_sku` + +```xml + + BMG_ + 0021.1111 + 0x2 + 0x2 + + INFO + + + IMGI + + + FWIM + + +``` + +### FWDATA + +* This is a superset of `FuIfwiFptFirmware` +* The `INFO` partition is a `FuIfwiCpdFirmware` is a `FuStructIgscFwdataVersion`. + This provides `oem_version`, `major_version` and `data_arb_svn`. +* The `SDTA` (also called a "GDTA") data partition is a `FuIfwiCpdFirmware` + +```xml + + 0x39 + 0xcb + 0x1 + + + 0x8086 + 0xe20b + 0x8086 + 0x1100 + + + + INFO + + + CKSM + + + GDTA + + +``` + +* The `SDTA` is parsed as: + +```xml + + 0x2 + 0x1 + + GDTA.man + 0xcb + + 0x23 + + + 0x16 + + + 0x1d + + + 0x25 + + + + GDTA + + + GDTA.met + + +``` + +* The `GDTA.man` partition of the CPD contains the manifest extensions. + +### OPROMCODE + +* The oprom expansion header is formatted as a `FuIfwiCpdFirmware` image +* The `OROM.man` section has two unknown (and unparsed) extension types + +```xml + + 0xf1 + 0x800 + + cpd + 0x4d4f524f + 0x2 + 0x1 + + OROM.man + 0x4200017 + + 0x23 + + + 0x16 + + + + CODE + + + CODE.met + + + +``` + +### OPROMDATA + +```xml + + 0xf0 + 0x800 + + + 0x8086 + 0xe20b + 0x8086 + 0x1100 + + + + cpd + 0x4d4f524f + 0x2 + 0x1 + + OROM.man + 0x4200017 + + 0x23 + + + 0x16 + + + 0x25 + + + + VBT + 0x1 + + + VBT.met + 0x2 + + + +``` -This plugin supports the following protocol ID: +This plugin supports the following protocol ID, used by all devices and sub-devices: * `com.intel.gsc` @@ -21,14 +184,21 @@ These devices use the standard PCI DeviceInstanceId values, e.g. -* `MEI\VID_8086&DEV_4905` +* `PCI\VEN_8086&DEV_E20B` They also define custom per-part PCI IDs such as: -* `MEI\VID_8086&DEV_4905&PART_FWCODE` -* `MEI\VID_8086&DEV_4905&PART_FWDATA` -* `MEI\VID_8086&DEV_4905&PART_OPROMCODE` -* `MEI\VID_8086&DEV_4905&PART_OPROMDATA` +* `PCI\VEN_8086&DEV_E20B&PART_FWCODE` +* `PCI\VEN_8086&DEV_E20B&PART_FWDATA` +* `PCI\VEN_8086&DEV_E20B&PART_OPROMCODE` +* `PCI\VEN_8086&DEV_E20B&PART_OPROMDATA` + +When the device needs recovery, the instance IDs will instead be: + +* `PCI\VEN_8086&DEV_E20B&PART_FWCODE_RECOVERY` +* `PCI\VEN_8086&DEV_E20B&PART_FWDATA_RECOVERY` +* `PCI\VEN_8086&DEV_E20B&PART_OPROMCODE_RECOVERY` +* `PCI\VEN_8086&DEV_E20B&PART_OPROMDATA_RECOVERY` ## Quirk Use @@ -53,10 +223,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.8.7`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Vitaly Lubart: @vlubart diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-aux-device.c fwupd-2.0.20/plugins/intel-gsc/fu-igsc-aux-device.c --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-aux-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-aux-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,6 @@ #include "fu-igsc-aux-device.h" #include "fu-igsc-aux-firmware.h" #include "fu-igsc-device.h" -#include "fu-igsc-heci.h" struct _FuIgscAuxDevice { FuDevice parent_instance; @@ -33,22 +32,24 @@ static gboolean fu_igsc_aux_device_probe(FuDevice *device, GError **error) { - FuDevice *parent = fu_device_get_parent(device); + FuDevice *parent; - /* fix name */ - if (parent != NULL) { - g_autofree gchar *name = NULL; - name = g_strdup_printf("%s Data", fu_device_get_name(parent)); - fu_device_set_name(device, name); - } + /* from the self tests */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; /* add extra instance IDs */ - fu_device_add_instance_str(device, "PART", "FWDATA"); - if (!fu_device_build_instance_id(device, error, "MEI", "VEN", "DEV", "PART", NULL)) + fu_device_add_instance_str(device, + "PART", + fu_device_has_private_flag(parent, FU_IGSC_DEVICE_FLAG_IS_WEDGED) + ? "FWDATA_RECOVERY" + : "FWDATA"); + if (!fu_device_build_instance_id(device, error, "PCI", "VEN", "DEV", "PART", NULL)) return FALSE; return fu_device_build_instance_id(device, error, - "MEI", + "PCI", "VEN", "DEV", "SUBSYS", @@ -60,19 +61,26 @@ fu_igsc_aux_device_setup(FuDevice *device, GError **error) { FuIgscAuxDevice *self = FU_IGSC_AUX_DEVICE(device); - FuIgscDevice *igsc_parent = FU_IGSC_DEVICE(fu_device_get_parent(device)); + FuIgscDevice *parent; g_autofree gchar *version = NULL; /* get version */ - if (!fu_igsc_device_get_aux_version(igsc_parent, + parent = FU_IGSC_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) + return FALSE; + if (!fu_igsc_device_get_aux_version(parent, &self->oem_version, &self->major_version, &self->major_vcn, error)) { - g_prefix_error(error, "failed to get aux version: "); + g_prefix_error_literal(error, "failed to get aux version: "); return FALSE; } - version = g_strdup_printf("%u.%x", self->major_version, self->oem_version); + if (fu_device_has_private_flag(FU_DEVICE(parent), FU_IGSC_DEVICE_FLAG_IS_WEDGED)) { + version = g_strdup("0.0"); + } else { + version = g_strdup_printf("%u.%u", self->major_version, self->oem_version); + } fu_device_set_version(device, version); /* success */ @@ -83,11 +91,11 @@ fu_igsc_aux_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIgscAuxDevice *self = FU_IGSC_AUX_DEVICE(device); - FuIgscDevice *igsc_parent = FU_IGSC_DEVICE(fu_device_get_parent(device)); + FuIgscDevice *parent; g_autoptr(FuIgscAuxFirmware) firmware = FU_IGSC_AUX_FIRMWARE(fu_igsc_aux_firmware_new()); /* parse container */ @@ -95,11 +103,14 @@ return NULL; /* search the device list for a match */ + parent = FU_IGSC_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) + return NULL; if (!fu_igsc_aux_firmware_match_device(firmware, - self->major_version, - self->major_vcn, - fu_igsc_device_get_ssvid(igsc_parent), - fu_igsc_device_get_ssdid(igsc_parent), + fu_device_get_vid(FU_DEVICE(parent)), + fu_device_get_pid(FU_DEVICE(parent)), + fu_igsc_device_get_ssvid(parent), + fu_igsc_device_get_ssdid(parent), error)) return NULL; @@ -126,7 +137,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "invalid manufacturer data version, got 0x%x, expected 0x%x", + "invalid OEM version, got 0x%x, expected higher than 0x%x", fu_igsc_aux_firmware_get_oem_version(firmware), self->oem_version); return NULL; @@ -143,7 +154,7 @@ FwupdInstallFlags flags, GError **error) { - FuIgscDevice *igsc_parent = FU_IGSC_DEVICE(fu_device_get_parent(device)); + FuIgscDevice *parent; g_autoptr(GBytes) fw_info = NULL; g_autoptr(GInputStream) stream_payload = NULL; @@ -156,7 +167,10 @@ fu_firmware_get_image_by_idx_stream(firmware, FU_IFWI_FPT_FIRMWARE_IDX_SDTA, error); if (stream_payload == NULL) return FALSE; - return fu_igsc_device_write_blob(igsc_parent, + parent = FU_IGSC_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) + return FALSE; + return fu_igsc_device_write_blob(parent, FU_IGSC_FWU_HECI_PAYLOAD_TYPE_FWDATA, fw_info, stream_payload, @@ -164,53 +178,47 @@ error); } -static gboolean -fu_igsc_aux_device_prepare(FuDevice *device, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - /* set PCI power policy */ - return fu_device_prepare(fu_device_get_parent(device), progress, flags, error); -} - -static gboolean -fu_igsc_aux_device_cleanup(FuDevice *device, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) +static void +fu_igsc_aux_device_set_progress(FuDevice *device, FuProgress *progress) { - /* set PCI power policy */ - return fu_device_cleanup(fu_device_get_parent(device), progress, flags, error); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); } static void fu_igsc_aux_device_init(FuIgscAuxDevice *self) { fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_IGSC_DEVICE); fu_device_add_protocol(FU_DEVICE(self), "com.intel.gsc"); fu_device_set_logical_id(FU_DEVICE(self), "fw-data"); + fu_device_set_name(FU_DEVICE(self), "Data"); } static void fu_igsc_aux_device_class_init(FuIgscAuxDeviceClass *klass) { FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->set_progress = fu_igsc_aux_device_set_progress; device_class->to_string = fu_igsc_aux_device_to_string; device_class->probe = fu_igsc_aux_device_probe; device_class->setup = fu_igsc_aux_device_setup; device_class->prepare_firmware = fu_igsc_aux_device_prepare_firmware; device_class->write_firmware = fu_igsc_aux_device_write_firmware; - device_class->prepare = fu_igsc_aux_device_prepare; - device_class->cleanup = fu_igsc_aux_device_cleanup; } FuIgscAuxDevice * -fu_igsc_aux_device_new(FuContext *ctx) +fu_igsc_aux_device_new(FuDevice *proxy) { - return g_object_new(FU_TYPE_IGSC_AUX_DEVICE, "context", ctx, NULL); + return g_object_new(FU_TYPE_IGSC_AUX_DEVICE, "proxy", proxy, NULL); } diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-aux-device.h fwupd-2.0.20/plugins/intel-gsc/fu-igsc-aux-device.h --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-aux-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-aux-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -13,4 +13,4 @@ G_DECLARE_FINAL_TYPE(FuIgscAuxDevice, fu_igsc_aux_device, FU, IGSC_AUX_DEVICE, FuDevice) FuIgscAuxDevice * -fu_igsc_aux_device_new(FuContext *ctx); +fu_igsc_aux_device_new(FuDevice *proxy); diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-aux-firmware.c fwupd-2.0.20/plugins/intel-gsc/fu-igsc-aux-firmware.c --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-aux-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-aux-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,44 +8,19 @@ #include "config.h" #include "fu-igsc-aux-firmware.h" -#include "fu-igsc-heci.h" -#include "fu-igsc-struct.h" +#include "fu-igsc-common.h" struct _FuIgscAuxFirmware { FuIfwiFptFirmware parent_instance; guint32 oem_version; guint16 major_version; guint16 major_vcn; - GPtrArray *device_infos; /* of igsc_fwdata_device_info */ - gboolean has_manifest_ext; + guint32 data_arb_svn; + GPtrArray *device_infos; /* of FuIgscFwdataDeviceInfo4 */ }; G_DEFINE_TYPE(FuIgscAuxFirmware, fu_igsc_aux_firmware, FU_TYPE_IFWI_FPT_FIRMWARE) -#define MFT_EXT_TYPE_DEVICE_IDS 37 -#define MFT_EXT_TYPE_FWDATA_UPDATE 29 - -struct mft_fwdata_update_ext { - guint32 extension_type; - guint32 extension_length; - guint32 oem_manuf_data_version; - guint16 major_vcn; - guint16 flags; -}; - -struct igsc_fwdata_device_info { - guint16 vendor_id; - guint16 device_id; - guint16 subsys_vendor_id; - guint16 subsys_device_id; -}; - -struct igsc_fwdata_version { - guint32 oem_manuf_data_version; - guint16 major_version; - guint16 major_vcn; -}; - static void fu_igsc_aux_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) { @@ -53,8 +28,8 @@ fu_xmlb_builder_insert_kx(bn, "oem_version", self->oem_version); fu_xmlb_builder_insert_kx(bn, "major_version", self->major_version); fu_xmlb_builder_insert_kx(bn, "major_vcn", self->major_vcn); - fu_xmlb_builder_insert_kx(bn, "device_infos", self->device_infos->len); - fu_xmlb_builder_insert_kb(bn, "has_manifest_ext", self->has_manifest_ext); + fu_xmlb_builder_insert_kx(bn, "data_arb_svn", self->data_arb_svn); + fu_igsc_fwdata_device_info_export(self->device_infos, bn); } gboolean @@ -68,10 +43,11 @@ g_return_val_if_fail(FU_IS_IGSC_AUX_FIRMWARE(self), FALSE); for (guint i = 0; i < self->device_infos->len; i++) { - struct igsc_fwdata_device_info *info = g_ptr_array_index(self->device_infos, i); - if (info->vendor_id == vendor_id && info->device_id == device_id && - info->subsys_vendor_id == subsys_vendor_id && - info->subsys_device_id == subsys_device_id) + FuIgscFwdataDeviceInfo4 *info = g_ptr_array_index(self->device_infos, i); + if (fu_igsc_fwdata_device_info4_get_vendor_id(info) == vendor_id && + fu_igsc_fwdata_device_info4_get_device_id(info) == device_id && + fu_igsc_fwdata_device_info4_get_subsys_vendor_id(info) == subsys_vendor_id && + fu_igsc_fwdata_device_info4_get_subsys_device_id(info) == subsys_device_id) return TRUE; } @@ -109,81 +85,25 @@ } static gboolean -fu_igsc_aux_firmware_parse_version(FuIgscAuxFirmware *self, GError **error) +fu_igsc_aux_firmware_parse_info(FuIgscAuxFirmware *self, GError **error) { - gsize bufsz = 0; - const guint8 *buf; - struct igsc_fwdata_version version = {0x0}; - g_autoptr(GBytes) fw_info = NULL; + g_autoptr(FuStructIgscFwdataVersion) st = NULL; + g_autoptr(GInputStream) stream = NULL; - fw_info = fu_firmware_get_image_by_idx_bytes(FU_FIRMWARE(self), - FU_IFWI_FPT_FIRMWARE_IDX_SDTA, + stream = fu_firmware_get_image_by_idx_stream(FU_FIRMWARE(self), + FU_IFWI_FPT_FIRMWARE_IDX_INFO, error); - if (fw_info == NULL) + if (stream == NULL) return FALSE; - buf = g_bytes_get_data(fw_info, &bufsz); - - if (!fu_memcpy_safe((guint8 *)&version, - sizeof(version), - 0x0, /* dst */ - buf, - bufsz, - FU_STRUCT_IGSC_FWU_HECI_IMAGE_METADATA_SIZE, /* src */ - sizeof(version), - error)) { - g_prefix_error(error, "no version: "); - return FALSE; - } - self->oem_version = version.oem_manuf_data_version; - self->major_vcn = version.major_vcn; - self->major_version = version.major_version; - return TRUE; -} - -static gboolean -fu_igsc_aux_firmware_parse_extension(FuIgscAuxFirmware *self, FuFirmware *fw, GError **error) -{ - const guint8 *buf; - gsize bufsz = 0; - g_autoptr(GBytes) blob = NULL; - - /* get data */ - blob = fu_firmware_get_bytes(fw, error); - if (blob == NULL) - return FALSE; - buf = g_bytes_get_data(blob, &bufsz); - - if (fu_firmware_get_idx(fw) == MFT_EXT_TYPE_DEVICE_IDS) { - for (gsize offset = 0; offset < bufsz; - offset += sizeof(struct igsc_fwdata_device_info)) { - struct igsc_fwdata_device_info device_info = {0x0}; - if (!fu_memcpy_safe((guint8 *)&device_info, - sizeof(device_info), - 0x0, /* dst */ - buf, - bufsz, - offset, /* src */ - sizeof(device_info), - error)) { - g_prefix_error(error, "no ext header: "); - return FALSE; - } - g_ptr_array_add(self->device_infos, - fu_memdup_safe((const guint8 *)&device_info, - sizeof(device_info), - NULL)); - } - } else if (fu_firmware_get_idx(fw) == MFT_EXT_TYPE_FWDATA_UPDATE) { - if (bufsz != sizeof(struct mft_fwdata_update_ext)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "signed data update manifest ext was 0x%x bytes", - (guint)bufsz); - return FALSE; - } - self->has_manifest_ext = TRUE; - } + st = fu_struct_igsc_fwdata_version_parse_stream(stream, + FU_STRUCT_IGSC_FWU_HECI_IMAGE_METADATA_SIZE, + error); + if (st == NULL) + return FALSE; + self->oem_version = fu_struct_igsc_fwdata_version_get_oem_manuf_data_version(st); + self->major_vcn = fu_struct_igsc_fwdata_version_get_major_vcn(st); + self->major_version = fu_struct_igsc_fwdata_version_get_major_version(st); + self->data_arb_svn = fu_struct_igsc_fwdata_version_get_data_arb_svn(st); /* success */ return TRUE; @@ -192,13 +112,14 @@ static gboolean fu_igsc_aux_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIgscAuxFirmware *self = FU_IGSC_AUX_FIRMWARE(firmware); + gboolean has_manifest_ext = FALSE; g_autoptr(FuFirmware) fw_cpd = fu_ifwi_cpd_firmware_new(); g_autoptr(FuFirmware) fw_manifest = NULL; - g_autoptr(GBytes) blob_dataimg = NULL; + g_autoptr(GInputStream) stream_dataimg = NULL; g_autoptr(GPtrArray) imgs = NULL; /* FuIfwiFptFirmware->parse */ @@ -207,13 +128,13 @@ return FALSE; /* parse data section */ - blob_dataimg = - fu_firmware_get_image_by_idx_bytes(firmware, FU_IFWI_FPT_FIRMWARE_IDX_SDTA, error); - if (blob_dataimg == NULL) + stream_dataimg = + fu_firmware_get_image_by_idx_stream(firmware, FU_IFWI_FPT_FIRMWARE_IDX_SDTA, error); + if (stream_dataimg == NULL) return FALSE; /* parse as CPD */ - if (!fu_firmware_parse_bytes(fw_cpd, blob_dataimg, 0x0, flags, error)) + if (!fu_firmware_parse_stream(fw_cpd, stream_dataimg, 0x0, flags, error)) return FALSE; /* get manifest */ @@ -226,10 +147,12 @@ imgs = fu_firmware_get_images(fw_manifest); for (guint i = 0; i < imgs->len; i++) { FuFirmware *img = g_ptr_array_index(imgs, i); - if (!fu_igsc_aux_firmware_parse_extension(self, img, error)) + if (!fu_igsc_fwdata_device_info_parse(self->device_infos, img, error)) return FALSE; + if (fu_firmware_get_idx(img) == FU_IGSC_FWU_EXT_TYPE_FWDATA_UPDATE) + has_manifest_ext = TRUE; } - if (!self->has_manifest_ext || self->device_infos->len == 0) { + if (!has_manifest_ext || self->device_infos->len == 0) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, @@ -238,8 +161,10 @@ } /* parse the info block */ - if (!fu_igsc_aux_firmware_parse_version(self, error)) + if (!fu_igsc_aux_firmware_parse_info(self, error)) { + g_prefix_error_literal(error, "failed to parse info block: "); return FALSE; + } /* success */ return TRUE; @@ -288,7 +213,8 @@ static void fu_igsc_aux_firmware_init(FuIgscAuxFirmware *self) { - self->device_infos = g_ptr_array_new_with_free_func(g_free); + self->device_infos = + g_ptr_array_new_with_free_func((GDestroyNotify)fu_igsc_fwdata_device_info4_unref); } static void diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-code-firmware.c fwupd-2.0.20/plugins/intel-gsc/fu-igsc-code-firmware.c --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-code-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-code-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,6 +13,7 @@ struct _FuIgscCodeFirmware { FuIfwiFptFirmware parent_instance; guint32 hw_sku; + guint32 arb_svn; }; G_DEFINE_TYPE(FuIgscCodeFirmware, fu_igsc_code_firmware, FU_TYPE_IFWI_FPT_FIRMWARE) @@ -25,6 +26,7 @@ { FuIgscCodeFirmware *self = FU_IGSC_CODE_FIRMWARE(firmware); fu_xmlb_builder_insert_kx(bn, "hw_sku", self->hw_sku); + fu_xmlb_builder_insert_kx(bn, "arb_svn", self->arb_svn); } guint32 @@ -34,14 +36,17 @@ return self->hw_sku; } +guint32 +fu_igsc_code_firmware_get_arb_svn(FuIgscCodeFirmware *self) +{ + g_return_val_if_fail(FU_IS_IFWI_FPT_FIRMWARE(self), G_MAXUINT32); + return self->arb_svn; +} + static gboolean fu_igsc_code_firmware_parse_imgi(FuIgscCodeFirmware *self, GInputStream *stream, GError **error) { - g_autoptr(GByteArray) st_inf = NULL; - - /* the command is only supported on DG2 */ - if (g_strcmp0(fu_firmware_get_id(FU_FIRMWARE(self)), "DG02") != 0) - return TRUE; + g_autoptr(FuStructIgscFwuGwsImageInfo) st_inf = NULL; /* get hw_sku */ st_inf = fu_struct_igsc_fwu_gws_image_info_parse_stream(stream, 0x0, error); @@ -54,14 +59,15 @@ static gboolean fu_igsc_code_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIgscCodeFirmware *self = FU_IGSC_CODE_FIRMWARE(firmware); gsize streamsz = 0; g_autofree gchar *project = NULL; g_autofree gchar *version = NULL; - g_autoptr(GByteArray) st_md1 = NULL; + g_autoptr(FuStructIgscFwuFwImageData) st_imgdata = NULL; + g_autoptr(FuStructIgscFwuImageMetadataV1) st_md1 = NULL; g_autoptr(GInputStream) stream_imgi = NULL; g_autoptr(GInputStream) stream_info = NULL; @@ -79,14 +85,18 @@ /* FuIfwiFptFirmware->parse */ if (!FU_FIRMWARE_CLASS(fu_igsc_code_firmware_parent_class) - ->parse(firmware, stream, flags, error)) + ->parse(firmware, stream, flags, error)) { + g_prefix_error_literal(error, "failed to parse as FuIfwiFptFirmware: "); return FALSE; + } stream_info = fu_firmware_get_image_by_idx_stream(FU_FIRMWARE(self), FU_IFWI_FPT_FIRMWARE_IDX_INFO, error); - if (stream_info == NULL) + if (stream_info == NULL) { + g_prefix_error_literal(error, "info not found: "); return FALSE; + } /* check metadata header format */ st_md1 = fu_struct_igsc_fwu_image_metadata_v1_parse_stream(stream_info, 0x0, error); @@ -106,6 +116,10 @@ fu_struct_igsc_fwu_image_metadata_v1_get_version_build(st_md1)); fu_firmware_set_version(FU_FIRMWARE(self), version); + /* get the SVN */ + st_imgdata = fu_struct_igsc_fwu_image_metadata_v1_get_image_data(st_md1); + self->arb_svn = fu_struct_igsc_fwu_fw_image_data_get_arb_svn(st_imgdata); + /* get instance ID for image */ stream_imgi = fu_firmware_get_image_by_idx_stream(FU_FIRMWARE(self), FU_IFWI_FPT_FIRMWARE_IDX_IMGI, diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-code-firmware.h fwupd-2.0.20/plugins/intel-gsc/fu-igsc-code-firmware.h --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-code-firmware.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-code-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -20,3 +20,5 @@ fu_igsc_code_firmware_new(void); guint32 fu_igsc_code_firmware_get_hw_sku(FuIgscCodeFirmware *self); +guint32 +fu_igsc_code_firmware_get_arb_svn(FuIgscCodeFirmware *self); diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-common.c fwupd-2.0.20/plugins/intel-gsc/fu-igsc-common.c --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,136 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-igsc-common.h" + +static void +fu_igsc_fwdata_device_info_export_one(FuIgscFwdataDeviceInfo4 *st, XbBuilderNode *bn) +{ + fu_xmlb_builder_insert_kx(bn, "vendor_id", fu_igsc_fwdata_device_info4_get_vendor_id(st)); + fu_xmlb_builder_insert_kx(bn, "device_id", fu_igsc_fwdata_device_info4_get_device_id(st)); + fu_xmlb_builder_insert_kx(bn, + "subsys_vendor_id", + fu_igsc_fwdata_device_info4_get_subsys_vendor_id(st)); + fu_xmlb_builder_insert_kx(bn, + "subsys_device_id", + fu_igsc_fwdata_device_info4_get_subsys_device_id(st)); +} + +void +fu_igsc_fwdata_device_info_export(GPtrArray *device_infos, XbBuilderNode *bn) +{ + g_autoptr(XbBuilderNode) bc = NULL; + + if (device_infos->len == 0) + return; + bc = xb_builder_node_insert(bn, "device_infos", NULL); + for (guint i = 0; i < device_infos->len; i++) { + FuIgscFwdataDeviceInfo4 *st = g_ptr_array_index(device_infos, i); + g_autoptr(XbBuilderNode) bm = xb_builder_node_insert(bc, "match", NULL); + fu_igsc_fwdata_device_info_export_one(st, bm); + } +} + +static gboolean +fu_igsc_fwdata_device_info_parse_device_type(GPtrArray *device_infos, + GInputStream *stream, + GError **error) +{ + gsize streamsz = 0; + + if (!fu_input_stream_size(stream, &streamsz, error)) + return FALSE; + for (gsize offset = 0; offset < streamsz; offset += FU_IGSC_FWDATA_DEVICE_INFO2_SIZE) { + g_autoptr(FuIgscFwdataDeviceInfo2) st = NULL; + g_autoptr(FuIgscFwdataDeviceInfo4) st4 = fu_igsc_fwdata_device_info4_new(); + st = fu_igsc_fwdata_device_info2_parse_stream(stream, offset, error); + if (st == NULL) + return FALSE; + fu_igsc_fwdata_device_info4_set_vendor_id(st4, 0x0); + fu_igsc_fwdata_device_info4_set_device_id(st4, 0x0); + fu_igsc_fwdata_device_info4_set_subsys_vendor_id( + st4, + fu_igsc_fwdata_device_info2_get_subsys_vendor_id(st)); + fu_igsc_fwdata_device_info4_set_subsys_device_id( + st4, + fu_igsc_fwdata_device_info2_get_subsys_device_id(st)); + g_ptr_array_add(device_infos, g_steal_pointer(&st4)); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_igsc_fwdata_device_info_parse_device_id_array(GPtrArray *device_infos, + GInputStream *stream, + GError **error) +{ + gsize streamsz = 0; + + if (!fu_input_stream_size(stream, &streamsz, error)) + return FALSE; + for (gsize offset = 0; offset < streamsz; offset += FU_IGSC_FWDATA_DEVICE_INFO4_SIZE) { + g_autoptr(FuIgscFwdataDeviceInfo4) st = NULL; + st = fu_igsc_fwdata_device_info4_parse_stream(stream, offset, error); + if (st == NULL) + return FALSE; + g_ptr_array_add(device_infos, g_steal_pointer(&st)); + } + + /* success */ + return TRUE; +} + +gboolean +fu_igsc_fwdata_device_info_parse(GPtrArray *device_infos, FuFirmware *fw, GError **error) +{ + FuIgscFwuExtType ext_type = fu_firmware_get_idx(fw); + g_autoptr(GInputStream) stream = NULL; + + /* get data */ + stream = fu_firmware_get_stream(fw, error); + if (stream == NULL) + return FALSE; + g_debug("found manifest extension: 0x%x [%s]", + ext_type, + fu_igsc_fwu_ext_type_to_string(ext_type)); + if (ext_type == FU_IGSC_FWU_EXT_TYPE_DEVICE_TYPE) + return fu_igsc_fwdata_device_info_parse_device_type(device_infos, stream, error); + if (ext_type == FU_IGSC_FWU_EXT_TYPE_DEVICE_ID_ARRAY) + return fu_igsc_fwdata_device_info_parse_device_id_array(device_infos, + stream, + error); + + /* unknown is success */ + return TRUE; +} + +gboolean +fu_igsc_heci_check_status(FuIgscFwuHeciStatus status, GError **error) +{ + const FuErrorMapEntry entries[] = { + {FU_IGSC_FWU_HECI_STATUS_SUCCESS, FWUPD_ERROR_LAST, NULL}, + {FU_IGSC_FWU_HECI_STATUS_SIZE_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "read/write/erase is bigger than partition"}, + {FU_IGSC_FWU_HECI_STATUS_UPDATE_OPROM_INVALID_STRUCTURE, + FWUPD_ERROR_NOT_SUPPORTED, + "wrong oprom signature"}, + {FU_IGSC_FWU_HECI_STATUS_UPDATE_OPROM_SECTION_NOT_EXIST, + FWUPD_ERROR_NOT_FOUND, + "update oprom section does not exists"}, + {FU_IGSC_FWU_HECI_STATUS_INVALID_COMMAND, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid HECI message"}, + {FU_IGSC_FWU_HECI_STATUS_INVALID_PARAMS, + FWUPD_ERROR_INVALID_DATA, + "invalid command parameters"}, + }; + return fu_error_map_entry_to_gerror(status, entries, G_N_ELEMENTS(entries), error); +} diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-common.h fwupd-2.0.20/plugins/intel-gsc/fu-igsc-common.h --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#include "fu-igsc-struct.h" + +void +fu_igsc_fwdata_device_info_export(GPtrArray *device_infos, XbBuilderNode *bn) G_GNUC_NON_NULL(1, 2); +gboolean +fu_igsc_fwdata_device_info_parse(GPtrArray *device_infos, FuFirmware *fw, GError **error) + G_GNUC_NON_NULL(1, 2); + +gboolean +fu_igsc_heci_check_status(FuIgscFwuHeciStatus status, GError **error); diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-device.c fwupd-2.0.20/plugins/intel-gsc/fu-igsc-device.c --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,35 +9,34 @@ #include "fu-igsc-aux-device.h" #include "fu-igsc-code-firmware.h" +#include "fu-igsc-common.h" #include "fu-igsc-device.h" #include "fu-igsc-oprom-device.h" -#include "fu-igsc-struct.h" struct _FuIgscDevice { - FuMeiDevice parent_instance; + FuHeciDevice parent_instance; gchar *project; guint32 hw_sku; guint16 subsystem_vendor; guint16 subsystem_model; gboolean oprom_code_devid_enforcement; + guint8 svn_executing; + guint8 svn_min_allowed; }; #define FU_IGSC_DEVICE_FLAG_HAS_AUX "has-aux" #define FU_IGSC_DEVICE_FLAG_HAS_OPROM "has-oprom" +#define FU_IGSC_DEVICE_FLAG_HAS_SKU "has-sku" -#define FU_IGSC_DEVICE_POWER_WRITE_TIMEOUT 1500 /* ms */ -#define FU_IGSC_DEVICE_MEI_WRITE_TIMEOUT 60000 /* 60 sec */ -#define FU_IGSC_DEVICE_MEI_READ_TIMEOUT 480000 /* 480 sec */ - -G_DEFINE_TYPE(FuIgscDevice, fu_igsc_device, FU_TYPE_MEI_DEVICE) - -#define GSC_FWU_STATUS_SUCCESS 0x0 -#define GSC_FWU_STATUS_SIZE_ERROR 0x5 -#define GSC_FWU_STATUS_UPDATE_OPROM_INVALID_STRUCTURE 0x1035 -#define GSC_FWU_STATUS_UPDATE_OPROM_SECTION_NOT_EXIST 0x1032 -#define GSC_FWU_STATUS_INVALID_COMMAND 0x8D -#define GSC_FWU_STATUS_INVALID_PARAMS 0x85 -#define GSC_FWU_STATUS_FAILURE 0x9E +#define FU_IGSC_DEVICE_MEI_WRITE_TIMEOUT 60000 /* 60 sec */ +#define FU_IGSC_DEVICE_MEI_READ_TIMEOUT 480000 /* 480 sec */ + +#define HECI1_CSE_FS_MODE_MASK 0x3 +#define HECI1_CSE_FS_CP_MODE 0x3 + +#define HECI1_CSE_FS_BACKGROUND_OPERATION_NEEDED_BIT (1 << 13) + +G_DEFINE_TYPE(FuIgscDevice, fu_igsc_device, FU_TYPE_HECI_DEVICE) #define GSC_FWU_GET_CONFIG_FORMAT_VERSION 0x1 @@ -53,6 +52,8 @@ idt, "OpromCodeDevidEnforcement", self->oprom_code_devid_enforcement); + fwupd_codec_string_append_hex(str, idt, "SvnExecuting", self->svn_executing); + fwupd_codec_string_append_hex(str, idt, "SvnMinAllowed", self->svn_min_allowed); } gboolean @@ -77,68 +78,6 @@ } static gboolean -fu_igsc_device_heci_validate_response_header(FuIgscDevice *self, - struct gsc_fwu_heci_response *resp_header, - FuIgscFwuHeciCommandId command_id, - GError **error) -{ - if (resp_header->header.command_id != command_id) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid command ID (%d): ", - resp_header->header.command_id); - return FALSE; - } - if (!resp_header->header.is_response) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "not a response"); - return FALSE; - } - if (resp_header->status != GSC_FWU_STATUS_SUCCESS) { - const gchar *msg; - switch (resp_header->status) { - case GSC_FWU_STATUS_SIZE_ERROR: - msg = "num of bytes to read/write/erase is bigger than partition size"; - break; - case GSC_FWU_STATUS_UPDATE_OPROM_INVALID_STRUCTURE: - msg = "wrong oprom signature"; - break; - case GSC_FWU_STATUS_UPDATE_OPROM_SECTION_NOT_EXIST: - msg = "update oprom section does not exists on flash"; - break; - case GSC_FWU_STATUS_INVALID_COMMAND: - msg = "invalid HECI message sent"; - break; - case GSC_FWU_STATUS_INVALID_PARAMS: - msg = "invalid command parameters"; - break; - case GSC_FWU_STATUS_FAILURE: - /* fall through */ - default: - msg = "general firmware error"; - break; - } - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "HECI message failed: %s [0x%x]: ", - msg, - resp_header->status); - return FALSE; - } - if (resp_header->reserved != 0) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "HECI message response is leaking data"); - return FALSE; - } - - /* success */ - return TRUE; -} - -static gboolean fu_igsc_device_command(FuIgscDevice *self, const guint8 *req_buf, gsize req_bufsz, @@ -147,6 +86,8 @@ GError **error) { gsize resp_readsz = 0; + + fu_dump_raw(G_LOG_DOMAIN, "MEI-write", req_buf, req_bufsz); if (!fu_mei_device_write(FU_MEI_DEVICE(self), req_buf, req_bufsz, @@ -160,15 +101,7 @@ FU_IGSC_DEVICE_MEI_READ_TIMEOUT, error)) return FALSE; - if (resp_readsz != resp_bufsz) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "read 0x%x bytes but expected 0x%x", - (guint)resp_readsz, - (guint)resp_bufsz); - return FALSE; - } + fu_dump_raw(G_LOG_DOMAIN, "MEI-read", resp_buf, resp_readsz); return TRUE; } @@ -179,51 +112,49 @@ gsize bufsz, GError **error) { - struct gsc_fwu_heci_version_req req = {.header.command_id = - FU_IGSC_FWU_HECI_COMMAND_ID_GET_IP_VERSION, - .partition = partition}; - guint8 res_buf[100]; - struct gsc_fwu_heci_version_resp *res = (struct gsc_fwu_heci_version_resp *)res_buf; + g_autofree guint8 *res_buf = NULL; + gsize res_bufsz = FU_IGSC_FWU_HECI_VERSION_RES_SIZE + bufsz; + g_autoptr(FuIgscFwuHeciVersionReq) st_req = fu_igsc_fwu_heci_version_req_new(); + g_autoptr(FuIgscFwuHeciVersionRes) st_res = NULL; + + /* connect to interface */ + if (!fu_mei_device_connect(FU_MEI_DEVICE(self), FU_HECI_DEVICE_UUID_FWUPDATE, 0, error)) { + g_prefix_error_literal(error, "failed to connect: "); + return FALSE; + } + res_buf = g_malloc0(res_bufsz); + fu_igsc_fwu_heci_version_req_set_partition(st_req, partition); if (!fu_igsc_device_command(self, - (const guint8 *)&req, - sizeof(req), + st_req->buf->data, + st_req->buf->len, res_buf, - sizeof(struct gsc_fwu_heci_version_resp) + bufsz, + res_bufsz, error)) { - g_prefix_error(error, "invalid HECI message response: "); + g_prefix_error_literal(error, "invalid HECI message response: "); return FALSE; } - if (!fu_igsc_device_heci_validate_response_header(self, - &res->response, - req.header.command_id, - error)) + st_res = fu_igsc_fwu_heci_version_res_parse(res_buf, res_bufsz, 0x0, error); + if (st_res == NULL) return FALSE; - if (res->partition != partition) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid HECI message response payload: 0x%x: ", - res->partition); + if (!fu_igsc_heci_check_status(fu_igsc_fwu_heci_version_res_get_status(st_res), error)) return FALSE; - } - if (bufsz > 0 && res->version_length != bufsz) { + if (fu_igsc_fwu_heci_version_res_get_partition(st_res) != partition) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "invalid HECI message response version_length: 0x%x, expected 0x%x: ", - res->version_length, - (guint)bufsz); + "invalid HECI message response partition: 0x%x", + fu_igsc_fwu_heci_version_res_get_partition(st_res)); return FALSE; } - if (buf != NULL) { + if (bufsz > 0) { if (!fu_memcpy_safe(buf, bufsz, 0x0, /* dst */ - res->version, - res->version_length, - 0x0, /* src*/ - res->version_length, + res_buf, + res_bufsz, + st_res->buf->len, /* src */ + fu_igsc_fwu_heci_version_res_get_version_length(st_res), error)) { return FALSE; } @@ -240,30 +171,35 @@ guint16 *major_vcn, GError **error) { - struct gsc_fw_data_heci_version_req req = { - .header.command_id = FU_IGSC_FWU_HECI_COMMAND_ID_GET_GFX_DATA_UPDATE_INFO}; - struct gsc_fw_data_heci_version_resp res = {0x0}; + guint8 res_buf[FU_IGSC_FW_DATA_HECI_VERSION_RES_SIZE] = {0}; + g_autoptr(FuIgscFwDataHeciVersionReq) st_req = fu_igsc_fw_data_heci_version_req_new(); + g_autoptr(FuIgscFwDataHeciVersionRes) st_res = NULL; + + /* connect to interface */ + if (!fu_mei_device_connect(FU_MEI_DEVICE(self), FU_HECI_DEVICE_UUID_FWUPDATE, 0, error)) { + g_prefix_error_literal(error, "failed to connect: "); + return FALSE; + } if (!fu_igsc_device_command(self, - (const guint8 *)&req, - sizeof(req), - (guint8 *)&res, - sizeof(res), + st_req->buf->data, + st_req->buf->len, + res_buf, + sizeof(res_buf), error)) return FALSE; - if (!fu_igsc_device_heci_validate_response_header(self, - &res.response, - req.header.command_id, - error)) + st_res = fu_igsc_fw_data_heci_version_res_parse(res_buf, sizeof(res_buf), 0x0, error); + if (st_res == NULL) + return FALSE; + if (!fu_igsc_heci_check_status(fu_igsc_fw_data_heci_version_res_get_status(st_res), error)) return FALSE; - /* success */ - *major_vcn = res.major_vcn; - *major_version = res.major_version; - if (res.oem_version_fitb_valid) { - *oem_version = res.oem_version_fitb; + *major_vcn = fu_igsc_fw_data_heci_version_res_get_major_vcn(st_res); + *major_version = fu_igsc_fw_data_heci_version_res_get_major_version(st_res); + if (fu_igsc_fw_data_heci_version_res_get_oem_version_fitb_valid(st_res)) { + *oem_version = fu_igsc_fw_data_heci_version_res_get_oem_version_fitb(st_res); } else { - *oem_version = res.oem_version_nvm; + *oem_version = fu_igsc_fw_data_heci_version_res_get_oem_version_nvm(st_res); } return TRUE; } @@ -271,129 +207,121 @@ static gboolean fu_igsc_device_get_subsystem_ids(FuIgscDevice *self, GError **error) { - struct gsc_fwu_heci_get_subsystem_ids_message_req req = { - .header.command_id = FU_IGSC_FWU_HECI_COMMAND_ID_GET_SUBSYSTEM_IDS}; - struct gsc_fwu_heci_get_subsystem_ids_message_resp res = {0x0}; + guint8 res_buf[FU_IGSC_FWU_HECI_GET_SUBSYSTEM_IDS_RES_SIZE] = {0}; + g_autoptr(FuIgscFwuHeciGetSubsystemIdsReq) st_req = + fu_igsc_fwu_heci_get_subsystem_ids_req_new(); + g_autoptr(FuIgscFwuHeciGetSubsystemIdsRes) st_res = NULL; + + /* connect to interface */ + if (!fu_mei_device_connect(FU_MEI_DEVICE(self), FU_HECI_DEVICE_UUID_FWUPDATE, 0, error)) { + g_prefix_error_literal(error, "failed to connect: "); + return FALSE; + } if (!fu_igsc_device_command(self, - (const guint8 *)&req, - sizeof(req), - (guint8 *)&res, - sizeof(res), + st_req->buf->data, + st_req->buf->len, + res_buf, + sizeof(res_buf), error)) return FALSE; - if (!fu_igsc_device_heci_validate_response_header(self, - &res.response, - req.header.command_id, - error)) + st_res = fu_igsc_fwu_heci_get_subsystem_ids_res_parse(res_buf, sizeof(res_buf), 0x0, error); + if (st_res == NULL) + return FALSE; + if (!fu_igsc_heci_check_status(fu_igsc_fwu_heci_get_subsystem_ids_res_get_status(st_res), + error)) return FALSE; /* success */ - self->subsystem_vendor = res.ssvid; - self->subsystem_model = res.ssdid; + self->subsystem_vendor = fu_igsc_fwu_heci_get_subsystem_ids_res_get_ssvid(st_res); + self->subsystem_model = fu_igsc_fwu_heci_get_subsystem_ids_res_get_ssdid(st_res); return TRUE; } -#define GSC_IFWI_TAG_SOC2_SKU_BIT 0x1 -#define GSC_IFWI_TAG_SOC3_SKU_BIT 0x2 -#define GSC_IFWI_TAG_SOC1_SKU_BIT 0x4 - -#define GSC_DG2_SKUID_SOC1 0 -#define GSC_DG2_SKUID_SOC2 1 -#define GSC_DG2_SKUID_SOC3 2 - static gboolean fu_igsc_device_get_config(FuIgscDevice *self, GError **error) { - struct gsc_fwu_heci_get_config_message_req req = { - .header.command_id = FU_IGSC_FWU_HECI_COMMAND_ID_GET_CONFIG, - }; - struct gsc_fwu_heci_get_config_message_resp res = {0x0}; + guint8 res_buf[FU_IGSC_FWU_HECI_GET_CONFIG_RES_SIZE] = {0}; + g_autoptr(FuIgscFwuHeciGetConfigReq) st_req = fu_igsc_fwu_heci_get_config_req_new(); + g_autoptr(FuIgscFwuHeciGetConfigRes) st_res = NULL; + + /* connect to interface */ + if (!fu_mei_device_connect(FU_MEI_DEVICE(self), FU_HECI_DEVICE_UUID_FWUPDATE, 0, error)) { + g_prefix_error_literal(error, "failed to connect: "); + return FALSE; + } if (!fu_igsc_device_command(self, - (const guint8 *)&req, - sizeof(req), - (guint8 *)&res, - sizeof(res), - error)) { - g_prefix_error(error, "invalid HECI message response: "); + st_req->buf->data, + st_req->buf->len, + res_buf, + sizeof(res_buf), + error)) return FALSE; - } - if (!fu_igsc_device_heci_validate_response_header(self, - &res.response, - req.header.command_id, - error)) + st_res = fu_igsc_fwu_heci_get_config_res_parse(res_buf, sizeof(res_buf), 0x0, error); + if (st_res == NULL) return FALSE; - if (res.format_version != GSC_FWU_GET_CONFIG_FORMAT_VERSION) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid config version 0x%x, expected 0x%x", - res.format_version, - (guint)GSC_FWU_GET_CONFIG_FORMAT_VERSION); + if (!fu_igsc_heci_check_status(fu_igsc_fwu_heci_get_config_res_get_status(st_res), error)) return FALSE; - } - - /* convert to firmware bit mask for easier comparison */ - if (res.hw_sku == GSC_DG2_SKUID_SOC1) { - self->hw_sku = GSC_IFWI_TAG_SOC1_SKU_BIT; - } else if (res.hw_sku == GSC_DG2_SKUID_SOC3) { - self->hw_sku = GSC_IFWI_TAG_SOC3_SKU_BIT; - } else if (res.hw_sku == GSC_DG2_SKUID_SOC2) { - self->hw_sku = GSC_IFWI_TAG_SOC2_SKU_BIT; - } else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid hw sku 0x%x, expected 0..2", - res.hw_sku); - return FALSE; - } - - self->oprom_code_devid_enforcement = res.oprom_code_devid_enforcement; /* success */ + self->hw_sku = fu_igsc_fwu_heci_get_config_res_get_hw_sku(st_res); + self->oprom_code_devid_enforcement = + fu_igsc_fwu_heci_get_config_res_get_flags(st_res) & + FU_IGSC_FWU_HECI_GET_CONFIG_FLAG_OPROM_CODE_DEVID_ENFORCEMENT; return TRUE; } static gboolean -fu_igsc_device_open(FuDevice *device, GError **error) -{ - /* open then create context */ - if (!FU_DEVICE_CLASS(fu_igsc_device_parent_class)->open(device, error)) - return FALSE; - return fu_mei_device_connect(FU_MEI_DEVICE(device), 0, error); -} - -static gboolean fu_igsc_device_setup(FuDevice *device, GError **error) { FuIgscDevice *self = FU_IGSC_DEVICE(device); - FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); g_autofree gchar *version = NULL; - g_autoptr(GByteArray) fw_code_version = fu_struct_igsc_fw_version_new(); + g_autoptr(FuStructIgscFwVersion) st_fwversion = fu_struct_igsc_fw_version_new(); + + /* connect to interface */ + if (!fu_mei_device_connect(FU_MEI_DEVICE(self), FU_HECI_DEVICE_UUID_MCHI2, 0, error)) { + g_prefix_error_literal(error, "failed to connect: "); + return FALSE; + } + if (!fu_heci_device_arbh_svn_get_info(FU_HECI_DEVICE(self), + FU_MKHI_ARBH_SVN_INFO_ENTRY_USAGE_ID_CSE_RBE, + &self->svn_executing, + &self->svn_min_allowed, + error)) { + g_prefix_error_literal(error, "failed to get ARBH SVN: "); + return FALSE; + } /* get current version */ if (!fu_igsc_device_get_version_raw(self, FU_IGSC_FWU_HECI_PARTITION_VERSION_GFX_FW, - fw_code_version->data, - fw_code_version->len, + st_fwversion->buf->data, + st_fwversion->buf->len, error)) { - g_prefix_error(error, "cannot cannot get fw version: "); + g_prefix_error_literal(error, "cannot get fw version: "); return FALSE; } - self->project = fu_struct_igsc_fw_version_get_project(fw_code_version); - version = g_strdup_printf("%u.%u", - fu_struct_igsc_fw_version_get_hotfix(fw_code_version), - fu_struct_igsc_fw_version_get_build(fw_code_version)); + self->project = fu_struct_igsc_fw_version_get_project(st_fwversion); + if (fu_device_has_private_flag(device, FU_IGSC_DEVICE_FLAG_IS_WEDGED)) { + version = g_strdup("0.0"); + } else { + version = g_strdup_printf("%u.%u", + fu_struct_igsc_fw_version_get_hotfix(st_fwversion), + fu_struct_igsc_fw_version_get_build(st_fwversion)); + } fu_device_set_version(device, version); /* get hardware SKU if supported */ - if (g_strcmp0(self->project, "DG02") == 0) { + if (g_strcmp0(self->project, "DG02") == 0) + fu_device_add_private_flag(device, FU_IGSC_DEVICE_FLAG_HAS_SKU); + if (fu_device_has_private_flag(device, FU_IGSC_DEVICE_FLAG_HAS_SKU)) { if (!fu_igsc_device_get_config(self, error)) { - g_prefix_error(error, "cannot cannot get SKU: "); + g_prefix_error_literal(error, "cannot get SKU: "); return FALSE; } + } else { + g_debug("not getting config for %s", self->project); } /* allow vendors to differentiate their products */ @@ -407,16 +335,16 @@ /* some devices have children */ if (fu_device_has_private_flag(device, FU_IGSC_DEVICE_FLAG_HAS_AUX)) { - g_autoptr(FuIgscAuxDevice) device_child = fu_igsc_aux_device_new(ctx); + g_autoptr(FuIgscAuxDevice) device_child = fu_igsc_aux_device_new(device); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(device_child)); } if (fu_device_has_private_flag(device, FU_IGSC_DEVICE_FLAG_HAS_OPROM)) { g_autoptr(FuIgscOpromDevice) device_code = NULL; g_autoptr(FuIgscOpromDevice) device_data = NULL; device_code = - fu_igsc_oprom_device_new(ctx, FU_IGSC_FWU_HECI_PAYLOAD_TYPE_OPROM_CODE); + fu_igsc_oprom_device_new(device, FU_IGSC_FWU_HECI_PAYLOAD_TYPE_OPROM_CODE); device_data = - fu_igsc_oprom_device_new(ctx, FU_IGSC_FWU_HECI_PAYLOAD_TYPE_OPROM_DATA); + fu_igsc_oprom_device_new(device, FU_IGSC_FWU_HECI_PAYLOAD_TYPE_OPROM_DATA); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(device_code)); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(device_data)); } @@ -438,7 +366,7 @@ /* read value and convert to hex */ tmp = fu_mei_device_get_fw_status(FU_MEI_DEVICE(self), line - 1, error); if (tmp == NULL) { - g_prefix_error(error, "device is corrupted: "); + g_prefix_error_literal(error, "device is corrupted: "); return FALSE; } hex = g_strdup_printf("0x%s", tmp); @@ -457,18 +385,42 @@ fu_igsc_device_probe(FuDevice *device, GError **error) { FuIgscDevice *self = FU_IGSC_DEVICE(device); + g_autofree gchar *prop_wedged = NULL; /* check firmware status */ if (!fu_igsc_device_get_fw_status(self, 1, NULL, error)) return FALSE; + /* device is wedged and needs recovery */ + prop_wedged = fu_udev_device_read_property(FU_UDEV_DEVICE(device), "WEDGED", NULL); + if (g_strcmp0(prop_wedged, "vendor-specific") == 0) { + g_autofree gchar *attr_survivability_mode = NULL; + attr_survivability_mode = + fu_udev_device_read_sysfs(FU_UDEV_DEVICE(self), + "attr_survivability_mode", + FU_UDEV_DEVICE_ATTR_READ_TIMEOUT_DEFAULT, + error); + if (attr_survivability_mode == NULL) { + g_prefix_error_literal(error, + "cannot get survivability_mode for " + "WEDGED=vendor-specific: "); + return FALSE; + } + g_debug("survivability_mode: %s", attr_survivability_mode); + fu_device_add_private_flag(device, FU_IGSC_DEVICE_FLAG_IS_WEDGED); + } + /* add extra instance IDs */ - fu_device_add_instance_str(device, "PART", "FWCODE"); - if (!fu_device_build_instance_id(device, error, "MEI", "VEN", "DEV", "PART", NULL)) + fu_device_add_instance_str(device, + "PART", + fu_device_has_private_flag(device, FU_IGSC_DEVICE_FLAG_IS_WEDGED) + ? "FWCODE_RECOVERY" + : "FWCODE"); + if (!fu_device_build_instance_id(device, error, "PCI", "VEN", "DEV", "PART", NULL)) return FALSE; return fu_device_build_instance_id(device, error, - "MEI", + "PCI", "VEN", "DEV", "SUBSYS", @@ -480,10 +432,12 @@ fu_igsc_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIgscDevice *self = FU_IGSC_DEVICE(device); + guint fw_arb_svn; + guint fw_hw_sku; g_autoptr(FuFirmware) firmware = fu_igsc_code_firmware_new(); /* check project code */ @@ -498,47 +452,100 @@ self->project); return NULL; } - if (self->hw_sku != fu_igsc_code_firmware_get_hw_sku(FU_IGSC_CODE_FIRMWARE(firmware))) { + + /* check SKU */ + fw_hw_sku = fu_igsc_code_firmware_get_hw_sku(FU_IGSC_CODE_FIRMWARE(firmware)); + if (self->hw_sku != fw_hw_sku) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "firmware is for a different SKU, got 0x%x, expected 0x%x", - fu_igsc_code_firmware_get_hw_sku(FU_IGSC_CODE_FIRMWARE(firmware)), + fw_hw_sku, self->hw_sku); return NULL; } + /* check SVN */ + fw_arb_svn = fu_igsc_code_firmware_get_arb_svn(FU_IGSC_CODE_FIRMWARE(firmware)); + if (fw_arb_svn < self->svn_min_allowed) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware incompatible, ARB SVN was 0x%x, minimum required is 0x%x", + fw_arb_svn, + self->svn_min_allowed); + return NULL; + } + if (fw_arb_svn < self->svn_executing && (flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware incompatible, ARB SVN was 0x%x, hardware ARB SVN is 0x%x", + fw_arb_svn, + self->svn_executing); + return NULL; + } + /* success */ return g_steal_pointer(&firmware); } static gboolean -fu_igsc_device_update_end(FuIgscDevice *self, GError **error) +fu_igsc_device_update_end(FuIgscDevice *self, FuIgscFwuHeciPayloadType payload_type, GError **error) { - struct gsc_fwu_heci_end_req req = {.header.command_id = FU_IGSC_FWU_HECI_COMMAND_ID_END}; - return fu_mei_device_write(FU_MEI_DEVICE(self), - (const guint8 *)&req, - sizeof(req), - FU_IGSC_DEVICE_MEI_WRITE_TIMEOUT, - error); + guint8 res_buf[FU_IGSC_FWU_HECI_END_RES_SIZE] = {0}; + g_autoptr(FuIgscFwuHeciEndReq) st_req = fu_igsc_fwu_heci_end_req_new(); + g_autoptr(FuIgscFwuHeciEndRes) st_res = NULL; + + /* connect to interface */ + if (!fu_mei_device_connect(FU_MEI_DEVICE(self), FU_HECI_DEVICE_UUID_FWUPDATE, 0, error)) { + g_prefix_error_literal(error, "failed to connect: "); + return FALSE; + } + + /* we are not expecting a reply */ + if (payload_type == FU_IGSC_FWU_HECI_PAYLOAD_TYPE_GFX_FW || + payload_type == FU_IGSC_FWU_HECI_PAYLOAD_TYPE_FWDATA) { + fu_dump_raw(G_LOG_DOMAIN, "MEI-write", st_req->buf->data, st_req->buf->len); + return fu_mei_device_write(FU_MEI_DEVICE(self), + st_req->buf->data, + st_req->buf->len, + FU_IGSC_DEVICE_MEI_WRITE_TIMEOUT, + error); + } + if (!fu_igsc_device_command(self, + st_req->buf->data, + st_req->buf->len, + res_buf, + sizeof(res_buf), + error)) + return FALSE; + st_res = fu_igsc_fwu_heci_end_res_parse(res_buf, sizeof(res_buf), 0x0, error); + if (st_res == NULL) + return FALSE; + return fu_igsc_heci_check_status(fu_igsc_fwu_heci_end_res_get_status(st_res), error); } static gboolean fu_igsc_device_update_data(FuIgscDevice *self, const guint8 *data, gsize length, GError **error) { - struct gsc_fwu_heci_data_req req = {.header.command_id = FU_IGSC_FWU_HECI_COMMAND_ID_DATA, - .data_length = length}; - struct gsc_fwu_heci_data_resp res = {0x0}; - g_autoptr(GByteArray) buf = g_byte_array_new(); - - g_byte_array_append(buf, (const guint8 *)&req, sizeof(req)); - g_byte_array_append(buf, data, length); - if (!fu_igsc_device_command(self, buf->data, buf->len, (guint8 *)&res, sizeof(res), error)) - return FALSE; - return fu_igsc_device_heci_validate_response_header(self, - &res.response, - req.header.command_id, - error); + guint8 res_buf[FU_IGSC_FWU_HECI_DATA_RES_SIZE] = {0}; + g_autoptr(FuIgscFwuHeciDataReq) st_req = fu_igsc_fwu_heci_data_req_new(); + g_autoptr(FuIgscFwuHeciDataRes) st_res = NULL; + + fu_igsc_fwu_heci_data_req_set_data_length(st_req, length); + g_byte_array_append(st_req->buf, data, length); + if (!fu_igsc_device_command(self, + st_req->buf->data, + st_req->buf->len, + res_buf, + sizeof(res_buf), + error)) + return FALSE; + st_res = fu_igsc_fwu_heci_data_res_parse(res_buf, sizeof(res_buf), 0x0, error); + if (st_res == NULL) + return FALSE; + return fu_igsc_heci_check_status(fu_igsc_fwu_heci_data_res_get_status(st_res), error); } static gboolean @@ -548,36 +555,44 @@ GInputStream *fw, GError **error) { + guint8 res_buf[FU_IGSC_FWU_HECI_START_RES_SIZE] = {0}; gsize streamsz = 0; - struct gsc_fwu_heci_start_req req = {.header.command_id = FU_IGSC_FWU_HECI_COMMAND_ID_START, - .payload_type = payload_type, - .flags = {0}}; - struct gsc_fwu_heci_start_resp res = {0x0}; - g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autoptr(FuIgscFwuHeciStartReq) st_req = fu_igsc_fwu_heci_start_req_new(); + g_autoptr(FuIgscFwuHeciStartRes) st_res = NULL; - if (!fu_input_stream_size(fw, &streamsz, error)) + /* connect to interface */ + if (!fu_mei_device_connect(FU_MEI_DEVICE(self), FU_HECI_DEVICE_UUID_FWUPDATE, 0, error)) { + g_prefix_error_literal(error, "failed to connect: "); return FALSE; - req.update_img_length = streamsz; + } - g_byte_array_append(buf, (const guint8 *)&req, sizeof(req)); + if (!fu_input_stream_size(fw, &streamsz, error)) + return FALSE; + fu_igsc_fwu_heci_start_req_set_update_img_length(st_req, streamsz); + fu_igsc_fwu_heci_start_req_set_payload_type(st_req, payload_type); + fu_igsc_fwu_heci_start_req_set_flags(st_req, FU_IGSC_FWU_HECI_START_FLAG_NONE); if (fw_info != NULL) - fu_byte_array_append_bytes(buf, fw_info); - if (!fu_igsc_device_command(self, buf->data, buf->len, (guint8 *)&res, sizeof(res), error)) + fu_byte_array_append_bytes(st_req->buf, fw_info); + if (!fu_igsc_device_command(self, + st_req->buf->data, + st_req->buf->len, + res_buf, + sizeof(res_buf), + error)) return FALSE; - return fu_igsc_device_heci_validate_response_header(self, - &res.response, - req.header.command_id, - error); + st_res = fu_igsc_fwu_heci_start_res_parse(res_buf, sizeof(res_buf), 0x0, error); + if (st_res == NULL) + return FALSE; + return fu_igsc_heci_check_status(fu_igsc_fwu_heci_start_res_get_status(st_res), error); } static gboolean fu_igsc_device_no_update(FuIgscDevice *self, GError **error) { - struct gsc_fwu_heci_no_update_req req = {.header.command_id = - FU_IGSC_FWU_HECI_COMMAND_ID_NO_UPDATE}; + g_autoptr(FuIgscFwuHeciNoUpdateReq) st_req = fu_igsc_fwu_heci_no_update_req_new(); return fu_mei_device_write(FU_MEI_DEVICE(self), - (const guint8 *)&req, - sizeof(req), + st_req->buf->data, + st_req->buf->len, FU_IGSC_DEVICE_MEI_WRITE_TIMEOUT, error); } @@ -588,6 +603,12 @@ FuProgress *progress, GError **error) { + /* connect to interface */ + if (!fu_mei_device_connect(FU_MEI_DEVICE(self), FU_HECI_DEVICE_UUID_FWUPDATE, 0, error)) { + g_prefix_error_literal(error, "failed to connect: "); + return FALSE; + } + fu_progress_set_id(progress, G_STRLOC); fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { @@ -614,14 +635,15 @@ /* the expectation is that it will fail eventually */ static gboolean -fu_igsc_device_wait_for_reset(FuIgscDevice *self, GError **error) +fu_igsc_device_wait_for_version(FuIgscDevice *self, GError **error) { - g_autoptr(GByteArray) fw_code_version = fu_struct_igsc_fw_version_new(); + g_autoptr(FuStructIgscFwVersion) st_fwversion = fu_struct_igsc_fw_version_new(); for (guint i = 0; i < 20; i++) { + fu_mei_device_disconnect(FU_MEI_DEVICE(self)); if (!fu_igsc_device_get_version_raw(self, FU_IGSC_FWU_HECI_PARTITION_VERSION_GFX_FW, - fw_code_version->data, - fw_code_version->len, + st_fwversion->buf->data, + st_fwversion->buf->len, NULL)) return TRUE; fu_device_sleep(FU_DEVICE(self), 100); @@ -631,9 +653,30 @@ } static gboolean -fu_igsc_device_reconnect_cb(FuDevice *self, gpointer user_data, GError **error) +fu_igsc_device_reconnect_cb(FuDevice *device, gpointer user_data, GError **error) { - return fu_mei_device_connect(FU_MEI_DEVICE(self), 0, error); + fu_mei_device_disconnect(FU_MEI_DEVICE(device)); + return fu_mei_device_connect(FU_MEI_DEVICE(device), FU_HECI_DEVICE_UUID_FWUPDATE, 0, error); +} + +static gboolean +fu_igsc_device_wait_heci_finish_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuIgscDevice *self = FU_IGSC_DEVICE(device); + guint32 fw_status = 0; + + if (!fu_igsc_device_get_fw_status(self, 5, &fw_status, error)) + return FALSE; + if (fw_status & HECI1_CSE_FS_BACKGROUND_OPERATION_NEEDED_BIT) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "FWSTS5 is 0x%x", + fw_status); + return FALSE; + } + /* success */ + return TRUE; } gboolean @@ -646,26 +689,25 @@ { gboolean cp_mode; guint32 sts5 = 0; - gsize payloadsz = fu_mei_device_get_max_msg_length(FU_MEI_DEVICE(self)) - - sizeof(struct gsc_fwu_heci_data_req); + gsize payloadsz; g_autoptr(FuChunkArray) chunks = NULL; /* progress */ if (payload_type == FU_IGSC_FWU_HECI_PAYLOAD_TYPE_GFX_FW) { fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "get-status"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "update-start"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, "write-chunks"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "update-end"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "wait-for-reboot"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 46, "reconnect"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 20, "wait"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 20, "reconnect"); } else { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "get-status"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "update-start"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 96, "write-chunks"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "update-end"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "wait-for-reboot"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reconnect"); } @@ -677,12 +719,23 @@ /* start */ if (!fu_igsc_device_update_start(self, payload_type, fw_info, fw, error)) { - g_prefix_error(error, "failed to start: "); + g_prefix_error_literal(error, "failed to start: "); return FALSE; } fu_progress_step_done(progress); /* data */ + if (fu_mei_device_get_max_msg_length(FU_MEI_DEVICE(self)) < + FU_IGSC_FWU_HECI_DATA_REQ_SIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "mei device max msg length invalid: 0x%x", + fu_mei_device_get_max_msg_length(FU_MEI_DEVICE(self))); + return FALSE; + } + payloadsz = + fu_mei_device_get_max_msg_length(FU_MEI_DEVICE(self)) - FU_IGSC_FWU_HECI_DATA_REQ_SIZE; chunks = fu_chunk_array_new_from_stream(fw, FU_CHUNK_ADDR_OFFSET_NONE, FU_CHUNK_PAGESZ_NONE, @@ -695,38 +748,50 @@ fu_progress_step_done(progress); /* stop */ - if (!fu_igsc_device_update_end(self, error)) { - g_prefix_error(error, "failed to end: "); + if (!fu_igsc_device_update_end(self, payload_type, error)) { + g_prefix_error_literal(error, "failed to end: "); return FALSE; } fu_progress_step_done(progress); - /* detect a firmware reboot */ - if (payload_type == FU_IGSC_FWU_HECI_PAYLOAD_TYPE_GFX_FW || - payload_type == FU_IGSC_FWU_HECI_PAYLOAD_TYPE_FWDATA) { - if (!fu_igsc_device_wait_for_reset(self, error)) - return FALSE; - } - fu_progress_step_done(progress); - - /* after Gfx FW update there is a FW reset so driver reconnect is needed */ + /* after the Gfx FW update there is a FW reset so a driver reconnect is needed */ if (payload_type == FU_IGSC_FWU_HECI_PAYLOAD_TYPE_GFX_FW) { + /* we can't use WAIT_FOR_REPLUG as the mei device itself doesn't disappear and + * reappear, only the *fwu interface* does... */ + fu_device_sleep_full(FU_DEVICE(self), 5000, fu_progress_get_child(progress)); + fu_progress_step_done(progress); + + /* wait for the fwu interface to reply with the version information */ if (cp_mode) { - if (!fu_igsc_device_wait_for_reset(self, error)) + if (!fu_igsc_device_wait_for_version(self, error)) return FALSE; } if (!fu_device_retry_full(FU_DEVICE(self), fu_igsc_device_reconnect_cb, 200, - 300 /* ms */, + 1000 /* ms */, NULL, - error)) + error)) { + g_prefix_error_literal(error, "failed to wait for reconnect: "); return FALSE; + } if (!fu_igsc_device_no_update(self, error)) { - g_prefix_error(error, "failed to send no-update: "); + g_prefix_error_literal(error, "failed to send no-update: "); return FALSE; } - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + if (!fu_device_retry_full(FU_DEVICE(self), + fu_igsc_device_wait_heci_finish_cb, + 600, + 500 /* ms */, + NULL, + error)) { + g_prefix_error_literal(error, "failed to wait for reconnect: "); + return FALSE; + } + if (cp_mode) { + if (!fu_igsc_device_wait_for_version(self, error)) + return FALSE; + } } fu_progress_step_done(progress); @@ -754,59 +819,27 @@ fu_firmware_get_image_by_idx_stream(firmware, FU_IFWI_FPT_FIRMWARE_IDX_FWIM, error); if (stream_payload == NULL) return FALSE; - return fu_igsc_device_write_blob(self, - FU_IGSC_FWU_HECI_PAYLOAD_TYPE_GFX_FW, - fw_info, - stream_payload, - progress, - error); -} - -static gboolean -fu_igsc_device_set_pci_power_policy(FuIgscDevice *self, const gchar *val, GError **error) -{ - g_autoptr(FuDevice) parent = NULL; - - /* get PCI parent */ - parent = fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), "pci", error); - if (parent == NULL) + if (!fu_igsc_device_write_blob(self, + FU_IGSC_FWU_HECI_PAYLOAD_TYPE_GFX_FW, + fw_info, + stream_payload, + progress, + error)) return FALSE; - return fu_udev_device_write_sysfs(FU_UDEV_DEVICE(parent), - "power/control", - val, - FU_IGSC_DEVICE_POWER_WRITE_TIMEOUT, - error); -} -static gboolean -fu_igsc_device_prepare(FuDevice *device, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - FuIgscDevice *self = FU_IGSC_DEVICE(device); - return fu_igsc_device_set_pci_power_policy(self, "on", error); -} - -static gboolean -fu_igsc_device_cleanup(FuDevice *device, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - FuIgscDevice *self = FU_IGSC_DEVICE(device); - return fu_igsc_device_set_pci_power_policy(self, "auto", error); + /* restart */ + return TRUE; } static void -fu_igsc_device_set_progress(FuDevice *self, FuProgress *progress) +fu_igsc_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 96, "write"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); } static void @@ -816,16 +849,18 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_set_vendor(FU_DEVICE(self), "Intel"); - fu_device_set_name(FU_DEVICE(self), "Graphics Card"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_INSTALL_PARENT_FIRST); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_SAVE_INTO_BACKUP_REMOTE); fu_device_set_summary(FU_DEVICE(self), "Discrete Graphics Card"); - fu_device_add_icon(FU_DEVICE(self), "video-display"); fu_device_add_protocol(FU_DEVICE(self), "com.intel.gsc"); - fu_device_add_icon(FU_DEVICE(self), "gpu"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_GPU); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); fu_device_set_remove_delay(FU_DEVICE(self), 60000); fu_device_register_private_flag(FU_DEVICE(self), FU_IGSC_DEVICE_FLAG_HAS_AUX); fu_device_register_private_flag(FU_DEVICE(self), FU_IGSC_DEVICE_FLAG_HAS_OPROM); + fu_device_register_private_flag(FU_DEVICE(self), FU_IGSC_DEVICE_FLAG_IS_WEDGED); + fu_device_register_private_flag(FU_DEVICE(self), FU_IGSC_DEVICE_FLAG_HAS_SKU); } static void @@ -846,11 +881,8 @@ object_class->finalize = fu_igsc_device_finalize; device_class->set_progress = fu_igsc_device_set_progress; device_class->to_string = fu_igsc_device_to_string; - device_class->open = fu_igsc_device_open; device_class->setup = fu_igsc_device_setup; device_class->probe = fu_igsc_device_probe; - device_class->prepare = fu_igsc_device_prepare; - device_class->cleanup = fu_igsc_device_cleanup; device_class->prepare_firmware = fu_igsc_device_prepare_firmware; device_class->write_firmware = fu_igsc_device_write_firmware; } diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-device.h fwupd-2.0.20/plugins/intel-gsc/fu-igsc-device.h --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,19 +9,20 @@ #include -#include "fu-igsc-heci.h" #include "fu-igsc-struct.h" #define FU_TYPE_IGSC_DEVICE (fu_igsc_device_get_type()) -G_DECLARE_FINAL_TYPE(FuIgscDevice, fu_igsc_device, FU, IGSC_DEVICE, FuMeiDevice) +G_DECLARE_FINAL_TYPE(FuIgscDevice, fu_igsc_device, FU, IGSC_DEVICE, FuHeciDevice) + +#define FU_IGSC_DEVICE_FLAG_IS_WEDGED "is-wedged" gboolean -fu_igsc_device_get_oprom_code_devid_enforcement(FuIgscDevice *self); +fu_igsc_device_get_oprom_code_devid_enforcement(FuIgscDevice *self) G_GNUC_NON_NULL(1); guint16 -fu_igsc_device_get_ssvid(FuIgscDevice *self); +fu_igsc_device_get_ssvid(FuIgscDevice *self) G_GNUC_NON_NULL(1); guint16 -fu_igsc_device_get_ssdid(FuIgscDevice *self); +fu_igsc_device_get_ssdid(FuIgscDevice *self) G_GNUC_NON_NULL(1); gboolean fu_igsc_device_write_blob(FuIgscDevice *self, @@ -29,17 +30,19 @@ GBytes *fw_info, GInputStream *stream_payload, FuProgress *progress, - GError **error); + GError **error) G_GNUC_NON_NULL(1, 3); +gboolean +fu_igsc_device_restart(FuIgscDevice *self, GError **error) G_GNUC_NON_NULL(1); gboolean fu_igsc_device_get_aux_version(FuIgscDevice *self, guint32 *oem_version, guint16 *major_version, guint16 *major_vcn, - GError **error); + GError **error) G_GNUC_NON_NULL(1); gboolean fu_igsc_device_get_version_raw(FuIgscDevice *self, FuIgscFwuHeciPartitionVersion partition, guint8 *buf, gsize bufsz, - GError **error); + GError **error) G_GNUC_NON_NULL(1, 3); diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-heci.h fwupd-2.0.20/plugins/intel-gsc/fu-igsc-heci.h --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-heci.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-heci.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,130 +0,0 @@ -/* - * Copyright 2022 Intel - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define HECI1_CSE_FS_FWUPDATE_STATE_IDLE_BIT (1 << 11) -#define HECI1_CSE_FS_INITSTATE_COMPLETED_BIT (1 << 9) -#define HECI1_CSE_GS1_PHASE_FWUPDATE 7 -#define HECI1_CSE_FS_FWUPD_PHASE_SHIFT 28 -#define HECI1_CSE_FS_FWUPD_PHASE_MASK 0xF -#define HECI1_CSE_FS_FWUPD_PERCENT_SHIFT 16 -#define HECI1_CSE_FS_FWUPD_PERCENT_MASK 0xFF -#define HECI1_CSE_FS_MODE_MASK 0x3 -#define HECI1_CSE_FS_CP_MODE 0x3 - -struct gsc_fwu_heci_header { - guint8 command_id; - guint8 is_response : 1; - guint8 reserved : 7; - guint8 reserved2[2]; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_response { - struct gsc_fwu_heci_header header; - guint32 status; - guint32 reserved; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_version_req { - struct gsc_fwu_heci_header header; - guint32 partition; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_version_resp { - struct gsc_fwu_heci_response response; - guint32 partition; - guint32 version_length; - guint8 version[]; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fw_data_heci_version_req { - struct gsc_fwu_heci_header header; - guint32 reserved[2]; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fw_data_heci_version_resp { - struct gsc_fwu_heci_response response; - guint32 format_version; - guint32 oem_version_nvm; - guint32 oem_version_fitb; - guint16 major_version; - guint16 major_vcn; - guint32 oem_version_fitb_valid; - guint32 flags; - guint32 reserved[7]; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_get_config_message_req { - struct gsc_fwu_heci_header header; - guint32 reserved[2]; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_get_config_message_resp { - struct gsc_fwu_heci_response response; - guint32 format_version; - guint32 hw_step; - guint32 hw_sku; - guint32 oprom_code_devid_enforcement : 1; - guint32 flags : 31; - guint32 reserved[7]; - guint32 debug_config; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_get_subsystem_ids_message_req { - struct gsc_fwu_heci_header header; - guint32 reserved[2]; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_get_subsystem_ids_message_resp { - struct gsc_fwu_heci_response response; - guint16 ssvid; - guint16 ssdid; - guint32 reserved[2]; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_start_flags { - guint32 force_update : 1; - guint32 reserved : 31; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_start_req { - struct gsc_fwu_heci_header header; - guint32 update_img_length; - guint32 payload_type; - struct gsc_fwu_heci_start_flags flags; - guint32 reserved[8]; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_start_resp { - struct gsc_fwu_heci_response response; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_data_req { - struct gsc_fwu_heci_header header; - guint32 data_length; - guint32 reserved; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_data_resp { - struct gsc_fwu_heci_response response; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_end_req { - struct gsc_fwu_heci_header header; - guint32 reserved; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_end_resp { - struct gsc_fwu_heci_response response; -} __attribute__((packed)); /* nocheck:blocked */ - -struct gsc_fwu_heci_no_update_req { - struct gsc_fwu_heci_header header; - guint32 reserved; -} __attribute__((packed)); /* nocheck:blocked */ diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-oprom-device.c fwupd-2.0.20/plugins/intel-gsc/fu-igsc-oprom-device.c --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-oprom-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-oprom-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -33,34 +33,46 @@ fu_igsc_oprom_device_probe(FuDevice *device, GError **error) { FuIgscOpromDevice *self = FU_IGSC_OPROM_DEVICE(device); - FuDevice *parent = fu_device_get_parent(device); - g_autofree gchar *name = NULL; + FuDevice *parent = fu_device_get_parent(device, error); + + /* from the self tests */ + if (parent == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no parent FuIgscDevice"); + return FALSE; + } /* set strings now we know the type */ if (self->payload_type == FU_IGSC_FWU_HECI_PAYLOAD_TYPE_OPROM_CODE) { self->partition_version = FU_IGSC_FWU_HECI_PARTITION_VERSION_OPROM_CODE; - fu_device_add_instance_str(device, "PART", "OPROMCODE"); + fu_device_add_instance_str( + device, + "PART", + fu_device_has_private_flag(parent, FU_IGSC_DEVICE_FLAG_IS_WEDGED) + ? "OPROMCODE_RECOVERY" + : "OPROMCODE"); fu_device_set_logical_id(FU_DEVICE(self), "oprom-code"); - if (parent != NULL) { - name = g_strdup_printf("%s OptionROM Code", fu_device_get_name(parent)); - fu_device_set_name(FU_DEVICE(self), name); - } + fu_device_set_name(FU_DEVICE(self), "OptionROM Code"); } else if (self->payload_type == FU_IGSC_FWU_HECI_PAYLOAD_TYPE_OPROM_DATA) { self->partition_version = FU_IGSC_FWU_HECI_PARTITION_VERSION_OPROM_DATA; - fu_device_add_instance_str(device, "PART", "OPROMDATA"); + fu_device_add_instance_str( + device, + "PART", + fu_device_has_private_flag(parent, FU_IGSC_DEVICE_FLAG_IS_WEDGED) + ? "OPROMDATA_RECOVERY" + : "OPROMDATA"); fu_device_set_logical_id(FU_DEVICE(self), "oprom-data"); - if (parent != NULL) { - name = g_strdup_printf("%s OptionROM Data", fu_device_get_name(parent)); - fu_device_set_name(FU_DEVICE(self), name); - } + fu_device_set_name(FU_DEVICE(self), "OptionROM Data"); } /* add extra instance IDs */ - if (!fu_device_build_instance_id(device, error, "MEI", "VEN", "DEV", "PART", NULL)) + if (!fu_device_build_instance_id(device, error, "PCI", "VEN", "DEV", "PART", NULL)) return FALSE; return fu_device_build_instance_id(device, error, - "MEI", + "PCI", "VEN", "DEV", "SUBSYS", @@ -72,10 +84,10 @@ fu_igsc_oprom_device_setup(FuDevice *device, GError **error) { FuIgscOpromDevice *self = FU_IGSC_OPROM_DEVICE(device); - FuIgscDevice *igsc_parent = FU_IGSC_DEVICE(fu_device_get_parent(device)); - guint8 buf[8] = {0x0}; + FuIgscDevice *igsc_parent = FU_IGSC_DEVICE(fu_device_get_parent(device, error)); + guint8 buf[FU_STRUCT_IGSC_OPROM_VERSION_SIZE] = {0x0}; g_autofree gchar *version = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructIgscOpromVersion) st = NULL; /* get version */ if (!fu_igsc_device_get_version_raw(igsc_parent, @@ -83,18 +95,22 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to get oprom version: "); + g_prefix_error_literal(error, "failed to get oprom version: "); return FALSE; } st = fu_struct_igsc_oprom_version_parse(buf, sizeof(buf), 0x0, error); if (st == NULL) return FALSE; self->major_version = fu_struct_igsc_oprom_version_get_major(st); - version = g_strdup_printf("%u.%u.%u.%u", - self->major_version, - fu_struct_igsc_oprom_version_get_minor(st), - fu_struct_igsc_oprom_version_get_hotfix(st), - fu_struct_igsc_oprom_version_get_build(st)); + if (fu_device_has_private_flag(FU_DEVICE(igsc_parent), FU_IGSC_DEVICE_FLAG_IS_WEDGED)) { + version = g_strdup("0.0"); + } else { + version = g_strdup_printf("%u.%u.%u.%u", + self->major_version, + fu_struct_igsc_oprom_version_get_minor(st), + fu_struct_igsc_oprom_version_get_hotfix(st), + fu_struct_igsc_oprom_version_get_build(st)); + } fu_device_set_version(device, version); /* success */ @@ -105,30 +121,45 @@ fu_igsc_oprom_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIgscOpromDevice *self = FU_IGSC_OPROM_DEVICE(device); - FuIgscDevice *igsc_parent = FU_IGSC_DEVICE(fu_device_get_parent(device)); - guint16 vid = fu_device_get_vid(FU_DEVICE(igsc_parent)); - guint16 pid = fu_device_get_pid(FU_DEVICE(igsc_parent)); - guint16 subsys_vendor_id = fu_igsc_device_get_ssvid(igsc_parent); - guint16 subsys_device_id = fu_igsc_device_get_ssvid(igsc_parent); - g_autoptr(FuFirmware) firmware = NULL; - g_autoptr(FuFirmware) fw_linear = fu_linear_firmware_new(FU_TYPE_IGSC_OPROM_FIRMWARE); + FuIgscDevice *igsc_parent = FU_IGSC_DEVICE(fu_device_get_parent(device, error)); + g_autoptr(GInputStream) stream_igsc = NULL; + g_autoptr(FuFirmware) firmware_igsc = g_object_new(FU_TYPE_IGSC_OPROM_FIRMWARE, NULL); + g_autoptr(FuFirmware) firmware_oprom = NULL; + g_autoptr(FuFirmware) fw_linear = fu_linear_firmware_new(FU_TYPE_OPROM_FIRMWARE); + + /* sanity check */ + if (igsc_parent == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no IGSC parent"); + return NULL; + } /* parse container */ if (!fu_firmware_parse_stream(fw_linear, stream, 0x0, flags, error)) return NULL; /* get correct image */ - firmware = fu_firmware_get_image_by_idx(fw_linear, self->payload_type, error); - if (firmware == NULL) + firmware_oprom = fu_firmware_get_image_by_idx( + fw_linear, + self->payload_type == FU_IGSC_FWU_HECI_PAYLOAD_TYPE_OPROM_CODE ? FU_IGSC_OPROM_IDX_CODE + : FU_IGSC_OPROM_IDX_DATA, + error); + if (firmware_oprom == NULL) + return NULL; + + /* reparse with more specific requirements */ + stream_igsc = fu_firmware_get_stream(firmware_oprom, error); + if (stream_igsc == NULL) + return NULL; + if (!fu_firmware_parse_stream(firmware_igsc, stream_igsc, 0x0, flags, error)) return NULL; /* major numbers must be the same, unless the device's major is zero, * because some platforms may come originally with 0 major number */ - if (fu_igsc_oprom_firmware_get_major_version(FU_IGSC_OPROM_FIRMWARE(firmware)) != + if (fu_igsc_oprom_firmware_get_major_version(FU_IGSC_OPROM_FIRMWARE(firmware_igsc)) != self->major_version && self->major_version != 0) { g_set_error( @@ -136,7 +167,7 @@ FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "image major version is not compatible, got 0x%x, expected 0x%x", - fu_igsc_oprom_firmware_get_major_version(FU_IGSC_OPROM_FIRMWARE(firmware)), + fu_igsc_oprom_firmware_get_major_version(FU_IGSC_OPROM_FIRMWARE(firmware_igsc)), self->major_version); return NULL; } @@ -149,16 +180,17 @@ */ if (self->payload_type == FU_IGSC_FWU_HECI_PAYLOAD_TYPE_OPROM_CODE) { if (fu_igsc_device_get_oprom_code_devid_enforcement(igsc_parent)) { - if (!fu_igsc_oprom_firmware_match_device(FU_IGSC_OPROM_FIRMWARE(firmware), - vid, - pid, - subsys_vendor_id, - subsys_device_id, - error)) + if (!fu_igsc_oprom_firmware_match_device( + FU_IGSC_OPROM_FIRMWARE(firmware_igsc), + fu_device_get_vid(FU_DEVICE(igsc_parent)), + fu_device_get_pid(FU_DEVICE(igsc_parent)), + fu_igsc_device_get_ssvid(igsc_parent), + fu_igsc_device_get_ssdid(igsc_parent), + error)) return NULL; } else { if (fu_igsc_oprom_firmware_has_allowlist( - FU_IGSC_OPROM_FIRMWARE(firmware))) { + FU_IGSC_OPROM_FIRMWARE(firmware_igsc))) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -176,16 +208,18 @@ * The update is accepted only if the card's SSVID and SSDID are zero. */ if (self->payload_type == FU_IGSC_FWU_HECI_PAYLOAD_TYPE_OPROM_DATA) { - if (fu_igsc_oprom_firmware_has_allowlist(FU_IGSC_OPROM_FIRMWARE(firmware))) { - if (!fu_igsc_oprom_firmware_match_device(FU_IGSC_OPROM_FIRMWARE(firmware), - vid, - pid, - subsys_vendor_id, - subsys_device_id, - error)) + if (fu_igsc_oprom_firmware_has_allowlist(FU_IGSC_OPROM_FIRMWARE(firmware_igsc))) { + if (!fu_igsc_oprom_firmware_match_device( + FU_IGSC_OPROM_FIRMWARE(firmware_igsc), + fu_device_get_vid(FU_DEVICE(igsc_parent)), + fu_device_get_pid(FU_DEVICE(igsc_parent)), + fu_igsc_device_get_ssvid(igsc_parent), + fu_igsc_device_get_ssdid(igsc_parent), + error)) return NULL; } else { - if (subsys_vendor_id != 0x0 || subsys_device_id != 0x0) { + if (fu_igsc_device_get_ssvid(igsc_parent) != 0x0 || + fu_igsc_device_get_ssdid(igsc_parent) != 0x0) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -197,7 +231,7 @@ } /* success */ - return g_steal_pointer(&firmware); + return g_steal_pointer(&fw_linear); } static gboolean @@ -208,51 +242,59 @@ GError **error) { FuIgscOpromDevice *self = FU_IGSC_OPROM_DEVICE(device); - FuIgscDevice *igsc_parent = FU_IGSC_DEVICE(fu_device_get_parent(device)); - g_autoptr(GInputStream) stream_payload = NULL; - - /* get image */ - stream_payload = fu_firmware_get_stream(firmware, error); - if (stream_payload == NULL) + FuIgscDevice *parent; + g_autoptr(FuStructIgscFwuHeciImageMetadata) st_md = NULL; + g_autoptr(GBytes) fw_info = NULL; + g_autoptr(GInputStream) partial_stream = NULL; + g_autoptr(GInputStream) stream = NULL; + + /* get image, with no padding bytes */ + stream = fu_firmware_get_stream(firmware, error); + if (stream == NULL) + return FALSE; + partial_stream = + fu_partial_input_stream_new(stream, 0x0, fu_firmware_get_size(firmware), error); + if (partial_stream == NULL) return FALSE; + /* weirdly, this is just empty data */ + st_md = fu_struct_igsc_fwu_heci_image_metadata_new(); + fu_struct_igsc_fwu_heci_image_metadata_set_version_format(st_md, 0x0); + fw_info = fu_struct_igsc_fwu_heci_image_metadata_to_bytes(st_md); + /* OPROM image doesn't require metadata */ - return fu_igsc_device_write_blob(igsc_parent, + parent = FU_IGSC_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) + return FALSE; + return fu_igsc_device_write_blob(parent, self->payload_type, - NULL, - stream_payload, + fw_info, + partial_stream, progress, error); } -static gboolean -fu_igsc_oprom_device_prepare(FuDevice *device, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - /* set PCI power policy */ - return fu_device_prepare(fu_device_get_parent(device), progress, flags, error); -} - -static gboolean -fu_igsc_oprom_device_cleanup(FuDevice *device, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) +static void +fu_igsc_oprom_device_set_progress(FuDevice *device, FuProgress *progress) { - /* set PCI power policy */ - return fu_device_cleanup(fu_device_get_parent(device), progress, flags, error); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); } static void fu_igsc_oprom_device_init(FuIgscOpromDevice *self) { fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_IGSC_DEVICE); fu_device_add_protocol(FU_DEVICE(self), "com.intel.gsc"); } @@ -260,19 +302,18 @@ fu_igsc_oprom_device_class_init(FuIgscOpromDeviceClass *klass) { FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->set_progress = fu_igsc_oprom_device_set_progress; device_class->to_string = fu_igsc_oprom_device_to_string; device_class->probe = fu_igsc_oprom_device_probe; device_class->setup = fu_igsc_oprom_device_setup; device_class->prepare_firmware = fu_igsc_oprom_device_prepare_firmware; device_class->write_firmware = fu_igsc_oprom_device_write_firmware; - device_class->prepare = fu_igsc_oprom_device_prepare; - device_class->cleanup = fu_igsc_oprom_device_cleanup; } FuIgscOpromDevice * -fu_igsc_oprom_device_new(FuContext *ctx, FuIgscFwuHeciPayloadType payload_type) +fu_igsc_oprom_device_new(FuDevice *proxy, FuIgscFwuHeciPayloadType payload_type) { - FuIgscOpromDevice *self = g_object_new(FU_TYPE_IGSC_OPROM_DEVICE, "context", ctx, NULL); + FuIgscOpromDevice *self = g_object_new(FU_TYPE_IGSC_OPROM_DEVICE, "proxy", proxy, NULL); self->payload_type = payload_type; return FU_IGSC_OPROM_DEVICE(self); } diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-oprom-device.h fwupd-2.0.20/plugins/intel-gsc/fu-igsc-oprom-device.h --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-oprom-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-oprom-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,10 +9,10 @@ #include -#include "fu-igsc-heci.h" +#include "fu-igsc-struct.h" #define FU_TYPE_IGSC_OPROM_DEVICE (fu_igsc_oprom_device_get_type()) G_DECLARE_FINAL_TYPE(FuIgscOpromDevice, fu_igsc_oprom_device, FU, IGSC_OPROM_DEVICE, FuDevice) FuIgscOpromDevice * -fu_igsc_oprom_device_new(FuContext *ctx, FuIgscFwuHeciPayloadType payload_type); +fu_igsc_oprom_device_new(FuDevice *proxy, FuIgscFwuHeciPayloadType payload_type); diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-oprom-firmware.c fwupd-2.0.20/plugins/intel-gsc/fu-igsc-oprom-firmware.c --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-oprom-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-oprom-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -7,22 +7,15 @@ #include "config.h" +#include "fu-igsc-common.h" #include "fu-igsc-oprom-firmware.h" -#include "fu-igsc-struct.h" struct _FuIgscOpromFirmware { FuOpromFirmware parent_instance; guint16 major_version; - GPtrArray *device_infos; /* of FuIgscFwdataDeviceInfo */ + GPtrArray *device_infos; /* of FuIgscFwdataDeviceInfo4 */ }; -typedef struct { - guint16 vendor_id; /* may be 0x0 */ - guint16 device_id; /* may be 0x0 */ - guint16 subsys_vendor_id; - guint16 subsys_device_id; -} FuIgscFwdataDeviceInfo; - G_DEFINE_TYPE(FuIgscOpromFirmware, fu_igsc_oprom_firmware, FU_TYPE_OPROM_FIRMWARE) static void @@ -30,7 +23,7 @@ { FuIgscOpromFirmware *self = FU_IGSC_OPROM_FIRMWARE(firmware); fu_xmlb_builder_insert_kx(bn, "major_version", self->major_version); - fu_xmlb_builder_insert_kx(bn, "device_infos", self->device_infos->len); + fu_igsc_fwdata_device_info_export(self->device_infos, bn); } guint16 @@ -58,14 +51,16 @@ g_return_val_if_fail(FU_IS_IGSC_OPROM_FIRMWARE(self), FALSE); for (guint i = 0; i < self->device_infos->len; i++) { - FuIgscFwdataDeviceInfo *info = g_ptr_array_index(self->device_infos, i); - if (info->vendor_id == 0x0 && info->device_id == 0x0 && - info->subsys_vendor_id == subsys_vendor_id && - info->subsys_device_id == subsys_device_id) + FuIgscFwdataDeviceInfo4 *info = g_ptr_array_index(self->device_infos, i); + if (fu_igsc_fwdata_device_info4_get_vendor_id(info) == 0x0 && + fu_igsc_fwdata_device_info4_get_device_id(info) == 0x0 && + fu_igsc_fwdata_device_info4_get_subsys_vendor_id(info) == subsys_vendor_id && + fu_igsc_fwdata_device_info4_get_subsys_device_id(info) == subsys_device_id) return TRUE; - if (info->vendor_id == vendor_id && info->device_id == device_id && - info->subsys_vendor_id == subsys_vendor_id && - info->subsys_device_id == subsys_device_id) + if (fu_igsc_fwdata_device_info4_get_vendor_id(info) == vendor_id && + fu_igsc_fwdata_device_info4_get_device_id(info) == device_id && + fu_igsc_fwdata_device_info4_get_subsys_vendor_id(info) == subsys_vendor_id && + fu_igsc_fwdata_device_info4_get_subsys_device_id(info) == subsys_device_id) return TRUE; } @@ -81,76 +76,16 @@ return FALSE; } -/* extension types */ -#define MFT_EXT_TYPE_DEVICE_TYPE 7 -#define MDF_EXT_TYPE_MODULE_ATTR 10 -#define MFT_EXT_TYPE_SIGNED_PACKAGE_INFO 15 -#define MFT_EXT_TYPE_IFWI_PART_MAN 22 -#define MFT_EXT_TYPE_DEVICE_ID_ARRAY 55 - -static gboolean -fu_igsc_oprom_firmware_parse_extension(FuIgscOpromFirmware *self, FuFirmware *fw, GError **error) -{ - gsize streamsz = 0; - g_autoptr(GInputStream) stream = NULL; - - /* get data */ - stream = fu_firmware_get_stream(fw, error); - if (stream == NULL) - return FALSE; - if (!fu_input_stream_size(stream, &streamsz, error)) - return FALSE; - if (fu_firmware_get_idx(fw) == MFT_EXT_TYPE_DEVICE_TYPE) { - for (gsize offset = 0; offset < streamsz; - offset += FU_STRUCT_IGSC_OPROM_SUBSYSTEM_DEVICE_ID_SIZE) { - g_autofree FuIgscFwdataDeviceInfo *info = g_new0(FuIgscFwdataDeviceInfo, 1); - g_autoptr(GByteArray) st = NULL; - st = fu_struct_igsc_oprom_subsystem_device_id_parse_stream(stream, - offset, - error); - if (st == NULL) - return FALSE; - info->subsys_vendor_id = - fu_struct_igsc_oprom_subsystem_device_id_get_subsys_vendor_id(st); - info->subsys_device_id = - fu_struct_igsc_oprom_subsystem_device_id_get_subsys_device_id(st); - g_ptr_array_add(self->device_infos, g_steal_pointer(&info)); - } - } else if (fu_firmware_get_idx(fw) == MFT_EXT_TYPE_DEVICE_ID_ARRAY) { - for (gsize offset = 0; offset < streamsz; - offset += FU_STRUCT_IGSC_OPROM_SUBSYSTEM_DEVICE4_ID_SIZE) { - g_autofree FuIgscFwdataDeviceInfo *info = g_new0(FuIgscFwdataDeviceInfo, 1); - g_autoptr(GByteArray) st = NULL; - st = fu_struct_igsc_oprom_subsystem_device4_id_parse_stream(stream, - offset, - error); - if (st == NULL) - return FALSE; - info->vendor_id = - fu_struct_igsc_oprom_subsystem_device4_id_get_vendor_id(st); - info->device_id = - fu_struct_igsc_oprom_subsystem_device4_id_get_device_id(st); - info->subsys_vendor_id = - fu_struct_igsc_oprom_subsystem_device4_id_get_subsys_vendor_id(st); - info->subsys_device_id = - fu_struct_igsc_oprom_subsystem_device4_id_get_subsys_device_id(st); - g_ptr_array_add(self->device_infos, g_steal_pointer(&info)); - } - } - - /* success */ - return TRUE; -} - static gboolean fu_igsc_oprom_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIgscOpromFirmware *self = FU_IGSC_OPROM_FIRMWARE(firmware); g_autoptr(FuFirmware) fw_cpd = NULL; - g_autoptr(GPtrArray) cpd_imgs = NULL; + g_autoptr(FuFirmware) man_img = NULL; + g_autoptr(GPtrArray) man_ext_imgs = NULL; /* FuOpromFirmware->parse */ if (!FU_FIRMWARE_CLASS(fu_igsc_oprom_firmware_parent_class) @@ -159,33 +94,33 @@ /* check sanity */ if (fu_oprom_firmware_get_subsystem(FU_OPROM_FIRMWARE(firmware)) != - FU_OPROM_FIRMWARE_SUBSYSTEM_EFI_BOOT_SRV_DRV) { + FU_OPROM_SUBSYSTEM_EFI_BOOT_SRV_DRV) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid subsystem, got 0x%x, expected 0x%x", fu_oprom_firmware_get_subsystem(FU_OPROM_FIRMWARE(firmware)), - (guint)FU_OPROM_FIRMWARE_SUBSYSTEM_EFI_BOOT_SRV_DRV); + (guint)FU_OPROM_SUBSYSTEM_EFI_BOOT_SRV_DRV); return FALSE; } if (fu_oprom_firmware_get_machine_type(FU_OPROM_FIRMWARE(firmware)) != - FU_OPROM_FIRMWARE_MACHINE_TYPE_X64) { + FU_OPROM_MACHINE_TYPE_X64) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid machine type, got 0x%x, expected 0x%x", fu_oprom_firmware_get_machine_type(FU_OPROM_FIRMWARE(firmware)), - (guint)FU_OPROM_FIRMWARE_MACHINE_TYPE_X64); + (guint)FU_OPROM_MACHINE_TYPE_X64); return FALSE; } if (fu_oprom_firmware_get_compression_type(FU_OPROM_FIRMWARE(firmware)) != - FU_OPROM_FIRMWARE_COMPRESSION_TYPE_NONE) { + FU_OPROM_COMPRESSION_TYPE_NONE) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid compression type, got 0x%x, expected 0x%x (uncompressed)", fu_oprom_firmware_get_compression_type(FU_OPROM_FIRMWARE(firmware)), - (guint)FU_OPROM_FIRMWARE_COMPRESSION_TYPE_NONE); + (guint)FU_OPROM_COMPRESSION_TYPE_NONE); return FALSE; } @@ -200,12 +135,16 @@ "CPD was not FuIfwiCpdFirmware"); return FALSE; } + self->major_version = fu_firmware_get_version_raw(fw_cpd) >> 48; /* parse all the manifest extensions */ - cpd_imgs = fu_firmware_get_images(fw_cpd); - for (guint i = 0; i < cpd_imgs->len; i++) { - FuFirmware *img = g_ptr_array_index(cpd_imgs, i); - if (!fu_igsc_oprom_firmware_parse_extension(self, img, error)) + man_img = fu_firmware_get_image_by_id(fw_cpd, "OROM.man", error); + if (man_img == NULL) + return FALSE; + man_ext_imgs = fu_firmware_get_images(man_img); + for (guint i = 0; i < man_ext_imgs->len; i++) { + FuFirmware *img_man_ext = g_ptr_array_index(man_ext_imgs, i); + if (!fu_igsc_fwdata_device_info_parse(self->device_infos, img_man_ext, error)) return FALSE; } @@ -216,7 +155,8 @@ static void fu_igsc_oprom_firmware_init(FuIgscOpromFirmware *self) { - self->device_infos = g_ptr_array_new_with_free_func(g_free); + self->device_infos = + g_ptr_array_new_with_free_func((GDestroyNotify)fu_igsc_fwdata_device_info4_unref); } static void diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc-plugin.c fwupd-2.0.20/plugins/intel-gsc/fu-igsc-plugin.c --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,6 +14,8 @@ #include "fu-igsc-oprom-firmware.h" #include "fu-igsc-plugin.h" +#define FU_IGSC_PLUGIN_POWER_WRITE_TIMEOUT 1500 /* ms */ + struct _FuIgscPlugin { FuPlugin parent_instance; }; @@ -25,6 +27,60 @@ { } +static gboolean +fu_igsc_plugin_set_pci_power_policy(FuIgscDevice *self, const gchar *val, GError **error) +{ + g_autoptr(FuDevice) parent = NULL; + + /* get PCI parent */ + parent = fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), "pci", error); + if (parent == NULL) + return FALSE; + return fu_udev_device_write_sysfs(FU_UDEV_DEVICE(parent), + "power/control", + val, + FU_IGSC_PLUGIN_POWER_WRITE_TIMEOUT, + error); +} + +static gboolean +fu_igsc_plugin_composite_prepare(FuPlugin *plugin, GPtrArray *devices, GError **error) +{ + FuIgscDevice *device_igsc = NULL; + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + if (FU_IS_IGSC_DEVICE(device)) { + device_igsc = FU_IGSC_DEVICE(device); + break; + } + } + if (device_igsc != NULL) { + g_autoptr(GError) error_local = NULL; + if (!fu_igsc_plugin_set_pci_power_policy(device_igsc, "on", &error_local)) + g_debug("failed to set power policy: %s", error_local->message); + } + return TRUE; +} + +static gboolean +fu_igsc_plugin_composite_cleanup(FuPlugin *plugin, GPtrArray *devices, GError **error) +{ + FuIgscDevice *device_igsc = NULL; + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + if (FU_IS_IGSC_DEVICE(device)) { + device_igsc = FU_IGSC_DEVICE(device); + break; + } + } + if (device_igsc != NULL) { + g_autoptr(GError) error_local = NULL; + if (!fu_igsc_plugin_set_pci_power_policy(device_igsc, "off", &error_local)) + g_debug("failed to set power policy: %s", error_local->message); + } + return TRUE; +} + static void fu_igsc_plugin_constructed(GObject *obj) { @@ -43,4 +99,6 @@ { FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); plugin_class->constructed = fu_igsc_plugin_constructed; + plugin_class->composite_prepare = fu_igsc_plugin_composite_prepare; + plugin_class->composite_cleanup = fu_igsc_plugin_composite_cleanup; } diff -Nru fwupd-2.0.8/plugins/intel-gsc/fu-igsc.rs fwupd-2.0.20/plugins/intel-gsc/fu-igsc.rs --- fwupd-2.0.8/plugins/intel-gsc/fu-igsc.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/fu-igsc.rs 2026-02-26 11:36:18.000000000 +0000 @@ -20,14 +20,14 @@ #[derive(ParseStream)] #[repr(C, packed)] -struct FuStructIgscOpromSubsystemDeviceId { +struct FuIgscFwdataDeviceInfo2 { subsys_vendor_id: u16le, subsys_device_id: u16le, } -#[derive(ParseStream)] +#[derive(ParseStream, New)] #[repr(C, packed)] -struct FuStructIgscOpromSubsystemDevice4Id { +struct FuIgscFwdataDeviceInfo4 { vendor_id: u16le, device_id: u16le, subsys_vendor_id: u16le, @@ -67,7 +67,7 @@ vcn: u32le, } -#[derive(Getters, Default)] +#[derive(Getters, Default, New, ToBytes)] #[repr(C, packed)] struct FuStructIgscFwuHeciImageMetadata { version_format: u32le = 0x1, @@ -80,7 +80,7 @@ project: [char; 4], version_hotfix: u16le, // version of the overall IFWI image, i.e. the combination of IPs version_build: u16le, - // struct FuStructIgscFwuFwImageData + image_data: FuStructIgscFwuFwImageData, // struct FuStructIgscFwuIupData } @@ -91,6 +91,7 @@ OpromCode, } +#[repr(u32le)] enum FuIgscFwuHeciPayloadType { Invalid, GfxFw, @@ -99,6 +100,13 @@ Fwdata = 5, } +enum FuIgscOpromIdx { + Unknown = 0x0, + Data = 0xF0, + Code = 0xF1, +} + +#[repr(u8)] enum FuIgscFwuHeciCommandId { Invalid, Start, // start firmware updated flow @@ -112,3 +120,239 @@ GetGfxDataUpdateInfo, // get signed firmware data info GetSubsystemIds, // get subsystem ids (VID/DID) } + + +#[repr(u8)] +enum FuIgscFwuHeciHdrFlags { + None, + IsResponse, +} + +#[repr(u32le)] +enum FuIgscFwuHeciStatus { + Success = 0x0, + SizeError = 0x5, + InvalidParams = 0x85, + InvalidCommand = 0x8D, + Failure = 0x9E, + UpdateOpromInvalidStructure = 0x1035, + UpdateOpromSectionNotExist = 0x1032, +} + +// GetIpVersion +#[derive(New, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciVersionReq { + command_id: FuIgscFwuHeciCommandId == GetIpVersion, + hdr_flags: FuIgscFwuHeciHdrFlags == None, + _hdr_reserved: [u8; 2], + partition: u32le, +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciVersionRes { + command_id: FuIgscFwuHeciCommandId == GetIpVersion, + hdr_flags: FuIgscFwuHeciHdrFlags == IsResponse, + _hdr_reserved: [u8; 2], + status: FuIgscFwuHeciStatus, + _status_reserved: u32le, + partition: u32le, + version_length: u32le, + // version +} + +// GetGfxDataUpdateInfo +#[derive(New, Default)] +#[repr(C, packed)] +struct FuIgscFwDataHeciVersionReq { + command_id: FuIgscFwuHeciCommandId == GetGfxDataUpdateInfo, + hdr_flags: FuIgscFwuHeciHdrFlags == None, + _hdr_reserved: [u8; 2], + _reserved: [u32; 2], +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuIgscFwDataHeciVersionRes { + command_id: FuIgscFwuHeciCommandId == GetGfxDataUpdateInfo, + hdr_flags: FuIgscFwuHeciHdrFlags == IsResponse, + _hdr_reserved: [u8; 2], + status: FuIgscFwuHeciStatus, + _status_reserved: u32le, + _format_version: u32le, + oem_version_nvm: u32le, + oem_version_fitb: u32le, + major_version: u16le, + major_vcn: u16le, + oem_version_fitb_valid: u32le, + _flags: u32le, + _reserved: [u32; 7], +} + +// GetSubsystemIds +#[derive(New, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciGetSubsystemIdsReq { + command_id: FuIgscFwuHeciCommandId == GetSubsystemIds, + hdr_flags: FuIgscFwuHeciHdrFlags == None, + _hdr_reserved: [u8; 2], + _reserved: [u32le; 2], +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciGetSubsystemIdsRes { + command_id: FuIgscFwuHeciCommandId == GetSubsystemIds, + hdr_flags: FuIgscFwuHeciHdrFlags == IsResponse, + _hdr_reserved: [u8; 2], + status: FuIgscFwuHeciStatus, + _status_reserved: u32le, + ssvid: u16le, + ssdid: u16le, + _reserved: [u32le; 2], +} + +// GetConfig +#[derive(New, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciGetConfigReq { + command_id: FuIgscFwuHeciCommandId == GetConfig, + hdr_flags: FuIgscFwuHeciHdrFlags == None, + _hdr_reserved: [u8; 2], + _reserved: [u32le; 2], +} + +#[repr(u32le)] +enum FuIgscFwuHeciGetConfigFlags { + None, + OpromCodeDevidEnforcement, +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciGetConfigRes { + command_id: FuIgscFwuHeciCommandId == GetConfig, + hdr_flags: FuIgscFwuHeciHdrFlags == IsResponse, + _hdr_reserved: [u8; 2], + status: FuIgscFwuHeciStatus, + _status_reserved: u32le, + _format_version: u32le, + _hw_step: u32le, + hw_sku: u32le, + flags: FuIgscFwuHeciGetConfigFlags, + _reserved: [u32; 7], + _debug_config: u32le, +} + +// End +#[derive(New, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciEndReq { + command_id: FuIgscFwuHeciCommandId == End, + hdr_flags: FuIgscFwuHeciHdrFlags == None, + _hdr_reserved: [u8; 2], + _reserved: u32le, +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciEndRes { + command_id: FuIgscFwuHeciCommandId == End, + hdr_flags: FuIgscFwuHeciHdrFlags == IsResponse, + _hdr_reserved: [u8; 2], + status: FuIgscFwuHeciStatus, + _status_reserved: u32le, +} + +// Data +#[derive(New, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciDataReq { + command_id: FuIgscFwuHeciCommandId == Data, + hdr_flags: FuIgscFwuHeciHdrFlags == None, + _hdr_reserved: [u8; 2], + data_length: u32le, + _reserved: u32le, +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciDataRes { + command_id: FuIgscFwuHeciCommandId == Data, + hdr_flags: FuIgscFwuHeciHdrFlags == IsResponse, + _hdr_reserved: [u8; 2], + status: FuIgscFwuHeciStatus, + _status_reserved: u32le, +} + +// Start +#[repr(u32le)] +enum FuIgscFwuHeciStartFlags { + None, + ForceUpdate, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciStartReq { + command_id: FuIgscFwuHeciCommandId == Start, + hdr_flags: FuIgscFwuHeciHdrFlags == None, + _hdr_reserved: [u8; 2], + update_img_length: u32le, + payload_type: FuIgscFwuHeciPayloadType, + flags: FuIgscFwuHeciStartFlags, + _reserved: [u32le; 8], +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciStartRes { + command_id: FuIgscFwuHeciCommandId == Start, + hdr_flags: FuIgscFwuHeciHdrFlags == IsResponse, + _hdr_reserved: [u8; 2], + status: FuIgscFwuHeciStatus, + _status_reserved: u32le, +} + +// NoUpdate +#[derive(New, Default)] +#[repr(C, packed)] +struct FuIgscFwuHeciNoUpdateReq { + command_id: FuIgscFwuHeciCommandId == NoUpdate, + hdr_flags: FuIgscFwuHeciHdrFlags == None, + _hdr_reserved: [u8; 2], + _reserved: u32le, +} + +// blobs +#[derive(ParseStream)] +#[repr(C, packed)] +struct FuStructIgscFwdataVersion { + oem_manuf_data_version: u32le, + major_version: u16le, + major_vcn: u16le, + key_index: u8, + _reserved1: [u8; 3], + data_arb_svn: u32le, + _reserved2: [u8; 16], +} + +#[repr(C, packed)] +struct FuStructIgscFwdataUpdateExt { + extension_type: u32le, + extension_length: u32le, + oem_manuf_data_version: u32le, + major_vcn: u16le, + flags: u16le, +} + +#[derive(ToString)] +enum FuIgscFwuExtType { + DeviceType = 7, + ModuleAttr = 10, + SignedPackageInfo = 15, + FwdataUpdate = 29, + IfwiPartMan = 22, + DeviceIdArray = 37, +} diff -Nru fwupd-2.0.8/plugins/intel-gsc/igsc.quirk fwupd-2.0.20/plugins/intel-gsc/igsc.quirk --- fwupd-2.0.8/plugins/intel-gsc/igsc.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/igsc.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -1,123 +1,136 @@ -[87d90ca5-3495-4559-8105-3fbfa37b8b79] +[PCI\DRIVER_xe] Plugin = igsc -[MEI\VEN_8086&DEV_4905] +[PCI\VEN_8086&DEV_4905] Name = DG1 -[MEI\VEN_8086&DEV_4906] +[PCI\VEN_8086&DEV_4906] Name = DG1 -[MEI\VEN_8086&DEV_4907] +[PCI\VEN_8086&DEV_4907] Name = DG1 -[MEI\VEN_8086&DEV_4908] +[PCI\VEN_8086&DEV_4908] Name = DG1 -[MEI\VEN_8086&DEV_0201] +[PCI\VEN_8086&DEV_0201] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_0202] +[PCI\VEN_8086&DEV_0202] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_0203] +[PCI\VEN_8086&DEV_0203] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_0204] +[PCI\VEN_8086&DEV_0204] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_0205] +[PCI\VEN_8086&DEV_0205] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_0206] +[PCI\VEN_8086&DEV_0206] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_0207] +[PCI\VEN_8086&DEV_0207] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_0208] +[PCI\VEN_8086&DEV_0208] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_0209] +[PCI\VEN_8086&DEV_0209] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_020A] +[PCI\VEN_8086&DEV_020A] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_020B] +[PCI\VEN_8086&DEV_020B] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_020C] +[PCI\VEN_8086&DEV_020C] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_020D] +[PCI\VEN_8086&DEV_020D] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_020E] +[PCI\VEN_8086&DEV_020E] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_020F] +[PCI\VEN_8086&DEV_020F] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_0210] +[PCI\VEN_8086&DEV_0210] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_FF25] +[PCI\VEN_8086&DEV_FF25] Name = Xe_HP SDV -[MEI\VEN_8086&DEV_4F80] +[PCI\VEN_8086&DEV_4F80] Name = DG2_SOC1 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_4F81] +[PCI\VEN_8086&DEV_4F81] Name = DG2_SOC1 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_4F82] +[PCI\VEN_8086&DEV_4F82] Name = DG2_SOC1 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_4F83] +[PCI\VEN_8086&DEV_4F83] Name = DG2_SOC1 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_4F84] +[PCI\VEN_8086&DEV_4F84] Name = DG2_SOC1 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_5690] +[PCI\VEN_8086&DEV_5690] Name = Arc A770M Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_5691] +[PCI\VEN_8086&DEV_5691] Name = Arc A730M Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_5692] +[PCI\VEN_8086&DEV_5692] Name = Arc A550M Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_56A0] +[PCI\VEN_8086&DEV_56A0] Name = Arc A770 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_56A1] +[PCI\VEN_8086&DEV_56A1] Name = Arc A750 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_56A2] +[PCI\VEN_8086&DEV_56A2] Name = Arc A580 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_56C0] +[PCI\VEN_8086&DEV_56C0] Name = DG2_SOC1 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_4F87] +[PCI\VEN_8086&DEV_4F87] Name = DG2_SOC2 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_4F88] +[PCI\VEN_8086&DEV_4F88] Name = DG2_SOC2 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_5693] +[PCI\VEN_8086&DEV_5693] Name = Arc A370M Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_5694] +[PCI\VEN_8086&DEV_5694] Name = Arc A350M Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_5695] +[PCI\VEN_8086&DEV_5695] Name = Iris(R) Xe MAX A200M Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_56A5] +[PCI\VEN_8086&DEV_56A5] Name = Arc A380 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_56A6] +[PCI\VEN_8086&DEV_56A6] Name = Arc A310 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_56A7] +[PCI\VEN_8086&DEV_56A7] Name = DG2_SOC2 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_56A8] +[PCI\VEN_8086&DEV_56A8] Name = DG2_SOC2 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_56A9] +[PCI\VEN_8086&DEV_56A9] Name = DG2_SOC2 Flags = has-aux,has-oprom -[MEI\VEN_8086&DEV_0BD0] +[PCI\VEN_8086&DEV_0BD0] Name = PVC -[MEI\VEN_8086&DEV_0BD5] +[PCI\VEN_8086&DEV_0BD5] Name = PVC -[MEI\VEN_8086&DEV_56C1] +[PCI\VEN_8086&DEV_56C1] Name = ATS-M3 -Flags = has-aux,has-oprom +Flags = has-sku,has-aux,has-oprom + +[PCI\VEN_8086&DEV_E20B] +Name = Arc B580 +Flags = has-sku,has-aux,has-oprom +[PCI\VEN_8086&DEV_E20C] +Name = Arc B570 +Flags = has-sku,has-aux,has-oprom +[PCI\VEN_8086&DEV_E211] +Name = Arc Pro B60 +Flags = has-sku,has-aux,has-oprom +[PCI\VEN_8086&DEV_E212] +Name = Arc Pro B50 +Flags = has-sku,has-aux,has-oprom diff -Nru fwupd-2.0.8/plugins/intel-gsc/meson.build fwupd-2.0.20/plugins/intel-gsc/meson.build --- fwupd-2.0.8/plugins/intel-gsc/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginIgsc"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -6,6 +7,7 @@ plugin_builtins += static_library('fu_plugin_igsc', rustgen.process('fu-igsc.rs'), sources: [ + 'fu-igsc-common.c', 'fu-igsc-plugin.c', 'fu-igsc-device.c', 'fu-igsc-code-firmware.c', @@ -19,4 +21,4 @@ c_args: cargs, dependencies: plugin_deps, ) -endif +device_tests += files('tests/intel-gsc.json') diff -Nru fwupd-2.0.8/plugins/intel-gsc/tests/intel-gsc.json fwupd-2.0.20/plugins/intel-gsc/tests/intel-gsc.json --- fwupd-2.0.8/plugins/intel-gsc/tests/intel-gsc.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-gsc/tests/intel-gsc.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,44 @@ +{ + "name": "Intel GSC", + "interactive": false, + "steps": [ + { + "url": "d14216c9300ca87f4a2f50ea72bf2c18a8358f8edfd9afb9af5a4bc4b5d9d320-intel-arc-b580-21.1161.cab", + "emulation-url": "1e4fd1a62bdb6000187ab6627c2cca7f8b381fd5021b972dd93e72eafaa79185-emulation.zip", + "components": [ + { + "name": "FwCode", + "protocol": "com.intel.gsc", + "version": "21.1161", + "guids": [ + "c3808bdf-c31b-5c03-905f-6f223848cae0" + ] + }, + { + "name": "FwData", + "protocol": "com.intel.gsc", + "version": "203.60", + "guids": [ + "d7b15d5c-07a3-5055-81a6-fd6643411382" + ] + }, + { + "name": "OptionROM Code", + "protocol": "com.intel.gsc", + "version": "23.1061.0.0", + "guids": [ + "3ef75a3f-7174-5426-a43a-91fd6d2c9b9f" + ] + }, + { + "name": "OptionROM Data", + "protocol": "com.intel.gsc", + "version": "23.1061.0.0", + "guids": [ + "d791f9fd-2546-50d2-a65a-fe3e15b36817" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/intel-mchi/README.md fwupd-2.0.20/plugins/intel-mchi/README.md --- fwupd-2.0.8/plugins/intel-mchi/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mchi/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,43 @@ +--- +title: Plugin: Intel MCHI +--- + +## Introduction + +This plugin is used to talk to the Intel ME device using the Management Controller Host Interface +"MCHI" interface, also sometimes called "MCA". + +It allows us to get the Platform Key as used for BootGuard. + +## GUID Generation + +These devices use the existing GUIDs provided by the ME host interfaces. + +## Metadata + +There have been several BootGuard key leaks that can be detected using the ME device. +The metadata needed to match the KM checksum is found in the metadata, typically obtained from +the LVFS project. +This sets the `leaked-km` private flag which then causes the HSI `org.fwupd.hsi.Mei.KeyManifest` +attribute to fail, and also adds a device inhibit which shows in the command line and GUI tools. + +The `org.linuxfoundation.bootguard.config` component is currently used to match against both the +MCA and MKHI ME devices. The latest cabinet archive can also be installed into the `vendor-firmware` +remote found in `/usr/share/fwupd/remotes.d/vendor/firmware/` which allows the detection to work +even when offline -- although using the LVFS source is recommended for most users. + +New *OEM Public Key Hash* values found from `MEInfo` or calculated manually should be added to the +checksums page on the LVFS. + +## Vendor ID Security + +The devices are not upgradable and thus require no vendor ID set. + +## External Interface Access + +This plugin requires `ioctl(IOCTL_MEI_CONNECT_CLIENT)` to `/dev/mei0`. + +## Version Considerations + +This plugin has been available since fwupd version `2.0.9`, although the functionality was +previously introduced in the `intel-me` plugin released with fwupd version `1.8.7`. diff -Nru fwupd-2.0.8/plugins/intel-mchi/fu-intel-mchi-device.c fwupd-2.0.20/plugins/intel-mchi/fu-intel-mchi-device.c --- fwupd-2.0.8/plugins/intel-mchi/fu-intel-mchi-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mchi/fu-intel-mchi-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,159 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-intel-mchi-device.h" + +struct _FuIntelMchiDevice { + FuHeciDevice parent_instance; +}; + +G_DEFINE_TYPE(FuIntelMchiDevice, fu_intel_mchi_device, FU_TYPE_HECI_DEVICE) + +#define FU_INTEL_MCHI_DEVICE_FLAG_LEAKED_KM "leaked-km" + +static gboolean +fu_intel_mchi_device_add_checksum_for_id(FuIntelMchiDevice *self, + guint32 file_id, + guint32 section, + GError **error) +{ + g_autofree gchar *checksum = NULL; + g_autoptr(GByteArray) buf = NULL; + + /* + * Call READ_FILE_EX with a larger-than-required data size -- which hopefully works when + * SHA512 results start being returned too. + * + * Icelake/Jasperlake/Cometlake: 0x20 (SHA256) + * Elkhartlake/Tigerlake/Alderlake/Raptorlake: 0x30 (SHA384) + */ + buf = fu_heci_device_read_file_ex(FU_HECI_DEVICE(self), file_id, section, 0x40, error); + if (buf == NULL) + return FALSE; + + /* convert into checksum, but only if non-zero and set */ + checksum = fu_byte_array_to_string(buf); + if (g_str_has_prefix(checksum, "0000000000000000") || + g_str_has_prefix(checksum, "ffffffffffffffff")) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "checksum %s was invalid", + checksum); + return FALSE; + } + fu_device_add_checksum(FU_DEVICE(self), checksum); + + /* success */ + return TRUE; +} + +static gboolean +fu_intel_mchi_device_setup(FuDevice *device, GError **error) +{ + FuIntelMchiDevice *self = FU_INTEL_MCHI_DEVICE(device); + const guint32 file_ids[] = { + 0x40002300, /* CometLake: OEM Public Key Hash */ + 0x40005B00, /* TigerLake: 1st OEM Public Key Hash */ + 0x40005C00, /* TigerLake: 2nd OEM Public Key Hash */ + }; + + /* connect */ + if (!fu_mei_device_connect(FU_MEI_DEVICE(device), FU_HECI_DEVICE_UUID_MCHI, 0, error)) { + g_prefix_error_literal(error, "failed to connect to MCHI: "); + return FALSE; + } + + /* look for all the possible OEM Public Key hashes using the CML+ method */ + for (guint i = 0; i < G_N_ELEMENTS(file_ids); i++) { + g_autoptr(GError) error_local = NULL; + if (!fu_intel_mchi_device_add_checksum_for_id(self, + file_ids[i], + 0x0, + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) || + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA)) { + g_debug("ignoring: %s", error_local->message); + continue; + } + g_warning("failed to get public key using file-id 0x%x: %s", + file_ids[i], + error_local->message); + } + } + + /* no point even adding */ + if (fu_device_get_checksums(device)->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no OEM public keys found"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_intel_mchi_device_add_security_attrs(FuDevice *device, FuSecurityAttrs *attrs) +{ + FuIntelMchiDevice *self = FU_INTEL_MCHI_DEVICE(device); + g_autoptr(FwupdSecurityAttr) attr = NULL; + + /* create attr */ + attr = + fu_device_security_attr_new(FU_DEVICE(self), FWUPD_SECURITY_ATTR_ID_MEI_KEY_MANIFEST); + fwupd_security_attr_set_result_success(attr, FWUPD_SECURITY_ATTR_RESULT_VALID); + fu_security_attrs_append(attrs, attr); + + /* verify keys */ + if (fu_device_get_checksums(device)->len == 0) { + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA); + return; + } + if (fu_device_has_private_flag(device, FU_INTEL_MCHI_DEVICE_FLAG_LEAKED_KM)) { + fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); + return; + } + + /* success */ + fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); +} + +static void +fu_intel_mchi_device_version_notify_cb(FuDevice *device, GParamSpec *pspec, gpointer user_data) +{ + if (fu_device_has_private_flag(device, FU_INTEL_MCHI_DEVICE_FLAG_LEAKED_KM)) + fu_device_inhibit(device, "leaked-km", "Provisioned with a leaked private key"); +} + +static void +fu_intel_mchi_device_init(FuIntelMchiDevice *self) +{ + fu_device_set_logical_id(FU_DEVICE(self), "MCHI"); + fu_device_set_name(FU_DEVICE(self), "BootGuard Configuration"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_ONLY_CHECKSUM); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); + fu_device_register_private_flag(FU_DEVICE(self), FU_INTEL_MCHI_DEVICE_FLAG_LEAKED_KM); + g_signal_connect(FWUPD_DEVICE(self), + "notify::private-flags", + G_CALLBACK(fu_intel_mchi_device_version_notify_cb), + NULL); +} + +static void +fu_intel_mchi_device_class_init(FuIntelMchiDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->setup = fu_intel_mchi_device_setup; + device_class->add_security_attrs = fu_intel_mchi_device_add_security_attrs; +} diff -Nru fwupd-2.0.8/plugins/intel-mchi/fu-intel-mchi-device.h fwupd-2.0.20/plugins/intel-mchi/fu-intel-mchi-device.h --- fwupd-2.0.8/plugins/intel-mchi/fu-intel-mchi-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mchi/fu-intel-mchi-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_INTEL_MCHI_DEVICE (fu_intel_mchi_device_get_type()) +G_DECLARE_FINAL_TYPE(FuIntelMchiDevice, fu_intel_mchi_device, FU, INTEL_MCHI_DEVICE, FuHeciDevice) diff -Nru fwupd-2.0.8/plugins/intel-mchi/fu-intel-mchi-plugin.c fwupd-2.0.20/plugins/intel-mchi/fu-intel-mchi-plugin.c --- fwupd-2.0.8/plugins/intel-mchi/fu-intel-mchi-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mchi/fu-intel-mchi-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-intel-mchi-device.h" +#include "fu-intel-mchi-plugin.h" + +struct _FuIntelMchiPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuIntelMchiPlugin, fu_intel_mchi_plugin, FU_TYPE_PLUGIN) + +static void +fu_intel_mchi_plugin_init(FuIntelMchiPlugin *self) +{ +} + +static void +fu_intel_mchi_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "mei"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_INTEL_MCHI_DEVICE); +} + +static void +fu_intel_mchi_plugin_class_init(FuIntelMchiPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->constructed = fu_intel_mchi_plugin_constructed; +} diff -Nru fwupd-2.0.8/plugins/intel-mchi/fu-intel-mchi-plugin.h fwupd-2.0.20/plugins/intel-mchi/fu-intel-mchi-plugin.h --- fwupd-2.0.8/plugins/intel-mchi/fu-intel-mchi-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mchi/fu-intel-mchi-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuIntelMchiPlugin, fu_intel_mchi_plugin, FU, INTEL_MCHI_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/intel-mchi/intel-mchi.quirk fwupd-2.0.20/plugins/intel-mchi/intel-mchi.quirk --- fwupd-2.0.8/plugins/intel-mchi/intel-mchi.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mchi/intel-mchi.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,2 @@ +[dd17041c-09ea-4b17-a271-5b989867ec65] +Plugin = intel_mchi diff -Nru fwupd-2.0.8/plugins/intel-mchi/meson.build fwupd-2.0.20/plugins/intel-mchi/meson.build --- fwupd-2.0.8/plugins/intel-mchi/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mchi/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +host_machine.system() == 'linux' or subdir_done() + +cargs = ['-DG_LOG_DOMAIN="FuPluginIntelMchi"'] +plugins += {meson.current_source_dir().split('/')[-1]: true} + +plugin_quirks += files('intel-mchi.quirk') +plugin_builtins += static_library('fu_plugin_intel_mchi', + sources: [ + 'fu-intel-mchi-plugin.c', + 'fu-intel-mchi-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +enumeration_data += files('tests/intel-mchi-setup.json') +device_tests += files('tests/intel-mchi.json') diff -Nru fwupd-2.0.8/plugins/intel-mchi/tests/intel-mchi-setup.json fwupd-2.0.20/plugins/intel-mchi/tests/intel-mchi-setup.json --- fwupd-2.0.8/plugins/intel-mchi/tests/intel-mchi-setup.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mchi/tests/intel-mchi-setup.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,435 @@ +{ + "FwupdVersion": "2.0.9", + "UsbDevices": [ + { + "Created": "2025-04-28T12:18:53.041779Z", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:16.0/mei/mei0", + "DeviceFile": "/dev/mei0", + "Subsystem": "mei", + "Vendor": 32902, + "Model": 41184, + "Events": [ + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "mei" + }, + { + "Id": "GetSymlinkTarget:Attr=driver" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=235\nMINOR=0\nDEVNAME=mei0" + }, + { + "Id": "ReadProp:Key=DEVNAME", + "Data": "mei0" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=pci", + "GType": "FuPciDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:16.0", + "PhysicalId": "PCI_SLOT_NAME=0000:00:16.0" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "pci" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "mei_me" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A0E0\nPCI_SUBSYS_ID=17AA:22C7\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A0E0sv000017AAsd000022C7bc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa0e0" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A0E0\nPCI_SUBSYS_ID=17AA:22C7\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A0E0sv000017AAsd000022C7bc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa0e0" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=i2c", + "Error": 8, + "ErrorMsg": "no parent with subsystem i2c" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadAttr:Attr=revision", + "Data": "0x10" + }, + { + "Id": "ReadAttr:Attr=subsystem_vendor", + "Data": "0x17aa" + }, + { + "Id": "ReadAttr:Attr=subsystem_device", + "Data": "0x22c7" + }, + { + "Id": "ReadProp:Key=PCI_SLOT_NAME", + "Data": "0000:00:16.0" + }, + { + "Id": "GetBackendParent:Subsystem=(null)", + "GType": "FuPciDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:16.0", + "PhysicalId": "PCI_SLOT_NAME=0000:00:16.0" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "pci" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "mei_me" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A0E0\nPCI_SUBSYS_ID=17AA:22C7\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A0E0sv000017AAsd000022C7bc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa0e0" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A0E0\nPCI_SUBSYS_ID=17AA:22C7\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A0E0sv000017AAsd000022C7bc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa0e0" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=i2c", + "Error": 8, + "ErrorMsg": "no parent with subsystem i2c" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadAttr:Attr=revision", + "Data": "0x10" + }, + { + "Id": "ReadAttr:Attr=subsystem_vendor", + "Data": "0x17aa" + }, + { + "Id": "ReadAttr:Attr=subsystem_device", + "Data": "0x22c7" + }, + { + "Id": "ReadProp:Key=PCI_SLOT_NAME", + "Data": "0000:00:16.0" + }, + { + "Id": "ListAttr", + "Data": "uevent\npower_state\nbroken_parity_status\nsubsystem_device\n0000:00:16.0-6861ec7b-d07a-4673-856c-7f22b4d55769\ndma_mask_bits\n0000:00:16.0-3c4852d6-d47b-4f46-b05e-b5edc1aa440e\nvendor\nlocal_cpus\nfirmware_node\npower\n0000:00:16.0-dba4d603-d7ed-4931-8823-17ad585705d5\nclass\n0000:00:16.0-dd17041c-09ea-4b17-a271-5b989867ec65\nnuma_node\nresource\nrescan\nmei\nmsi_bus\ndevice\n0000:00:16.0-082ee5a7-7c25-470a-9643-0c06f0466ea1\n0000:00:16.0-42b3ce2f-bd9f-485a-96ae-26406230b1ff\n0000:00:16.0-309dcde8-ccb1-4062-8f78-600115a34327\n0000:00:16.0-55213584-9a29-4916-badf-0fb7ed682aeb\ndriver\n0000:00:16.0-8e6a6715-9abc-4043-88ef-9e39c6f63e0f\nlocal_cpulist\n0000:00:16.0-8c2f4425-77d6-4755-aca3-891fdbc66a58\ndriver_override\nsubsystem\nd3cold_allowed\nirq\nrevision\n0000:00:16.0-fbf6fcf1-96cf-4e2e-a6a6-1bab8cbe36b1\nconsistent_dma_mask_bits\nresource0\nconfig\nari_enabled\nmsi_irqs\nremove\n0000:00:16.0-b638ab7e-94e2-4ea2-a552-d1c54b627f04\nenable\nlink\n0000:00:16.0-f908627d-13bf-4a04-b91f-a64e9245323d\nmodalias\nsubsystem_vendor\n0000:00:16.0-5565a099-7fe2-45c1-a22b-d7e9dfea9a2e" + }, + { + "Id": "GetBackendParent:Subsystem=pci", + "GType": "FuPciDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:16.0", + "PhysicalId": "PCI_SLOT_NAME=0000:00:16.0" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "pci" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "mei_me" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A0E0\nPCI_SUBSYS_ID=17AA:22C7\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A0E0sv000017AAsd000022C7bc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa0e0" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A0E0\nPCI_SUBSYS_ID=17AA:22C7\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A0E0sv000017AAsd000022C7bc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa0e0" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=i2c", + "Error": 8, + "ErrorMsg": "no parent with subsystem i2c" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadAttr:Attr=revision", + "Data": "0x10" + }, + { + "Id": "ReadAttr:Attr=subsystem_vendor", + "Data": "0x17aa" + }, + { + "Id": "ReadAttr:Attr=subsystem_device", + "Data": "0x22c7" + }, + { + "Id": "ReadProp:Key=PCI_SLOT_NAME", + "Data": "0000:00:16.0" + }, + { + "Id": "GetBackendParent:Subsystem=(null)", + "GType": "FuPciDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:16.0", + "PhysicalId": "PCI_SLOT_NAME=0000:00:16.0" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "pci" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "mei_me" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A0E0\nPCI_SUBSYS_ID=17AA:22C7\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A0E0sv000017AAsd000022C7bc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa0e0" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=mei_me\nPCI_CLASS=78000\nPCI_ID=8086:A0E0\nPCI_SUBSYS_ID=17AA:22C7\nPCI_SLOT_NAME=0000:00:16.0\nMODALIAS=pci:v00008086d0000A0E0sv000017AAsd000022C7bc07sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x8086" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0xa0e0" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=i2c", + "Error": 8, + "ErrorMsg": "no parent with subsystem i2c" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x078000" + }, + { + "Id": "ReadAttr:Attr=revision", + "Data": "0x10" + }, + { + "Id": "ReadAttr:Attr=subsystem_vendor", + "Data": "0x17aa" + }, + { + "Id": "ReadAttr:Attr=subsystem_device", + "Data": "0x22c7" + }, + { + "Id": "ReadProp:Key=PCI_SLOT_NAME", + "Data": "0000:00:16.0" + }, + { + "Id": "ListAttr", + "Data": "uevent\npower_state\nbroken_parity_status\nsubsystem_device\n0000:00:16.0-6861ec7b-d07a-4673-856c-7f22b4d55769\ndma_mask_bits\n0000:00:16.0-3c4852d6-d47b-4f46-b05e-b5edc1aa440e\nvendor\nlocal_cpus\nfirmware_node\npower\n0000:00:16.0-dba4d603-d7ed-4931-8823-17ad585705d5\nclass\n0000:00:16.0-dd17041c-09ea-4b17-a271-5b989867ec65\nnuma_node\nresource\nrescan\nmei\nmsi_bus\ndevice\n0000:00:16.0-082ee5a7-7c25-470a-9643-0c06f0466ea1\n0000:00:16.0-42b3ce2f-bd9f-485a-96ae-26406230b1ff\n0000:00:16.0-309dcde8-ccb1-4062-8f78-600115a34327\n0000:00:16.0-55213584-9a29-4916-badf-0fb7ed682aeb\ndriver\n0000:00:16.0-8e6a6715-9abc-4043-88ef-9e39c6f63e0f\nlocal_cpulist\n0000:00:16.0-8c2f4425-77d6-4755-aca3-891fdbc66a58\ndriver_override\nsubsystem\nd3cold_allowed\nirq\nrevision\n0000:00:16.0-fbf6fcf1-96cf-4e2e-a6a6-1bab8cbe36b1\nconsistent_dma_mask_bits\nresource0\nconfig\nari_enabled\nmsi_irqs\nremove\n0000:00:16.0-b638ab7e-94e2-4ea2-a552-d1c54b627f04\nenable\nlink\n0000:00:16.0-f908627d-13bf-4a04-b91f-a64e9245323d\nmodalias\nsubsystem_vendor\n0000:00:16.0-5565a099-7fe2-45c1-a22b-d7e9dfea9a2e" + }, + { + "Id": "Ioctl:Request=0xc0104801,Data=HAQX3eoJF0uicVuYmGfsZQ==,Length=0x10", + "DataOut": "ABAAAAEJF0uicVuYmGfsZQ==" + }, + { + "Id": "Write:Data=CgoAAAAjAEAAAAAAQAAAAAA=,Length=0x11" + }, + { + "Id": "Read:Length=0x51", + "Data": "CooAADAAAADw0VJjs1SwGy8y2VxVcus99J9RbDQ7iQ/hkplkmVpVSxLH8mmfJajLZthjPDVC8iY=" + }, + { + "Id": "Write:Data=CgoAAABbAEAAAAAAQAAAAAA=,Length=0x11" + }, + { + "Id": "Read:Length=0x51", + "Data": "CooAADAAAADw0VJjs1SwGy8y2VxVcus99J9RbDQ7iQ/hkplkmVpVSxLH8mmfJajLZthjPDVC8iY=" + }, + { + "Id": "Write:Data=CgoAAABcAEAAAAAAQAAAAAA=,Length=0x11" + }, + { + "Id": "Read:Length=0x51", + "Data": "CooAADAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/intel-mchi/tests/intel-mchi.json fwupd-2.0.20/plugins/intel-mchi/tests/intel-mchi.json --- fwupd-2.0.8/plugins/intel-mchi/tests/intel-mchi.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mchi/tests/intel-mchi.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +{ + "name": "Intel MCHI", + "interactive": false, + "steps": [ + { + "emulation-file": "@enumeration_datadir@/intel-mchi-setup.json", + "components": [ + { + "guids": [ + "f18c7464-aa01-5b3d-bd4f-6623597d0f70" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/intel-me/README.md fwupd-2.0.20/plugins/intel-me/README.md --- fwupd-2.0.8/plugins/intel-me/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,52 +0,0 @@ ---- -title: Plugin: Intel ME ---- - -## Introduction - -This plugin is used to talk to the Intel ME device, typically CSME. - -It allows us to get the Platform Key as used for BootGuard and to get the -version number for the Intel AMT. - -If AMT is enabled and provisioned and the AMT version is between 6.0 and 11.2, -and you have not upgraded your firmware, you are vulnerable to CVE-2017-5689 and -you should disable AMT in your system firmware. - -This code is inspired by 'AMT status checker for Linux' by Matthew Garrett -which can be found here: - -That tool in turn is heavily based on mei-amt-version from samples/mei in the -Linux source tree and copyright Intel Corporation. - -## GUID Generation - -These devices use the existing GUIDs provided by the ME host interfaces. - -## Metadata - -There have been several BootGuard key leaks that can be detected using the ME device. -The metadata needed to match the KM checksum is found in the metadata, typically obtained from -the LVFS project. -This sets the `leaked-km` private flag which then causes the HSI `org.fwupd.hsi.Mei.KeyManifest` -attribute to fail, and also adds a device inhibit which shows in the command line and GUI tools. - -The `org.linuxfoundation.bootguard.config` component is currently used to match against both the -MCA and MKHI ME devices. The latest cabinet archive can also be installed into the `vendor-firmware` -remote found in `/usr/share/fwupd/remotes.d/vendor/firmware/` which allows the detection to work -even when offline -- although using the LVFS source is recommended for most users. - -New *OEM Public Key Hash* values found from `MEInfo` or calculated manually should be added to the -checksums page on the LVFS. - -## Vendor ID Security - -The devices are not upgradable and thus require no vendor ID set. - -## External Interface Access - -This plugin requires `ioctl(IOCTL_MEI_CONNECT_CLIENT)` to `/dev/mei0`. - -## Version Considerations - -This plugin has been available since fwupd version `1.8.7`. diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-amt-device.c fwupd-2.0.20/plugins/intel-me/fu-intel-me-amt-device.c --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-amt-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-amt-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,253 +0,0 @@ -/* - * Copyright 2012 Intel Corporation. - * Copyright 2017 Google, Inc. - * Copyright 2017 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-intel-me-amt-device.h" -#include "fu-intel-me-amt-struct.h" - -struct _FuIntelMeAmtDevice { - FuMeiDevice parent_instance; -}; - -G_DEFINE_TYPE(FuIntelMeAmtDevice, fu_intel_me_amt_device, FU_TYPE_MEI_DEVICE) - -#define FU_AMT_STATUS_HOST_IF_EMPTY_RESPONSE 0x4000 - -static gboolean -fu_intel_me_amt_device_status_set_error(guint32 status, GError **error) -{ - if (status == FU_AMT_STATUS_SUCCESS) - return TRUE; - if (status == FU_AMT_STATUS_INTERNAL_ERROR) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "internal error"); - return FALSE; - } - if (status == FU_AMT_STATUS_NOT_READY) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "not ready"); - return FALSE; - } - if (status == FU_AMT_STATUS_INVALID_AMT_MODE) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid AMT mode"); - return FALSE; - } - if (status == FU_AMT_STATUS_INVALID_MESSAGE_LENGTH) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "invalid message length"); - return FALSE; - } - if (status == FU_AMT_STATUS_HOST_IF_EMPTY_RESPONSE) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "Intel AMT is disabled"); - return FALSE; - } - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unknown error"); - return FALSE; -} - -static GByteArray * -fu_intel_me_amt_device_host_if_call(FuIntelMeAmtDevice *self, GByteArray *inbuf, GError **error) -{ - gsize outbufsz; - g_autoptr(GByteArray) outbuf = g_byte_array_new(); - - fu_byte_array_set_size(outbuf, fu_mei_device_get_max_msg_length(FU_MEI_DEVICE(self)), 0x0); - if (!fu_mei_device_write(FU_MEI_DEVICE(self), inbuf->data, inbuf->len, 5000, error)) - return NULL; - if (!fu_mei_device_read(FU_MEI_DEVICE(self), - outbuf->data, - outbuf->len, - &outbufsz, - 2000, - error)) - return NULL; - if (outbufsz <= 0) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, "empty response"); - return NULL; - } - g_byte_array_set_size(outbuf, outbufsz); - return g_steal_pointer(&outbuf); -} - -static gboolean -fu_intel_me_amt_device_get_provisioning_state(FuIntelMeAmtDevice *self, - FuAmtProvisioningState *provisioning_state, - GError **error) -{ - g_autofree struct FuAmtHostIfRespHeader *response = NULL; - g_autoptr(GByteArray) data = NULL; - g_autoptr(FuAmtHostIfMsgProvisioningStateRequest) st_req = - fu_amt_host_if_msg_provisioning_state_request_new(); - g_autoptr(FuAmtHostIfMsgProvisioningStateResponse) st_res = NULL; - - data = fu_intel_me_amt_device_host_if_call(self, st_req, error); - if (data == NULL) - return FALSE; - - /* parse response */ - st_res = - fu_amt_host_if_msg_provisioning_state_response_parse(data->data, data->len, 0x0, error); - if (st_res == NULL) - return FALSE; - if (!fu_intel_me_amt_device_status_set_error( - fu_amt_host_if_msg_provisioning_state_response_get_status(st_res), - error)) - return FALSE; - *provisioning_state = - fu_amt_host_if_msg_provisioning_state_response_get_provisioning_state(st_res); - - /* success */ - return TRUE; -} - -static gboolean -fu_intel_me_amt_device_open(FuDevice *device, GError **error) -{ - /* open then create context */ - if (!FU_DEVICE_CLASS(fu_intel_me_amt_device_parent_class)->open(device, error)) - return FALSE; - return fu_mei_device_connect(FU_MEI_DEVICE(device), 0, error); -} - -static gboolean -fu_intel_me_amt_device_ensure_version(FuIntelMeAmtDevice *self, GError **error) -{ - guint32 version_count; - g_autoptr(FuAmtHostIfMsgCodeVersionRequest) st_req = - fu_amt_host_if_msg_code_version_request_new(); - g_autoptr(FuAmtHostIfMsgCodeVersionResponse) st_res = NULL; - g_autoptr(GByteArray) data = NULL; - g_autoptr(GString) version_bl = g_string_new(NULL); - g_autoptr(GString) version_fw = g_string_new(NULL); - - data = fu_intel_me_amt_device_host_if_call(self, st_req, error); - if (data == NULL) - return FALSE; - - /* parse response */ - st_res = fu_amt_host_if_msg_code_version_response_parse(data->data, data->len, 0x0, error); - if (st_res == NULL) - return FALSE; - if (!fu_intel_me_amt_device_status_set_error( - fu_amt_host_if_msg_code_version_response_get_status(st_res), - error)) - return FALSE; - - /* parse each version */ - version_count = fu_amt_host_if_msg_code_version_response_get_version_count(st_res); - for (guint i = 0; i < version_count; i++) { - g_autofree gchar *description = NULL; - g_autofree gchar *version = NULL; - g_autoptr(FuAmtUnicodeString) st_str = NULL; - - st_str = fu_amt_unicode_string_parse(data->data, - data->len, - st_res->len + (i * FU_AMT_UNICODE_STRING_SIZE), - error); - if (st_str == NULL) - return FALSE; - - /* get description */ - if (fu_amt_unicode_string_get_description_length(st_str) > - FU_AMT_UNICODE_STRING_SIZE_DESCRIPTION_STRING) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "description string too large"); - return FALSE; - } - description = fu_amt_unicode_string_get_description_string(st_str); - - /* get version */ - if (fu_amt_unicode_string_get_version_length(st_str) > - FU_AMT_UNICODE_STRING_SIZE_VERSION_STRING) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "version string too large"); - return FALSE; - } - version = fu_amt_unicode_string_get_version_string(st_str); - - /* build something suitable for fwupd */ - if (g_strcmp0(description, "AMT") == 0) { - g_string_append(version_fw, version); - continue; - } - if (g_strcmp0(description, "Recovery Version") == 0) { - g_string_append(version_bl, version); - continue; - } - if (g_strcmp0(description, "Build Number") == 0) { - g_string_append_printf(version_fw, ".%s", version); - continue; - } - if (g_strcmp0(description, "Recovery Build Num") == 0) { - g_string_append_printf(version_bl, ".%s", version); - continue; - } - } - - /* success */ - if (version_fw->len > 0) - fu_device_set_version(FU_DEVICE(self), version_fw->str); - if (version_bl->len > 0) - fu_device_set_version_bootloader(FU_DEVICE(self), version_bl->str); - return TRUE; -} - -static gboolean -fu_intel_me_amt_device_setup(FuDevice *device, GError **error) -{ - FuIntelMeAmtDevice *self = FU_INTEL_ME_AMT_DEVICE(device); - FuAmtProvisioningState provisioning_state = FU_AMT_PROVISIONING_STATE_UNPROVISIONED; - - /* get versions */ - if (!fu_intel_me_amt_device_ensure_version(self, error)) { - g_prefix_error(error, "failed to check version: "); - return FALSE; - } - - /* get provisioning state */ - if (!fu_intel_me_amt_device_get_provisioning_state(self, &provisioning_state, error)) { - g_prefix_error(error, "failed to get provisioning state: "); - return FALSE; - } - if (provisioning_state < FU_AMT_PROVISIONING_STATE_LAST) { - g_autofree gchar *name = - g_strdup_printf("AMT [%s]", - fu_amt_provisioning_state_to_string(provisioning_state)); - fu_device_set_name(device, name); - } - - /* success */ - return TRUE; -} - -static void -fu_intel_me_amt_device_init(FuIntelMeAmtDevice *self) -{ - fu_device_set_logical_id(FU_DEVICE(self), "AMT"); - fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_INTEL_ME); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); - fu_device_add_icon(FU_DEVICE(self), "computer"); - fu_device_set_name(FU_DEVICE(self), "AMT"); - fu_device_set_summary(FU_DEVICE(self), - "Hardware and firmware technology for remote " - "out-of-band management"); -} - -static void -fu_intel_me_amt_device_class_init(FuIntelMeAmtDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->open = fu_intel_me_amt_device_open; - device_class->setup = fu_intel_me_amt_device_setup; -} diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-amt-device.h fwupd-2.0.20/plugins/intel-me/fu-intel-me-amt-device.h --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-amt-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-amt-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_INTEL_ME_AMT_DEVICE (fu_intel_me_amt_device_get_type()) -G_DECLARE_FINAL_TYPE(FuIntelMeAmtDevice, - fu_intel_me_amt_device, - FU, - INTEL_ME_AMT_DEVICE, - FuMeiDevice) diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-amt.rs fwupd-2.0.20/plugins/intel-me/fu-intel-me-amt.rs --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-amt.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-amt.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,83 +0,0 @@ -// Copyright 2024 Richard Hughes -// SPDX-License-Identifier: LGPL-2.1-or-later - -enum FuAmtStatus { - Success, - InternalError, - NotReady, - InvalidAmtMode, - InvalidMessageLength, -} - -#[derive(ToString)] -#[repr(u8)] -enum FuAmtProvisioningState { - Unprovisioned, - BeingProvisioned, - Provisioned, -} - -#[repr(u32le)] -enum FuAmtHostIfCommand { - ProvisioningModeRequest = 0x04000008, - ProvisioningStateRequest = 0x04000011, - CodeVersionsRequest = 0x0400001A, - ProvisioningModeResponse = 0x04800008, - ProvisioningStateResponse = 0x04800011, - CodeVersionsResponse = 0x0480001A, -} - -#[derive(New, Default)] -#[repr(C, packed)] -struct FuAmtHostIfMsgCodeVersionRequest { - version_major: u8 == 0x1, - version_minor: u8 == 0x1, - _reserved: u16le, - command: FuAmtHostIfCommand == CodeVersionsRequest, - length: u32le == 0x0, -} - -#[derive(Parse, Default)] -#[repr(C, packed)] -struct FuAmtHostIfMsgCodeVersionResponse { - version_major: u8 == 0x1, - version_minor: u8 == 0x1, - _reserved: u16le, - command: FuAmtHostIfCommand == CodeVersionsResponse, - _length: u32le, - status: u32le, - _bios: [char; 65], - version_count: u32le, - // now variable length of FuAmtUnicodeString -} - -#[derive(Parse)] -#[repr(C, packed)] -struct FuAmtUnicodeString { - description_length: u16le, - description_string: [char; 20], - version_length: u16le, - version_string: [char; 20], -} - -#[derive(New, Default)] -#[repr(C, packed)] -struct FuAmtHostIfMsgProvisioningStateRequest { - version_major: u8 == 0x1, - version_minor: u8 == 0x1, - _reserved: u16le, - command: FuAmtHostIfCommand == ProvisioningStateRequest, - length: u32le == 0x0, -} - -#[derive(Parse, Default)] -#[repr(C, packed)] -struct FuAmtHostIfMsgProvisioningStateResponse { - version_major: u8 == 0x1, - version_minor: u8 == 0x1, - _reserved: u16le, - command: FuAmtHostIfCommand == ProvisioningStateResponse, - length: u32le == 0x8, - status: u32le, - provisioning_state: FuAmtProvisioningState, -} diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-common.c fwupd-2.0.20/plugins/intel-me/fu-intel-me-common.c --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-common.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,73 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include - -#include "fu-intel-me-common.h" -#include "fu-intel-me-mkhi-struct.h" - -gboolean -fu_intel_me_mkhi_result_to_error(FuMkhiStatus result, GError **error) -{ - if (result == FU_MKHI_STATUS_SUCCESS) - return TRUE; - - switch (result) { - case FU_MKHI_STATUS_NOT_SUPPORTED: - case FU_MKHI_STATUS_NOT_AVAILABLE: - case FU_MKHI_STATUS_NOT_SET: - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "not supported [0x%x]", - result); - break; - default: - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "generic failure [0x%x]", - result); - break; - } - return FALSE; -} - -GString * -fu_intel_me_convert_checksum(GByteArray *buf, GError **error) -{ - gboolean seen_non00_data = FALSE; - gboolean seen_nonff_data = FALSE; - g_autoptr(GString) checksum = g_string_new(NULL); - - /* create checksum, but only if non-zero and set */ - for (gsize i = 0; i < buf->len; i++) { - if (!seen_non00_data && buf->data[i] != 0x00) - seen_non00_data = TRUE; - if (!seen_nonff_data && buf->data[i] != 0xFF) - seen_nonff_data = TRUE; - g_string_append_printf(checksum, "%02x", buf->data[i]); - } - if (!seen_non00_data) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "buffer was all 0x00"); - return NULL; - } - if (!seen_nonff_data) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "buffer was all 0xFF"); - return NULL; - } - - /* success */ - return g_steal_pointer(&checksum); -} diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-common.h fwupd-2.0.20/plugins/intel-me/fu-intel-me-common.h --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-common.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "fu-intel-me-mkhi-struct.h" - -GString * -fu_intel_me_convert_checksum(GByteArray *buf, GError **error); -gboolean -fu_intel_me_mkhi_result_to_error(FuMkhiStatus result, GError **error); diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-heci-device.c fwupd-2.0.20/plugins/intel-me/fu-intel-me-heci-device.c --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-heci-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-heci-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,160 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-intel-me-common.h" -#include "fu-intel-me-heci-device.h" - -G_DEFINE_TYPE(FuIntelMeHeciDevice, fu_intel_me_heci_device, FU_TYPE_MEI_DEVICE) - -#define FU_INTEL_ME_HECI_DEVICE_TIMEOUT 200 /* ms */ - -static gboolean -fu_intel_me_heci_device_open(FuDevice *device, GError **error) -{ - /* open then create context */ - if (!FU_DEVICE_CLASS(fu_intel_me_heci_device_parent_class)->open(device, error)) - return FALSE; - return fu_mei_device_connect(FU_MEI_DEVICE(device), 0, error); -} - -GByteArray * -fu_intel_me_heci_device_read_file(FuIntelMeHeciDevice *self, const gchar *filename, GError **error) -{ - guint32 data_size; - guint datasz_req = 0x80; - g_autoptr(GByteArray) bufout = g_byte_array_new(); - g_autoptr(GByteArray) buf_res = g_byte_array_new(); - g_autoptr(FuMkhiReadFileRequest) st_req = fu_mkhi_read_file_request_new(); - g_autoptr(FuMkhiReadFileResponse) st_res = NULL; - - /* request */ - if (!fu_mkhi_read_file_request_set_filename(st_req, filename, error)) - return NULL; - fu_mkhi_read_file_request_set_data_size(st_req, datasz_req); - fu_mkhi_read_file_request_set_flags(st_req, (1 << 3)); /* ?? */ - if (!fu_mei_device_write(FU_MEI_DEVICE(self), - st_req->data, - st_req->len, - FU_INTEL_ME_HECI_DEVICE_TIMEOUT, - error)) - return NULL; - - /* response */ - fu_byte_array_set_size(buf_res, FU_MKHI_READ_FILE_RESPONSE_SIZE + datasz_req, 0x0); - if (!fu_mei_device_read(FU_MEI_DEVICE(self), - buf_res->data, - buf_res->len, - NULL, - FU_INTEL_ME_HECI_DEVICE_TIMEOUT, - error)) - return NULL; - st_res = fu_mkhi_read_file_response_parse(buf_res->data, buf_res->len, 0x0, error); - if (st_res == NULL) - return NULL; - if (!fu_intel_me_mkhi_result_to_error(fu_mkhi_read_file_response_get_result(st_res), error)) - return NULL; - - /* verify we got what we asked for */ - data_size = fu_mkhi_read_file_response_get_data_size(st_res); - if (data_size > datasz_req) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid response data size, requested 0x%x and got 0x%x", - datasz_req, - data_size); - return NULL; - } - - /* success */ - g_byte_array_append(bufout, &buf_res->data[st_res->len], data_size); - return g_steal_pointer(&bufout); -} - -GByteArray * -fu_intel_me_heci_device_read_file_ex(FuIntelMeHeciDevice *self, - guint32 file_id, - guint32 section, - guint32 datasz_req, - GError **error) -{ - guint32 data_size; - g_autoptr(FuMkhiReadFileExRequest) st_req = fu_mkhi_read_file_ex_request_new(); - g_autoptr(FuMkhiReadFileExResponse) st_res = NULL; - g_autoptr(GByteArray) bufout = g_byte_array_new(); - g_autoptr(GByteArray) buf_res = g_byte_array_new(); - - /* request */ - fu_mkhi_read_file_ex_request_set_file_id(st_req, file_id); - fu_mkhi_read_file_ex_request_set_data_size(st_req, datasz_req); - fu_mkhi_read_file_ex_request_set_flags(st_req, section); - if (!fu_mei_device_write(FU_MEI_DEVICE(self), - st_req->data, - st_req->len, - FU_INTEL_ME_HECI_DEVICE_TIMEOUT, - error)) - return NULL; - - /* response */ - fu_byte_array_set_size(buf_res, FU_MKHI_READ_FILE_EX_REQUEST_SIZE + datasz_req, 0x0); - if (!fu_mei_device_read(FU_MEI_DEVICE(self), - buf_res->data, - buf_res->len, - NULL, - FU_INTEL_ME_HECI_DEVICE_TIMEOUT, - error)) - return NULL; - st_res = fu_mkhi_read_file_ex_response_parse(buf_res->data, buf_res->len, 0x0, error); - if (st_res == NULL) - return NULL; - if (!fu_intel_me_mkhi_result_to_error(fu_mkhi_read_file_ex_response_get_result(st_res), - error)) - return NULL; - - /* verify we got what we asked for */ - data_size = fu_mkhi_read_file_ex_response_get_data_size(st_res); - if (data_size > datasz_req) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid response data size, requested 0x%x and got 0x%x", - datasz_req, - data_size); - return NULL; - } - - /* success */ - g_byte_array_append(bufout, &buf_res->data[st_res->len], data_size); - return g_steal_pointer(&bufout); -} - -static void -fu_intel_me_heci_device_version_notify_cb(FuDevice *device, GParamSpec *pspec, gpointer user_data) -{ - if (fu_device_has_private_flag(device, FU_INTEL_ME_HECI_DEVICE_FLAG_LEAKED_KM)) - fu_device_inhibit(device, "leaked-km", "Provisioned with a leaked private key"); -} - -static void -fu_intel_me_heci_device_init(FuIntelMeHeciDevice *self) -{ - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_icon(FU_DEVICE(self), "computer"); - fu_device_register_private_flag(FU_DEVICE(self), FU_INTEL_ME_HECI_DEVICE_FLAG_LEAKED_KM); - g_signal_connect(FWUPD_DEVICE(self), - "notify::private-flags", - G_CALLBACK(fu_intel_me_heci_device_version_notify_cb), - NULL); -} - -static void -fu_intel_me_heci_device_class_init(FuIntelMeHeciDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->open = fu_intel_me_heci_device_open; -} diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-heci-device.h fwupd-2.0.20/plugins/intel-me/fu-intel-me-heci-device.h --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-heci-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-heci-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_INTEL_ME_HECI_DEVICE (fu_intel_me_heci_device_get_type()) -G_DECLARE_DERIVABLE_TYPE(FuIntelMeHeciDevice, - fu_intel_me_heci_device, - FU, - INTEL_ME_HECI_DEVICE, - FuMeiDevice) - -struct _FuIntelMeHeciDeviceClass { - FuMeiDeviceClass parent_class; -}; - -#define FU_INTEL_ME_HECI_DEVICE_FLAG_LEAKED_KM "leaked-km" - -GByteArray * -fu_intel_me_heci_device_read_file(FuIntelMeHeciDevice *self, const gchar *filename, GError **error); -GByteArray * -fu_intel_me_heci_device_read_file_ex(FuIntelMeHeciDevice *self, - guint32 file_id, - guint32 section, - guint32 datasz_req, - GError **error); diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-mca-device.c fwupd-2.0.20/plugins/intel-me/fu-intel-me-mca-device.c --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-mca-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-mca-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,133 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-intel-me-common.h" -#include "fu-intel-me-mca-device.h" - -struct _FuIntelMeMcaDevice { - FuIntelMeHeciDevice parent_instance; -}; - -G_DEFINE_TYPE(FuIntelMeMcaDevice, fu_intel_me_mca_device, FU_TYPE_INTEL_ME_HECI_DEVICE) - -static gboolean -fu_intel_me_mca_device_add_checksum_for_id(FuIntelMeMcaDevice *self, - guint32 file_id, - guint32 section, - GError **error) -{ - g_autoptr(GByteArray) buf = NULL; - g_autoptr(GString) checksum = NULL; - - /* - * Call READ_FILE_EX with a larger-than-required data size -- which hopefully works when - * SHA512 results start being returned too. - * - * Icelake/Jasperlake/Cometlake: 0x20 (SHA256) - * Elkhartlake/Tigerlake/Alderlake/Raptorlake: 0x30 (SHA384) - */ - buf = fu_intel_me_heci_device_read_file_ex(FU_INTEL_ME_HECI_DEVICE(self), - file_id, - section, - 0x40, - error); - if (buf == NULL) - return FALSE; - - /* convert into checksum, but only if non-zero and set */ - checksum = fu_intel_me_convert_checksum(buf, error); - if (checksum == NULL) - return FALSE; - fu_device_add_checksum(FU_DEVICE(self), checksum->str); - - /* success */ - return TRUE; -} - -static gboolean -fu_intel_me_mca_device_setup(FuDevice *device, GError **error) -{ - FuIntelMeMcaDevice *self = FU_INTEL_ME_MCA_DEVICE(device); - const guint32 file_ids[] = {0x40002300, /* CometLake: OEM Public Key Hash */ - 0x40005B00, /* TigerLake: 1st OEM Public Key Hash */ - 0x40005C00 /* TigerLake: 2nd OEM Public Key Hash */, - G_MAXUINT32}; - - /* look for all the possible OEM Public Key hashes using the CML+ method */ - for (guint i = 0; file_ids[i] != G_MAXUINT32; i++) { - g_autoptr(GError) error_local = NULL; - if (!fu_intel_me_mca_device_add_checksum_for_id(self, - file_ids[i], - 0x0, - &error_local)) { - if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) || - g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA)) { - continue; - } - g_warning("failed to get public key using file-id 0x%x: %s", - file_ids[i], - error_local->message); - } - } - - /* no point even adding */ - if (fu_device_get_checksums(device)->len == 0) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no OEM public keys found"); - return FALSE; - } - - /* success */ - return TRUE; -} - -static void -fu_intel_me_mca_device_add_security_attrs(FuDevice *device, FuSecurityAttrs *attrs) -{ - FuIntelMeMcaDevice *self = FU_INTEL_ME_MCA_DEVICE(device); - g_autoptr(FwupdSecurityAttr) attr = NULL; - - /* create attr */ - attr = - fu_device_security_attr_new(FU_DEVICE(self), FWUPD_SECURITY_ATTR_ID_MEI_KEY_MANIFEST); - fwupd_security_attr_set_result_success(attr, FWUPD_SECURITY_ATTR_RESULT_VALID); - fu_security_attrs_append(attrs, attr); - - /* verify keys */ - if (fu_device_get_checksums(device)->len == 0) { - fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA); - return; - } - if (fu_device_has_private_flag(device, FU_INTEL_ME_HECI_DEVICE_FLAG_LEAKED_KM)) { - fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); - return; - } - - /* success */ - fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS); -} - -static void -fu_intel_me_mca_device_init(FuIntelMeMcaDevice *self) -{ - fu_device_set_logical_id(FU_DEVICE(self), "MCA"); - fu_device_set_name(FU_DEVICE(self), "BootGuard Configuration"); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_ONLY_CHECKSUM); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS); -} - -static void -fu_intel_me_mca_device_class_init(FuIntelMeMcaDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->setup = fu_intel_me_mca_device_setup; - device_class->add_security_attrs = fu_intel_me_mca_device_add_security_attrs; -} diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-mca-device.h fwupd-2.0.20/plugins/intel-me/fu-intel-me-mca-device.h --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-mca-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-mca-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#include "fu-intel-me-heci-device.h" - -#define FU_TYPE_INTEL_ME_MCA_DEVICE (fu_intel_me_mca_device_get_type()) -G_DECLARE_FINAL_TYPE(FuIntelMeMcaDevice, - fu_intel_me_mca_device, - FU, - INTEL_ME_MCA_DEVICE, - FuIntelMeHeciDevice) diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-mkhi-device.c fwupd-2.0.20/plugins/intel-me/fu-intel-me-mkhi-device.c --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-mkhi-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-mkhi-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,90 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-intel-me-common.h" -#include "fu-intel-me-mkhi-device.h" - -struct _FuIntelMeMkhiDevice { - FuIntelMeHeciDevice parent_instance; -}; - -G_DEFINE_TYPE(FuIntelMeMkhiDevice, fu_intel_me_mkhi_device, FU_TYPE_INTEL_ME_HECI_DEVICE) - -static gboolean -fu_intel_me_mkhi_device_add_checksum_for_filename(FuIntelMeMkhiDevice *self, - const gchar *filename, - GError **error) -{ - g_autoptr(GByteArray) buf = NULL; - g_autoptr(GString) checksum = NULL; - - /* read from the MFS */ - buf = fu_intel_me_heci_device_read_file(FU_INTEL_ME_HECI_DEVICE(self), filename, error); - if (buf == NULL) - return FALSE; - - /* convert into checksum, but only if non-zero and set */ - checksum = fu_intel_me_convert_checksum(buf, error); - if (checksum == NULL) - return FALSE; - fu_device_add_checksum(FU_DEVICE(self), checksum->str); - - /* success */ - return TRUE; -} - -static gboolean -fu_intel_me_mkhi_device_setup(FuDevice *device, GError **error) -{ - FuIntelMeMkhiDevice *self = FU_INTEL_ME_MKHI_DEVICE(device); - const gchar *fns[] = {"/fpf/OemCred", NULL}; - - /* this is the legacy way to get the hash, which is removed in newer ME versions due to - * possible path traversal attacks */ - for (guint i = 0; fns[i] != NULL; i++) { - g_autoptr(GError) error_local = NULL; - if (!fu_intel_me_mkhi_device_add_checksum_for_filename(self, - fns[i], - &error_local)) { - if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) - continue; - g_warning("failed to get public key using %s: %s", - fns[i], - error_local->message); - } - } - - /* no point even adding */ - if (fu_device_get_checksums(device)->len == 0) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no OEM public keys found"); - return FALSE; - } - - /* success */ - return TRUE; -} - -static void -fu_intel_me_mkhi_device_init(FuIntelMeMkhiDevice *self) -{ - fu_device_set_logical_id(FU_DEVICE(self), "MKHI"); - fu_device_set_name(FU_DEVICE(self), "BootGuard Configuration"); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_ONLY_CHECKSUM); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS); -} - -static void -fu_intel_me_mkhi_device_class_init(FuIntelMeMkhiDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->setup = fu_intel_me_mkhi_device_setup; -} diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-mkhi-device.h fwupd-2.0.20/plugins/intel-me/fu-intel-me-mkhi-device.h --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-mkhi-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-mkhi-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#include "fu-intel-me-heci-device.h" - -#define FU_TYPE_INTEL_ME_MKHI_DEVICE (fu_intel_me_mkhi_device_get_type()) -G_DECLARE_FINAL_TYPE(FuIntelMeMkhiDevice, - fu_intel_me_mkhi_device, - FU, - INTEL_ME_MKHI_DEVICE, - FuIntelMeHeciDevice) diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-mkhi.rs fwupd-2.0.20/plugins/intel-me/fu-intel-me-mkhi.rs --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-mkhi.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-mkhi.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,95 +0,0 @@ -// Copyright 2024 Richard Hughes -// SPDX-License-Identifier: LGPL-2.1-or-later - -#[repr(u8)] -enum FuMkhiGroupId { - Cbm, - Pm, // no longer used - Pwd, - Fwcaps, - App, // no longer used - Fwupdate, // for manufacturing downgrade - FirmwareUpdate, - Bist, - Mdes, - MeDbg, - Mca, // sometimes called "FPF" - Gen = 0xFF, -} - -enum FuMkhiStatus { - Success, - InvalidState, - MessageSkipped, - SizeError = 0x05, - NotSet = 0x0F, // guessed - NotAvailable = 0x18, // guessed - InvalidAccess = 0x84, - InvalidParams = 0x85, - NotReady = 0x88, - NotSupported = 0x89, - InvalidAddress = 0x8C, - InvalidCommand = 0x8D, - Failure = 0x9E, - InvalidResource = 0xE4, - ResourceInUse = 0xE5, - NoResource = 0xE6, - GeneralError = 0xFF, -} - -#[repr(u8)] -enum FuMkhiCommand { - ReadFile = 0x02, - ReadFileEx = 0x0A, - // not real commands, but makes debugging easier - ReadFileResponse = 0x82, - ReadFileExResponse = 0x8A, -} - -#[derive(New, Default)] -#[repr(C, packed)] -struct FuMkhiReadFileRequest { - group_id: FuMkhiGroupId == Mca, - command: FuMkhiCommand == ReadFile, - _rsvd: u8, - result: u8 == 0x0, - filename: [char; 0x40], - offset: u32le == 0x0, - data_size: u32le, - flags: u8, -} - -#[derive(Parse, Default)] -#[repr(C, packed)] -struct FuMkhiReadFileResponse { - group_id: FuMkhiGroupId == Mca, - command: FuMkhiCommand == ReadFileResponse, - _rsvd: u8, - result: u8, - data_size: u32le, - // payload here -} - -#[derive(New, Default)] -#[repr(C, packed)] -struct FuMkhiReadFileExRequest { - group_id: FuMkhiGroupId == Mca, - command: FuMkhiCommand == ReadFileEx, - _rsvd: u8, - result: u8 == 0x0, - file_id: u32le, - offset: u32le == 0x0, - data_size: u32le, - flags: u8, -} - -#[derive(Parse, Default)] -#[repr(C, packed)] -struct FuMkhiReadFileExResponse { - group_id: FuMkhiGroupId == Mca, - command: FuMkhiCommand == ReadFileExResponse, - _rsvd: u8, - result: u8, - data_size: u32le, - // payload here -} diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-plugin.c fwupd-2.0.20/plugins/intel-me/fu-intel-me-plugin.c --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-plugin.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,42 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-intel-me-amt-device.h" -#include "fu-intel-me-heci-device.h" -#include "fu-intel-me-mca-device.h" -#include "fu-intel-me-mkhi-device.h" -#include "fu-intel-me-plugin.h" - -struct _FuIntelMePlugin { - FuPlugin parent_instance; -}; - -G_DEFINE_TYPE(FuIntelMePlugin, fu_intel_me_plugin, FU_TYPE_PLUGIN) - -static void -fu_intel_me_plugin_init(FuIntelMePlugin *self) -{ -} - -static void -fu_intel_me_plugin_constructed(GObject *obj) -{ - FuPlugin *plugin = FU_PLUGIN(obj); - fu_plugin_add_udev_subsystem(plugin, "mei"); - fu_plugin_add_device_gtype(plugin, FU_TYPE_INTEL_ME_AMT_DEVICE); - fu_plugin_add_device_gtype(plugin, FU_TYPE_INTEL_ME_MCA_DEVICE); - fu_plugin_add_device_gtype(plugin, FU_TYPE_INTEL_ME_MKHI_DEVICE); - fu_plugin_add_device_gtype(plugin, FU_TYPE_INTEL_ME_HECI_DEVICE); /* coverage */ -} - -static void -fu_intel_me_plugin_class_init(FuIntelMePluginClass *klass) -{ - FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); - plugin_class->constructed = fu_intel_me_plugin_constructed; -} diff -Nru fwupd-2.0.8/plugins/intel-me/fu-intel-me-plugin.h fwupd-2.0.20/plugins/intel-me/fu-intel-me-plugin.h --- fwupd-2.0.8/plugins/intel-me/fu-intel-me-plugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/fu-intel-me-plugin.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -G_DECLARE_FINAL_TYPE(FuIntelMePlugin, fu_intel_me_plugin, FU, INTEL_ME_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/intel-me/intel-me.quirk fwupd-2.0.20/plugins/intel-me/intel-me.quirk --- fwupd-2.0.8/plugins/intel-me/intel-me.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/intel-me.quirk 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -# PTHI client (via the HECI device) -[12f80028-b4b7-4b2d-aca8-46e0ff65814c] -Plugin = intel_me -GType = FuIntelMeAmtDevice - -# MKHI (legacy) -[8e6a6715-9abc-4043-88ef-9e39c6f63e0f] -Plugin = intel_me -GType = FuIntelMeMkhiDevice - -# MCA? -[dd17041c-09ea-4b17-a271-5b989867ec65] -Plugin = intel_me -GType = FuIntelMeMcaDevice diff -Nru fwupd-2.0.8/plugins/intel-me/meson.build fwupd-2.0.20/plugins/intel-me/meson.build --- fwupd-2.0.8/plugins/intel-me/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-me/meson.build 1970-01-01 00:00:00.000000000 +0000 @@ -1,24 +0,0 @@ -if host_machine.system() == 'linux' -cargs = ['-DG_LOG_DOMAIN="FuPluginIntelMe"'] -plugins += {meson.current_source_dir().split('/')[-1]: true} - -plugin_quirks += files('intel-me.quirk') -plugin_builtins += static_library('fu_plugin_intel_me', - rustgen.process( - 'fu-intel-me-amt.rs', - 'fu-intel-me-mkhi.rs', - ), - sources: [ - 'fu-intel-me-common.c', - 'fu-intel-me-plugin.c', - 'fu-intel-me-amt-device.c', - 'fu-intel-me-heci-device.c', - 'fu-intel-me-mca-device.c', - 'fu-intel-me-mkhi-device.c', - ], - include_directories: plugin_incdirs, - link_with: plugin_libs, - c_args: cargs, - dependencies: plugin_deps, -) -endif diff -Nru fwupd-2.0.8/plugins/intel-mkhi/README.md fwupd-2.0.20/plugins/intel-mkhi/README.md --- fwupd-2.0.8/plugins/intel-mkhi/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mkhi/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,42 @@ +--- +title: Plugin: Intel MKHI +--- + +## Introduction + +This plugin is used to talk to the Intel ME device using the legacy MKHI interface. + +It allows us to get the Platform Key as used for BootGuard. + +## GUID Generation + +These devices use the existing GUIDs provided by the ME host interfaces. + +## Metadata + +There have been several BootGuard key leaks that can be detected using the ME device. +The metadata needed to match the KM checksum is found in the metadata, typically obtained from +the LVFS project. +This sets the `leaked-km` private flag which then causes the HSI `org.fwupd.hsi.Mei.KeyManifest` +attribute to fail, and also adds a device inhibit which shows in the command line and GUI tools. + +The `org.linuxfoundation.bootguard.config` component is currently used to match against both the +MCA and MKHI ME devices. The latest cabinet archive can also be installed into the `vendor-firmware` +remote found in `/usr/share/fwupd/remotes.d/vendor/firmware/` which allows the detection to work +even when offline -- although using the LVFS source is recommended for most users. + +New *OEM Public Key Hash* values found from `MEInfo` or calculated manually should be added to the +checksums page on the LVFS. + +## Vendor ID Security + +The devices are not upgradable and thus require no vendor ID set. + +## External Interface Access + +This plugin requires `ioctl(IOCTL_MEI_CONNECT_CLIENT)` to `/dev/mei0`. + +## Version Considerations + +This plugin has been available since fwupd version `2.0.9`, although the functionality was +previously introduced in the `intel-me` plugin released with fwupd version `1.8.7`. diff -Nru fwupd-2.0.8/plugins/intel-mkhi/fu-intel-mkhi-device.c fwupd-2.0.20/plugins/intel-mkhi/fu-intel-mkhi-device.c --- fwupd-2.0.8/plugins/intel-mkhi/fu-intel-mkhi-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mkhi/fu-intel-mkhi-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-intel-mkhi-device.h" + +struct _FuIntelMkhiDevice { + FuHeciDevice parent_instance; +}; + +G_DEFINE_TYPE(FuIntelMkhiDevice, fu_intel_mkhi_device, FU_TYPE_HECI_DEVICE) + +#define FU_INTEL_MKHI_DEVICE_FLAG_LEAKED_KM "leaked-km" + +static gboolean +fu_intel_mkhi_device_add_checksum_for_filename(FuIntelMkhiDevice *self, + const gchar *filename, + GError **error) +{ + g_autofree gchar *checksum = NULL; + g_autoptr(GByteArray) buf = NULL; + + /* read from the MFS */ + buf = fu_heci_device_read_file(FU_HECI_DEVICE(self), filename, error); + if (buf == NULL) + return FALSE; + + /* convert into checksum, but only if non-zero and set */ + checksum = fu_byte_array_to_string(buf); + if (g_str_has_prefix(checksum, "0000000000000000") || + g_str_has_prefix(checksum, "ffffffffffffffff")) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "checksum %s was invalid", + checksum); + return FALSE; + } + fu_device_add_checksum(FU_DEVICE(self), checksum); + + /* success */ + return TRUE; +} + +static gboolean +fu_intel_mkhi_device_setup(FuDevice *device, GError **error) +{ + FuIntelMkhiDevice *self = FU_INTEL_MKHI_DEVICE(device); + + /* connect */ + if (!fu_mei_device_connect(FU_MEI_DEVICE(device), FU_HECI_DEVICE_UUID_MKHI, 0, error)) { + g_prefix_error_literal(error, "failed to connect to MKHI: "); + return FALSE; + } + + /* this is the legacy way to get the hash, which is removed in newer ME versions due to + * possible path traversal attacks */ + if (!fu_intel_mkhi_device_add_checksum_for_filename(self, "/fpf/OemCred", error)) + return FALSE; + + /* no point even adding */ + if (fu_device_get_checksums(device)->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no OEM public keys found"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_intel_mkhi_device_version_notify_cb(FuDevice *device, GParamSpec *pspec, gpointer user_data) +{ + if (fu_device_has_private_flag(device, FU_INTEL_MKHI_DEVICE_FLAG_LEAKED_KM)) + fu_device_inhibit(device, "leaked-km", "Provisioned with a leaked private key"); +} + +static void +fu_intel_mkhi_device_init(FuIntelMkhiDevice *self) +{ + fu_device_set_logical_id(FU_DEVICE(self), "MKHI"); + fu_device_set_name(FU_DEVICE(self), "BootGuard Configuration"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_ONLY_CHECKSUM); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); + fu_device_register_private_flag(FU_DEVICE(self), FU_INTEL_MKHI_DEVICE_FLAG_LEAKED_KM); + g_signal_connect(FWUPD_DEVICE(self), + "notify::private-flags", + G_CALLBACK(fu_intel_mkhi_device_version_notify_cb), + NULL); +} + +static void +fu_intel_mkhi_device_class_init(FuIntelMkhiDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->setup = fu_intel_mkhi_device_setup; +} diff -Nru fwupd-2.0.8/plugins/intel-mkhi/fu-intel-mkhi-device.h fwupd-2.0.20/plugins/intel-mkhi/fu-intel-mkhi-device.h --- fwupd-2.0.8/plugins/intel-mkhi/fu-intel-mkhi-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mkhi/fu-intel-mkhi-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_INTEL_MKHI_DEVICE (fu_intel_mkhi_device_get_type()) +G_DECLARE_FINAL_TYPE(FuIntelMkhiDevice, fu_intel_mkhi_device, FU, INTEL_MKHI_DEVICE, FuHeciDevice) diff -Nru fwupd-2.0.8/plugins/intel-mkhi/fu-intel-mkhi-plugin.c fwupd-2.0.20/plugins/intel-mkhi/fu-intel-mkhi-plugin.c --- fwupd-2.0.8/plugins/intel-mkhi/fu-intel-mkhi-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mkhi/fu-intel-mkhi-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-intel-mkhi-device.h" +#include "fu-intel-mkhi-plugin.h" + +struct _FuIntelMkhiPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuIntelMkhiPlugin, fu_intel_mkhi_plugin, FU_TYPE_PLUGIN) + +static void +fu_intel_mkhi_plugin_init(FuIntelMkhiPlugin *self) +{ +} + +static void +fu_intel_mkhi_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "mei"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_INTEL_MKHI_DEVICE); +} + +static void +fu_intel_mkhi_plugin_class_init(FuIntelMkhiPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->constructed = fu_intel_mkhi_plugin_constructed; +} diff -Nru fwupd-2.0.8/plugins/intel-mkhi/fu-intel-mkhi-plugin.h fwupd-2.0.20/plugins/intel-mkhi/fu-intel-mkhi-plugin.h --- fwupd-2.0.8/plugins/intel-mkhi/fu-intel-mkhi-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mkhi/fu-intel-mkhi-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuIntelMkhiPlugin, fu_intel_mkhi_plugin, FU, INTEL_MKHI_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/intel-mkhi/intel-mkhi.quirk fwupd-2.0.20/plugins/intel-mkhi/intel-mkhi.quirk --- fwupd-2.0.8/plugins/intel-mkhi/intel-mkhi.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mkhi/intel-mkhi.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,2 @@ +[8e6a6715-9abc-4043-88ef-9e39c6f63e0f] +Plugin = intel_mkhi diff -Nru fwupd-2.0.8/plugins/intel-mkhi/meson.build fwupd-2.0.20/plugins/intel-mkhi/meson.build --- fwupd-2.0.8/plugins/intel-mkhi/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-mkhi/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +host_machine.system() == 'linux' or subdir_done() + +cargs = ['-DG_LOG_DOMAIN="FuPluginIntelMkhi"'] +plugins += {meson.current_source_dir().split('/')[-1]: true} + +plugin_quirks += files('intel-mkhi.quirk') +plugin_builtins += static_library('fu_plugin_intel_mkhi', + sources: [ + 'fu-intel-mkhi-plugin.c', + 'fu-intel-mkhi-device.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) diff -Nru fwupd-2.0.8/plugins/intel-usb4/fu-intel-usb4-device.c fwupd-2.0.20/plugins/intel-usb4/fu-intel-usb4-device.c --- fwupd-2.0.8/plugins/intel-usb4/fu-intel-usb4-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-usb4/fu-intel-usb4-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -97,7 +97,7 @@ } /* verify status for specific hub mailbox register */ if (mbox_reg == MBOX_REG) { - g_autoptr(GByteArray) st_regex = NULL; + g_autoptr(FuStructIntelUsb4Mbox) st_regex = NULL; st_regex = fu_struct_intel_usb4_mbox_parse(buf, bufsz, 0x0, error); if (st_regex == NULL) @@ -179,7 +179,7 @@ /* read 4 bytes per iteration */ for (gint i = 0; i < bufsz / 4; i++) { if (!fu_intel_usb4_device_get_mmio(self, i, ptr, 0x4, error)) { - g_prefix_error(error, "failed to read mbox data registers: "); + g_prefix_error_literal(error, "failed to read mbox data registers: "); return FALSE; } ptr += 4; @@ -226,9 +226,9 @@ GError **error) { gint max_tries = 100; - g_autoptr(GByteArray) st_regex = fu_struct_intel_usb4_mbox_new(); + g_autoptr(FuStructIntelUsb4Mbox) st_regex = fu_struct_intel_usb4_mbox_new(); - /* Write metadata register for operations that use it */ + /* write metadata register for operations that use it */ switch (opcode) { case FU_INTEL_USB4_OPCODE_NVM_WRITE: case FU_INTEL_USB4_OPCODE_NVM_AUTH_WRITE: @@ -262,7 +262,11 @@ /* write the operation and poll completion or error */ fu_struct_intel_usb4_mbox_set_opcode(st_regex, opcode); fu_struct_intel_usb4_mbox_set_status(st_regex, MBOX_OPVALID); - if (!fu_intel_usb4_device_set_mmio(self, MBOX_REG, st_regex->data, st_regex->len, error)) + if (!fu_intel_usb4_device_set_mmio(self, + MBOX_REG, + st_regex->buf->data, + st_regex->buf->len, + error)) return FALSE; /* leave early as successful USB4 AUTH resets the device immediately */ @@ -273,8 +277,8 @@ g_autoptr(GError) error_local = NULL; if (fu_intel_usb4_device_get_mmio(self, MBOX_REG, - st_regex->data, - st_regex->len, + st_regex->buf->data, + st_regex->buf->len, &error_local)) return TRUE; if (i == max_tries) { @@ -310,10 +314,10 @@ fu_chunk_get_data_sz(chk) / 4); if (!fu_intel_usb4_device_operation(self, FU_INTEL_USB4_OPCODE_NVM_READ, - st->data, - st->len, + st->buf->data, + st->buf->len, error)) { - g_prefix_error(error, "hub NVM read error: "); + g_prefix_error_literal(error, "hub NVM read error: "); return FALSE; } @@ -322,7 +326,7 @@ fu_chunk_get_data_out(chk), fu_chunk_get_data_sz(chk), error)) { - g_prefix_error(error, "hub firmware mbox data read error: "); + g_prefix_error_literal(error, "hub firmware mbox data read error: "); return FALSE; } } @@ -338,14 +342,14 @@ FuProgress *progress, GError **error) { - guint8 metadata[4]; + guint8 metadata[4] = {0}; g_autoptr(FuChunkArray) chunks = NULL; if (nvm_addr % 4 != 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "Invalid NVM write offset 0x%x, must be DW aligned: ", + "Invalid NVM write offset 0x%x, must be DW aligned", nvm_addr); return FALSE; } @@ -353,7 +357,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "Invalid NVM length 0x%x, must be 64 byte aligned: ", + "Invalid NVM length 0x%x, must be 64 byte aligned", (guint)g_bytes_get_size(blob)); return FALSE; } @@ -365,7 +369,7 @@ metadata, sizeof(metadata), error)) { - g_prefix_error(error, "hub NVM set offset error: "); + g_prefix_error_literal(error, "hub NVM set offset error: "); return FALSE; } @@ -390,7 +394,7 @@ fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), error)) { - g_prefix_error(error, "hub mbox data write error: "); + g_prefix_error_literal(error, "hub mbox data write error: "); return FALSE; } /* ask hub to write 64 bytes from data regs to NVM */ @@ -399,7 +403,7 @@ NULL, 0, error)) { - g_prefix_error(error, "hub NVM write operation error: "); + g_prefix_error_literal(error, "hub NVM write operation error: "); return FALSE; } @@ -425,7 +429,7 @@ NULL, 0, error)) { - g_prefix_error(error, "NVM authenticate failed: "); + g_prefix_error_literal(error, "NVM authenticate failed: "); fu_device_set_update_state(device, FWUPD_UPDATE_STATE_FAILED); return FALSE; } @@ -437,7 +441,7 @@ fu_intel_usb4_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuIntelUsb4Device *self = FU_INTEL_USB4_DEVICE(device); @@ -453,7 +457,7 @@ fw_vendor_id = fu_intel_thunderbolt_nvm_get_vendor_id(FU_INTEL_THUNDERBOLT_NVM(firmware)); fw_model_id = fu_intel_thunderbolt_nvm_get_model_id(FU_INTEL_THUNDERBOLT_NVM(firmware)); if (self->nvm_vendor_id != fw_vendor_id || self->nvm_model_id != fw_model_id) { - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) == 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -510,7 +514,7 @@ NULL, 0, error)) { - g_prefix_error(error, "NVM authenticate failed: "); + g_prefix_error_literal(error, "NVM authenticate failed: "); return FALSE; } fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); @@ -534,12 +538,12 @@ /* read from device and parse firmware */ if (!fu_intel_usb4_device_nvm_read(self, buf, sizeof(buf), 0, error)) { - g_prefix_error(error, "NVM read error: "); + g_prefix_error_literal(error, "NVM read error: "); return FALSE; } blob = g_bytes_new(buf, sizeof(buf)); - if (!fu_firmware_parse_bytes(fw, blob, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) { - g_prefix_error(error, "NVM parse error: "); + if (!fu_firmware_parse_bytes(fw, blob, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, error)) { + g_prefix_error_literal(error, "NVM parse error: "); return FALSE; } self->nvm_vendor_id = fu_intel_thunderbolt_nvm_get_vendor_id(FU_INTEL_THUNDERBOLT_NVM(fw)); @@ -562,7 +566,7 @@ } static void -fu_intel_usb4_device_set_progress(FuDevice *self, FuProgress *progress) +fu_intel_usb4_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/intel-usb4/fu-intel-usb4-plugin.c fwupd-2.0.20/plugins/intel-usb4/fu-intel-usb4-plugin.c --- fwupd-2.0.8/plugins/intel-usb4/fu-intel-usb4-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-usb4/fu-intel-usb4-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,6 +25,7 @@ fu_intel_usb4_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_INTEL_USB4_DEVICE); } diff -Nru fwupd-2.0.8/plugins/intel-usb4/fu-intel-usb4.rs fwupd-2.0.20/plugins/intel-usb4/fu-intel-usb4.rs --- fwupd-2.0.8/plugins/intel-usb4/fu-intel-usb4.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/intel-usb4/fu-intel-usb4.rs 2026-02-26 11:36:18.000000000 +0000 @@ -4,11 +4,11 @@ // hub operation #[repr(u16le)] enum FuIntelUsb4Opcode { - NVM_WRITE = 0x20, - NVM_AUTH_WRITE = 0x21, - NVM_READ = 0x22, - NVM_SET_OFFSET = 0x23, - DROM_READ = 0x24, + NvmWrite = 0x20, + NvmAuthWrite = 0x21, + NvmRead = 0x22, + NvmSetOffset = 0x23, + DromRead = 0x24, } #[derive(New, Parse)] diff -Nru fwupd-2.0.8/plugins/iommu/meson.build fwupd-2.0.20/plugins/iommu/meson.build --- fwupd-2.0.8/plugins/iommu/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/iommu/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if hsi and (host_cpu == 'x86' or host_cpu == 'x86_64') +hsi or subdir_done() +host_cpu in ['x86', 'x86_64'] or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginIommu"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -14,4 +16,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/jabra/fu-jabra-device.c fwupd-2.0.20/plugins/jabra/fu-jabra-device.c --- fwupd-2.0.8/plugins/jabra/fu-jabra-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra/fu-jabra-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -48,14 +48,21 @@ GError **error) { FuJabraDevice *self = FU_JABRA_DEVICE(device); - gsize magiclen = strlen(self->magic); + gsize magiclen; guint8 adr = 0x00; guint8 rep = 0x00; guint8 iface_hid; guint8 buf[33] = {0x00}; g_autoptr(GError) error_local = NULL; + /* sanity check */ + if (self->magic == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no magic"); + return FALSE; + } + /* parse string and create magic packet */ + magiclen = strlen(self->magic); if (!fu_firmware_strparse_uint8_safe(self->magic, magiclen, 0, &rep, error)) return FALSE; if (!fu_firmware_strparse_uint8_safe(self->magic, magiclen, 2, &adr, error)) diff -Nru fwupd-2.0.8/plugins/jabra/fu-jabra-plugin.c fwupd-2.0.20/plugins/jabra/fu-jabra-plugin.c --- fwupd-2.0.8/plugins/jabra/fu-jabra-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra/fu-jabra-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -61,6 +61,7 @@ FuPlugin *plugin = FU_PLUGIN(obj); FuContext *ctx = fu_plugin_get_context(plugin); fu_context_add_quirk_key(ctx, "JabraMagic"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_JABRA_DEVICE); } diff -Nru fwupd-2.0.8/plugins/jabra/tests/jabra-speak-410.json fwupd-2.0.20/plugins/jabra/tests/jabra-speak-410.json --- fwupd-2.0.8/plugins/jabra/tests/jabra-speak-410.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra/tests/jabra-speak-410.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/eab97d7e745e372e435dbd76404c3929730ac082-Jabra-SPEAK_410-1.8.cab", + "url": "eab97d7e745e372e435dbd76404c3929730ac082-Jabra-SPEAK_410-1.8.cab", "components": [ { "version": "1.8", @@ -14,7 +14,7 @@ ] }, { - "url": "https://fwupd.org/downloads/50a03efc5df333a948e159854ea40e1a3786c34c-Jabra-SPEAK_410-1.11.cab", + "url": "50a03efc5df333a948e159854ea40e1a3786c34c-Jabra-SPEAK_410-1.11.cab", "components": [ { "version": "1.11", diff -Nru fwupd-2.0.8/plugins/jabra/tests/jabra-speak-510.json fwupd-2.0.20/plugins/jabra/tests/jabra-speak-510.json --- fwupd-2.0.8/plugins/jabra/tests/jabra-speak-510.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra/tests/jabra-speak-510.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/45f88c50e79cfd30b6599df463463578d52f2fe9-Jabra-SPEAK_510-2.10.cab", + "url": "45f88c50e79cfd30b6599df463463578d52f2fe9-Jabra-SPEAK_510-2.10.cab", "components": [ { "version": "2.10", @@ -14,7 +14,7 @@ ] }, { - "url": "https://fwupd.org/downloads/c0523a98ef72508b5c7ddd687418b915ad5f4eb9-Jabra-SPEAK_510-2.14.cab", + "url": "c0523a98ef72508b5c7ddd687418b915ad5f4eb9-Jabra-SPEAK_510-2.14.cab", "components": [ { "version": "2.14", diff -Nru fwupd-2.0.8/plugins/jabra/tests/jabra-speak-710.json fwupd-2.0.20/plugins/jabra/tests/jabra-speak-710.json --- fwupd-2.0.8/plugins/jabra/tests/jabra-speak-710.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra/tests/jabra-speak-710.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/d2910cdbc45cf172767d05e60d9e39a07a10d242-Jabra-SPEAK_710-1.10.cab", + "url": "d2910cdbc45cf172767d05e60d9e39a07a10d242-Jabra-SPEAK_710-1.10.cab", "components": [ { "version": "1.10", @@ -14,7 +14,7 @@ ] }, { - "url": "https://fwupd.org/downloads/a5c627ae42de4e5c3ae3df28977f480624f96f66-Jabra-SPEAK_710-1.28.cab", + "url": "a5c627ae42de4e5c3ae3df28977f480624f96f66-Jabra-SPEAK_710-1.28.cab", "components": [ { "version": "1.28", diff -Nru fwupd-2.0.8/plugins/jabra-file/README.md fwupd-2.0.20/plugins/jabra-file/README.md --- fwupd-2.0.8/plugins/jabra-file/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-file/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -# Jabra FILE +--- +title: Plugin: Jabra File +--- ## Introduction @@ -25,10 +27,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Gianmarco: @gdpcastro diff -Nru fwupd-2.0.8/plugins/jabra-file/fu-jabra-file-device.c fwupd-2.0.20/plugins/jabra-file/fu-jabra-file-device.c --- fwupd-2.0.8/plugins/jabra-file/fu-jabra-file-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-file/fu-jabra-file-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,7 +19,7 @@ #define FU_JABRA_FILE_STANDARD_RECEIVE_TIMEOUT 1000 /* ms */ struct _FuJabraFileDevice { - FuHidDevice parent_instance; + FuUsbDevice parent_instance; guint8 sequence_number; guint8 address; guint8 epin; @@ -27,7 +27,7 @@ guint dfu_pid; }; -G_DEFINE_TYPE(FuJabraFileDevice, fu_jabra_file_device, FU_TYPE_HID_DEVICE) +G_DEFINE_TYPE(FuJabraFileDevice, fu_jabra_file_device, FU_TYPE_USB_DEVICE) static void fu_jabra_file_device_to_string(FuDevice *device, guint idt, GString *str) @@ -85,13 +85,13 @@ FuJabraFilePacket *cmd_req = (FuJabraFilePacket *)user_data; if (!fu_usb_device_interrupt_transfer(FU_USB_DEVICE(self), self->epout, - cmd_req->data, - cmd_req->len, + cmd_req->buf->data, + cmd_req->buf->len, NULL, FU_JABRA_FILE_STANDARD_SEND_TIMEOUT, NULL, /* cancellable */ error)) { - g_prefix_error(error, "failed to write to device: "); + g_prefix_error_literal(error, "failed to write to device: "); return FALSE; } return TRUE; @@ -116,32 +116,32 @@ if (!fu_usb_device_interrupt_transfer(FU_USB_DEVICE(self), self->epin, - cmd_rsp->data, - cmd_rsp->len, + cmd_rsp->buf->data, + cmd_rsp->buf->len, NULL, FU_JABRA_FILE_STANDARD_RECEIVE_TIMEOUT, NULL, /* cancellable */ error)) { - g_prefix_error(error, "failed to read from device: "); + g_prefix_error_literal(error, "failed to read from device: "); return FALSE; } - if (cmd_rsp->data[2] == self->address && - (cmd_rsp->data[5] != FU_JABRA_FILE_PACKET_CMD_IDENTITY && - cmd_rsp->data[5] != FU_JABRA_FILE_PACKET_CMD_FILE && - cmd_rsp->data[5] != FU_JABRA_FILE_PACKET_CMD_DFU && - cmd_rsp->data[5] != FU_JABRA_FILE_PACKET_CMD_VIDEO && - cmd_rsp->data[5] != FU_JABRA_FILE_PACKET_CMD_ACK && - cmd_rsp->data[5] != FU_JABRA_FILE_PACKET_CMD_NACK)) { + if (cmd_rsp->buf->data[2] == self->address && + (cmd_rsp->buf->data[5] != FU_JABRA_FILE_PACKET_CMD_IDENTITY && + cmd_rsp->buf->data[5] != FU_JABRA_FILE_PACKET_CMD_FILE && + cmd_rsp->buf->data[5] != FU_JABRA_FILE_PACKET_CMD_DFU && + cmd_rsp->buf->data[5] != FU_JABRA_FILE_PACKET_CMD_VIDEO && + cmd_rsp->buf->data[5] != FU_JABRA_FILE_PACKET_CMD_ACK && + cmd_rsp->buf->data[5] != FU_JABRA_FILE_PACKET_CMD_NACK)) { /* unrelated report, ignore and rx again */ if (!fu_usb_device_interrupt_transfer(FU_USB_DEVICE(self), 0x82, - cmd_rsp->data, - cmd_rsp->len, + cmd_rsp->buf->data, + cmd_rsp->buf->len, NULL, FU_JABRA_FILE_STANDARD_RECEIVE_TIMEOUT, NULL, /* cancellable */ error)) { - g_prefix_error(error, "failed to read from device: "); + g_prefix_error_literal(error, "failed to read from device: "); return FALSE; } } @@ -172,12 +172,12 @@ cmd_rsp = fu_jabra_file_device_rx(self, error); if (cmd_rsp == NULL) return FALSE; - if (self->sequence_number != cmd_rsp->data[3]) { + if (self->sequence_number != cmd_rsp->buf->data[3]) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "sequence_number error -- got 0x%x, expected 0x%x", - cmd_rsp->data[3], + cmd_rsp->buf->data[3], self->sequence_number); return FALSE; } @@ -219,10 +219,10 @@ cmd_rsp = fu_jabra_file_device_rx_with_sequence(self, error); if (cmd_rsp == NULL) return FALSE; - name = fu_memstrsafe(cmd_rsp->data, - cmd_rsp->len, + name = fu_memstrsafe(cmd_rsp->buf->data, + cmd_rsp->buf->len, FU_JABRA_FILE_PACKET_OFFSET_PAYLOAD + 1, - cmd_rsp->len - (FU_JABRA_FILE_PACKET_OFFSET_PAYLOAD + 1), + cmd_rsp->buf->len - (FU_JABRA_FILE_PACKET_OFFSET_PAYLOAD + 1), error); if (name == NULL) return FALSE; @@ -247,8 +247,8 @@ cmd_rsp = fu_jabra_file_device_rx_with_sequence(self, error); if (cmd_rsp == NULL) return FALSE; - self->dfu_pid = - fu_memread_uint16(cmd_rsp->data + FU_JABRA_FILE_PACKET_OFFSET_PAYLOAD, G_LITTLE_ENDIAN); + self->dfu_pid = fu_memread_uint16(cmd_rsp->buf->data + FU_JABRA_FILE_PACKET_OFFSET_PAYLOAD, + G_LITTLE_ENDIAN); return TRUE; } @@ -270,10 +270,10 @@ cmd_rsp = fu_jabra_file_device_rx_with_sequence(self, error); if (cmd_rsp == NULL) return FALSE; - version = fu_memstrsafe(cmd_rsp->data, - cmd_rsp->len, + version = fu_memstrsafe(cmd_rsp->buf->data, + cmd_rsp->buf->len, FU_JABRA_FILE_PACKET_OFFSET_PAYLOAD + 1, - cmd_rsp->len - (FU_JABRA_FILE_PACKET_OFFSET_PAYLOAD + 1), + cmd_rsp->buf->len - (FU_JABRA_FILE_PACKET_OFFSET_PAYLOAD + 1), error); if (version == NULL) return FALSE; @@ -321,15 +321,15 @@ cmd_rsp2 = fu_jabra_file_device_rx_with_sequence(self, error); if (cmd_rsp2 == NULL) return FALSE; - if (cmd_rsp2->data[5] == FU_JABRA_FILE_PACKET_CMD_NACK) { + if (cmd_rsp2->buf->data[5] == FU_JABRA_FILE_PACKET_CMD_NACK) { *match = FALSE; return TRUE; } if (!fu_memcpy_safe(device_checksum, sizeof(device_checksum), 0, - cmd_rsp2->data, - cmd_rsp2->len, + cmd_rsp2->buf->data, + cmd_rsp2->buf->len, 12, sizeof(device_checksum), error)) @@ -373,17 +373,17 @@ cmd_rsp = fu_jabra_file_device_rx_with_sequence(self, error); if (cmd_rsp == NULL) return FALSE; - if (cmd_rsp->data[5] == FU_JABRA_FILE_PACKET_CMD_NACK && cmd_rsp->data[6] == 0xF7) + if (cmd_rsp->buf->data[5] == FU_JABRA_FILE_PACKET_CMD_NACK && cmd_rsp->buf->data[6] == 0xF7) return TRUE; - if (cmd_rsp->data[5] == FU_JABRA_FILE_PACKET_CMD_ACK) + if (cmd_rsp->buf->data[5] == FU_JABRA_FILE_PACKET_CMD_ACK) return TRUE; g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "internal error: expected 0xFF, got 0x%02x 0x%02x", - cmd_rsp->data[5], - cmd_rsp->data[6]); + cmd_rsp->buf->data[5], + cmd_rsp->buf->data[6]); return FALSE; } @@ -406,8 +406,8 @@ fu_memwrite_uint32(data + 1, fu_firmware_get_size(firmware), G_BIG_ENDIAN); if (!fu_jabra_file_packet_set_payload(cmd_req, data, sizeof(data), error)) return FALSE; - if (!fu_memcpy_safe(cmd_req->data, - cmd_req->len, + if (!fu_memcpy_safe(cmd_req->buf->data, + cmd_req->buf->len, FU_JABRA_FILE_PACKET_OFFSET_PAYLOAD + sizeof(data), upgrade, sizeof(upgrade), @@ -420,13 +420,13 @@ cmd_rsp = fu_jabra_file_device_rx_with_sequence(self, error); if (cmd_rsp == NULL) return FALSE; - if (cmd_rsp->data[5] != FU_JABRA_FILE_PACKET_CMD_ACK) { + if (cmd_rsp->buf->data[5] != FU_JABRA_FILE_PACKET_CMD_ACK) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "internal error: expected 0xFF, got 0x%02x 0x%02x", - cmd_rsp->data[5], - cmd_rsp->data[6]); + cmd_rsp->buf->data[5], + cmd_rsp->buf->data[6]); return FALSE; } return TRUE; @@ -451,8 +451,8 @@ fu_jabra_file_packet_set_cmd(cmd_req, FU_JABRA_FILE_PACKET_CMD_FILE); if (!fu_jabra_file_packet_set_payload(cmd_req, data, sizeof(data), error)) return FALSE; - if (!fu_memcpy_safe(cmd_req->data, - cmd_req->len, + if (!fu_memcpy_safe(cmd_req->buf->data, + cmd_req->buf->len, FU_JABRA_FILE_PACKET_OFFSET_PAYLOAD + sizeof(data), buf, bufsz, @@ -467,13 +467,13 @@ cmd_rsp = fu_jabra_file_device_rx(self, error); if (cmd_rsp == NULL) return FALSE; - if (cmd_rsp->data[5] != FU_JABRA_FILE_PACKET_CMD_ACK) { + if (cmd_rsp->buf->data[5] != FU_JABRA_FILE_PACKET_CMD_ACK) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "internal error: expected 0xFF, got 0x%02x 0x%02x", - cmd_rsp->data[5], - cmd_rsp->data[6]); + cmd_rsp->buf->data[5], + cmd_rsp->buf->data[6]); return FALSE; } } @@ -527,7 +527,7 @@ cmd_rsp = fu_jabra_file_device_rx_with_sequence(self, error); if (cmd_rsp == NULL) return FALSE; - if (cmd_rsp->data[7] != 0x00) { + if (cmd_rsp->buf->data[7] != 0x00) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_AUTH_FAILED, "is busy"); return FALSE; } @@ -556,13 +556,13 @@ cmd_rsp = fu_jabra_file_device_rx_with_sequence(self, error); if (cmd_rsp == NULL) return FALSE; - if (cmd_rsp->data[5] != FU_JABRA_FILE_PACKET_CMD_ACK) { + if (cmd_rsp->buf->data[5] != FU_JABRA_FILE_PACKET_CMD_ACK) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "internal error: expected 0xFF, got 0x%02x 0x%02x", - cmd_rsp->data[5], - cmd_rsp->data[6]); + cmd_rsp->buf->data[5], + cmd_rsp->buf->data[6]); return FALSE; } return TRUE; @@ -572,7 +572,7 @@ fu_jabra_file_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuJabraFileDevice *self = FU_JABRA_FILE_DEVICE(device); @@ -656,10 +656,11 @@ if (!fu_jabra_file_device_file_checksum(self, firmware_checksum, &match, error)) return FALSE; if (!match) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "error transferring file to device, checksum doesn't match"); + g_set_error_literal( + error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "error transferring file to device, checksum does not match"); return FALSE; } } else { @@ -689,7 +690,7 @@ } static void -fu_jabra_file_device_set_progress(FuDevice *self, FuProgress *progress) +fu_jabra_file_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/jabra-file/fu-jabra-file-firmware.c fwupd-2.0.20/plugins/jabra-file/fu-jabra-file-firmware.c --- fwupd-2.0.8/plugins/jabra-file/fu-jabra-file-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-file/fu-jabra-file-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,7 @@ #include "fu-jabra-file-firmware.h" struct _FuJabraFileFirmware { - FuArchiveFirmware parent_instance; + FuFirmware parent_instance; guint16 dfu_pid; }; @@ -71,7 +71,7 @@ static gboolean fu_jabra_file_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuJabraFileFirmware *self = FU_JABRA_FILE_FIRMWARE(firmware); @@ -95,6 +95,8 @@ img_xml = fu_archive_firmware_get_image_fnmatch(FU_ARCHIVE_FIRMWARE(firmware_archive), "info.xml", error); + if (img_xml == NULL) + return FALSE; img_blob = fu_firmware_get_bytes(img_xml, error); if (img_blob == NULL) return FALSE; diff -Nru fwupd-2.0.8/plugins/jabra-file/fu-jabra-file-plugin.c fwupd-2.0.20/plugins/jabra-file/fu-jabra-file-plugin.c --- fwupd-2.0.8/plugins/jabra-file/fu-jabra-file-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-file/fu-jabra-file-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,12 +19,14 @@ static void fu_jabra_file_plugin_init(FuJabraFilePlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void fu_jabra_file_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_JABRA_FILE_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_JABRA_FILE_FIRMWARE); } diff -Nru fwupd-2.0.8/plugins/jabra-file/tests/jabra-panacast-50.json fwupd-2.0.20/plugins/jabra-file/tests/jabra-panacast-50.json --- fwupd-2.0.8/plugins/jabra-file/tests/jabra-panacast-50.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-file/tests/jabra-panacast-50.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/887800a9dec01390fa1581f95c07ec5d54aa0bdcda9e7fb5f9bb782702eedfe0-jabra-panacast-50-8.0.7.cab", - "emulation-url": "https://fwupd.org/downloads/779eda568d6146da1c02ee36d65e3b0dcc9b0094b924ccc976ab90b01967ed51-emulation.zip", + "url": "887800a9dec01390fa1581f95c07ec5d54aa0bdcda9e7fb5f9bb782702eedfe0-jabra-panacast-50-8.0.7.cab", + "emulation-url": "779eda568d6146da1c02ee36d65e3b0dcc9b0094b924ccc976ab90b01967ed51-emulation.zip", "emulation-file": "@enumeration_datadir@/jabra-panacast-50-setup.json", "components": [ { diff -Nru fwupd-2.0.8/plugins/jabra-gnp/README.md fwupd-2.0.20/plugins/jabra-gnp/README.md --- fwupd-2.0.8/plugins/jabra-gnp/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -# Jabra GNP +--- +title: Plugin: Jabra GNP +--- ## Introduction @@ -39,10 +41,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.9.2`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Gianmarco: @gdpcastro diff -Nru fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-child-device.c fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-child-device.c --- fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-child-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-child-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,422 @@ +/* + * Copyright 2023 GN Audio + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-jabra-gnp-child-device.h" +#include "fu-jabra-gnp-common.h" +#include "fu-jabra-gnp-device.h" +#include "fu-jabra-gnp-firmware.h" +#include "fu-jabra-gnp-image.h" + +struct _FuJabraGnpChildDevice { + FuDevice parent_instance; + guint8 fwu_protocol; + guint8 sequence_number; + guint8 address; + guint16 dfu_pid; +}; + +G_DEFINE_TYPE(FuJabraGnpChildDevice, fu_jabra_gnp_child_device, FU_TYPE_DEVICE) + +static void +fu_jabra_gnp_child_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuJabraGnpChildDevice *self = FU_JABRA_GNP_CHILD_DEVICE(device); + fwupd_codec_string_append_hex(str, idt, "FwuProtocol", self->fwu_protocol); + fwupd_codec_string_append_hex(str, idt, "SequenceNumber", self->sequence_number); + fwupd_codec_string_append_hex(str, idt, "Address", self->address); + fwupd_codec_string_append_hex(str, idt, "DfuPid", self->dfu_pid); +} + +void +fu_jabra_gnp_child_device_set_dfu_pid_and_seq(FuJabraGnpChildDevice *self, guint16 dfu_pid) +{ + g_return_if_fail(FU_IS_JABRA_GNP_CHILD_DEVICE(self)); + self->dfu_pid = dfu_pid; + self->sequence_number = 0x00; +} + +gboolean +fu_jabra_gnp_child_device_tx_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuJabraGnpTxData *tx_data = (FuJabraGnpTxData *)user_data; + FuJabraGnpDevice *parent; + + parent = FU_JABRA_GNP_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_control_transfer(FU_USB_DEVICE(parent), + FU_USB_DIRECTION_HOST_TO_DEVICE, + FU_USB_REQUEST_TYPE_CLASS, + FU_USB_RECIPIENT_INTERFACE, + 0x09, + 0x0200 | FU_JABRA_GNP_IFACE, + fu_jabra_gnp_device_get_iface_hid(parent), + tx_data->buf->data, + tx_data->buf->len, + NULL, + tx_data->timeout, + NULL, /* cancellable */ + error)) { + g_prefix_error_literal(error, "failed to write to device: "); + return FALSE; + } + return TRUE; +} + +gboolean +fu_jabra_gnp_child_device_rx_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuJabraGnpChildDevice *self = FU_JABRA_GNP_CHILD_DEVICE(device); + const guint8 match_buf[FU_JABRA_GNP_BUF_SIZE] = { + FU_JABRA_GNP_IFACE, + 0x00, + self->address, + 0x00, + 0x0A, + 0x12, + 0x02, + }; + const guint8 empty_buf[FU_JABRA_GNP_BUF_SIZE] = {0x00}; + FuJabraGnpRxData *rx_data = (FuJabraGnpRxData *)user_data; + FuJabraGnpDevice *parent; + + parent = FU_JABRA_GNP_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) + return FALSE; + if (!fu_usb_device_interrupt_transfer(FU_USB_DEVICE(parent), + fu_jabra_gnp_device_get_epin(parent), + rx_data->rxbuf, + FU_JABRA_GNP_BUF_SIZE, + NULL, + rx_data->timeout, + NULL, /* cancellable */ + error)) { + g_prefix_error_literal(error, "failed to read from device: "); + return FALSE; + } + if (rx_data->rxbuf[2] == match_buf[2] && rx_data->rxbuf[3] == match_buf[3] && + rx_data->rxbuf[5] == match_buf[5] && rx_data->rxbuf[6] == match_buf[6]) { + /* battery report, ignpre and rx again */ + if (!fu_usb_device_interrupt_transfer(FU_USB_DEVICE(parent), + 0x81, + rx_data->rxbuf, + FU_JABRA_GNP_BUF_SIZE, + NULL, + rx_data->timeout, + NULL, /* cancellable */ + error)) { + g_prefix_error_literal(error, "failed to read from device: "); + return FALSE; + } + } + + if (fu_memcmp_safe(rx_data->rxbuf, + sizeof(rx_data->rxbuf), + 0, + empty_buf, + sizeof(rx_data->rxbuf), + 0, + sizeof(rx_data->rxbuf), + error)) { + g_prefix_error_literal(error, "error reading from device: "); + return FALSE; + } + return TRUE; +} + +gboolean +fu_jabra_gnp_child_device_rx_with_sequence_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuJabraGnpChildDevice *self = FU_JABRA_GNP_CHILD_DEVICE(device); + FuJabraGnpRxData *rx_data = (FuJabraGnpRxData *)user_data; + + if (!fu_device_retry_full(FU_DEVICE(self), + fu_jabra_gnp_child_device_rx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + rx_data, + error)) + return FALSE; + if (self->sequence_number != rx_data->rxbuf[3]) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "sequence_number error -- got 0x%x, expected 0x%x", + rx_data->rxbuf[3], + self->sequence_number); + return FALSE; + } + self->sequence_number += 1; + return TRUE; +} + +static FuFirmware * +fu_jabra_gnp_child_device_prepare_firmware(FuDevice *device, + GInputStream *stream, + FuProgress *progress, + FuFirmwareParseFlags flags, + GError **error) +{ + FuJabraGnpChildDevice *self = FU_JABRA_GNP_CHILD_DEVICE(device); + g_autoptr(FuFirmware) firmware = fu_jabra_gnp_firmware_new(); + + /* unzip and get images */ + if (!fu_firmware_parse_stream(firmware, stream, 0x0, flags, error)) + return NULL; + if (fu_jabra_gnp_firmware_get_dfu_pid(FU_JABRA_GNP_FIRMWARE(firmware)) != self->dfu_pid) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "wrong DFU PID, got 0x%x, expected 0x%x", + fu_jabra_gnp_firmware_get_dfu_pid(FU_JABRA_GNP_FIRMWARE(firmware)), + self->dfu_pid); + return NULL; + } + return g_steal_pointer(&firmware); +} + +static gboolean +fu_jabra_gnp_child_device_setup(FuDevice *device, GError **error) +{ + FuJabraGnpChildDevice *self = FU_JABRA_GNP_CHILD_DEVICE(device); + + if (!fu_jabra_gnp_ensure_name(device, self->address, self->sequence_number, error)) + return FALSE; + if (!fu_jabra_gnp_ensure_version(device, self->address, self->sequence_number, error)) + return FALSE; + if (!fu_jabra_gnp_read_dfu_pid(device, + self->address, + self->sequence_number, + &self->dfu_pid, + error)) + return FALSE; + if (!fu_jabra_gnp_ensure_battery_level(device, self->address, self->sequence_number, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_jabra_gnp_child_device_write_image(FuJabraGnpChildDevice *self, + FuFirmware *firmware, + FuFirmware *img, + FuProgress *progress, + GError **error) +{ + const guint chunk_size = 52; + g_autoptr(FuChunkArray) chunks = NULL; + g_autoptr(GInputStream) stream = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "write-partition"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 1, "start"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 5, "flash-erase-done"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 91, "write-chunks"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "read-verify-status"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "write-version"); + + /* write partition */ + stream = fu_firmware_get_stream(img, error); + if (stream == NULL) + return FALSE; + if (!fu_jabra_gnp_write_partition(FU_DEVICE(self), + self->address, + self->sequence_number, + fu_firmware_get_idx(img), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* start erasing */ + if (!fu_jabra_gnp_start(FU_DEVICE(self), self->address, self->sequence_number, error)) + return FALSE; + fu_progress_step_done(progress); + + /* poll for erase done */ + if (!fu_jabra_gnp_flash_erase_done(FU_DEVICE(self), self->address, error)) + return FALSE; + fu_progress_step_done(progress); + + /* write chunks */ + chunks = fu_chunk_array_new_from_stream(stream, + FU_CHUNK_ADDR_OFFSET_NONE, + FU_CHUNK_PAGESZ_NONE, + chunk_size, + error); + if (chunks == NULL) + return FALSE; + if (self->fwu_protocol == FU_JABRA_GNP_PROTOCOL_OTA) { + if (!fu_jabra_gnp_write_crc(FU_DEVICE(self), + self->address, + self->sequence_number, + fu_jabra_gnp_image_get_crc32(FU_JABRA_GNP_IMAGE(img)), + fu_chunk_array_length(chunks), + FU_JABRA_GNP_PRELOAD_COUNT, + error)) + return FALSE; + } else { + /* self->fwu_protocol == FU_JABRA_GNP_PROTOCOL_EXTENDED_OTA*/ + if (!fu_jabra_gnp_write_extended_crc( + FU_DEVICE(self), + self->address, + self->sequence_number, + fu_jabra_gnp_image_get_crc32(FU_JABRA_GNP_IMAGE(img)), + fu_chunk_array_length(chunks), + FU_JABRA_GNP_PRELOAD_COUNT, + error)) + return FALSE; + } + if (!fu_jabra_gnp_write_chunks(FU_DEVICE(self), + self->address, + chunks, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify */ + if (!fu_jabra_gnp_read_verify_status(FU_DEVICE(self), self->address, error)) + return FALSE; + fu_progress_step_done(progress); + + /* write version */ + if (!fu_jabra_gnp_write_version( + FU_DEVICE(self), + self->address, + self->sequence_number, + fu_jabra_gnp_firmware_get_version_data(FU_JABRA_GNP_FIRMWARE(firmware)), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_jabra_gnp_child_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuJabraGnpChildDevice *self = FU_JABRA_GNP_CHILD_DEVICE(device); + g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + fu_progress_add_step(progress, + FWUPD_STATUS_UNKNOWN, + fu_firmware_get_size(img), + fu_firmware_get_id(img)); + } + if (!fu_jabra_gnp_read_fwu_protocol(device, + self->address, + self->sequence_number, + &self->fwu_protocol, + error)) + return FALSE; + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + if (!fu_jabra_gnp_child_device_write_image(self, + firmware, + img, + fu_progress_get_child(progress), + error)) { + g_prefix_error(error, "failed to write %s: ", fu_firmware_get_id(img)); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* write squif */ + return fu_jabra_gnp_write_dfu_from_squif(device, + self->address, + self->sequence_number, + error); +} + +static gboolean +fu_jabra_gnp_child_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuJabraGnpChildDevice *self = FU_JABRA_GNP_CHILD_DEVICE(device); + fu_device_sleep_full(FU_DEVICE(self), 45000, progress); + return TRUE; +} + +static gboolean +fu_jabra_gnp_child_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuJabraGnpChildDevice *self = FU_JABRA_GNP_CHILD_DEVICE(device); + + if (g_strcmp0(key, "JabraGnpAddress") == 0) { + guint64 val = 0; + if (!fu_strtoull(value, &val, 0x0, G_MAXUINT8, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->address = (guint8)val; + return TRUE; + } + + /* failed */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_jabra_gnp_child_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 75, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 15, "reload"); +} + +static void +fu_jabra_gnp_child_device_init(FuJabraGnpChildDevice *self) +{ + self->address = FU_JABRA_GNP_ADDRESS_OTA_CHILD; + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); + /* prohibit to close parent's communication descriptor */ + fu_device_set_logical_id(FU_DEVICE(self), "ota_device"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_ADD_COUNTERPART_GUIDS); + fu_device_add_protocol(FU_DEVICE(self), "com.jabra.gnp"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_JABRA_GNP_FIRMWARE); + fu_device_set_remove_delay(FU_DEVICE(self), 10000); +} + +static void +fu_jabra_gnp_child_device_class_init(FuJabraGnpChildDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->to_string = fu_jabra_gnp_child_device_to_string; + device_class->prepare_firmware = fu_jabra_gnp_child_device_prepare_firmware; + device_class->setup = fu_jabra_gnp_child_device_setup; + device_class->write_firmware = fu_jabra_gnp_child_device_write_firmware; + device_class->attach = fu_jabra_gnp_child_device_attach; + device_class->set_quirk_kv = fu_jabra_gnp_child_device_set_quirk_kv; + device_class->set_progress = fu_jabra_gnp_child_device_set_progress; +} diff -Nru fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-child-device.h fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-child-device.h --- fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-child-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-child-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright 2023 GN Audio A/S + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_JABRA_GNP_CHILD_DEVICE (fu_jabra_gnp_child_device_get_type()) +G_DECLARE_FINAL_TYPE(FuJabraGnpChildDevice, + fu_jabra_gnp_child_device, + FU, + JABRA_GNP_CHILD_DEVICE, + FuDevice) + +void +fu_jabra_gnp_child_device_set_dfu_pid_and_seq(FuJabraGnpChildDevice *self, guint16 dfu_pid) + G_GNUC_NON_NULL(1); + +gboolean +fu_jabra_gnp_child_device_tx_cb(FuDevice *device, gpointer user_data, GError **error) + G_GNUC_NON_NULL(1); + +gboolean +fu_jabra_gnp_child_device_rx_cb(FuDevice *device, gpointer user_data, GError **error) + G_GNUC_NON_NULL(1); + +gboolean +fu_jabra_gnp_child_device_rx_with_sequence_cb(FuDevice *device, gpointer user_data, GError **error) + G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-common.c fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-common.c --- fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,7 +6,17 @@ #include "config.h" +#include "fu-jabra-gnp-child-device.h" #include "fu-jabra-gnp-common.h" +#include "fu-jabra-gnp-device.h" +#include "fu-jabra-gnp-firmware.h" +#include "fu-jabra-gnp-image.h" +#include "fu-jabra-gnp-struct.h" + +/* + * NOTE: DO NOT ALLOW ANY MORE MAGIC CONSTANTS IN THIS FILE + * nocheck:magic-inlines=400 + */ static guint64 fu_jabra_gnp_update_crc(guint64 acc, guint64 delta) @@ -65,10 +75,813 @@ g_byte_array_append(buf, tmp, sizeof(tmp)); fu_byte_array_append_bytes(buf, bytes); - for (guint i = buf->len; i > 0; i -= 2) { + for (gint i = buf->len; i > 0; i -= 2) { if (i > 1) crc = fu_jabra_gnp_update_crc(crc, buf->data[i - 2] & 0xFF); crc = fu_jabra_gnp_update_crc(crc, buf->data[i - 1] & 0xFF); } return crc; } + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_ensure_name(FuDevice *device, guint8 address, guint8 seq, GError **error) +{ + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); + FuJabraGnpTxData tx_data = { + .buf = st->buf, + .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, + }; + g_autofree gchar *name = NULL; + + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, address); + fu_struct_jabra_gnp_packet_set_sequence_number(st, seq); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x02); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x00); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_tx_cb + : fu_jabra_gnp_device_tx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &tx_data, + error)) + return FALSE; + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_with_sequence_cb + : fu_jabra_gnp_device_rx_with_sequence_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + name = fu_memstrsafe(rx_data.rxbuf, + sizeof(rx_data.rxbuf), + 0x8, + sizeof(rx_data.rxbuf) - 8, + error); + if (name == NULL) + return FALSE; + fu_device_set_name(device, name); + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_ensure_battery_level(FuDevice *device, guint8 address, guint8 seq, GError **error) +{ + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); + FuJabraGnpTxData tx_data = { + .buf = st->buf, + .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, + }; + guint8 battery_level = 0; + + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, address); + fu_struct_jabra_gnp_packet_set_sequence_number(st, seq); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x12); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x02); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_tx_cb + : fu_jabra_gnp_device_tx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &tx_data, + error)) + return FALSE; + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_with_sequence_cb + : fu_jabra_gnp_device_rx_with_sequence_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + if (!fu_memread_uint8_safe(rx_data.rxbuf, FU_JABRA_GNP_BUF_SIZE, 8, &battery_level, error)) + return FALSE; + if (battery_level == 0x00) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "battery level was 0"); + return FALSE; + } + fu_device_set_battery_level(device, battery_level); + fu_device_set_battery_threshold(device, 30); + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_read_dfu_pid(FuDevice *device, + guint8 address, + guint8 seq, + guint16 *dfu_pid, + GError **error) +{ + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); + FuJabraGnpTxData tx_data = { + .buf = st->buf, + .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, + }; + + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, address); + fu_struct_jabra_gnp_packet_set_sequence_number(st, seq); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x02); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x13); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_tx_cb + : fu_jabra_gnp_device_tx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &tx_data, + error)) + return FALSE; + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_with_sequence_cb + : fu_jabra_gnp_device_rx_with_sequence_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + *dfu_pid = fu_memread_uint16(rx_data.rxbuf + 7, G_LITTLE_ENDIAN); + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_ensure_version(FuDevice *device, guint8 address, guint8 seq, GError **error) +{ + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); + FuJabraGnpTxData tx_data = { + .buf = st->buf, + .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, + }; + g_autofree gchar *version = NULL; + + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, address); + fu_struct_jabra_gnp_packet_set_sequence_number(st, seq); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x02); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x03); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_tx_cb + : fu_jabra_gnp_device_tx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &tx_data, + error)) + return FALSE; + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_with_sequence_cb + : fu_jabra_gnp_device_rx_with_sequence_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + + version = fu_memstrsafe(rx_data.rxbuf, + sizeof(rx_data.rxbuf), + 0x8, + sizeof(rx_data.rxbuf) - 8, + error); + + if (version == NULL) + return FALSE; + + /* some devices append a few extra non number characters to the version, which can confuse + * fwupd's formats, so remove it */ + while (!(g_str_has_suffix(version, "0") || g_str_has_suffix(version, "1") || + g_str_has_suffix(version, "2") || g_str_has_suffix(version, "3") || + g_str_has_suffix(version, "4") || g_str_has_suffix(version, "5") || + g_str_has_suffix(version, "6") || g_str_has_suffix(version, "7") || + g_str_has_suffix(version, "8") || g_str_has_suffix(version, "9"))) + version[strlen(version) - 1] = '\0'; + + fu_device_set_version(device, version); + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_read_fwu_protocol(FuDevice *device, + guint8 address, + guint8 seq, + guint8 *fwu_protocol, + GError **error) +{ + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); + FuJabraGnpTxData tx_data = { + .buf = st->buf, + .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, + }; + + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, address); + fu_struct_jabra_gnp_packet_set_sequence_number(st, seq); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x02); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x14); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_tx_cb + : fu_jabra_gnp_device_tx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &tx_data, + error)) + return FALSE; + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_with_sequence_cb + : fu_jabra_gnp_device_rx_with_sequence_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + if (rx_data.rxbuf[7] != FU_JABRA_GNP_PROTOCOL_OTA && + rx_data.rxbuf[7] != FU_JABRA_GNP_PROTOCOL_EXTENDED_OTA) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "unrecognized protocol: expected 7 or 16, got %d", + rx_data.rxbuf[7]); + return FALSE; + } + *fwu_protocol = rx_data.rxbuf[7]; + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_write_partition(FuDevice *device, + guint8 address, + guint8 seq, + guint8 part, + GError **error) +{ + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); + FuJabraGnpTxData tx_data = { + .buf = st->buf, + .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, + }; + + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, address); + fu_struct_jabra_gnp_packet_set_sequence_number(st, seq); + fu_struct_jabra_gnp_packet_set_cmd_length(st, 0x87); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x0F); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x2D); + fu_byte_array_append_uint8(st->buf, part); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_tx_cb + : fu_jabra_gnp_device_tx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &tx_data, + error)) + return FALSE; + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_with_sequence_cb + : fu_jabra_gnp_device_rx_with_sequence_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + if (rx_data.rxbuf[5] != 0xFF) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "internal error: expected 0xFF, got 0x%02x 0x%02x", + rx_data.rxbuf[5], + rx_data.rxbuf[6]); + return FALSE; + } + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_start(FuDevice *device, guint8 address, guint8 seq, GError **error) +{ + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); + FuJabraGnpTxData tx_data = { + .buf = st->buf, + .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, + }; + + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, address); + fu_struct_jabra_gnp_packet_set_sequence_number(st, seq); + fu_struct_jabra_gnp_packet_set_cmd_length(st, 0x86); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x0F); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x17); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_tx_cb + : fu_jabra_gnp_device_tx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &tx_data, + error)) + return FALSE; + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_with_sequence_cb + : fu_jabra_gnp_device_rx_with_sequence_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + if (rx_data.rxbuf[5] != 0xFF) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "internal error: expected 0xFF, got 0x%02x 0x%02x", + rx_data.rxbuf[5], + rx_data.rxbuf[6]); + return FALSE; + } + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_flash_erase_done(FuDevice *device, guint8 address, GError **error) +{ + const guint8 match_buf[FU_JABRA_GNP_BUF_SIZE] = { + FU_JABRA_GNP_IFACE, + 0x00, + address, + 0x00, + 0x06, + 0x0F, + 0x18, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_EXTRA_LONG_RECEIVE_TIMEOUT, + }; + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_cb + : fu_jabra_gnp_device_rx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + if (rx_data.rxbuf[5] != match_buf[5] || rx_data.rxbuf[6] != match_buf[6]) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "internal error, buf did not match"); + return FALSE; + } + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_write_crc(FuDevice *device, + guint8 address, + guint8 seq, + guint32 crc, + guint total_chunks, + guint preload_count, + GError **error) +{ + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); + FuJabraGnpTxData tx_data = { + .buf = st->buf, + .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, + }; + + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, address); + fu_struct_jabra_gnp_packet_set_sequence_number(st, seq); + fu_struct_jabra_gnp_packet_set_cmd_length(st, 0x8E); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x0F); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x19); + + fu_byte_array_append_uint32(st->buf, crc, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(st->buf, total_chunks, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(st->buf, preload_count, G_LITTLE_ENDIAN); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_tx_cb + : fu_jabra_gnp_device_tx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &tx_data, + error)) + return FALSE; + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_with_sequence_cb + : fu_jabra_gnp_device_rx_with_sequence_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + if (rx_data.rxbuf[5] != 0xFF) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "internal error: expected 0xFF, got 0x%02x 0x%02x", + rx_data.rxbuf[5], + rx_data.rxbuf[6]); + return FALSE; + } + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_write_extended_crc(FuDevice *device, + guint8 address, + guint8 seq, + guint32 crc, + guint total_chunks, + guint preload_count, + GError **error) +{ + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); + FuJabraGnpTxData tx_data = { + .buf = st->buf, + .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, + }; + + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, address); + fu_struct_jabra_gnp_packet_set_sequence_number(st, seq); + fu_struct_jabra_gnp_packet_set_cmd_length(st, 0x92); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x0F); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x19); + fu_byte_array_append_uint32(st->buf, crc, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(st->buf, 0x00, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(st->buf, preload_count, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(st->buf, total_chunks, G_LITTLE_ENDIAN); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_tx_cb + : fu_jabra_gnp_device_tx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &tx_data, + error)) + return FALSE; + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_with_sequence_cb + : fu_jabra_gnp_device_rx_with_sequence_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + if (rx_data.rxbuf[5] != 0xFF) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "internal error: expected 0xFF, got 0x%02x 0x%02x", + rx_data.rxbuf[5], + rx_data.rxbuf[6]); + return FALSE; + } + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +static gboolean +fu_jabra_gnp_write_chunk(FuDevice *device, + guint8 address, + guint32 chunk_number, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + guint8 write_length = 0x00 + bufsz + 10; + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); + FuJabraGnpTxData tx_data = { + .buf = st->buf, + .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, + }; + + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, address); + fu_struct_jabra_gnp_packet_set_sequence_number(st, 0x00); + fu_struct_jabra_gnp_packet_set_cmd_length(st, write_length); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x0F); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x1A); + fu_byte_array_append_uint16(st->buf, chunk_number, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(st->buf, bufsz, G_LITTLE_ENDIAN); + g_byte_array_append(st->buf, buf, bufsz); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); + + return fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_tx_cb + : fu_jabra_gnp_device_tx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &tx_data, + error); +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_write_chunks(FuDevice *device, + guint8 address, + FuChunkArray *chunks, + FuProgress *progress, + GError **error) +{ + gboolean failed_chunk = FALSE; + + const guint8 match_buf[FU_JABRA_GNP_BUF_SIZE] = { + FU_JABRA_GNP_IFACE, + 0x00, + address, + 0x00, + 0x06, + 0x0F, + 0x1B, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, + }; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); + for (gint chunk_number = 0; (guint)chunk_number < fu_chunk_array_length(chunks); + chunk_number++) { + g_autoptr(FuChunk) chk = NULL; + + /* prepare chunk */ + chk = fu_chunk_array_index(chunks, chunk_number, error); + if (chk == NULL) + return FALSE; + if (!fu_jabra_gnp_write_chunk(device, + address, + chunk_number, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + if (((chunk_number % FU_JABRA_GNP_PRELOAD_COUNT) == 0) || + (guint)chunk_number == fu_chunk_array_length(chunks) - 1) { + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_cb + : fu_jabra_gnp_device_rx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + if (rx_data.rxbuf[5] != match_buf[5] || rx_data.rxbuf[6] != match_buf[6]) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "internal error, buf did not match"); + return FALSE; + } + if (fu_memread_uint16(rx_data.rxbuf + 7, G_LITTLE_ENDIAN) == chunk_number || + fu_memread_uint16(rx_data.rxbuf + 7, G_LITTLE_ENDIAN) == + (chunk_number % 0xFFFF) - 1) { + failed_chunk = FALSE; + } else { + chunk_number--; + failed_chunk = TRUE; + } + } + if (!failed_chunk) + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_read_verify_status(FuDevice *device, guint8 address, GError **error) +{ + const guint8 match_buf[FU_JABRA_GNP_BUF_SIZE] = { + FU_JABRA_GNP_IFACE, + 0x00, + address, + 0x00, + 0x06, + 0x0F, + 0x1C, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_LONG_RECEIVE_TIMEOUT, + }; + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_cb + : fu_jabra_gnp_device_rx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + if (rx_data.rxbuf[5] != match_buf[5] || rx_data.rxbuf[6] != match_buf[6]) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "internal error, buf did not match"); + return FALSE; + } + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_write_version(FuDevice *device, + guint8 address, + guint8 seq, + FuJabraGnpVersionData *version_data, + GError **error) +{ + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); + FuJabraGnpTxData tx_data = { + .buf = st->buf, + .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, + }; + + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, address); + fu_struct_jabra_gnp_packet_set_sequence_number(st, seq); + fu_struct_jabra_gnp_packet_set_cmd_length(st, 0x89); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x0F); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x1E); + fu_byte_array_append_uint8(st->buf, version_data->major); + fu_byte_array_append_uint8(st->buf, version_data->minor); + fu_byte_array_append_uint8(st->buf, version_data->micro); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_tx_cb + : fu_jabra_gnp_device_tx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &tx_data, + error)) + return FALSE; + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_with_sequence_cb + : fu_jabra_gnp_device_rx_with_sequence_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + if (rx_data.rxbuf[5] != 0xFF) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "internal error: expected 0xFF, got 0x%02x 0x%02x", + rx_data.rxbuf[5], + rx_data.rxbuf[6]); + return FALSE; + } + return TRUE; +} + +/* nocheck:name -- this should probably be implemented using an interface */ +gboolean +fu_jabra_gnp_write_dfu_from_squif(FuDevice *device, guint8 address, guint8 seq, GError **error) +{ + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); + FuJabraGnpTxData tx_data = { + .buf = st->buf, + .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, + }; + FuJabraGnpRxData rx_data = { + .rxbuf = {0x00}, + .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, + }; + + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, address); + fu_struct_jabra_gnp_packet_set_sequence_number(st, seq); + fu_struct_jabra_gnp_packet_set_cmd_length(st, 0x86); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x0F); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x1D); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); + + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_tx_cb + : fu_jabra_gnp_device_tx_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &tx_data, + error)) + return FALSE; + if (!fu_device_retry_full(device, + address == FU_JABRA_GNP_ADDRESS_OTA_CHILD + ? fu_jabra_gnp_child_device_rx_with_sequence_cb + : fu_jabra_gnp_device_rx_with_sequence_cb, + FU_JABRA_GNP_MAX_RETRIES, + FU_JABRA_GNP_RETRY_DELAY, + &rx_data, + error)) + return FALSE; + if (rx_data.rxbuf[5] != 0xFF) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "internal error: expected 0xFF, got 0x%02x 0x%02x", + rx_data.rxbuf[5], + rx_data.rxbuf[6]); + return FALSE; + } + return TRUE; +} diff -Nru fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-common.h fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-common.h --- fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,5 +8,107 @@ #include +#define FU_JABRA_GNP_BUF_SIZE 64 +#define FU_JABRA_GNP_MAX_RETRIES 3 +#define FU_JABRA_GNP_PRELOAD_COUNT 10 +#define FU_JABRA_GNP_RETRY_DELAY 100 /* ms */ +#define FU_JABRA_GNP_STANDARD_SEND_TIMEOUT 3000 /* ms */ +#define FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT 1000 /* ms */ +#define FU_JABRA_GNP_LONG_RECEIVE_TIMEOUT 30000 /* ms */ +#define FU_JABRA_GNP_EXTRA_LONG_RECEIVE_TIMEOUT 60000 /* ms */ + +#define FU_JABRA_GNP_IFACE 0x05 + +#define FU_JABRA_GNP_ADDRESS_PARENT 0x01 +#define FU_JABRA_GNP_ADDRESS_OTA_CHILD 0x04 + +#define FU_JABRA_GNP_PROTOCOL_OTA 7 +#define FU_JABRA_GNP_PROTOCOL_EXTENDED_OTA 16 + +typedef struct { + GByteArray *buf; + const guint timeout; +} FuJabraGnpTxData; + +typedef struct { + guint8 rxbuf[FU_JABRA_GNP_BUF_SIZE]; + const guint timeout; +} FuJabraGnpRxData; + +typedef struct { + guint8 major; + guint8 minor; + guint8 micro; +} FuJabraGnpVersionData; + guint64 fu_jabra_gnp_calculate_crc(GBytes *bytes); + +gboolean +fu_jabra_gnp_ensure_name(FuDevice *self, guint8 address, guint8 seq, GError **error) + G_GNUC_NON_NULL(1); +gboolean +fu_jabra_gnp_ensure_battery_level(FuDevice *self, guint8 address, guint8 seq, GError **error) + G_GNUC_NON_NULL(1); +gboolean +fu_jabra_gnp_read_dfu_pid(FuDevice *self, + guint8 address, + guint8 seq, + guint16 *dfu_pid, + GError **error) G_GNUC_NON_NULL(1, 4); +gboolean +fu_jabra_gnp_ensure_version(FuDevice *self, guint8 address, guint8 seq, GError **error) + G_GNUC_NON_NULL(1); + +gboolean +fu_jabra_gnp_read_fwu_protocol(FuDevice *self, + guint8 address, + guint8 seq, + guint8 *fwu_protocol, + GError **error) G_GNUC_NON_NULL(1, 4); +gboolean +fu_jabra_gnp_write_partition(FuDevice *self, + guint8 address, + guint8 seq, + guint8 part, + GError **error) G_GNUC_NON_NULL(1); + +gboolean +fu_jabra_gnp_start(FuDevice *self, guint8 address, guint8 seq, GError **error) G_GNUC_NON_NULL(1); + +gboolean +fu_jabra_gnp_flash_erase_done(FuDevice *self, guint8 address, GError **error) G_GNUC_NON_NULL(1); +gboolean +fu_jabra_gnp_write_crc(FuDevice *self, + guint8 address, + guint8 seq, + guint32 crc, + guint total_chunks, + guint preload_count, + GError **error) G_GNUC_NON_NULL(1); + +gboolean +fu_jabra_gnp_write_extended_crc(FuDevice *self, + guint8 address, + guint8 seq, + guint32 crc, + guint total_chunks, + guint preload_count, + GError **error) G_GNUC_NON_NULL(1); +gboolean +fu_jabra_gnp_write_chunks(FuDevice *self, + guint8 address, + FuChunkArray *chunks, + FuProgress *progress, + GError **error) G_GNUC_NON_NULL(1, 3); +gboolean +fu_jabra_gnp_read_verify_status(FuDevice *self, guint8 address, GError **error) G_GNUC_NON_NULL(1); +gboolean +fu_jabra_gnp_write_version(FuDevice *self, + guint8 address, + guint8 seq, + FuJabraGnpVersionData *version_data, + GError **error) G_GNUC_NON_NULL(1, 4); +gboolean +fu_jabra_gnp_write_dfu_from_squif(FuDevice *self, guint8 address, guint8 seq, GError **error) + G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-device.c fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-device.c --- fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,48 +6,44 @@ #include "config.h" +#include "fu-jabra-gnp-child-device.h" +#include "fu-jabra-gnp-common.h" #include "fu-jabra-gnp-device.h" #include "fu-jabra-gnp-firmware.h" #include "fu-jabra-gnp-image.h" - -#define FU_JABRA_GNP_BUF_SIZE 64 -#define FU_JABRA_GNP_MAX_RETRIES 3 -#define FU_JABRA_GNP_PRELOAD_COUNT 10 -#define FU_JABRA_GNP_RETRY_DELAY 100 /* ms */ -#define FU_JABRA_GNP_STANDARD_SEND_TIMEOUT 3000 /* ms */ -#define FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT 1000 /* ms */ -#define FU_JABRA_GNP_LONG_RECEIVE_TIMEOUT 30000 /* ms */ -#define FU_JABRA_GNP_EXTRA_LONG_RECEIVE_TIMEOUT 60000 /* ms */ - -#define FU_JABRA_GNP_IFACE 0x05 - -#define FU_JABRA_GNP_ADDRESS_PARENT 0x01 -#define FU_JABRA_GNP_ADDRESS_OTA_CHILD 0x04 +#include "fu-jabra-gnp-struct.h" struct _FuJabraGnpDevice { FuUsbDevice parent_instance; + guint8 fwu_protocol; guint8 iface_hid; guint8 sequence_number; guint8 address; - guint dfu_pid; + guint8 epin; + guint16 dfu_pid; }; -typedef struct { - guint8 txbuf[FU_JABRA_GNP_BUF_SIZE]; - const guint timeout; -} FuJabraGnpTxData; - -typedef struct { - guint8 rxbuf[FU_JABRA_GNP_BUF_SIZE]; - const guint timeout; -} FuJabraGnpRxData; - G_DEFINE_TYPE(FuJabraGnpDevice, fu_jabra_gnp_device, FU_TYPE_USB_DEVICE) +guint8 +fu_jabra_gnp_device_get_iface_hid(FuJabraGnpDevice *self) +{ + g_return_val_if_fail(FU_IS_JABRA_GNP_DEVICE(self), G_MAXUINT8); + return self->iface_hid; +} + +guint8 +fu_jabra_gnp_device_get_epin(FuJabraGnpDevice *self) +{ + g_return_val_if_fail(FU_IS_JABRA_GNP_DEVICE(self), G_MAXUINT8); + return self->epin; +} + static void fu_jabra_gnp_device_to_string(FuDevice *device, guint idt, GString *str) { FuJabraGnpDevice *self = FU_JABRA_GNP_DEVICE(device); + fwupd_codec_string_append_hex(str, idt, "FwuProtocol", self->fwu_protocol); fwupd_codec_string_append_hex(str, idt, "IfaceHid", self->iface_hid); fwupd_codec_string_append_hex(str, idt, "SequenceNumber", self->sequence_number); fwupd_codec_string_append_hex(str, idt, "Address", self->address); @@ -70,13 +66,53 @@ } static gboolean +fu_jabra_gnp_device_probe(FuDevice *device, GError **error) +{ + FuJabraGnpDevice *self = FU_JABRA_GNP_DEVICE(device); + g_autoptr(GPtrArray) ifaces = NULL; + + ifaces = fu_usb_device_get_interfaces(FU_USB_DEVICE(self), error); + if (ifaces == NULL) { + g_prefix_error_literal(error, "update interface not found: "); + return FALSE; + } + + for (guint i = 0; i < ifaces->len; i++) { + FuUsbInterface *iface = g_ptr_array_index(ifaces, i); + if (fu_usb_interface_get_class(iface) == FU_USB_CLASS_HID) { + FuUsbEndpoint *ep1; + g_autoptr(GPtrArray) endpoints = fu_usb_interface_get_endpoints(iface); + + if (endpoints == NULL || endpoints->len < 1) + continue; + ep1 = g_ptr_array_index(endpoints, 0); + self->epin = fu_usb_endpoint_get_address(ep1); + } + } + if (self->epin == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "update endpoints not found"); + return FALSE; + } + + self->iface_hid = + _fu_usb_device_get_interface_for_class(FU_USB_DEVICE(self), FU_USB_CLASS_HID, error); + if (self->iface_hid == 0xFF) { + g_prefix_error_literal(error, "cannot find HID interface: "); + return FALSE; + } + fu_usb_device_add_interface(FU_USB_DEVICE(self), self->iface_hid); + return TRUE; +} + +gboolean fu_jabra_gnp_device_tx_cb(FuDevice *device, gpointer user_data, GError **error) { FuJabraGnpDevice *self = FU_JABRA_GNP_DEVICE(device); FuJabraGnpTxData *tx_data = (FuJabraGnpTxData *)user_data; - FuUsbDevice *target = self->address == FU_JABRA_GNP_ADDRESS_OTA_CHILD - ? FU_USB_DEVICE(fu_device_get_parent(device)) - : FU_USB_DEVICE(self); + FuUsbDevice *target = FU_USB_DEVICE(self); if (!fu_usb_device_control_transfer(FU_USB_DEVICE(target), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_CLASS, @@ -84,19 +120,19 @@ 0x09, 0x0200 | FU_JABRA_GNP_IFACE, self->iface_hid, - tx_data->txbuf, - FU_JABRA_GNP_BUF_SIZE, + tx_data->buf->data, + tx_data->buf->len, NULL, tx_data->timeout, NULL, /* cancellable */ error)) { - g_prefix_error(error, "failed to write to device: "); + g_prefix_error_literal(error, "failed to write to device: "); return FALSE; } return TRUE; } -static gboolean +gboolean fu_jabra_gnp_device_rx_cb(FuDevice *device, gpointer user_data, GError **error) { FuJabraGnpDevice *self = FU_JABRA_GNP_DEVICE(device); @@ -104,37 +140,33 @@ {FU_JABRA_GNP_IFACE, 0x00, self->address, 0x00, 0x0A, 0x12, 0x02}; const guint8 empty_buf[FU_JABRA_GNP_BUF_SIZE] = {0x00}; FuJabraGnpRxData *rx_data = (FuJabraGnpRxData *)user_data; - FuUsbDevice *target = self->address == FU_JABRA_GNP_ADDRESS_OTA_CHILD - ? FU_USB_DEVICE(fu_device_get_parent(device)) - : FU_USB_DEVICE(self); - + FuUsbDevice *target = FU_USB_DEVICE(self); if (!fu_usb_device_interrupt_transfer(target, - 0x81, + self->epin, rx_data->rxbuf, FU_JABRA_GNP_BUF_SIZE, NULL, rx_data->timeout, NULL, /* cancellable */ error)) { - g_prefix_error(error, "failed to read from device: "); + g_prefix_error_literal(error, "failed to read from device: "); return FALSE; } if (rx_data->rxbuf[2] == match_buf[2] && rx_data->rxbuf[5] == match_buf[5] && rx_data->rxbuf[6] == match_buf[6]) { /* battery report, ignore and rx again */ if (!fu_usb_device_interrupt_transfer(target, - 0x81, + self->epin, rx_data->rxbuf, FU_JABRA_GNP_BUF_SIZE, NULL, rx_data->timeout, NULL, /* cancellable */ error)) { - g_prefix_error(error, "failed to read from device: "); + g_prefix_error_literal(error, "failed to read from device: "); return FALSE; } } - if (fu_memcmp_safe(rx_data->rxbuf, sizeof(rx_data->rxbuf), 0, @@ -143,13 +175,13 @@ 0, sizeof(rx_data->rxbuf), error)) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "error readuing from device"); + g_prefix_error_literal(error, "error reading from device: "); return FALSE; } return TRUE; } -static gboolean +gboolean fu_jabra_gnp_device_rx_with_sequence_cb(FuDevice *device, gpointer user_data, GError **error) { FuJabraGnpDevice *self = FU_JABRA_GNP_DEVICE(device); @@ -174,69 +206,14 @@ self->sequence_number += 1; return TRUE; } - -static gboolean -fu_jabra_gnp_device_read_name(FuJabraGnpDevice *self, GError **error) -{ - FuJabraGnpTxData tx_data = { - .txbuf = - { - FU_JABRA_GNP_IFACE, - self->address, - 0x00, - self->sequence_number, - 0x46, - 0x02, - 0x00, - }, - .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, - }; - FuJabraGnpRxData rx_data = { - .rxbuf = {0x00}, - .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, - }; - g_autofree gchar *name = NULL; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_tx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&tx_data, - error)) - return FALSE; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_rx_with_sequence_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, - error)) - return FALSE; - name = fu_memstrsafe(rx_data.rxbuf, - sizeof(rx_data.rxbuf), - 0x8, - sizeof(rx_data.rxbuf) - 8, - error); - if (name == NULL) - return FALSE; - fu_device_set_name(FU_DEVICE(self), name); - return TRUE; -} - static gboolean fu_jabra_gnp_device_read_child_dfu_pid(FuJabraGnpDevice *self, guint16 *child_dfu_pid, GError **error) { + g_autoptr(FuStructJabraGnpPacket) st = fu_struct_jabra_gnp_packet_new(); FuJabraGnpTxData tx_data = { - .txbuf = - { - FU_JABRA_GNP_IFACE, - FU_JABRA_GNP_ADDRESS_OTA_CHILD, - 0x00, - self->sequence_number, - 0x46, - 0x02, - 0x13, - }, + .buf = st->buf, .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, }; FuJabraGnpRxData rx_data = { @@ -246,567 +223,38 @@ g_return_val_if_fail(child_dfu_pid != NULL, FALSE); - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_tx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&tx_data, - error)) - return FALSE; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_rx_with_sequence_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, - error)) - return FALSE; - /* no child device to respond properly */ - if (rx_data.rxbuf[5] == 0xFE && rx_data.rxbuf[6] == 0xF4) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "internal error: no child device responded"); - return FALSE; - } - /* success */ - *child_dfu_pid = fu_memread_uint16(rx_data.rxbuf + 7, G_LITTLE_ENDIAN); - return TRUE; -} + /* set up request */ + fu_struct_jabra_gnp_packet_set_dst(st, FU_JABRA_GNP_ADDRESS_OTA_CHILD); + fu_struct_jabra_gnp_packet_set_sequence_number(st, self->sequence_number); + fu_struct_jabra_gnp_packet_set_cmd(st, 0x02); + fu_struct_jabra_gnp_packet_set_sub_cmd(st, 0x13); + fu_byte_array_set_size(st->buf, FU_JABRA_GNP_BUF_SIZE, 0x0); -static gboolean -fu_jabra_gnp_device_read_child_battery_level(FuJabraGnpDevice *self, GError **error) -{ - FuJabraGnpTxData tx_data = { - .txbuf = - { - FU_JABRA_GNP_IFACE, - self->address, - 0x00, - self->sequence_number, - 0x46, - 0x12, - 0x02, - }, - .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, - }; - FuJabraGnpRxData rx_data = { - .rxbuf = {0x00}, - .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, - }; - guint8 battery_level = 0; if (!fu_device_retry_full(FU_DEVICE(self), fu_jabra_gnp_device_tx_cb, FU_JABRA_GNP_MAX_RETRIES, FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&tx_data, + &tx_data, error)) return FALSE; if (!fu_device_retry_full(FU_DEVICE(self), fu_jabra_gnp_device_rx_with_sequence_cb, FU_JABRA_GNP_MAX_RETRIES, FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, + &rx_data, error)) return FALSE; - if (!fu_memread_uint8_safe(rx_data.rxbuf, FU_JABRA_GNP_BUF_SIZE, 8, &battery_level, error)) - return FALSE; - if (battery_level == 0x00) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "battery level was 0"); - return FALSE; - } - fu_device_set_battery_level(FU_DEVICE(self), battery_level); - fu_device_set_battery_threshold(FU_DEVICE(self), 30); - return TRUE; -} - -static gboolean -fu_jabra_gnp_device_read_dfu_pid(FuJabraGnpDevice *self, GError **error) -{ - FuJabraGnpTxData tx_data = { - .txbuf = - { - FU_JABRA_GNP_IFACE, - self->address, - 0x00, - self->sequence_number, - 0x46, - 0x02, - 0x13, - }, - .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, - }; - FuJabraGnpRxData rx_data = { - .rxbuf = {0x00}, - .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, - }; - - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_tx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&tx_data, - error)) - return FALSE; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_rx_with_sequence_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, - error)) - return FALSE; - self->dfu_pid = fu_memread_uint16(rx_data.rxbuf + 7, G_LITTLE_ENDIAN); - return TRUE; -} - -static gboolean -fu_jabra_gnp_device_read_version(FuJabraGnpDevice *self, GError **error) -{ - FuJabraGnpTxData tx_data = { - .txbuf = - { - FU_JABRA_GNP_IFACE, - self->address, - 0x00, - self->sequence_number, - 0x46, - 0x02, - 0x03, - }, - .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, - }; - FuJabraGnpRxData rx_data = { - .rxbuf = {0x00}, - .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, - }; - g_autofree gchar *version = NULL; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_tx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&tx_data, - error)) - return FALSE; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_rx_with_sequence_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, - error)) - return FALSE; - version = fu_memstrsafe(rx_data.rxbuf, - sizeof(rx_data.rxbuf), - 0x8, - sizeof(rx_data.rxbuf) - 8, - error); - if (version == NULL) - return FALSE; - fu_device_set_version(FU_DEVICE(self), version); - return TRUE; -} - -static gboolean -fu_jabra_gnp_device_write_partition(FuJabraGnpDevice *self, guint8 part, GError **error) -{ - FuJabraGnpTxData tx_data = { - .txbuf = - { - FU_JABRA_GNP_IFACE, - self->address, - 0x00, - self->sequence_number, - 0x87, - 0x0F, - 0x2D, - part, - }, - .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, - }; - FuJabraGnpRxData rx_data = { - .rxbuf = {0x00}, - .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, - }; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_tx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&tx_data, - error)) - return FALSE; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_rx_with_sequence_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, - error)) - return FALSE; - if (rx_data.rxbuf[5] != 0xFF) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "internal error: expected 0xFF, got 0x%02x 0x%02x", - rx_data.rxbuf[5], - rx_data.rxbuf[6]); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_jabra_gnp_device_start(FuJabraGnpDevice *self, GError **error) -{ - FuJabraGnpTxData tx_data = { - .txbuf = - { - FU_JABRA_GNP_IFACE, - self->address, - 0x00, - self->sequence_number, - 0x86, - 0x0F, - 0x17, - }, - .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, - }; - FuJabraGnpRxData rx_data = { - .rxbuf = {0x00}, - .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, - }; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_tx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&tx_data, - error)) - return FALSE; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_rx_with_sequence_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, - error)) - return FALSE; - if (rx_data.rxbuf[5] != 0xFF) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "internal error: expected 0xFF, got 0x%02x 0x%02x", - rx_data.rxbuf[5], - rx_data.rxbuf[6]); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_jabra_gnp_device_flash_erase_done(FuJabraGnpDevice *self, GError **error) -{ - const guint8 match_buf[FU_JABRA_GNP_BUF_SIZE] = - {FU_JABRA_GNP_IFACE, 0x00, self->address, 0x00, 0x06, 0x0F, 0x18}; - FuJabraGnpRxData rx_data = { - .rxbuf = {0x00}, - .timeout = FU_JABRA_GNP_EXTRA_LONG_RECEIVE_TIMEOUT, - }; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_rx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, - error)) - return FALSE; - if (rx_data.rxbuf[5] != match_buf[5] || rx_data.rxbuf[6] != match_buf[6]) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "internal error, buf did not match"); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_jabra_gnp_device_write_crc(FuJabraGnpDevice *self, - guint32 crc, - guint total_chunks, - guint preload_count, - GError **error) -{ - FuJabraGnpTxData tx_data = { - .txbuf = - { - FU_JABRA_GNP_IFACE, - self->address, - 0x00, - self->sequence_number, - 0x8E, - 0x0F, - 0x19, - }, - .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, - }; - FuJabraGnpRxData rx_data = { - .rxbuf = {0x00}, - .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, - }; - - fu_memwrite_uint32(tx_data.txbuf + 7, crc, G_LITTLE_ENDIAN); - fu_memwrite_uint16(tx_data.txbuf + 11, total_chunks, G_LITTLE_ENDIAN); - fu_memwrite_uint16(tx_data.txbuf + 13, preload_count, G_LITTLE_ENDIAN); - - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_tx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&tx_data, - error)) - return FALSE; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_rx_with_sequence_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, - error)) - return FALSE; - if (rx_data.rxbuf[5] != 0xFF) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "internal error: expected 0xFF, got 0x%02x 0x%02x", - rx_data.rxbuf[5], - rx_data.rxbuf[6]); + /* no child device to respond properly */ + if (rx_data.rxbuf[5] == 0xFE && (rx_data.rxbuf[6] == 0xF4 || rx_data.rxbuf[6] == 0xF3)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "internal error: no child device responded"); return FALSE; } - return TRUE; -} - -static gboolean -fu_jabra_gnp_device_write_chunk(FuJabraGnpDevice *self, - guint32 chunk_number, - const guint8 *buf, - gsize bufsz, - GError **error) -{ - guint8 write_length = 0x00 + bufsz + 10; - FuJabraGnpTxData tx_data = { - .txbuf = - { - FU_JABRA_GNP_IFACE, - self->address, - 0x00, - 0x00, - write_length, - 0x0F, - 0x1A, - }, - .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, - }; - fu_memwrite_uint16(tx_data.txbuf + 7, chunk_number, G_LITTLE_ENDIAN); - fu_memwrite_uint16(tx_data.txbuf + 9, bufsz, G_LITTLE_ENDIAN); - if (!fu_memcpy_safe(tx_data.txbuf, - sizeof(tx_data.txbuf), - 11, - buf, - bufsz, - 0x0, - bufsz, - error)) - return FALSE; - return fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_tx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&tx_data, - error); -} - -static gboolean -fu_jabra_gnp_device_write_chunks(FuJabraGnpDevice *self, - FuChunkArray *chunks, - FuProgress *progress, - GError **error) -{ - gboolean failed_chunk = FALSE; - - const guint8 match_buf[FU_JABRA_GNP_BUF_SIZE] = { - FU_JABRA_GNP_IFACE, - 0x00, - self->address, - 0x00, - 0x06, - 0x0F, - 0x1B, - }; - FuJabraGnpRxData rx_data = { - .rxbuf = {0x00}, - .timeout = FU_JABRA_GNP_LONG_RECEIVE_TIMEOUT, - }; - - /* progress */ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); - for (gint chunk_number = 0; (guint)chunk_number < fu_chunk_array_length(chunks); - chunk_number++) { - g_autoptr(FuChunk) chk = NULL; - - /* prepare chunk */ - chk = fu_chunk_array_index(chunks, chunk_number, error); - if (chk == NULL) - return FALSE; - if (!fu_jabra_gnp_device_write_chunk(self, - chunk_number, - fu_chunk_get_data(chk), - fu_chunk_get_data_sz(chk), - error)) - return FALSE; - if (((chunk_number % FU_JABRA_GNP_PRELOAD_COUNT) == 0) || - (guint)chunk_number == fu_chunk_array_length(chunks) - 1) { - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_rx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, - error)) - return FALSE; - if (rx_data.rxbuf[5] != match_buf[5] || rx_data.rxbuf[6] != match_buf[6]) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "internal error, buf did not match"); - return FALSE; - } - if (fu_memread_uint16(rx_data.rxbuf + 7, G_LITTLE_ENDIAN) != chunk_number) { - chunk_number--; - failed_chunk = TRUE; - } else - failed_chunk = FALSE; - } - if (!failed_chunk) - fu_progress_step_done(progress); - } /* success */ - return TRUE; -} - -static gboolean -fu_jabra_gnp_device_read_verify_status(FuJabraGnpDevice *self, GError **error) -{ - const guint8 match_buf[FU_JABRA_GNP_BUF_SIZE] = - {FU_JABRA_GNP_IFACE, 0x00, self->address, 0x00, 0x06, 0x0F, 0x1C}; - FuJabraGnpRxData rx_data = { - .rxbuf = {0x00}, - .timeout = FU_JABRA_GNP_LONG_RECEIVE_TIMEOUT, - }; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_rx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, - error)) - return FALSE; - if (rx_data.rxbuf[5] != match_buf[5] || rx_data.rxbuf[6] != match_buf[6]) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "internal error, buf did not match"); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_jabra_gnp_device_write_version(FuJabraGnpDevice *self, - FuJabraGnpVersionData *version_data, - GError **error) -{ - FuJabraGnpTxData tx_data = { - .txbuf = - { - FU_JABRA_GNP_IFACE, - self->address, - 0x00, - self->sequence_number, - 0x89, - 0x0F, - 0x1E, - version_data->major, - version_data->minor, - version_data->micro, - }, - .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, - }; - FuJabraGnpRxData rx_data = { - .rxbuf = {0x00}, - .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, - }; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_tx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&tx_data, - error)) - return FALSE; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_rx_with_sequence_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, - error)) - return FALSE; - if (rx_data.rxbuf[5] != 0xFF) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "internal error: expected 0xFF, got 0x%02x 0x%02x", - rx_data.rxbuf[5], - rx_data.rxbuf[6]); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_jabra_gnp_device_write_dfu_from_squif(FuJabraGnpDevice *self, GError **error) -{ - FuJabraGnpTxData tx_data = { - .txbuf = - { - FU_JABRA_GNP_IFACE, - self->address, - 0x00, - self->sequence_number, - 0x86, - 0x0F, - 0x1D, - }, - .timeout = FU_JABRA_GNP_STANDARD_SEND_TIMEOUT, - }; - FuJabraGnpRxData rx_data = { - .rxbuf = {0x00}, - .timeout = FU_JABRA_GNP_STANDARD_RECEIVE_TIMEOUT, - }; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_tx_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&tx_data, - error)) - return FALSE; - if (!fu_device_retry_full(FU_DEVICE(self), - fu_jabra_gnp_device_rx_with_sequence_cb, - FU_JABRA_GNP_MAX_RETRIES, - FU_JABRA_GNP_RETRY_DELAY, - (gpointer)&rx_data, - error)) - return FALSE; - if (rx_data.rxbuf[5] != 0xFF) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "internal error: expected 0xFF, got 0x%02x 0x%02x", - rx_data.rxbuf[5], - rx_data.rxbuf[6]); - return FALSE; - } + *child_dfu_pid = fu_memread_uint16(rx_data.rxbuf + 7, G_LITTLE_ENDIAN); return TRUE; } @@ -814,7 +262,7 @@ fu_jabra_gnp_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuJabraGnpDevice *self = FU_JABRA_GNP_DEVICE(device); @@ -836,35 +284,9 @@ } static gboolean -fu_jabra_gnp_device_probe(FuDevice *device, GError **error) +fu_jabra_gnp_device_add_child(FuJabraGnpDevice *self, guint16 dfu_pid, GError **error) { - g_autoptr(GError) error_local = NULL; - FuJabraGnpDevice *self = FU_JABRA_GNP_DEVICE(device); - - /* already set by parent */ - if (self->address == FU_JABRA_GNP_ADDRESS_OTA_CHILD) - return TRUE; - - self->iface_hid = _fu_usb_device_get_interface_for_class(FU_USB_DEVICE(self), - FU_USB_CLASS_HID, - &error_local); - if (self->iface_hid == 0xFF) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "cannot find HID interface: %s", - error_local->message); - return FALSE; - } - fu_usb_device_add_interface(FU_USB_DEVICE(self), self->iface_hid); - return TRUE; -} - -static gboolean -fu_jabra_gnp_device_add_child(FuDevice *device, guint dfu_pid, GError **error) -{ - FuJabraGnpDevice *self = FU_JABRA_GNP_DEVICE(device); - g_autoptr(FuJabraGnpDevice) child = NULL; + g_autoptr(FuJabraGnpChildDevice) child = NULL; /* sanity check */ if (self->address != FU_JABRA_GNP_ADDRESS_PARENT) { @@ -877,22 +299,13 @@ return FALSE; } - child = g_object_new(FU_TYPE_JABRA_GNP_DEVICE, "parent", FU_DEVICE(self), NULL); - child->iface_hid = self->iface_hid; - child->sequence_number = 0; - child->address = FU_JABRA_GNP_ADDRESS_OTA_CHILD; - child->dfu_pid = dfu_pid; - - /* prohibit to close parent's communication descriptor */ - fu_device_add_private_flag(FU_DEVICE(child), FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN); - + child = g_object_new(FU_TYPE_JABRA_GNP_CHILD_DEVICE, "parent", FU_DEVICE(self), NULL); + fu_jabra_gnp_child_device_set_dfu_pid_and_seq(child, dfu_pid); fu_device_incorporate(FU_DEVICE(child), FU_DEVICE(self), FU_DEVICE_INCORPORATE_FLAG_PHYSICAL_ID); - fu_device_set_logical_id(FU_DEVICE(child), "ota_device"); - if (!fu_device_setup(FU_DEVICE(child), error)) { - g_prefix_error(error, "failed to setup child device: "); + g_prefix_error_literal(error, "failed to setup child device: "); return FALSE; } @@ -907,6 +320,7 @@ "PID", NULL)) return FALSE; + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(child)); /* success */ @@ -918,11 +332,15 @@ { FuJabraGnpDevice *self = FU_JABRA_GNP_DEVICE(device); g_autoptr(GError) error_local = NULL; - if (!fu_jabra_gnp_device_read_name(self, error)) + if (!fu_jabra_gnp_ensure_name(device, self->address, self->sequence_number, error)) return FALSE; - if (!fu_jabra_gnp_device_read_version(self, error)) + if (!fu_jabra_gnp_ensure_version(device, self->address, self->sequence_number, error)) return FALSE; - if (!fu_jabra_gnp_device_read_dfu_pid(self, error)) + if (!fu_jabra_gnp_read_dfu_pid(device, + self->address, + self->sequence_number, + &self->dfu_pid, + error)) return FALSE; if (self->address == FU_JABRA_GNP_ADDRESS_PARENT) { guint16 child_dfu_pid = 0; @@ -931,7 +349,7 @@ return TRUE; } if (child_dfu_pid > 0x0) { - if (!fu_jabra_gnp_device_add_child(FU_DEVICE(self), child_dfu_pid, error)) { + if (!fu_jabra_gnp_device_add_child(self, child_dfu_pid, error)) { g_prefix_error( error, "found child device with PID 0x%x, but failed to add as child " @@ -943,11 +361,6 @@ } } } - if (self->address == FU_JABRA_GNP_ADDRESS_OTA_CHILD) { - if (!fu_jabra_gnp_device_read_child_battery_level(self, error)) - return FALSE; - } - return TRUE; } @@ -975,17 +388,21 @@ stream = fu_firmware_get_stream(img, error); if (stream == NULL) return FALSE; - if (!fu_jabra_gnp_device_write_partition(self, fu_firmware_get_idx(img), error)) + if (!fu_jabra_gnp_write_partition(FU_DEVICE(self), + self->address, + self->sequence_number, + fu_firmware_get_idx(img), + error)) return FALSE; fu_progress_step_done(progress); /* start erasing */ - if (!fu_jabra_gnp_device_start(self, error)) + if (!fu_jabra_gnp_start(FU_DEVICE(self), self->address, self->sequence_number, error)) return FALSE; fu_progress_step_done(progress); /* poll for erase done */ - if (!fu_jabra_gnp_device_flash_erase_done(self, error)) + if (!fu_jabra_gnp_flash_erase_done(FU_DEVICE(self), self->address, error)) return FALSE; fu_progress_step_done(progress); @@ -997,24 +414,45 @@ error); if (chunks == NULL) return FALSE; - if (!fu_jabra_gnp_device_write_crc(self, - fu_jabra_gnp_image_get_crc32(FU_JABRA_GNP_IMAGE(img)), - fu_chunk_array_length(chunks), - FU_JABRA_GNP_PRELOAD_COUNT, - error)) - return FALSE; - if (!fu_jabra_gnp_device_write_chunks(self, chunks, fu_progress_get_child(progress), error)) + if (self->fwu_protocol == FU_JABRA_GNP_PROTOCOL_OTA) { + if (!fu_jabra_gnp_write_crc(FU_DEVICE(self), + self->address, + self->sequence_number, + fu_jabra_gnp_image_get_crc32(FU_JABRA_GNP_IMAGE(img)), + fu_chunk_array_length(chunks), + FU_JABRA_GNP_PRELOAD_COUNT, + error)) + return FALSE; + } else { + /* FU_JABRA_GNP_PROTOCOL_EXTENDED_OTA */ + if (!fu_jabra_gnp_write_extended_crc( + FU_DEVICE(self), + self->address, + self->sequence_number, + fu_jabra_gnp_image_get_crc32(FU_JABRA_GNP_IMAGE(img)), + fu_chunk_array_length(chunks), + FU_JABRA_GNP_PRELOAD_COUNT, + error)) + return FALSE; + } + if (!fu_jabra_gnp_write_chunks(FU_DEVICE(self), + self->address, + chunks, + fu_progress_get_child(progress), + error)) return FALSE; fu_progress_step_done(progress); /* verify */ - if (!fu_jabra_gnp_device_read_verify_status(self, error)) + if (!fu_jabra_gnp_read_verify_status(FU_DEVICE(self), self->address, error)) return FALSE; fu_progress_step_done(progress); /* write version */ - if (!fu_jabra_gnp_device_write_version( - self, + if (!fu_jabra_gnp_write_version( + FU_DEVICE(self), + self->address, + self->sequence_number, fu_jabra_gnp_firmware_get_version_data(FU_JABRA_GNP_FIRMWARE(firmware)), error)) return FALSE; @@ -1044,6 +482,12 @@ fu_firmware_get_size(img), fu_firmware_get_id(img)); } + if (!fu_jabra_gnp_read_fwu_protocol(device, + self->address, + self->sequence_number, + &self->fwu_protocol, + error)) + return FALSE; for (guint i = 0; i < imgs->len; i++) { FuFirmware *img = g_ptr_array_index(imgs, i); if (!fu_jabra_gnp_device_write_image(self, @@ -1057,23 +501,10 @@ fu_progress_step_done(progress); } /* write squif */ - if (!fu_jabra_gnp_device_write_dfu_from_squif(self, error)) - return FALSE; - - return TRUE; -} - -static gboolean -fu_jabra_gnp_device_attach(FuDevice *device, FuProgress *progress, GError **error) -{ - FuJabraGnpDevice *self = FU_JABRA_GNP_DEVICE(device); - /* ota device needs some time to restart and reconnect */ - if (self->address == FU_JABRA_GNP_ADDRESS_OTA_CHILD) { - fu_device_sleep_full(FU_DEVICE(self), 45000, progress); - fu_device_set_remove_delay(FU_DEVICE(self), 10000); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - } - return TRUE; + return fu_jabra_gnp_write_dfu_from_squif(device, + self->address, + self->sequence_number, + error); } static gboolean @@ -1101,7 +532,7 @@ } static void -fu_jabra_gnp_device_set_progress(FuDevice *self, FuProgress *progress) +fu_jabra_gnp_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -1134,7 +565,6 @@ device_class->probe = fu_jabra_gnp_device_probe; device_class->setup = fu_jabra_gnp_device_setup; device_class->write_firmware = fu_jabra_gnp_device_write_firmware; - device_class->attach = fu_jabra_gnp_device_attach; device_class->set_quirk_kv = fu_jabra_gnp_device_set_quirk_kv; device_class->set_progress = fu_jabra_gnp_device_set_progress; } diff -Nru fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-device.h fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-device.h --- fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -10,3 +10,18 @@ #define FU_TYPE_JABRA_GNP_DEVICE (fu_jabra_gnp_device_get_type()) G_DECLARE_FINAL_TYPE(FuJabraGnpDevice, fu_jabra_gnp_device, FU, JABRA_GNP_DEVICE, FuUsbDevice) + +guint8 +fu_jabra_gnp_device_get_iface_hid(FuJabraGnpDevice *self) G_GNUC_NON_NULL(1); +guint8 +fu_jabra_gnp_device_get_epin(FuJabraGnpDevice *self) G_GNUC_NON_NULL(1); + +gboolean +fu_jabra_gnp_device_tx_cb(FuDevice *device, gpointer user_data, GError **error) G_GNUC_NON_NULL(1); + +gboolean +fu_jabra_gnp_device_rx_cb(FuDevice *device, gpointer user_data, GError **error) G_GNUC_NON_NULL(1); + +gboolean +fu_jabra_gnp_device_rx_with_sequence_cb(FuDevice *device, gpointer user_data, GError **error) + G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-firmware.c fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-firmware.c --- fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,12 +11,6 @@ #include "fu-jabra-gnp-firmware.h" #include "fu-jabra-gnp-image.h" -struct _FuJabraGnpFirmware { - FuArchiveFirmware parent_instance; - guint16 dfu_pid; - FuJabraGnpVersionData version_data; -}; - G_DEFINE_TYPE(FuJabraGnpFirmware, fu_jabra_gnp_firmware, FU_TYPE_FIRMWARE) static void @@ -104,7 +98,7 @@ static gboolean fu_jabra_gnp_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuJabraGnpFirmware *self = FU_JABRA_GNP_FIRMWARE(firmware); @@ -162,7 +156,7 @@ g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } - if (!fu_firmware_add_image_full(firmware, FU_FIRMWARE(img), error)) + if (!fu_firmware_add_image(firmware, FU_FIRMWARE(img), error)) return FALSE; } diff -Nru fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-firmware.h fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-firmware.h --- fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-firmware.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,14 +8,15 @@ #include +#include "fu-jabra-gnp-common.h" + #define FU_TYPE_JABRA_GNP_FIRMWARE (fu_jabra_gnp_firmware_get_type()) G_DECLARE_FINAL_TYPE(FuJabraGnpFirmware, fu_jabra_gnp_firmware, FU, JABRA_GNP_FIRMWARE, FuFirmware) - -typedef struct { - guint8 major; - guint8 minor; - guint8 micro; -} FuJabraGnpVersionData; +struct _FuJabraGnpFirmware { + FuFirmware parent_instance; + guint16 dfu_pid; + FuJabraGnpVersionData version_data; +}; FuFirmware * fu_jabra_gnp_firmware_new(void); diff -Nru fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-image.c fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-image.c --- fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-image.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-image.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,7 +12,7 @@ #include "fu-jabra-gnp-image.h" struct _FuJabraGnpImage { - FuArchiveFirmware parent_instance; + FuFirmware parent_instance; guint32 crc32; }; @@ -57,13 +57,19 @@ /* get the CRC */ crc_str = xb_node_query_text(n, "crc", error); - if (crc_str == NULL) { - fwupd_error_convert(error); - return FALSE; - } - if (!fu_strtoull(crc_str, &crc_expected, 0x0, 0xFFFFFFFF, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "cannot parse crc of %s: ", crc_str); - return FALSE; + if (crc_str != NULL) { + if (!fu_strtoull(crc_str, + &crc_expected, + 0x0, + 0xFFFFFFFF, + FU_INTEGER_BASE_AUTO, + error)) { + g_prefix_error(error, "cannot parse crc of %s: ", crc_str); + return FALSE; + } + } else { + /* crc entry may not exist in the file */ + g_clear_error(error); } /* get the partition number */ @@ -99,7 +105,7 @@ /* verify the CRC */ self->crc32 = fu_jabra_gnp_calculate_crc(blob); - if (self->crc32 != (guint32)crc_expected) { + if (crc_str != NULL && self->crc32 != (guint32)crc_expected) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, diff -Nru fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-plugin.c fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-plugin.c --- fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -28,6 +28,7 @@ FuPlugin *plugin = FU_PLUGIN(obj); FuContext *ctx = fu_plugin_get_context(plugin); fu_context_add_quirk_key(ctx, "JabraGnpAddress"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_JABRA_GNP_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_JABRA_GNP_FIRMWARE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_JABRA_GNP_IMAGE); /* coverage */ diff -Nru fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp.rs fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp.rs --- fwupd-2.0.8/plugins/jabra-gnp/fu-jabra-gnp.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/fu-jabra-gnp.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,15 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructJabraGnpPacket { + iface: u8 == 0x05, + dst: u8, + _src: u8, + sequence_number: u8, + cmd_length: u8 = 0x46, + cmd: u8, + sub_cmd: u8, + //payload: [u8; 57], +} diff -Nru fwupd-2.0.8/plugins/jabra-gnp/jabra-gnp.quirk fwupd-2.0.20/plugins/jabra-gnp/jabra-gnp.quirk --- fwupd-2.0.8/plugins/jabra-gnp/jabra-gnp.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/jabra-gnp.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -274,3 +274,12 @@ [USB\VID_0B0E&PID_2E57] Plugin = jabra_gnp JabraGnpAddress = 0x01 + +[USB\VID_0B0E&PID_253D] +Plugin = jabra_gnp + +[USB\VID_0B0E&PID_253F] +Plugin = jabra_gnp + +[USB\VID_0B0E&PID_253E] +Plugin = jabra_gnp diff -Nru fwupd-2.0.8/plugins/jabra-gnp/meson.build fwupd-2.0.20/plugins/jabra-gnp/meson.build --- fwupd-2.0.8/plugins/jabra-gnp/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,9 @@ plugin_quirks += files('jabra-gnp.quirk') plugin_builtins += static_library('fu_plugin_jabra_gnp', + rustgen.process('fu-jabra-gnp.rs'), sources: [ + 'fu-jabra-gnp-child-device.c', 'fu-jabra-gnp-common.c', 'fu-jabra-gnp-device.c', 'fu-jabra-gnp-firmware.c', diff -Nru fwupd-2.0.8/plugins/jabra-gnp/tests/jabra-evolve2-75.json fwupd-2.0.20/plugins/jabra-gnp/tests/jabra-evolve2-75.json --- fwupd-2.0.8/plugins/jabra-gnp/tests/jabra-evolve2-75.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/jabra-gnp/tests/jabra-evolve2-75.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/335f10696cd57ae74b2115a9e44a39a2c5208e7e090d1f4e72bb91bc65a7480d-jabra-evolve2-75-1.8.13.cab", + "url": "335f10696cd57ae74b2115a9e44a39a2c5208e7e090d1f4e72bb91bc65a7480d-jabra-evolve2-75-1.8.13.cab", + "emulation-url": "3b7aff78e1ae5cbd225144271b2022e895b6926da1a2c2f3a9d6da6867e2cdd7-ev2_ota.zip", "components": [ { "version": "1.8.13", @@ -17,7 +18,7 @@ ] }, { - "url": "https://fwupd.org/downloads/8fa6ff5d43c0cb5807f136c6edc90c146f3796b9228706ebed6f6348d9c358cd-jabra-evolve2-75-1.7.4.cab", + "url": "8fa6ff5d43c0cb5807f136c6edc90c146f3796b9228706ebed6f6348d9c358cd-jabra-evolve2-75-1.7.4.cab", "components": [ { "version": "1.7.4", @@ -31,8 +32,8 @@ ] }, { - "url": "https://fwupd.org/downloads/acef8b8dc286659a6e783e49aaf55872d4ce6f0f0c50330452c5db756cb2d307-jabra-evolve2-75-1.11.7.cab", - "emulation-url": "https://fwupd.org/downloads/169609950e6182d1c0add7aae7b2627510b29651287312bcd6e924f76c167cf3-emulation.zip", + "url": "acef8b8dc286659a6e783e49aaf55872d4ce6f0f0c50330452c5db756cb2d307-jabra-evolve2-75-1.11.7.cab", + "emulation-url": "6865efa6bc318f67f32c588558f006c9314575e6599038426097f163685cfb6a-emulation.zip", "components": [ { "version": "1.11.7", diff -Nru fwupd-2.0.8/plugins/kinetic-dp/README.md fwupd-2.0.20/plugins/kinetic-dp/README.md --- fwupd-2.0.8/plugins/kinetic-dp/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/kinetic-dp/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -54,10 +54,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.9.8`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Francis Lee @FrancisLeeKinetic diff -Nru fwupd-2.0.8/plugins/kinetic-dp/fu-kinetic-dp-device.c fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-device.c --- fwupd-2.0.8/plugins/kinetic-dp/fu-kinetic-dp-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -102,7 +102,7 @@ DPCD_SIZE_IEEE_OUI, FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "aux dpcd read OUI failed: "); + g_prefix_error_literal(error, "aux dpcd read OUI failed: "); return FALSE; } return TRUE; @@ -117,7 +117,7 @@ DPCD_SIZE_IEEE_OUI, FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "aux dpcd write OUI failed: "); + g_prefix_error_literal(error, "aux dpcd write OUI failed: "); return FALSE; } return TRUE; @@ -135,7 +135,7 @@ sizeof(priv->customer_board), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "aux dpcd read customer board failed: "); + g_prefix_error_literal(error, "aux dpcd read customer board failed: "); return FALSE; } fu_device_add_instance_u8(FU_DEVICE(self), "CHW", priv->customer_board); @@ -147,7 +147,7 @@ sizeof(priv->customer_id), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "aux dpcd read customer ID failed: "); + g_prefix_error_literal(error, "aux dpcd read customer ID failed: "); return FALSE; } fu_device_add_instance_u8(FU_DEVICE(self), "CID", priv->customer_id); @@ -161,7 +161,7 @@ NULL)) return FALSE; - /* Kinetic EV board */ + /* EV board */ if (priv->customer_id == 0x0) { fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_ENFORCE_REQUIRES); @@ -245,10 +245,10 @@ fu_kinetic_dp_device_init(FuKineticDpDevice *self) { fu_device_add_protocol(FU_DEVICE(self), "com.kinet-ic.dp"); - fu_device_set_vendor(FU_DEVICE(self), "Kinetic Technologies"); + fu_device_set_vendor(FU_DEVICE(self), "Kinetic"); fu_device_build_vendor_id_u16(FU_DEVICE(self), "DRM_DP_AUX_DEV", 0x329A); fu_device_set_summary(FU_DEVICE(self), "DisplayPort Protocol Converter"); - fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_VIDEO_DISPLAY); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); diff -Nru fwupd-2.0.8/plugins/kinetic-dp/fu-kinetic-dp-plugin.c fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-plugin.c --- fwupd-2.0.8/plugins/kinetic-dp/fu-kinetic-dp-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -104,7 +104,7 @@ dev = fu_kinetic_dp_plugin_create_device(FU_DPAUX_DEVICE(device), error); if (dev == NULL) return FALSE; - locker = fu_device_locker_new(dev, error); + locker = fu_device_locker_new(FU_DEVICE(dev), error); if (locker == NULL) return FALSE; fu_plugin_device_add(FU_PLUGIN(self), FU_DEVICE(dev)); diff -Nru fwupd-2.0.8/plugins/kinetic-dp/fu-kinetic-dp-puma-device.c fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-puma-device.c --- fwupd-2.0.8/plugins/kinetic-dp/fu-kinetic-dp-puma-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-puma-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -54,7 +54,7 @@ sizeof(status), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to read PUMA_DPCD_CMD_STATUS_REG for status: "); + g_prefix_error_literal(error, "failed to read PUMA_DPCD_CMD_STATUS_REG status: "); return FALSE; } if (status != status_want) { @@ -86,7 +86,7 @@ sizeof(status), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to read PUMA_DPCD_SINK_MODE_REG for status: "); + g_prefix_error_literal(error, "failed to read PUMA_DPCD_SINK_MODE_REG status: "); return FALSE; } if (status != status_want) { @@ -115,9 +115,9 @@ sizeof(cmd), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, - "failed to write PUMA_DPCD_SINK_MODE_REG with " - "CODE_LOAD_REQUEST: "); + g_prefix_error_literal(error, + "failed to write PUMA_DPCD_SINK_MODE_REG with " + "CODE_LOAD_REQUEST: "); return FALSE; } @@ -128,7 +128,7 @@ POLL_INTERVAL_MS, GUINT_TO_POINTER(FU_KINETIC_DP_PUMA_REQUEST_CODE_LOAD_READY), error)) { - g_prefix_error(error, "timeout waiting for REQUEST_FW_UPDATE_READY: "); + g_prefix_error_literal(error, "timeout waiting for REQUEST_FW_UPDATE_READY: "); return FALSE; } @@ -137,10 +137,7 @@ } static gboolean -fu_kinetic_dp_puma_device_send_chunk(FuKineticDpPumaDevice *self, - FuIOChannel *io_channel, - GBytes *fw, - GError **error) +fu_kinetic_dp_puma_device_send_chunk(FuKineticDpPumaDevice *self, GBytes *fw, GError **error) { g_autoptr(FuChunkArray) chunks = fu_chunk_array_new_from_bytes(fw, FU_CHUNK_ADDR_OFFSET_NONE, FU_CHUNK_PAGESZ_NONE, 16); @@ -168,7 +165,6 @@ static gboolean fu_kinetic_dp_puma_device_send_payload(FuKineticDpPumaDevice *self, - FuIOChannel *io_channel, GBytes *fw, FuProgress *progress, guint32 wait_time_ms, @@ -194,7 +190,7 @@ /* send a maximum 32KB chunk of payload to AUX window */ chk_blob = fu_chunk_get_bytes(chk); - if (!fu_kinetic_dp_puma_device_send_chunk(self, io_channel, chk_blob, error)) { + if (!fu_kinetic_dp_puma_device_send_chunk(self, chk_blob, error)) { g_prefix_error(error, "failed to AUX write at 0x%x: ", (guint)fu_chunk_get_address(chk)); @@ -208,7 +204,7 @@ POLL_INTERVAL_MS, GUINT_TO_POINTER(FU_KINETIC_DP_PUMA_MODE_CHUNK_PROCESSED), error)) { - g_prefix_error(error, "timeout waiting for MODE_CHUNK_PROCESSED: "); + g_prefix_error_literal(error, "timeout waiting for MODE_CHUNK_PROCESSED: "); return FALSE; } fu_progress_step_done(progress); @@ -217,17 +213,15 @@ } static gboolean -fu_kinetic_dp_puma_device_wait_drv_ready(FuKineticDpPumaDevice *self, - FuIOChannel *io_channel, - GError **error) +fu_kinetic_dp_puma_device_wait_drv_ready(FuKineticDpPumaDevice *self, GError **error) { guint8 flashinfo[FU_STRUCT_KINETIC_DP_FLASH_INFO_SIZE] = {0}; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructKineticDpFlashInfo) st = NULL; self->flash_id = 0; self->flash_size = 0; self->read_flash_prog_time = 10; - g_debug("wait for isp driver ready..."); + g_debug("wait for isp driver ready…"); /* wait for the command to be processed */ if (!fu_device_retry_full(FU_DEVICE(self), @@ -236,7 +230,7 @@ POLL_INTERVAL_MS, GUINT_TO_POINTER(FU_KINETIC_DP_PUMA_REQUEST_CODE_BOOTUP_DONE), error)) { - g_prefix_error(error, "timeout waiting for REQUEST_FW_UPDATE_READY: "); + g_prefix_error_literal(error, "timeout waiting for REQUEST_FW_UPDATE_READY: "); return FALSE; } if (!fu_dpaux_device_read(FU_DPAUX_DEVICE(self), @@ -245,7 +239,7 @@ sizeof(flashinfo), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to read Flash Info from Isp Driver: "); + g_prefix_error_literal(error, "failed to read Flash Info from Isp Driver: "); return FALSE; } st = fu_struct_kinetic_dp_flash_info_parse(flashinfo, sizeof(flashinfo), 0x0, error); @@ -263,20 +257,18 @@ FuProgress *progress, GError **error) { - FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); if (!fu_kinetic_dp_puma_device_enter_code_loading_mode(self, error)) { - g_prefix_error(error, "enter code loading mode failed: "); + g_prefix_error_literal(error, "enter code loading mode failed: "); return FALSE; } fu_kinetic_dp_puma_device_send_payload(self, - io_channel, fw, progress, PUMA_CHUNK_PROCESS_MAX_WAIT, TRUE, error); - if (!fu_kinetic_dp_puma_device_wait_drv_ready(self, io_channel, error)) { - g_prefix_error(error, "wait for ISP driver ready failed: "); + if (!fu_kinetic_dp_puma_device_wait_drv_ready(self, error)) { + g_prefix_error_literal(error, "wait for ISP driver ready failed: "); return FALSE; } if (self->flash_size >= 0x400) @@ -313,16 +305,17 @@ sizeof(cmd), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, - "failed to write PUMA_DPCD_SINK_MODE_REG with FW_UPDATE_REQUEST: "); + g_prefix_error_literal( + error, + "failed to write PUMA_DPCD_SINK_MODE_REG with FW_UPDATE_REQUEST: "); return FALSE; } if (fu_kinetic_dp_device_get_fw_state(FU_KINETIC_DP_DEVICE(self)) == FU_KINETIC_DP_FW_STATE_APP) { guint8 flashinfo[FU_STRUCT_KINETIC_DP_FLASH_INFO_SIZE] = {0}; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructKineticDpFlashInfo) st = NULL; - /* Puma takes about 18ms (Winbond EF13) to get ISP driver ready for flash info */ + /* takes about 18ms (Winbond EF13) to get ISP driver ready for flash info */ fu_device_sleep(FU_DEVICE(self), 18); if (!fu_device_retry_full( FU_DEVICE(self), @@ -331,7 +324,8 @@ POLL_INTERVAL_MS, GUINT_TO_POINTER(FU_KINETIC_DP_PUMA_MODE_FLASH_INFO_READY), error)) { - g_prefix_error(error, "timeout waiting for MODE_FLASH_INFO_READY: "); + g_prefix_error_literal(error, + "timeout waiting for MODE_FLASH_INFO_READY: "); return FALSE; } @@ -342,7 +336,7 @@ sizeof(flashinfo), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to read Flash Info: "); + g_prefix_error_literal(error, "failed to read Flash Info: "); return FALSE; } st = @@ -371,7 +365,7 @@ } /* checking for flash erase done */ - g_debug("waiting for flash erasing..."); + g_debug("waiting for flash erasing…"); if (self->read_flash_prog_time) fu_device_sleep(FU_DEVICE(self), self->read_flash_prog_time); else @@ -382,7 +376,7 @@ POLL_INTERVAL_MS, GUINT_TO_POINTER(FU_KINETIC_DP_PUMA_REQUEST_FW_UPDATE_READY), error)) { - g_prefix_error(error, "timeout waiting for REQUEST_FW_UPDATE_READY: "); + g_prefix_error_literal(error, "timeout waiting for REQUEST_FW_UPDATE_READY: "); return FALSE; } @@ -451,8 +445,9 @@ sizeof(cmd), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, - "failed to write PUMA_DPCD_SINK_MODE_REG with CHIP_RESET_REQUEST: "); + g_prefix_error_literal( + error, + "failed to write PUMA_DPCD_SINK_MODE_REG with CHIP_RESET_REQUEST: "); return FALSE; } @@ -469,7 +464,6 @@ { FuKineticDpPumaDevice *self = FU_KINETIC_DP_PUMA_DEVICE(device); FuKineticDpPumaFirmware *dp_firmware = FU_KINETIC_DP_PUMA_FIRMWARE(firmware); - FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); g_autoptr(GBytes) app_fw_blob = NULL; /* progress */ @@ -514,13 +508,12 @@ if (app_fw_blob == NULL) return FALSE; if (!fu_kinetic_dp_puma_device_send_payload(self, - io_channel, app_fw_blob, fu_progress_get_child(progress), PUMA_CHUNK_PROCESS_MAX_WAIT, FALSE, error)) { - g_prefix_error(error, "sending App Firmware payload failed: "); + g_prefix_error_literal(error, "sending App Firmware payload failed: "); return FALSE; } fu_progress_step_done(progress); @@ -533,7 +526,7 @@ POLL_INTERVAL_MS, GUINT_TO_POINTER(FU_KINETIC_DP_PUMA_REQUEST_FW_UPDATE_DONE), error)) { - g_prefix_error(error, "validating App Firmware failed: "); + g_prefix_error_literal(error, "validating App Firmware failed: "); return FALSE; } fu_progress_step_done(progress); @@ -543,7 +536,7 @@ } static void -fu_kinetic_dp_puma_device_set_progress(FuDevice *self, FuProgress *progress) +fu_kinetic_dp_puma_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/kinetic-dp/fu-kinetic-dp-puma-firmware.c fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-puma-firmware.c --- fwupd-2.0.8/plugins/kinetic-dp/fu-kinetic-dp-puma-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-puma-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,7 +13,7 @@ #include "fu-kinetic-dp-secure-device.h" struct _FuKineticDpPumaFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; }; typedef struct { @@ -109,7 +109,7 @@ guint8 checksum_actual; guint8 cmdb_sig[FU_KINETIC_DP_PUMA_REQUEST_FW_CMDB_SIG_SIZE] = {'P', 'M', 'D', 'B'}; g_autoptr(GByteArray) cmdb_tmp = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructKineticDpPumaHeader) st = NULL; /* sanity check */ if (!fu_input_stream_size(stream, &streamsz, error)) @@ -127,16 +127,16 @@ st = fu_struct_kinetic_dp_puma_header_parse_stream(stream, 0x0, error); if (st == NULL) return FALSE; - offset += st->len; + offset += st->buf->len; code_size += FU_STRUCT_KINETIC_DP_PUMA_HEADER_SIZE; for (guint i = 0; i < FU_STRUCT_KINETIC_DP_PUMA_HEADER_DEFAULT_OBJECT_COUNT; i++) { - g_autoptr(GByteArray) st_obj = + g_autoptr(FuStructKineticDpPumaHeaderInfo) st_obj = fu_struct_kinetic_dp_puma_header_info_parse_stream(stream, offset, error); if (st_obj == NULL) return FALSE; code_size += fu_struct_kinetic_dp_puma_header_info_get_length(st_obj) + FU_STRUCT_KINETIC_DP_PUMA_HEADER_INFO_SIZE; - offset += st_obj->len; + offset += st_obj->buf->len; } if (code_size < (512 * 1024) + offset) { g_set_error(error, @@ -177,17 +177,17 @@ if (cmdb_tmp == NULL) return FALSE; if (cmdb_tmp->len != FU_KINETIC_DP_PUMA_REQUEST_CMDB_SIZE) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid firmware -- cmdb block invalid"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "invalid firmware -- cmdb block invalid"); return FALSE; } if (memcmp(cmdb_tmp->data, cmdb_sig, sizeof(cmdb_sig)) != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid firmware -- cmdb block not found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "invalid firmware -- cmdb block not found"); return FALSE; } @@ -222,7 +222,7 @@ static gboolean fu_kinetic_dp_puma_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuKineticDpPumaFirmware *self = FU_KINETIC_DP_PUMA_FIRMWARE(firmware); @@ -249,7 +249,7 @@ if (!fu_firmware_parse_stream(isp_drv_img, isp_drv_stream, 0x0, flags, error)) return FALSE; fu_firmware_set_idx(isp_drv_img, FU_KINETIC_DP_FIRMWARE_IDX_ISP_DRV); - if (!fu_firmware_add_image_full(firmware, isp_drv_img, error)) + if (!fu_firmware_add_image(firmware, isp_drv_img, error)) return FALSE; /* add App FW as a new image into firmware */ @@ -272,14 +272,14 @@ if (!fu_firmware_parse_stream(app_fw_img, app_fw_stream, 0x0, flags, error)) return FALSE; fu_firmware_set_idx(app_fw_img, FU_KINETIC_DP_FIRMWARE_IDX_APP_FW); - if (!fu_firmware_add_image_full(firmware, app_fw_img, error)) + if (!fu_firmware_add_image(firmware, app_fw_img, error)) return FALSE; /* figure out which chip App FW it is for */ if (!fu_kinetic_dp_puma_firmware_parse_chip_id(app_fw_stream, &priv->chip_id, error)) return FALSE; if (!fu_kinetic_dp_puma_firmware_parse_app_fw(self, app_fw_stream, error)) { - g_prefix_error(error, "failed to parse info from Puma App firmware: "); + g_prefix_error_literal(error, "failed to parse info from Puma App firmware: "); return FALSE; } diff -Nru fwupd-2.0.8/plugins/kinetic-dp/fu-kinetic-dp-secure-device.c fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-secure-device.c --- fwupd-2.0.8/plugins/kinetic-dp/fu-kinetic-dp-secure-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-secure-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -68,7 +68,7 @@ 1, FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to read DPCD_KT_PARAM_REG: "); + g_prefix_error_literal(error, "failed to read DPCD_KT_PARAM_REG: "); return FALSE; } return TRUE; @@ -87,7 +87,7 @@ sizeof(cmd_id), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to write DPCD_KT_CMD_STATUS_REG: "); + g_prefix_error_literal(error, "failed to write DPCD_KT_CMD_STATUS_REG: "); return FALSE; } return TRUE; @@ -104,7 +104,7 @@ sizeof(cmd_id), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to write DPCD_KT_CMD_STATUS_REG: "); + g_prefix_error_literal(error, "failed to write DPCD_KT_CMD_STATUS_REG: "); return FALSE; } return TRUE; @@ -125,7 +125,7 @@ sizeof(status), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to read DPCD_ADDR_CMD_STATUS_REG: "); + g_prefix_error_literal(error, "failed to read DPCD_ADDR_CMD_STATUS_REG: "); return FALSE; } @@ -137,7 +137,7 @@ g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "chunk data CRC failed: "); + "chunk data CRC failed"); return FALSE; } g_set_error(error, @@ -178,7 +178,7 @@ poll_interval_ms, GUINT_TO_POINTER(cmd_id), error)) { - g_prefix_error(error, "timeout waiting for prop command: "); + g_prefix_error_literal(error, "timeout waiting for prop command: "); return FALSE; } @@ -202,7 +202,7 @@ 1, FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to read DPCD_ISP_REPLY_DATA_LEN_REG: "); + g_prefix_error_literal(error, "failed to read DPCD_ISP_REPLY_DATA_LEN_REG: "); return FALSE; } @@ -223,7 +223,7 @@ read_data_len, FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to read DPCD_ISP_REPLY_DATA_REG: "); + g_prefix_error_literal(error, "failed to read DPCD_ISP_REPLY_DATA_REG: "); return FALSE; } *read_len = read_data_len; @@ -255,7 +255,7 @@ len, FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to write DPCD_KT_REPLY_DATA_REG: "); + g_prefix_error_literal(error, "failed to write DPCD_KT_REPLY_DATA_REG: "); return FALSE; } return fu_dpaux_device_write(FU_DPAUX_DEVICE(self), @@ -425,7 +425,7 @@ buf_crc16, sizeof(buf_crc16), error)) { - g_prefix_error(error, "failed to send CRC16 to reply data register: "); + g_prefix_error_literal(error, "failed to send CRC16 to reply register: "); return FALSE; } @@ -437,7 +437,7 @@ wait_interval_ms, &status, error)) { - g_prefix_error(error, "target failed to process payload chunk: "); + g_prefix_error_literal(error, "target failed to process payload chunk: "); return FALSE; } fu_progress_step_done(progress); @@ -504,7 +504,7 @@ poll_interval_ms, NULL, error)) { - g_prefix_error(error, "timeout waiting for DPCD_ISP_SINK_STATUS_REG: "); + g_prefix_error_literal(error, "timeout waiting for DPCD_ISP_SINK_STATUS_REG: "); return FALSE; } return TRUE; @@ -516,7 +516,7 @@ guint8 status; guint8 read_len; guint8 reply_data[6] = {0}; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructKineticDpFlashInfo) st = NULL; /* in Jaguar, it takes about FU_KINETIC_DP_DEVICE_TIMEOUT ms to boot up and initialize */ self->flash_id = 0; @@ -548,7 +548,7 @@ sizeof(reply_data), &read_len, error)) { - g_prefix_error(error, "failed to read flash ID and size: "); + g_prefix_error_literal(error, "failed to read flash ID and size: "); return FALSE; } st = fu_struct_kinetic_dp_flash_info_parse(reply_data, sizeof(reply_data), 0x0, error); @@ -589,18 +589,18 @@ if (!fu_kinetic_dp_secure_device_enter_code_loading_mode(self, g_bytes_get_size(fw), error)) { - g_prefix_error(error, "enabling code-loading mode failed: "); + g_prefix_error_literal(error, "enabling code-loading mode failed: "); return FALSE; } if (!fu_kinetic_dp_secure_device_send_payload(self, fw, 10000, 50, progress, error)) { - g_prefix_error(error, "sending ISP driver payload failed: "); + g_prefix_error_literal(error, "sending ISP driver payload failed: "); return FALSE; } - g_debug("sending ISP driver payload..."); + g_debug("sending ISP driver payload…"); if (!fu_kinetic_dp_secure_device_execute_isp_drv(self, error)) { - g_prefix_error(error, "ISP driver booting up failed: "); + g_prefix_error_literal(error, "ISP driver booting up failed: "); return FALSE; } return TRUE; @@ -650,7 +650,7 @@ return FALSE; if (!fu_kinetic_dp_secure_device_write_dpcd_reply_data_reg(self, buf, sizeof(buf), error)) { - g_prefix_error(error, "send payload size failed: "); + g_prefix_error_literal(error, "send payload size failed: "); return FALSE; } @@ -661,7 +661,7 @@ 500, &status, error)) { - g_prefix_error(error, "entering F/W update mode failed: "); + g_prefix_error_literal(error, "entering F/W update mode failed: "); return FALSE; } @@ -705,7 +705,7 @@ 200, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to send certificates: "); + g_prefix_error_literal(error, "failed to send certificates: "); return FALSE; } } @@ -724,7 +724,7 @@ 200, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to send ESM payload: "); + g_prefix_error_literal(error, "failed to send ESM payload: "); return FALSE; } fu_progress_step_done(progress); @@ -742,7 +742,7 @@ 200, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to send App FW payload: "); + g_prefix_error_literal(error, "failed to send App FW payload: "); return FALSE; } fu_progress_step_done(progress); @@ -763,7 +763,7 @@ 200, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to send App init data: "); + g_prefix_error_literal(error, "failed to send App init data: "); return FALSE; } fu_progress_step_done(progress); @@ -781,7 +781,7 @@ 200, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to send App ID data: "); + g_prefix_error_literal(error, "failed to send App ID data: "); return FALSE; } fu_progress_step_done(progress); @@ -804,7 +804,7 @@ sizeof(status), FU_KINETIC_DP_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to read DPCD_MCA_CMD_REG: "); + g_prefix_error_literal(error, "failed to read DPCD_MCA_CMD_REG: "); return FALSE; } @@ -834,7 +834,7 @@ guint8 cmd_id = FU_KINETIC_DP_DPCD_CMD_INSTALL_IMAGES; if (!fu_kinetic_dp_secure_device_write_kt_prop_cmd(self, cmd_id, error)) { - g_prefix_error(error, "failed to send DPCD command: "); + g_prefix_error_literal(error, "failed to send DPCD command: "); return FALSE; } @@ -844,7 +844,7 @@ INSTALL_IMAGE_POLL_INTERVAL_MS, NULL, error)) { - g_prefix_error(error, "timeout waiting for install command to be processed "); + g_prefix_error_literal(error, "timeout waiting for install to be processed: "); return FALSE; } @@ -1005,7 +1005,7 @@ } static void -fu_kinetic_dp_secure_device_set_progress(FuDevice *self, FuProgress *progress) +fu_kinetic_dp_secure_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/kinetic-dp/fu-kinetic-dp-secure-firmware.c fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-secure-firmware.c --- fwupd-2.0.8/plugins/kinetic-dp/fu-kinetic-dp-secure-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/kinetic-dp/fu-kinetic-dp-secure-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,7 +12,7 @@ #include "fu-kinetic-dp-secure-firmware.h" struct _FuKineticDpSecureFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; }; typedef struct { @@ -146,7 +146,7 @@ gsize streamsz = 0; guint32 app_code_block_size; guint32 std_fw_ver = 0; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructKineticDpJaguarFooter) st = NULL; /* sanity check */ if (!fu_input_stream_size(stream, &streamsz, error)) @@ -187,7 +187,7 @@ static gboolean fu_kinetic_dp_secure_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuKineticDpSecureFirmware *self = FU_KINETIC_DP_SECURE_FIRMWARE(firmware); @@ -225,7 +225,7 @@ if (!fu_firmware_parse_stream(isp_drv_img, isp_drv_stream, 0x0, flags, error)) return FALSE; fu_firmware_set_idx(isp_drv_img, FU_KINETIC_DP_FIRMWARE_IDX_ISP_DRV); - if (!fu_firmware_add_image_full(firmware, isp_drv_img, error)) + if (!fu_firmware_add_image(firmware, isp_drv_img, error)) return FALSE; /* add App FW as a new image into firmware */ @@ -238,7 +238,7 @@ if (!fu_firmware_parse_stream(app_fw_img, app_fw_stream, 0x0, flags, error)) return FALSE; fu_firmware_set_idx(app_fw_img, FU_KINETIC_DP_FIRMWARE_IDX_APP_FW); - if (!fu_firmware_add_image_full(firmware, app_fw_img, error)) + if (!fu_firmware_add_image(firmware, app_fw_img, error)) return FALSE; /* figure out which chip App FW it is for */ @@ -248,7 +248,7 @@ error)) return FALSE; if (!fu_kinetic_dp_secure_firmware_parse_app_fw(self, stream, error)) { - g_prefix_error(error, "failed to parse info from Jaguar or Mustang App firmware: "); + g_prefix_error_literal(error, "failed to parse Jaguar or Mustang App firmware: "); return FALSE; } diff -Nru fwupd-2.0.8/plugins/kinetic-dp/meson.build fwupd-2.0.20/plugins/kinetic-dp/meson.build --- fwupd-2.0.8/plugins/kinetic-dp/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/kinetic-dp/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginKineticDp"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -20,4 +21,3 @@ dependencies: plugin_deps, ) plugin_builtins += plugin_builtin_kinetic_dp -endif diff -Nru fwupd-2.0.8/plugins/legion-hid/README.md fwupd-2.0.20/plugins/legion-hid/README.md --- fwupd-2.0.8/plugins/legion-hid/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,47 @@ +--- +title: Plugin: Legion HID +--- + +## Introduction + +The Legion HID plugin is used for interacting with the MCU on Legion Go 2 devices. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob in +a packed binary file format. + +This plugin supports the following protocol ID: + +* `com.lenovo.legion-hid` + +## GUID Generation + +These devices use the standard DeviceInstanceId values, e.g. + +* `HIDRAW\VEN_17EF&DEV_61EB` + +Additionally, for child devices two custom instance IDs are created: + +* `HIDRAW\VEN_17EF&DEV_61EB&CHILD_LEFT` +* `HIDRAW\VEN_17EF&DEV_61EB&CHILD_RIGHT` + +## Update Behavior + +The device will restart after update. + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x17EF` + +## Quirk Use + +This plugin uses the following plugin-specific quirks: + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. + +## Version Considerations + +This plugin has been available since fwupd version `2.0.18`. diff -Nru fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-child.c fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-child.c --- fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-child.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-child.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,129 @@ +/* + * Copyright 2025 lazro <2059899519@qq.com> + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-legion-hid-child.h" +#include "fu-legion-hid-device.h" +#include "fu-legion-hid-firmware.h" + +struct _FuLegionHidChild { + FuDevice parent_instance; + FuLegionHidDeviceId id; +}; + +G_DEFINE_TYPE(FuLegionHidChild, fu_legion_hid_child, FU_TYPE_DEVICE) + +static gboolean +fu_legion_hid_child_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLegionHidChild *self = FU_LEGION_HID_CHILD(device); + FuLegionHidDevice *proxy; + guint32 version = 0; + g_autoptr(FuFirmware) img = NULL; + + img = fu_firmware_get_image_by_id(firmware, fu_device_get_logical_id(device), error); + if (img == NULL) + return FALSE; + proxy = FU_LEGION_HID_DEVICE(fu_device_get_proxy(device, error)); + if (proxy == NULL) + return FALSE; + if (!fu_legion_hid_device_execute_upgrade(proxy, img, error)) { + g_prefix_error(error, "execute %s failed: ", fu_device_get_logical_id(device)); + return FALSE; + } + /* + * If only the controller is updated, the MCU will not restart, + * so the version number needs to be reset.If the version is not reset, + * fwupd will report an update failure. + */ + if (!fu_legion_hid_device_get_version(proxy, self->id, &version, error)) + return FALSE; + fu_device_set_version_raw(device, version); + + /* success */ + return TRUE; +} + +static gchar * +fu_legion_hid_child_convert_version(FuDevice *device, guint64 version_raw) +{ + return g_strdup_printf("%X", (guint)version_raw); +} + +static void +fu_legion_hid_child_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static gboolean +fu_legion_hid_child_setup(FuDevice *device, GError **error) +{ + FuLegionHidChild *self = FU_LEGION_HID_CHILD(device); + FuLegionHidDevice *proxy; + guint32 version = 0; + + proxy = FU_LEGION_HID_DEVICE(fu_device_get_proxy(device, error)); + if (proxy == NULL) + return FALSE; + if (!fu_legion_hid_device_get_version(proxy, self->id, &version, error)) + return FALSE; + fu_device_set_version_raw(device, version); + + fu_device_add_instance_str(device, "CHILD", fu_device_get_logical_id(device)); + if (!fu_device_build_instance_id(device, error, "HIDRAW", "VEN", "DEV", "CHILD", NULL)) + return FALSE; + + /* success */ + return TRUE; +} + +static void +fu_legion_hid_child_class_init(FuLegionHidChildClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->setup = fu_legion_hid_child_setup; + device_class->write_firmware = fu_legion_hid_child_write_firmware; + device_class->set_progress = fu_legion_hid_child_set_progress; + device_class->convert_version = fu_legion_hid_child_convert_version; +} + +static void +fu_legion_hid_child_init(FuLegionHidChild *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_protocol(FU_DEVICE(self), "com.lenovo.legion-hid"); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_LEGION_HID_FIRMWARE); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_LEGION_HID_DEVICE); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); +} + +FuLegionHidChild * +fu_legion_hid_child_new(FuDevice *parent, FuLegionHidDeviceId id) +{ + FuLegionHidChild *self = + g_object_new(FU_TYPE_LEGION_HID_CHILD, "proxy", parent, "parent", parent, NULL); + self->id = id; + fu_device_incorporate(FU_DEVICE(self), + parent, + FU_DEVICE_INCORPORATE_FLAG_VID | FU_DEVICE_INCORPORATE_FLAG_PID); + return self; +} diff -Nru fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-child.h fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-child.h --- fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-child.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-child.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Copyright 2025 lazro <2059899519@qq.com> + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#include "fu-legion-hid-struct.h" + +#define FU_TYPE_LEGION_HID_CHILD (fu_legion_hid_child_get_type()) +G_DECLARE_FINAL_TYPE(FuLegionHidChild, fu_legion_hid_child, FU, LEGION_HID_CHILD, FuDevice) + +FuLegionHidChild * +fu_legion_hid_child_new(FuDevice *parent, FuLegionHidDeviceId id); diff -Nru fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-device.c fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-device.c --- fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,786 @@ +/* + * Copyright 2025 lazro <2059899519@qq.com> + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-legion-hid-child.h" +#include "fu-legion-hid-device.h" +#include "fu-legion-hid-firmware.h" + +struct _FuLegionHidDevice { + FuHidrawDevice parent_instance; +}; + +G_DEFINE_TYPE(FuLegionHidDevice, fu_legion_hid_device, FU_TYPE_HIDRAW_DEVICE) + +#define FU_LEGION_HID_DEVICE_IO_TIMEOUT 500 +#define FU_LEGION_HID_DEVICE_REBOOT_WAIT_TIME (10 * 1000) + +#define FU_LEGION_HID_DEVICE_FW_SIGNED_LENGTH 384 +#define FU_LEGION_HID_DEVICE_FW_ID_LENGTH 4 +#define FU_LEGION_HID_DEVICE_FW_PACKET_LENGTH 32 +#define FU_LEGION_HID_DEVICE_FW_REPORT_LENGTH 64 + +typedef struct { + GByteArray *res; + guint8 main_id; + guint8 sub_id; + FuLegionHidDeviceId dev_id; + guint8 step; +} FuLegionHidRetryHelper; + +static gboolean +fu_legion_hid_device_read_normal_response_retry_cb(FuDevice *device, + gpointer user_data, + GError **error) +{ + FuLegionHidRetryHelper *helper = (FuLegionHidRetryHelper *)user_data; + GByteArray *res = helper->res; + + g_byte_array_set_size(res, FU_LEGION_HID_DEVICE_FW_REPORT_LENGTH); + if (!fu_udev_device_read(FU_UDEV_DEVICE(device), + res->data, + res->len, + NULL, + FU_LEGION_HID_DEVICE_IO_TIMEOUT, + FU_IO_CHANNEL_FLAG_NONE, + error)) + return FALSE; + if (res->data[2] != helper->main_id || res->data[3] != helper->sub_id || + res->data[4] != helper->dev_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BUSY, + "response mismatch filter(%u, %u, %u), retrying...", + helper->main_id, + helper->sub_id, + helper->dev_id); + return FALSE; + } + + /* success */ + return TRUE; +} + +static GByteArray * +fu_legion_hid_device_read_response(FuLegionHidDevice *self, + FuLegionHidRetryHelper *helper, + GError **error) +{ + g_autoptr(GByteArray) res = g_byte_array_sized_new(FU_LEGION_HID_DEVICE_FW_REPORT_LENGTH); + helper->res = res; + if (!fu_device_retry_full(FU_DEVICE(self), + fu_legion_hid_device_read_normal_response_retry_cb, + 120, + 0, + helper, + error)) + return NULL; + return g_steal_pointer(&res); +} + +static gboolean +fu_legion_hid_device_check_upgrade_device_id(guint8 rsp_id, guint8 dev_id) +{ + if (rsp_id == dev_id) + return TRUE; + if (rsp_id == FU_LEGION_HID_DEVICE_ID_GAMEPAD_L && + (dev_id == FU_LEGION_HID_DEVICE_ID_GAMEPAD_L2 || + dev_id == FU_LEGION_HID_DEVICE_ID_GAMEPAD_L3)) + return TRUE; + if (rsp_id == FU_LEGION_HID_DEVICE_ID_GAMEPAD_R && + (dev_id == FU_LEGION_HID_DEVICE_ID_GAMEPAD_R2 || + dev_id == FU_LEGION_HID_DEVICE_ID_GAMEPAD_R3)) + return TRUE; + return FALSE; +} + +static gboolean +fu_legion_hid_device_read_upgrade_response_retry_cb(FuDevice *device, + gpointer user_data, + GError **error) +{ + FuLegionHidRetryHelper *helper = (FuLegionHidRetryHelper *)user_data; + GByteArray *res = helper->res; + g_autoptr(FuStructLegionHidUpgradeRsp) st_rsp = NULL; + + g_byte_array_set_size(res, FU_LEGION_HID_DEVICE_FW_REPORT_LENGTH); + if (!fu_udev_device_read(FU_UDEV_DEVICE(device), + res->data, + res->len, + NULL, + FU_LEGION_HID_DEVICE_IO_TIMEOUT, + FU_IO_CHANNEL_FLAG_NONE, + error)) + return FALSE; + st_rsp = fu_struct_legion_hid_upgrade_rsp_parse(res->data, res->len, 0x0, error); + if (st_rsp == NULL) { + g_prefix_error_literal(error, "device report upgrade command failed: "); + return FALSE; + } + if (fu_struct_legion_hid_upgrade_rsp_get_main_id(st_rsp) != helper->main_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BUSY, + "response main ID was 0x%x, expected 0x%x", + fu_struct_legion_hid_upgrade_rsp_get_main_id(st_rsp), + helper->main_id); + return FALSE; + } + if (fu_struct_legion_hid_upgrade_rsp_get_sub_id(st_rsp) != helper->sub_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BUSY, + "response sub ID was 0x%x, expected 0x%x", + fu_struct_legion_hid_upgrade_rsp_get_sub_id(st_rsp), + helper->sub_id); + return FALSE; + } + if (fu_struct_legion_hid_upgrade_rsp_get_step(st_rsp) != helper->step) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BUSY, + "response step was 0x%x, expected 0x%x", + fu_struct_legion_hid_upgrade_rsp_get_step(st_rsp), + helper->step); + return FALSE; + } + if (!fu_legion_hid_device_check_upgrade_device_id( + fu_struct_legion_hid_upgrade_rsp_get_id(st_rsp), + helper->dev_id)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BUSY, + "response dev ID was 0x%x, expected 0x%x", + fu_struct_legion_hid_upgrade_rsp_get_id(st_rsp), + helper->dev_id); + return FALSE; + } + + /* success */ + return TRUE; +} + +static GByteArray * +fu_legion_hid_device_read_upgrade_response(FuLegionHidDevice *self, + FuLegionHidRetryHelper *helper, + GError **error) +{ + g_autoptr(GByteArray) res = g_byte_array_sized_new(FU_LEGION_HID_DEVICE_FW_REPORT_LENGTH); + helper->res = res; + if (!fu_device_retry_full(FU_DEVICE(self), + fu_legion_hid_device_read_upgrade_response_retry_cb, + 120, + 0, + helper, + error)) + return NULL; + return g_steal_pointer(&res); +} + +static gboolean +fu_legion_hid_device_read_upgrade_query_size_response_retry_cb(FuDevice *device, + gpointer user_data, + GError **error) +{ + FuLegionHidRetryHelper *helper = (FuLegionHidRetryHelper *)user_data; + GByteArray *res = helper->res; + g_autoptr(FuStructLegionHidUpgradeQuerySizeRsp) st_rsp = NULL; + + g_byte_array_set_size(res, FU_LEGION_HID_DEVICE_FW_REPORT_LENGTH); + if (!fu_udev_device_read(FU_UDEV_DEVICE(device), + res->data, + res->len, + NULL, + FU_LEGION_HID_DEVICE_IO_TIMEOUT, + FU_IO_CHANNEL_FLAG_NONE, + error)) + return FALSE; + st_rsp = fu_struct_legion_hid_upgrade_query_size_rsp_parse(res->data, res->len, 0x0, error); + if (st_rsp == NULL) { + g_prefix_error_literal(error, "device report upgrade command failed: "); + return FALSE; + } + if (fu_struct_legion_hid_upgrade_query_size_rsp_get_main_id(st_rsp) != helper->main_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BUSY, + "response main ID was 0x%x, expected 0x%x", + fu_struct_legion_hid_upgrade_query_size_rsp_get_main_id(st_rsp), + helper->main_id); + return FALSE; + } + if (fu_struct_legion_hid_upgrade_query_size_rsp_get_sub_id(st_rsp) != helper->sub_id) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BUSY, + "response sub ID was 0x%x, expected 0x%x", + fu_struct_legion_hid_upgrade_query_size_rsp_get_sub_id(st_rsp), + helper->sub_id); + return FALSE; + } + if (fu_struct_legion_hid_upgrade_query_size_rsp_get_step(st_rsp) != helper->step) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BUSY, + "response step was 0x%x, expected 0x%x", + fu_struct_legion_hid_upgrade_query_size_rsp_get_step(st_rsp), + helper->step); + return FALSE; + } + if (!fu_legion_hid_device_check_upgrade_device_id( + fu_struct_legion_hid_upgrade_query_size_rsp_get_id(st_rsp), + helper->dev_id)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BUSY, + "response dev ID was 0x%x, expected 0x%x", + fu_struct_legion_hid_upgrade_query_size_rsp_get_id(st_rsp), + helper->dev_id); + return FALSE; + } + if (fu_struct_legion_hid_upgrade_query_size_rsp_get_response(st_rsp) == + FU_LEGION_HID_RESPONSE_STATUS_BUSY) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_BUSY, + "response report device is busy"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static GByteArray * +fu_legion_hid_device_read_upgrade_query_size_response(FuLegionHidDevice *self, + FuLegionHidRetryHelper *helper, + GError **error) +{ + g_autoptr(GByteArray) res = g_byte_array_sized_new(FU_LEGION_HID_DEVICE_FW_REPORT_LENGTH); + helper->res = res; + if (!fu_device_retry_full(FU_DEVICE(self), + fu_legion_hid_device_read_upgrade_query_size_response_retry_cb, + 120, + 0, + helper, + error)) + return NULL; + return g_steal_pointer(&res); +} + +static gboolean +fu_legion_hid_device_upgrade_start(FuLegionHidDevice *self, + FuLegionHidDeviceId id, + GInputStream *stream, + GError **error) +{ + FuLegionHidRetryHelper helper = {0}; + guint16 crc16 = 0; + gsize streamsz = 0; + g_autoptr(GByteArray) res = NULL; + g_autoptr(FuStructLegionHidUpgradeCmd) st_cmd = fu_struct_legion_hid_upgrade_cmd_new(); + g_autoptr(FuStructLegionHidUpgradeStartParam) st_content = + fu_struct_legion_hid_upgrade_start_param_new(); + + if (!fu_input_stream_size(stream, &streamsz, error)) + return FALSE; + if (!fu_input_stream_compute_crc16(stream, FU_CRC_KIND_B16_XMODEM, &crc16, error)) + return FALSE; + fu_struct_legion_hid_upgrade_start_param_set_crc16(st_content, crc16); + fu_struct_legion_hid_upgrade_start_param_set_size(st_content, streamsz); + fu_struct_legion_hid_upgrade_cmd_set_length(st_cmd, st_content->buf->len + 5); + fu_struct_legion_hid_upgrade_cmd_set_device_id(st_cmd, id); + if (!fu_struct_legion_hid_upgrade_cmd_set_data(st_cmd, + st_content->buf->data, + st_content->buf->len, + error)) + return FALSE; + if (!fu_udev_device_write(FU_UDEV_DEVICE(self), + st_cmd->buf->data, + st_cmd->buf->len, + FU_LEGION_HID_DEVICE_IO_TIMEOUT, + FU_IO_CHANNEL_FLAG_NONE, + error)) + return FALSE; + + helper.main_id = fu_struct_legion_hid_upgrade_cmd_get_main_id(st_cmd); + helper.sub_id = fu_struct_legion_hid_upgrade_cmd_get_sub_id(st_cmd); + helper.dev_id = id; + helper.step = FU_LEGION_HID_UPGRADE_STEP_START; + res = fu_legion_hid_device_read_upgrade_response(self, &helper, error); + if (res == NULL) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_legion_hid_device_upgrade_query_size(FuLegionHidDevice *self, + FuLegionHidDeviceId id, + guint16 *max_size, + GError **error) +{ + FuLegionHidRetryHelper helper = {0}; + g_autoptr(FuStructLegionHidUpgradeQuerySizeRsp) st_rsp = NULL; + g_autoptr(GByteArray) res = NULL; + g_autoptr(FuStructLegionHidUpgradeCmd) st_cmd = fu_struct_legion_hid_upgrade_cmd_new(); + guint8 content[] = { + 0x02, + FU_LEGION_HID_UPGRADE_STEP_QUERY_SIZE, + FU_LEGION_HID_CMD_CONSTANT_SN, + }; + + fu_struct_legion_hid_upgrade_cmd_set_length(st_cmd, sizeof(content) + 5); + fu_struct_legion_hid_upgrade_cmd_set_device_id(st_cmd, id); + if (!fu_struct_legion_hid_upgrade_cmd_set_data(st_cmd, content, sizeof(content), error)) + return FALSE; + if (!fu_udev_device_write(FU_UDEV_DEVICE(self), + st_cmd->buf->data, + st_cmd->buf->len, + FU_LEGION_HID_DEVICE_IO_TIMEOUT, + FU_IO_CHANNEL_FLAG_NONE, + error)) + return FALSE; + + helper.main_id = fu_struct_legion_hid_upgrade_cmd_get_main_id(st_cmd); + helper.sub_id = fu_struct_legion_hid_upgrade_cmd_get_sub_id(st_cmd); + helper.dev_id = id; + helper.step = FU_LEGION_HID_UPGRADE_STEP_QUERY_SIZE; + res = fu_legion_hid_device_read_upgrade_query_size_response(self, &helper, error); + if (res == NULL) + return FALSE; + st_rsp = fu_struct_legion_hid_upgrade_query_size_rsp_parse(res->data, res->len, 0x0, error); + if (st_rsp == NULL) + return FALSE; + if (fu_struct_legion_hid_upgrade_query_size_rsp_get_response(st_rsp) == + FU_LEGION_HID_RESPONSE_STATUS_FAIL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "device report query size command failed with %u", + fu_struct_legion_hid_upgrade_query_size_rsp_get_response(st_rsp)); + return FALSE; + } + if (max_size != NULL) { + if (!fu_memread_uint16_safe( + res->data, + res->len, + FU_STRUCT_LEGION_HID_UPGRADE_QUERY_SIZE_RSP_OFFSET_RESPONSE, + max_size, + G_BIG_ENDIAN, + error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_legion_hid_device_upgrade_write_data_chunk(FuLegionHidDevice *self, + FuLegionHidDeviceId id, + guint16 max_size, + guint *send_size, + FuChunk *chk, + GError **error) +{ + guint ready_send_size = 0; + g_autoptr(FuStructLegionHidUpgradeCmd) st_cmd = fu_struct_legion_hid_upgrade_cmd_new(); + g_autoptr(FuStructLegionHidUpgradePacket) st_packet = + fu_struct_legion_hid_upgrade_packet_new(); + + if (!fu_struct_legion_hid_upgrade_packet_set_data(st_packet, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + fu_struct_legion_hid_upgrade_cmd_set_length(st_cmd, st_packet->buf->len + 5); + fu_struct_legion_hid_upgrade_cmd_set_device_id(st_cmd, id); + fu_struct_legion_hid_upgrade_cmd_set_param(st_cmd, + FU_LEGION_HID_CMD_CONSTANT_UPGRADE_SEND_DATA); + if (!fu_struct_legion_hid_upgrade_cmd_set_data(st_cmd, + st_packet->buf->data, + st_packet->buf->len, + error)) + return FALSE; + ready_send_size = *send_size + fu_chunk_get_data_sz(chk); + if (!fu_udev_device_write(FU_UDEV_DEVICE(self), + st_cmd->buf->data, + st_cmd->buf->len, + FU_LEGION_HID_DEVICE_IO_TIMEOUT, + FU_IO_CHANNEL_FLAG_NONE, + error)) + return FALSE; + *send_size = ready_send_size; + if (ready_send_size % max_size == 0) { + FuLegionHidRetryHelper helper = {0}; + guint32 recieved_size = 0; + g_autoptr(GByteArray) res = NULL; + + helper.main_id = fu_struct_legion_hid_upgrade_cmd_get_main_id(st_cmd); + helper.sub_id = fu_struct_legion_hid_upgrade_cmd_get_sub_id(st_cmd); + helper.dev_id = id; + helper.step = FU_LEGION_HID_UPGRADE_STEP_WRITE_DATA; + res = fu_legion_hid_device_read_upgrade_response(self, &helper, error); + if (res == NULL) + return FALSE; + if (!fu_memread_uint32_safe(res->data, + res->len, + FU_STRUCT_LEGION_HID_UPGRADE_RSP_SIZE, + &recieved_size, + G_BIG_ENDIAN, + error)) + return FALSE; + if (recieved_size != *send_size) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "device report received size %u mismatch send size %u", + recieved_size, + *send_size); + return FALSE; + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_legion_hid_device_upgrade_write_data_chunks(FuLegionHidDevice *self, + FuLegionHidDeviceId id, + guint16 max_size, + FuChunkArray *chunks, + GError **error) +{ + guint send_size = 0; + + for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { + g_autoptr(FuChunk) chk = NULL; + + chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) + return FALSE; + if (!fu_legion_hid_device_upgrade_write_data_chunk(self, + id, + max_size, + &send_size, + chk, + error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_legion_hid_device_upgrade_write_data(FuLegionHidDevice *self, + FuLegionHidDeviceId id, + guint16 max_size, + GInputStream *stream, + GError **error) +{ + g_autoptr(FuChunkArray) chunks = NULL; + + if (max_size == 0 || max_size % FU_LEGION_HID_DEVICE_FW_PACKET_LENGTH != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "device report max size %u is invalid", + max_size); + return FALSE; + } + + chunks = fu_chunk_array_new_from_stream(stream, + FU_CHUNK_ADDR_OFFSET_NONE, + FU_CHUNK_PAGESZ_NONE, + FU_LEGION_HID_DEVICE_FW_PACKET_LENGTH, + error); + if (chunks == NULL) + return FALSE; + return fu_legion_hid_device_upgrade_write_data_chunks(self, id, max_size, chunks, error); +} + +static gboolean +fu_legion_hid_device_upgrade_verify(FuLegionHidDevice *self, FuLegionHidDeviceId id, GError **error) +{ + FuLegionHidRetryHelper helper = {0}; + g_autoptr(GByteArray) res = NULL; + g_autoptr(FuStructLegionHidUpgradeCmd) st_cmd = fu_struct_legion_hid_upgrade_cmd_new(); + guint8 content[] = { + 0x02, + FU_LEGION_HID_UPGRADE_STEP_VERIFY, + FU_LEGION_HID_CMD_CONSTANT_SN, + }; + + fu_struct_legion_hid_upgrade_cmd_set_length(st_cmd, sizeof(content) + 5); + fu_struct_legion_hid_upgrade_cmd_set_device_id(st_cmd, id); + if (!fu_struct_legion_hid_upgrade_cmd_set_data(st_cmd, content, sizeof(content), error)) + return FALSE; + if (!fu_udev_device_write(FU_UDEV_DEVICE(self), + st_cmd->buf->data, + st_cmd->buf->len, + FU_LEGION_HID_DEVICE_IO_TIMEOUT, + FU_IO_CHANNEL_FLAG_NONE, + error)) + return FALSE; + + helper.main_id = fu_struct_legion_hid_upgrade_cmd_get_main_id(st_cmd); + helper.sub_id = fu_struct_legion_hid_upgrade_cmd_get_sub_id(st_cmd); + helper.dev_id = id; + helper.step = FU_LEGION_HID_UPGRADE_STEP_VERIFY; + res = fu_legion_hid_device_read_upgrade_response(self, &helper, error); + if (res == NULL) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_legion_hid_device_get_version_internal(FuLegionHidDevice *self, + FuLegionHidDeviceId id, + guint32 *version, + GError **error) +{ + FuLegionHidRetryHelper helper = {0}; + guint8 content[] = { + FU_LEGION_HID_CMD_CONSTANT_SN, + }; + g_autoptr(FuStructLegionHidNormalCmd) st_cmd = fu_struct_legion_hid_normal_cmd_new(); + g_autoptr(GByteArray) res = NULL; + + fu_struct_legion_hid_normal_cmd_set_length(st_cmd, sizeof(content) + 4); + fu_struct_legion_hid_normal_cmd_set_main_id(st_cmd, 0x79); + fu_struct_legion_hid_normal_cmd_set_sub_id(st_cmd, 0x01); + fu_struct_legion_hid_normal_cmd_set_device_id(st_cmd, id); + if (!fu_struct_legion_hid_normal_cmd_set_data(st_cmd, content, sizeof(content), error)) + return FALSE; + if (!fu_udev_device_write(FU_UDEV_DEVICE(self), + st_cmd->buf->data, + st_cmd->buf->len, + FU_LEGION_HID_DEVICE_IO_TIMEOUT, + FU_IO_CHANNEL_FLAG_NONE, + error)) + return FALSE; + + helper.main_id = fu_struct_legion_hid_normal_cmd_get_main_id(st_cmd); + helper.sub_id = fu_struct_legion_hid_normal_cmd_get_sub_id(st_cmd); + helper.dev_id = id; + res = fu_legion_hid_device_read_response(self, &helper, error); + if (res == NULL) + return FALSE; + if (!fu_memread_uint32_safe(res->data, res->len, 13, version, G_BIG_ENDIAN, error)) + return FALSE; + if (*version == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "version 0 is invalid"); + return FALSE; + } + + /* success */ + return TRUE; +} + +typedef struct { + FuLegionHidDeviceId id; + guint32 *version; +} FuLegionHidDeviceVersionHelper; + +static gboolean +fu_legion_hid_device_get_version_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuLegionHidDevice *self = FU_LEGION_HID_DEVICE(device); + FuLegionHidDeviceVersionHelper *helper = (FuLegionHidDeviceVersionHelper *)user_data; + return fu_legion_hid_device_get_version_internal(self, helper->id, helper->version, error); +} + +gboolean +fu_legion_hid_device_get_version(FuLegionHidDevice *self, + FuLegionHidDeviceId id, + guint32 *version, + GError **error) +{ + FuLegionHidDeviceVersionHelper helper = { + .id = id, + .version = version, + }; + return fu_device_retry_full(FU_DEVICE(self), + fu_legion_hid_device_get_version_cb, + 10, + 2000, + &helper, + error); +} + +static gboolean +fu_legion_hid_device_get_id(GInputStream *stream, FuLegionHidDeviceId *id, GError **error) +{ + guint offset = 0; + gsize streamsz = 0; + + if (!fu_input_stream_size(stream, &streamsz, error)) + return FALSE; + offset = + streamsz - FU_LEGION_HID_DEVICE_FW_SIGNED_LENGTH - FU_LEGION_HID_DEVICE_FW_ID_LENGTH; + for (guint i = 0; i < FU_LEGION_HID_DEVICE_FW_ID_LENGTH; i++) { + guint8 id_tmp = 0; + if (!fu_input_stream_read_u8(stream, offset + i, &id_tmp, error)) + return FALSE; + if (id_tmp == FU_LEGION_HID_DEVICE_ID_DONGLE || + id_tmp == FU_LEGION_HID_DEVICE_ID_GAMEPAD_L3 || + id_tmp == FU_LEGION_HID_DEVICE_ID_GAMEPAD_R3) { + if (id != NULL) + *id = id_tmp; + return TRUE; + } + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "firmware device id is invalid"); + return FALSE; +} + +gboolean +fu_legion_hid_device_execute_upgrade(FuLegionHidDevice *self, FuFirmware *firmware, GError **error) +{ + FuLegionHidDeviceId id = 0; + guint16 max_size = 0; + g_autoptr(GInputStream) stream = NULL; + + stream = fu_firmware_get_stream(firmware, error); + if (stream == NULL) + return FALSE; + if (!fu_legion_hid_device_get_id(stream, &id, error)) + return FALSE; + if (!fu_legion_hid_device_upgrade_start(self, id, stream, error)) + return FALSE; + if (!fu_legion_hid_device_upgrade_query_size(self, id, &max_size, error)) + return FALSE; + if (!fu_legion_hid_device_upgrade_write_data(self, id, max_size, stream, error)) + return FALSE; + if (!fu_legion_hid_device_upgrade_verify(self, id, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_legion_hid_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLegionHidDevice *self = FU_LEGION_HID_DEVICE(device); + g_autoptr(FuFirmware) img = NULL; + + img = fu_firmware_get_image_by_id(firmware, FU_LEGION_HID_FIRMWARE_ID_MCU, error); + if (img == NULL) + return FALSE; + if (!fu_legion_hid_device_execute_upgrade(self, img, error)) { + g_prefix_error_literal(error, "execute upgrade mcu failed: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gchar * +fu_legion_hid_device_convert_version(FuDevice *device, guint64 version_raw) +{ + return g_strdup_printf("%X", (guint)version_raw); +} + +static void +fu_legion_hid_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static gboolean +fu_legion_hid_device_setup(FuDevice *device, GError **error) +{ + FuLegionHidDevice *self = FU_LEGION_HID_DEVICE(device); + guint32 version = 0; + g_autoptr(FuHidDescriptor) descriptor = NULL; + g_autoptr(FuHidReport) report = NULL; + g_autoptr(FuLegionHidChild) child_left = NULL; + g_autoptr(FuLegionHidChild) child_right = NULL; + + /* device needs to be open, hence not in ->probe() */ + descriptor = fu_hidraw_device_parse_descriptor(FU_HIDRAW_DEVICE(device), error); + if (descriptor == NULL) + return FALSE; + report = fu_hid_descriptor_find_report(descriptor, + error, + "usage-page", + 0xFFA0, + "usage", + 0x01, + "collection", + 0x01, + NULL); + if (report == NULL) + return FALSE; + + /* MCU */ + if (!fu_legion_hid_device_get_version(self, FU_LEGION_HID_DEVICE_ID_RX, &version, error)) + return FALSE; + fu_device_set_version_raw(FU_DEVICE(self), version); + + /* left */ + child_left = fu_legion_hid_child_new(device, FU_LEGION_HID_DEVICE_ID_GAMEPAD_L); + fu_device_set_logical_id(FU_DEVICE(child_left), FU_LEGION_HID_FIRMWARE_ID_LEFT); + fu_device_set_name(FU_DEVICE(child_left), "Left"); + fu_device_add_child(device, FU_DEVICE(child_left)); + + /* right */ + child_right = fu_legion_hid_child_new(device, FU_LEGION_HID_DEVICE_ID_GAMEPAD_R); + fu_device_set_logical_id(FU_DEVICE(child_right), FU_LEGION_HID_FIRMWARE_ID_RIGHT); + fu_device_set_name(FU_DEVICE(child_right), "Right"); + fu_device_add_child(device, FU_DEVICE(child_right)); + + /* success */ + return TRUE; +} + +static void +fu_legion_hid_device_class_init(FuLegionHidDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->setup = fu_legion_hid_device_setup; + device_class->write_firmware = fu_legion_hid_device_write_firmware; + device_class->set_progress = fu_legion_hid_device_set_progress; + device_class->convert_version = fu_legion_hid_device_convert_version; +} + +static void +fu_legion_hid_device_init(FuLegionHidDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_protocol(FU_DEVICE(self), "com.lenovo.legion-hid"); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_LEGION_HID_FIRMWARE); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); +} diff -Nru fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-device.h fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-device.h --- fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * Copyright 2025 lazro <2059899519@qq.com> + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#include "fu-legion-hid-struct.h" + +#define FU_TYPE_LEGION_HID_DEVICE (fu_legion_hid_device_get_type()) +G_DECLARE_FINAL_TYPE(FuLegionHidDevice, fu_legion_hid_device, FU, LEGION_HID_DEVICE, FuHidrawDevice) + +gboolean +fu_legion_hid_device_execute_upgrade(FuLegionHidDevice *self, FuFirmware *firmware, GError **error); +gboolean +fu_legion_hid_device_get_version(FuLegionHidDevice *self, + FuLegionHidDeviceId id, + guint32 *version, + GError **error); diff -Nru fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-firmware.c fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-firmware.c --- fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-firmware.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,107 @@ +/* + * Copyright 2025 hya1711 <591770796@qq.com> + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-legion-hid-firmware.h" +#include "fu-legion-hid-struct.h" + +struct _FuLegionHidFirmware { + FuFirmware parent_instance; +}; + +G_DEFINE_TYPE(FuLegionHidFirmware, fu_legion_hid_firmware, FU_TYPE_FIRMWARE) + +static gboolean +fu_legion_hid_firmware_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ + guint offset = FU_STRUCT_LEGION_HID_BIN_HEADER_SIZE; + g_autoptr(FuFirmware) img_left = fu_firmware_new(); + g_autoptr(FuFirmware) img_mcu = fu_firmware_new(); + g_autoptr(FuFirmware) img_right = fu_firmware_new(); + g_autoptr(FuStructLegionHidBinHeader) st_header = NULL; + g_autoptr(GInputStream) stream_left = NULL; + g_autoptr(GInputStream) stream_mcu = NULL; + g_autoptr(GInputStream) stream_right = NULL; + + st_header = fu_struct_legion_hid_bin_header_parse_stream(stream, 0x00, error); + if (st_header == NULL) + return FALSE; + + /* MCU */ + stream_mcu = + fu_partial_input_stream_new(stream, + offset, + fu_struct_legion_hid_bin_header_get_mcu_size(st_header), + error); + if (stream_mcu == NULL) + return FALSE; + if (!fu_firmware_parse_stream(img_mcu, stream_mcu, 0x00, flags, error)) + return FALSE; + fu_firmware_set_id(img_mcu, FU_LEGION_HID_FIRMWARE_ID_MCU); + fu_firmware_set_offset(img_mcu, offset); + fu_firmware_set_version_format(img_mcu, FWUPD_VERSION_FORMAT_PLAIN); + fu_firmware_set_version_raw(img_mcu, + fu_struct_legion_hid_bin_header_get_mcu_version(st_header)); + if (!fu_firmware_add_image(firmware, img_mcu, error)) + return FALSE; + offset += fu_struct_legion_hid_bin_header_get_mcu_size(st_header); + + /* left */ + stream_left = + fu_partial_input_stream_new(stream, + offset, + fu_struct_legion_hid_bin_header_get_left_size(st_header), + error); + if (stream_left == NULL) + return FALSE; + if (!fu_firmware_parse_stream(img_left, stream_left, 0x00, flags, error)) + return FALSE; + fu_firmware_set_id(img_left, FU_LEGION_HID_FIRMWARE_ID_LEFT); + fu_firmware_set_offset(img_left, offset); + fu_firmware_set_version_format(img_left, FWUPD_VERSION_FORMAT_PLAIN); + fu_firmware_set_version_raw(img_left, + fu_struct_legion_hid_bin_header_get_left_version(st_header)); + if (!fu_firmware_add_image(firmware, img_left, error)) + return FALSE; + offset += fu_struct_legion_hid_bin_header_get_left_size(st_header); + + /* right */ + stream_right = + fu_partial_input_stream_new(stream, + offset, + fu_struct_legion_hid_bin_header_get_right_size(st_header), + error); + if (stream_right == NULL) + return FALSE; + if (!fu_firmware_parse_stream(img_right, stream_right, 0x00, flags, error)) + return FALSE; + fu_firmware_set_id(img_right, FU_LEGION_HID_FIRMWARE_ID_RIGHT); + fu_firmware_set_offset(img_right, offset); + fu_firmware_set_version_format(img_right, FWUPD_VERSION_FORMAT_PLAIN); + fu_firmware_set_version_raw(img_right, + fu_struct_legion_hid_bin_header_get_right_version(st_header)); + if (!fu_firmware_add_image(firmware, img_right, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static void +fu_legion_hid_firmware_init(FuLegionHidFirmware *self) +{ +} + +static void +fu_legion_hid_firmware_class_init(FuLegionHidFirmwareClass *klass) +{ + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + firmware_class->parse = fu_legion_hid_firmware_parse; +} diff -Nru fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-firmware.h fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-firmware.h --- fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-firmware.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2025 hya1711 <591770796@qq.com> + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_LEGION_HID_FIRMWARE (fu_legion_hid_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuLegionHidFirmware, + fu_legion_hid_firmware, + FU, + LEGION_HID_FIRMWARE, + FuFirmware) + +#define FU_LEGION_HID_FIRMWARE_ID_MCU NULL +#define FU_LEGION_HID_FIRMWARE_ID_LEFT "LEFT" +#define FU_LEGION_HID_FIRMWARE_ID_RIGHT "RIGHT" diff -Nru fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-plugin.c fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-plugin.c --- fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,40 @@ +/* + * Copyright 2025 lazro <2059899519@qq.com> + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-legion-hid-child.h" +#include "fu-legion-hid-device.h" +#include "fu-legion-hid-firmware.h" +#include "fu-legion-hid-plugin.h" + +struct _FuLegionHidPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuLegionHidPlugin, fu_legion_hid_plugin, FU_TYPE_PLUGIN) + +static void +fu_legion_hid_plugin_init(FuLegionHidPlugin *self) +{ +} + +static void +fu_legion_hid_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_LEGION_HID_FIRMWARE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_LEGION_HID_CHILD); + fu_plugin_set_device_gtype_default(plugin, FU_TYPE_LEGION_HID_DEVICE); +} + +static void +fu_legion_hid_plugin_class_init(FuLegionHidPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->constructed = fu_legion_hid_plugin_constructed; +} diff -Nru fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-plugin.h fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-plugin.h --- fwupd-2.0.8/plugins/legion-hid/fu-legion-hid-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/fu-legion-hid-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * Copyright 2025 lazro <2059899519@qq.com> + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuLegionHidPlugin, fu_legion_hid_plugin, FU, LEGION_HID_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/legion-hid/fu-legion-hid.rs fwupd-2.0.20/plugins/legion-hid/fu-legion-hid.rs --- fwupd-2.0.8/plugins/legion-hid/fu-legion-hid.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/fu-legion-hid.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,120 @@ +// Copyright 2025 hya1711 <591770796@qq.com> +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[repr(u8)] +enum FuLegionHidUpgradeStep { + Start = 0x50, + QuerySize, + WriteData, + Verify, +} + +#[repr(u8)] +enum FuLegionHidResponseStatus { + Ok = 0x00, + Fail, + Busy, +} + +#[repr(u8)] +enum FuLegionHidDeviceId { + Unknown, + Rx, + Dongle, + GamepadL, + GamepadR, + GamepadL2, + GamepadR2, + GamepadL3, + GamepadR3, +} + +#[repr(u8)] +enum FuLegionHidCmdConstant { + Sn = 0x01, + UpgradeSendCmd = 0x01, + UpgradeSendData = 0x02, + OutputReportId = 0x05, +} + +#[derive(New, Getters, Default)] +#[repr(C, packed)] +struct FuStructLegionHidUpgradeCmd { + report_id: FuLegionHidCmdConstant = OutputReportId, + length: u8, + main_id: u8 = 0x53, + sub_id: u8 = 0x11, + device_id: FuLegionHidDeviceId, + param: FuLegionHidCmdConstant = UpgradeSendCmd, + data: [u8; 58], +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuStructLegionHidUpgradeRsp { + report_id: u8, + length: u8, + main_id: u8, + sub_id: u8, + id: FuLegionHidDeviceId, + param: u8, + data_length: u8, + step: u8, + reserved: u8, + response: FuLegionHidResponseStatus == Ok, +} + +#[derive(Parse, Default)] +#[repr(C, packed)] +struct FuStructLegionHidUpgradeQuerySizeRsp { + report_id: u8, + length: u8, + main_id: u8, + sub_id: u8, + id: FuLegionHidDeviceId, + param: u8, + data_length: u8, + step: u8, + reserved: u8, + response: u8, +} + +#[derive(New, Getters, Default)] +#[repr(C, packed)] +struct FuStructLegionHidUpgradeStartParam { + length: u8 = 0x08, + step: FuLegionHidUpgradeStep = Start, + flag: u8 = 0x00, + crc16: u16be, + size: u24be, + sn: FuLegionHidCmdConstant = Sn, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructLegionHidUpgradePacket { + data: [u8; 32], + sn: FuLegionHidCmdConstant = Sn, +} + +#[derive(New, Getters, Default)] +#[repr(C, packed)] +struct FuStructLegionHidNormalCmd { + report_id: FuLegionHidCmdConstant = OutputReportId, + length: u8, + main_id: u8, + sub_id: u8, + device_id: FuLegionHidDeviceId, + data: [u8; 59], +} + +#[derive(Setters, Default, ParseStream)] +#[repr(C, packed)] +struct FuStructLegionHidBinHeader { + mcu_size: u32le, + mcu_version: u32le, + left_size: u32le, + left_version: u32le, + right_size: u32le, + right_version: u32le, +} diff -Nru fwupd-2.0.8/plugins/legion-hid/legion-hid.quirk fwupd-2.0.20/plugins/legion-hid/legion-hid.quirk --- fwupd-2.0.8/plugins/legion-hid/legion-hid.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/legion-hid.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +[HIDRAW\VEN_17EF&DEV_61EB] +Plugin = legion_hid + +[HIDRAW\VEN_17EF&DEV_61EC] +Plugin = legion_hid + +[HIDRAW\VEN_17EF&DEV_61ED] +Plugin = legion_hid + +[HIDRAW\VEN_17EF&DEV_61EE] +Plugin = legion_hid diff -Nru fwupd-2.0.8/plugins/legion-hid/meson.build fwupd-2.0.20/plugins/legion-hid/meson.build --- fwupd-2.0.8/plugins/legion-hid/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,21 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginLegionHid"'] + +plugins += {meson.current_source_dir().split('/')[-1]: true} + +plugin_quirks += files('legion-hid.quirk') + +plugin_builtins += static_library('fu_plugin_legion_hid', + rustgen.process('fu-legion-hid.rs'), + sources: [ + 'fu-legion-hid-plugin.c', + 'fu-legion-hid-child.c', + 'fu-legion-hid-device.c', + 'fu-legion-hid-firmware.c' + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) + +device_tests += files('tests/legion-hid.json') diff -Nru fwupd-2.0.8/plugins/legion-hid/tests/legion-hid.json fwupd-2.0.20/plugins/legion-hid/tests/legion-hid.json --- fwupd-2.0.8/plugins/legion-hid/tests/legion-hid.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid/tests/legion-hid.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,30 @@ +{ + "name": "Legion Go2 device", + "interactive": false, + "steps": [ + { + "url": "c6751cb3154b682ca3c39908531b4c306af30885dbf0742dead8f1cd3e97899b-2025112_LegionGo2_MCU_116504366.cab", + "emulation-url": "248ed3f905d2ea332cd2d44d82f1dd844406ece479f806c9ad38cb389546c628-20251125-emulation.zip", + "components": [ + { + "version": "250925A", + "guids": [ + "81ad5a9e-6804-5c65-9082-2910aa257c24" + ] + }, + { + "version": "250926A", + "guids": [ + "8fbe73d6-a93e-5a98-ac1b-4c9fe957354b" + ] + }, + { + "version": "250926A", + "guids": [ + "f8a1dd90-90a4-5570-9793-6d059e1cadd7" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/legion-hid2/README.md fwupd-2.0.20/plugins/legion-hid2/README.md --- fwupd-2.0.8/plugins/legion-hid2/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -38,10 +38,3 @@ This plugin has been available since fwupd version `2.0.0`. Since: 2.0.0 - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -@superm1 diff -Nru fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-bl-device.c fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-bl-device.c --- fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-bl-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-bl-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,81 @@ +/* + * Copyright 2025 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-legion-hid2-bl-device.h" +#include "fu-legion-hid2-device.h" +#include "fu-legion-hid2-struct.h" + +struct _FuLegionHid2BlDevice { + FuDevice parent_instance; +}; + +G_DEFINE_TYPE(FuLegionHid2BlDevice, fu_legion_hid2_bl_device, FU_TYPE_DEVICE) + +static gboolean +fu_legion_hid2_bl_device_probe(FuDevice *device, GError **error) +{ + return fu_device_build_instance_id(device, NULL, "USB", "VID", "PID", "TP", NULL); +} + +static gboolean +fu_legion_hid2_bl_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLegionHid2BlDevice *self = FU_LEGION_HID2_BL_DEVICE(device); + FuLegionHid2Device *proxy; + + proxy = FU_LEGION_HID2_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not yet implemented for BL touchpads"); + return FALSE; +} + +static gchar * +fu_legion_hid2_bl_device_convert_version(FuDevice *device, guint64 version_raw) +{ + return fu_version_from_uint32(version_raw, fu_device_get_version_format(device)); +} + +static void +fu_legion_hid2_bl_device_init(FuLegionHid2BlDevice *self) +{ + fu_device_set_name(FU_DEVICE(self), "Touchpad"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FALLBACK); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY); + fu_device_add_protocol(FU_DEVICE(self), "com.lenovo.legion-hid2"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER); + fu_device_set_logical_id(FU_DEVICE(self), "touchpad"); + fu_device_set_vendor(FU_DEVICE(self), "Better Life"); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_LEGION_HID2_DEVICE); + fu_device_add_instance_strsafe(FU_DEVICE(self), "TP", "BL"); +} + +static void +fu_legion_hid2_bl_device_class_init(FuLegionHid2BlDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->probe = fu_legion_hid2_bl_device_probe; + device_class->write_firmware = fu_legion_hid2_bl_device_write_firmware; + device_class->convert_version = fu_legion_hid2_bl_device_convert_version; +} + +FuDevice * +fu_legion_hid2_bl_device_new(FuDevice *proxy) +{ + return g_object_new(FU_TYPE_LEGION_HID2_BL_DEVICE, "proxy", proxy, NULL); +} diff -Nru fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-bl-device.h fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-bl-device.h --- fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-bl-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-bl-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright 2025 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_LEGION_HID2_BL_DEVICE (fu_legion_hid2_bl_device_get_type()) +G_DECLARE_FINAL_TYPE(FuLegionHid2BlDevice, + fu_legion_hid2_bl_device, + FU, + LEGION_HID2_BL_DEVICE, + FuDevice) + +FuDevice * +fu_legion_hid2_bl_device_new(FuDevice *proxy) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-device.c fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-device.c --- fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,15 +6,17 @@ #include "config.h" +#include "fu-legion-hid2-bl-device.h" #include "fu-legion-hid2-device.h" #include "fu-legion-hid2-firmware.h" +#include "fu-legion-hid2-sipo-device.h" #include "fu-legion-hid2-struct.h" struct _FuLegionHid2Device { - FuHidDevice parent_instance; + FuHidrawDevice parent_instance; }; -G_DEFINE_TYPE(FuLegionHid2Device, fu_legion_hid2_device, FU_TYPE_HID_DEVICE) +G_DEFINE_TYPE(FuLegionHid2Device, fu_legion_hid2_device, FU_TYPE_HIDRAW_DEVICE) #define FU_LEGION_HID2_DEVICE_TIMEOUT 200 /* ms */ @@ -25,26 +27,25 @@ GError **error) { if (req != NULL) { - if (!fu_hid_device_set_report(FU_HID_DEVICE(self), - req->data[0], - req->data, - req->len, - FU_LEGION_HID2_DEVICE_TIMEOUT, - FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, - error)) { - g_prefix_error(error, "failed to send packet: "); + if (!fu_udev_device_write(FU_UDEV_DEVICE(self), + req->data, + req->len, + FU_LEGION_HID2_DEVICE_TIMEOUT, + FU_IO_CHANNEL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to write packet: "); return FALSE; } } if (res != NULL) { - if (!fu_hid_device_get_report(FU_HID_DEVICE(self), - res->data[0], - res->data, - res->len, - FU_LEGION_HID2_DEVICE_TIMEOUT, - FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, - error)) { - g_prefix_error(error, "failed to receive packet: "); + if (!fu_udev_device_read(FU_UDEV_DEVICE(self), + res->data, + res->len, + NULL, + FU_LEGION_HID2_DEVICE_TIMEOUT, + FU_IO_CHANNEL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to read packet: "); return FALSE; } } @@ -58,421 +59,215 @@ return fu_version_from_uint32(version_raw, fu_device_get_version_format(device)); } -static void -fu_legion_hid2_device_set_progress(FuDevice *self, FuProgress *progress) -{ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 6, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 76, "write"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 17, "attach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); -} - static gboolean -fu_legion_hid2_device_ensure_version(FuDevice *device, GError **error) +fu_legion_hid2_device_ensure_version(FuLegionHid2Device *self, GError **error) { - guint32 version; - g_autoptr(GByteArray) cmd = fu_struct_legion_get_version_new(); - g_autoptr(GByteArray) result = fu_struct_legion_version_new(); + g_autoptr(FuStructLegionGetVersion) st_cmd = fu_struct_legion_get_version_new(); + g_autoptr(FuStructLegionVersion) st_res = fu_struct_legion_version_new(); - if (!fu_legion_hid2_device_transfer(FU_LEGION_HID2_DEVICE(device), cmd, result, error)) + if (!fu_legion_hid2_device_transfer(self, st_cmd->buf, st_res->buf, error)) return FALSE; - version = fu_struct_legion_version_get_version(result); - fu_device_set_version_raw(device, version); + fu_device_set_version_raw(FU_DEVICE(self), fu_struct_legion_version_get_version(st_res)); return TRUE; } -static gboolean -fu_legion_hid2_device_ensure_mcu_id(FuDevice *device, GError **error) +/* + * older MCU firmware doesn't support TP child commands, so setup needs to + * to be non-fatal or the MCU won't enumerate. + */ +static void +fu_legion_hid2_device_setup_touchpad_direct(FuLegionHid2Device *self) { - g_autoptr(GByteArray) cmd = fu_struct_legion_get_mcu_id_new(); - g_autoptr(GByteArray) result = fu_struct_legion_mcu_id_new(); - - if (!fu_legion_hid2_device_transfer(FU_LEGION_HID2_DEVICE(device), cmd, result, error)) - return FALSE; + g_autoptr(FuStructLegionGetPlTest) st_cmd = fu_struct_legion_get_pl_test_new(); + g_autoptr(FuStructLegionGetPlTestResult) st_man = fu_struct_legion_get_pl_test_result_new(); + g_autoptr(FuStructLegionGetPlTestResult) st_ver = fu_struct_legion_get_pl_test_result_new(); + g_autoptr(FuDevice) child = NULL; + g_autoptr(GError) error_child = NULL; - return TRUE; -} + /* determine which vendor touchpad */ + fu_struct_legion_get_pl_test_set_index(st_cmd, FU_LEGION_HID2_PL_TEST_TP_MANUFACTURER); + if (!fu_legion_hid2_device_transfer(self, st_cmd->buf, st_man->buf, &error_child)) { + g_debug("failed to get touchpad manufacturer: %s", error_child->message); + return; + } + switch (fu_struct_legion_get_pl_test_result_get_content(st_man)) { + case FU_LEGION_HID2_TP_MAN_BETTER_LIFE: + child = fu_legion_hid2_bl_device_new(FU_DEVICE(self)); + break; + case FU_LEGION_HID2_TP_MAN_SIPO: + child = fu_legion_hid2_sipo_device_new(FU_DEVICE(self)); + break; + default: + case FU_LEGION_HID2_TP_MAN_NONE: + g_info("no touchpad found, skipping child device setup"); + return; + } -static gboolean -fu_legion_hid2_device_probe(FuDevice *device, GError **error) -{ - if (fu_device_has_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { - fu_hid_device_set_interface(FU_HID_DEVICE(device), 0); - fu_hid_device_set_ep_addr_in(FU_HID_DEVICE(device), 0x81); - fu_hid_device_set_ep_addr_out(FU_HID_DEVICE(device), 1); - } else { - fu_hid_device_set_interface(FU_HID_DEVICE(device), 3); - fu_hid_device_set_ep_addr_in(FU_HID_DEVICE(device), 0x84); - fu_hid_device_set_ep_addr_out(FU_HID_DEVICE(device), 0x4); + /* lookup firmware from MCU (*NOT* from touchpad directly) */ + fu_struct_legion_get_pl_test_set_index(st_cmd, FU_LEGION_HID2_PL_TEST_TP_VERSION); + if (!fu_legion_hid2_device_transfer(self, st_cmd->buf, st_ver->buf, &error_child)) { + g_debug("failed to get touchpad version: %s", error_child->message); + return; } - /* FuHidDevice->probe */ - if (!FU_DEVICE_CLASS(fu_legion_hid2_device_parent_class)->probe(device, error)) - return FALSE; + fu_device_set_version_raw(child, fu_struct_legion_get_pl_test_result_get_content(st_ver)); - /* success */ - return TRUE; + fu_device_add_child(FU_DEVICE(self), child); } static gboolean -fu_legion_hid2_device_setup(FuDevice *device, GError **error) +fu_legion_hid2_device_setup_touchpad(FuLegionHid2Device *self, GError **error) { - /* HidDevice->setup */ - if (!FU_DEVICE_CLASS(fu_legion_hid2_device_parent_class)->setup(device, error)) - return FALSE; - - /* can't use anything but write and reset in IAP mode */ - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) - return TRUE; + guint64 version = 0; + g_autoptr(FuDevice) child = NULL; + g_autofree gchar *tp_version = NULL; + g_autofree gchar *manufacturer = NULL; + g_autoptr(FuDevice) hid_device = NULL; - if (!fu_legion_hid2_device_ensure_version(device, error)) + /* get parent */ + hid_device = fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), "hid", error); + if (hid_device == NULL) return FALSE; - if (!fu_legion_hid2_device_ensure_mcu_id(device, error)) + manufacturer = fu_udev_device_read_property(FU_UDEV_DEVICE(hid_device), + "LEGOS_TP_MANUFACTURER", + error); + if (manufacturer == NULL) + return FALSE; + tp_version = + fu_udev_device_read_property(FU_UDEV_DEVICE(hid_device), "LEGOS_TP_VERSION", error); + if (tp_version == NULL) return FALSE; - /* success */ - return TRUE; -} - -static FuFirmware * -fu_legion_hid2_device_prepare_firmware(FuDevice *device, - GInputStream *stream, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - guint32 version; - g_autofree gchar *version_str = NULL; - g_autoptr(FuFirmware) firmware = fu_legion_hid2_firmware_new(); - - /* sanity check */ - if (!fu_firmware_parse_stream(firmware, stream, 0x0, flags, error)) - return NULL; - - version = fu_legion_hid2_firmware_get_version(firmware); - if (fu_device_get_version_raw(device) > version) { - version_str = fu_version_from_uint32(version, FWUPD_VERSION_FORMAT_QUAD); - if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "downgrading from %s to %s is not supported", - fu_device_get_version(device), - version_str); - return NULL; - } - g_warning("firmware %s is a downgrade but is being force installed anyway", - version_str); - } - - return g_steal_pointer(&firmware); -} - -static GByteArray * -fu_legion_hid2_device_tlv(FuLegionHid2Device *self, GByteArray *cmd, GError **error) -{ - g_autoptr(GByteArray) result = fu_struct_legion_iap_tlv_new(); - const guint8 *value; - guint8 expected; - guint16 tag; - - if (fu_struct_legion_iap_tlv_get_tag(cmd) == FU_LEGION_IAP_HOST_TAG_IAP_UPDATE) - expected = FU_LEGION_IAP_ERROR_IAP_CERTIFIED; - else - expected = FU_LEGION_IAP_ERROR_IAP_OK; - - if (!fu_legion_hid2_device_transfer(self, cmd, result, error)) - return NULL; - - tag = fu_struct_legion_iap_tlv_get_tag(result); - if (tag != FU_LEGION_IAP_DEVICE_TAG_IAP_ACK) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "failed to transmit TLV, result: %u", - tag); - return NULL; - } - value = fu_struct_legion_iap_tlv_get_value(result, NULL); - if (value[0] != expected) { + if (g_strcmp0(manufacturer, "SIPO") == 0) { + child = fu_legion_hid2_sipo_device_new(FU_DEVICE(self)); + } else if (g_strcmp0(manufacturer, "BetterLife") == 0) { + child = fu_legion_hid2_bl_device_new(FU_DEVICE(self)); + } else { g_set_error(error, FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "failed to transmit TLV, data: %u", - value[0]); - return NULL; - } - - return g_steal_pointer(&result); -} - -static gboolean -fu_legion_hid2_device_unlock_flash(FuLegionHid2Device *self, GError **error) -{ - g_autoptr(GByteArray) cmd = fu_struct_legion_iap_tlv_new(); - g_autoptr(GByteArray) result = NULL; - - fu_struct_legion_iap_tlv_set_tag(cmd, FU_LEGION_IAP_HOST_TAG_IAP_UNLOCK); - - result = fu_legion_hid2_device_tlv(self, cmd, error); - if (result == NULL) { - g_prefix_error(error, "failed to unlock: "); + FWUPD_ERROR_NOT_SUPPORTED, + "unknown touchpad manufacturer '%s'", + manufacturer); return FALSE; } - return TRUE; -} - -static gboolean -fu_legion_hid2_device_verify_signature(FuLegionHid2Device *self, GError **error) -{ - g_autoptr(GByteArray) cmd = fu_struct_legion_iap_tlv_new(); - g_autoptr(GByteArray) result = NULL; - - fu_struct_legion_iap_tlv_set_tag(cmd, FU_LEGION_IAP_HOST_TAG_IAP_UPDATE); - - result = fu_legion_hid2_device_tlv(self, cmd, error); - if (result == NULL) { - g_prefix_error(error, "failed to verify signature: "); + if (!fu_strtoull(tp_version, &version, 0x0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, error)) return FALSE; - } - - return TRUE; -} - -static gboolean -fu_legion_hid2_device_verify_code(FuLegionHid2Device *self, GError **error) -{ - g_autoptr(GByteArray) cmd = fu_struct_legion_iap_tlv_new(); - g_autoptr(GByteArray) result = NULL; - - fu_struct_legion_iap_tlv_set_tag(cmd, FU_LEGION_IAP_HOST_TAG_IAP_VERIFY); - result = fu_legion_hid2_device_tlv(self, cmd, error); - if (result == NULL) { - g_prefix_error(error, "failed to verify code: "); - return FALSE; - } + fu_device_set_version_raw(child, version); + fu_device_add_child(FU_DEVICE(self), child); return TRUE; } static gboolean -fu_legion_hid2_device_write_data_chunks(FuLegionHid2Device *self, - FuChunkArray *chunks, - FuProgress *progress, - guint16 tag, - GError **error) -{ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); - for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - g_autoptr(FuChunk) chk = NULL; - g_autoptr(GByteArray) req = fu_struct_legion_iap_tlv_new(); - g_autoptr(GByteArray) res = NULL; - - fu_struct_legion_iap_tlv_set_tag(req, tag); - - chk = fu_chunk_array_index(chunks, i, error); - if (chk == NULL) - return FALSE; - - if (!fu_struct_legion_iap_tlv_set_value(req, - fu_chunk_get_data(chk), - fu_chunk_get_data_sz(chk), - error)) - return FALSE; +fu_legion_hid2_device_setup_version(FuLegionHid2Device *self, GError **error) +{ + FuDevice *device = FU_DEVICE(self); - fu_struct_legion_iap_tlv_set_length(req, fu_chunk_get_data_sz(chk)); + /* compatibility with older releases that used USB Instance ID */ + fu_device_add_instance_u16(device, "VID", fu_device_get_vid(device)); + fu_device_add_instance_u16(device, "PID", fu_device_get_pid(device)); + fu_device_build_instance_id_full(device, + FU_DEVICE_INSTANCE_FLAG_GENERIC | + FU_DEVICE_INSTANCE_FLAG_VISIBLE | + FU_DEVICE_INSTANCE_FLAG_QUIRKS, + NULL, + "USB", + "VID", + "PID", + NULL); - res = fu_legion_hid2_device_tlv(self, req, error); - if (res == NULL) { - g_prefix_error(error, "failed to write data chunks: "); - return FALSE; - } + /* version set from kernel core */ + if (fu_device_get_version_raw(device) != 0) + return TRUE; - fu_progress_step_done(progress); - } + /* fallback to direct communication */ + if (!fu_legion_hid2_device_ensure_version(self, error)) + return FALSE; - /* success */ return TRUE; } static gboolean -fu_legion_hid2_device_wait_for_complete_cb(FuDevice *device, gpointer user_data, GError **error) +fu_legion_hid2_device_validate_descriptor(FuLegionHid2Device *self, GError **error) { - g_autoptr(GByteArray) cmd = fu_struct_legion_iap_tlv_new(); - g_autoptr(GByteArray) result = NULL; - const guint8 *value; - - fu_struct_legion_iap_tlv_set_tag(cmd, FU_LEGION_IAP_HOST_TAG_IAP_CARRY); + g_autoptr(FuHidDescriptor) descriptor = NULL; + g_autoptr(FuHidReport) report = NULL; + g_autoptr(GPtrArray) imgs = NULL; - result = fu_legion_hid2_device_tlv(FU_LEGION_HID2_DEVICE(device), cmd, error); - if (result == NULL) { - g_prefix_error(error, "failed to verify code: "); - return FALSE; - } - value = fu_struct_legion_iap_tlv_get_value(result, NULL); - if (value[1] < 100) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_BUSY, - "device is %d percent done", - value[1]); + descriptor = fu_hidraw_device_parse_descriptor(FU_HIDRAW_DEVICE(self), error); + if (descriptor == NULL) return FALSE; - } - - return TRUE; -} - -static gboolean -fu_legion_hid2_device_write_data(FuLegionHid2Device *self, - FuFirmware *firmware, - FuProgress *progress, - GError **error) -{ - g_autoptr(GInputStream) stream = NULL; - g_autoptr(FuChunkArray) chunks = NULL; - - stream = fu_firmware_get_image_by_id_stream(firmware, FU_FIRMWARE_ID_PAYLOAD, error); - if (stream == NULL) - return FALSE; - - chunks = fu_chunk_array_new_from_stream(stream, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - FU_STRUCT_LEGION_IAP_TLV_SIZE_VALUE, - error); - if (chunks == NULL) - return FALSE; - if (!fu_legion_hid2_device_write_data_chunks(self, - chunks, - progress, - FU_LEGION_IAP_HOST_TAG_IAP_DATA, - error)) + report = fu_hid_descriptor_find_report(descriptor, + error, + "usage-page", + 0xFFA0, + "usage", + 0x01, + "collection", + 0x01, + NULL); + if (report == NULL) return FALSE; - return TRUE; -} - -static gboolean -fu_legion_hid2_device_write_sig(FuLegionHid2Device *self, - FuFirmware *firmware, - FuProgress *progress, - GError **error) -{ - g_autoptr(GInputStream) stream = NULL; - g_autoptr(FuChunkArray) chunks = NULL; - - stream = fu_firmware_get_image_by_id_stream(firmware, FU_FIRMWARE_ID_SIGNATURE, error); - if (stream == NULL) - return FALSE; - - chunks = fu_chunk_array_new_from_stream(stream, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - FU_STRUCT_LEGION_IAP_TLV_SIZE_VALUE, - error); - if (chunks == NULL) - return FALSE; - if (!fu_legion_hid2_device_write_data_chunks(self, - chunks, - progress, - FU_LEGION_IAP_HOST_TAG_IAP_SIGNATURE, - error)) + imgs = fu_firmware_get_images(FU_FIRMWARE(descriptor)); + if (imgs->len != 4) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "HID descriptor does not contain exactly 4 reports"); return FALSE; + } return TRUE; } static gboolean -fu_legion_hid2_device_write_firmware(FuDevice *device, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) +fu_legion_hid2_device_setup(FuDevice *device, GError **error) { FuLegionHid2Device *self = FU_LEGION_HID2_DEVICE(device); + g_autoptr(GError) error_touchpad = NULL; - g_return_val_if_fail(device != NULL, FALSE); - g_return_val_if_fail(FU_IS_FIRMWARE(firmware), FALSE); - - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 29, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 29, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 19, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 19, NULL); - - if (!fu_legion_hid2_device_unlock_flash(self, error)) + if (!fu_legion_hid2_device_validate_descriptor(self, error)) return FALSE; - fu_progress_step_done(progress); - if (!fu_legion_hid2_device_write_data(self, - firmware, - fu_progress_get_child(progress), - error)) + if (!fu_legion_hid2_device_setup_version(FU_LEGION_HID2_DEVICE(device), error)) return FALSE; - fu_progress_step_done(progress); - if (!fu_legion_hid2_device_write_sig(self, - firmware, - fu_progress_get_child(progress), - error)) - return FALSE; - fu_progress_step_done(progress); - - if (!fu_legion_hid2_device_verify_signature(self, error)) - return FALSE; - fu_progress_step_done(progress); - - if (!fu_device_retry_full(device, - fu_legion_hid2_device_wait_for_complete_cb, - 50, - 200, - NULL, - error)) - return FALSE; - fu_progress_step_done(progress); - - if (!fu_legion_hid2_device_verify_code(self, error)) - return FALSE; - fu_progress_step_done(progress); - - /* restart dev is moved to attach command! */ + if (!fu_legion_hid2_device_setup_touchpad(FU_LEGION_HID2_DEVICE(device), &error_touchpad)) { + g_debug("failed to setup touchpad from HID properties: %s", + error_touchpad->message); + fu_legion_hid2_device_setup_touchpad_direct(FU_LEGION_HID2_DEVICE(device)); + } + /* success */ return TRUE; } static gboolean fu_legion_hid2_device_detach(FuDevice *device, FuProgress *progress, GError **error) { - g_autoptr(GByteArray) cmd = NULL; - g_autoptr(GByteArray) result = NULL; - guint ret; - - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) - return TRUE; - - cmd = fu_struct_legion_start_iap_new(); - result = fu_struct_legion_iap_result_new(); - - if (!fu_legion_hid2_device_transfer(FU_LEGION_HID2_DEVICE(device), cmd, result, error)) - return FALSE; - - ret = fu_struct_legion_iap_result_get_ret(result); - if (ret != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "failed to enable IAP, result: %u", - ret); - return FALSE; + g_autoptr(FuStructLegionStartIap) st_cmd = NULL; + g_autoptr(FuStructLegionIapResult) st_res = NULL; + g_autoptr(GError) error_local = NULL; + + st_cmd = fu_struct_legion_start_iap_new(); + st_res = fu_struct_legion_iap_result_new(); + + if (!fu_legion_hid2_device_transfer(FU_LEGION_HID2_DEVICE(device), + st_cmd->buf, + st_res->buf, + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_READ) || + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT)) { + g_debug("%s", error_local->message); + } else { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } } fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); @@ -480,29 +275,6 @@ return TRUE; } -static gboolean -fu_legion_hid2_device_attach(FuDevice *device, FuProgress *progress, GError **error) -{ - g_autoptr(GByteArray) cmd = NULL; - g_autoptr(GByteArray) result = NULL; - g_autoptr(GError) error_attach = NULL; - - if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) - return TRUE; - - cmd = fu_struct_legion_iap_tlv_new(); - - fu_struct_legion_iap_tlv_set_tag(cmd, FU_LEGION_IAP_HOST_TAG_IAP_RESTART); - - result = fu_legion_hid2_device_tlv(FU_LEGION_HID2_DEVICE(device), cmd, &error_attach); - if (result == NULL) - g_debug("failed to attach: %s", error_attach->message); - - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - - return TRUE; -} - static void fu_legion_hid2_device_init(FuLegionHid2Device *self) { @@ -511,7 +283,10 @@ fu_device_add_protocol(FU_DEVICE(self), "com.lenovo.legion-hid2"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_LEGION_HID2_FIRMWARE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); } static void @@ -519,11 +294,6 @@ { FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); device_class->setup = fu_legion_hid2_device_setup; - device_class->probe = fu_legion_hid2_device_probe; - device_class->prepare_firmware = fu_legion_hid2_device_prepare_firmware; device_class->convert_version = fu_legion_hid2_device_convert_version; - device_class->write_firmware = fu_legion_hid2_device_write_firmware; device_class->detach = fu_legion_hid2_device_detach; - device_class->attach = fu_legion_hid2_device_attach; - device_class->set_progress = fu_legion_hid2_device_set_progress; } diff -Nru fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-device.h fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-device.h --- fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,4 +9,8 @@ #include #define FU_TYPE_LEGION_HID2_DEVICE (fu_legion_hid2_device_get_type()) -G_DECLARE_FINAL_TYPE(FuLegionHid2Device, fu_legion_hid2_device, FU, LEGION_HID2_DEVICE, FuHidDevice) +G_DECLARE_FINAL_TYPE(FuLegionHid2Device, + fu_legion_hid2_device, + FU, + LEGION_HID2_DEVICE, + FuHidrawDevice) diff -Nru fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-firmware.c fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-firmware.c --- fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,73 +13,58 @@ struct _FuLegionHid2Firmware { FuFirmware parent_instance; - guint32 version; }; G_DEFINE_TYPE(FuLegionHid2Firmware, fu_legion_hid2_firmware, FU_TYPE_FIRMWARE) -static void -fu_legion_hid2_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) -{ - FuLegionHid2Firmware *self = FU_LEGION_HID2_FIRMWARE(firmware); - g_autofree gchar *version = - fu_version_from_uint32(self->version, FWUPD_VERSION_FORMAT_QUAD); - - fu_xmlb_builder_insert_kv(bn, "version", version); -} - -guint32 -fu_legion_hid2_firmware_get_version(FuFirmware *firmware) -{ - FuLegionHid2Firmware *self = FU_LEGION_HID2_FIRMWARE(firmware); - return self->version; -} - static gboolean fu_legion_hid2_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { - FuLegionHid2Firmware *self = FU_LEGION_HID2_FIRMWARE(firmware); g_autoptr(FuFirmware) img_payload = fu_firmware_new(); g_autoptr(FuFirmware) img_sig = fu_firmware_new(); g_autoptr(GInputStream) stream_payload = NULL; g_autoptr(GInputStream) stream_sig = NULL; - g_autoptr(GByteArray) header = NULL; - g_autoptr(GByteArray) version = NULL; + g_autoptr(FuStructLegionHid2Header) st_header = NULL; + g_autoptr(FuStructLegionHid2Version) st_version = NULL; - header = fu_struct_legion_hid2_header_parse_stream(stream, 0x0, error); - if (header == NULL) + st_header = fu_struct_legion_hid2_header_parse_stream(stream, 0x0, error); + if (st_header == NULL) return FALSE; - stream_sig = fu_partial_input_stream_new(stream, - fu_struct_legion_hid2_header_get_sig_add(header), - fu_struct_legion_hid2_header_get_sig_len(header), - error); + stream_sig = + fu_partial_input_stream_new(stream, + fu_struct_legion_hid2_header_get_sig_add(st_header), + fu_struct_legion_hid2_header_get_sig_len(st_header), + error); if (stream_sig == NULL) return FALSE; if (!fu_firmware_parse_stream(img_sig, stream_sig, 0x0, flags, error)) return FALSE; fu_firmware_set_id(img_sig, FU_FIRMWARE_ID_SIGNATURE); - fu_firmware_add_image(firmware, img_sig); + if (!fu_firmware_add_image(firmware, img_sig, error)) + return FALSE; stream_payload = fu_partial_input_stream_new(stream, - fu_struct_legion_hid2_header_get_data_add(header), - fu_struct_legion_hid2_header_get_data_len(header), + fu_struct_legion_hid2_header_get_data_add(st_header), + fu_struct_legion_hid2_header_get_data_len(st_header), error); if (stream_payload == NULL) return FALSE; if (!fu_firmware_parse_stream(img_payload, stream_payload, 0x0, flags, error)) return FALSE; fu_firmware_set_id(img_payload, FU_FIRMWARE_ID_PAYLOAD); - fu_firmware_add_image(firmware, img_payload); + if (!fu_firmware_add_image(firmware, img_payload, error)) + return FALSE; - version = fu_struct_legion_hid2_version_parse_stream(stream, VERSION_OFFSET, error); - if (version == NULL) + st_version = fu_struct_legion_hid2_version_parse_stream(stream, VERSION_OFFSET, error); + if (st_version == NULL) return FALSE; - self->version = fu_struct_legion_hid2_version_get_version(version); + fu_firmware_set_version_raw(firmware, + fu_struct_legion_hid2_version_get_version(st_version)); return TRUE; } @@ -87,6 +72,13 @@ static void fu_legion_hid2_firmware_init(FuLegionHid2Firmware *self) { + fu_firmware_set_version_format(FU_FIRMWARE(self), FWUPD_VERSION_FORMAT_QUAD); +} + +static gchar * +fu_legion_hid2_firmware_convert_version(FuFirmware *firmware, guint64 version_raw) +{ + return fu_version_from_uint32(version_raw, fu_firmware_get_version_format(firmware)); } static void @@ -94,11 +86,5 @@ { FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); firmware_class->parse = fu_legion_hid2_firmware_parse; - firmware_class->export = fu_legion_hid2_firmware_export; -} - -FuFirmware * -fu_legion_hid2_firmware_new(void) -{ - return FU_FIRMWARE(g_object_new(FU_TYPE_LEGION_HID2_FIRMWARE, NULL)); + firmware_class->convert_version = fu_legion_hid2_firmware_convert_version; } diff -Nru fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-firmware.h fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-firmware.h --- fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-firmware.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -17,6 +17,3 @@ FuFirmware * fu_legion_hid2_firmware_new(void); - -guint32 -fu_legion_hid2_firmware_get_version(FuFirmware *firmware); diff -Nru fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-iap-device.c fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-iap-device.c --- fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-iap-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-iap-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,374 @@ +/* + * Copyright 2025 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ +#include "config.h" + +#include "fu-legion-hid2-firmware.h" +#include "fu-legion-hid2-iap-device.h" +#include "fu-legion-hid2-struct.h" + +struct _FuLegionHid2IapDevice { + FuHidrawDevice parent_instance; +}; + +G_DEFINE_TYPE(FuLegionHid2IapDevice, fu_legion_hid2_iap_device, FU_TYPE_HIDRAW_DEVICE) + +#define FU_LEGION_HID2_IAP_DEVICE_TIMEOUT 200 /* ms */ + +static gboolean +fu_legion_hid2_iap_device_transfer(FuLegionHid2IapDevice *self, + GByteArray *req, + GByteArray *res, + GError **error) +{ + if (req != NULL) { + if (!fu_udev_device_write(FU_UDEV_DEVICE(self), + req->data, + req->len, + FU_LEGION_HID2_IAP_DEVICE_TIMEOUT, + FU_IO_CHANNEL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to write packet: "); + return FALSE; + } + } + if (res != NULL) { + if (!fu_udev_device_read(FU_UDEV_DEVICE(self), + res->data, + res->len, + NULL, + FU_LEGION_HID2_IAP_DEVICE_TIMEOUT, + FU_IO_CHANNEL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to read packet: "); + return FALSE; + } + } + + return TRUE; +} + +static FuStructLegionIapTlv * +fu_legion_hid2_iap_device_tlv(FuLegionHid2IapDevice *self, + FuStructLegionIapTlv *st_req, + GError **error) +{ + g_autoptr(FuStructLegionIapTlv) st_res = fu_struct_legion_iap_tlv_new(); + const guint8 *value; + guint8 expected; + guint16 tag; + + if (fu_struct_legion_iap_tlv_get_tag(st_req) == FU_LEGION_IAP_HOST_TAG_IAP_UPDATE) + expected = FU_LEGION_IAP_ERROR_IAP_CERTIFIED; + else + expected = FU_LEGION_IAP_ERROR_IAP_OK; + + if (!fu_legion_hid2_iap_device_transfer(self, st_req->buf, st_res->buf, error)) + return NULL; + + tag = fu_struct_legion_iap_tlv_get_tag(st_res); + if (tag != FU_LEGION_IAP_DEVICE_TAG_IAP_ACK) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to transmit TLV, st_res: %u", + tag); + return NULL; + } + value = fu_struct_legion_iap_tlv_get_value(st_res, NULL); + if (value[0] != expected) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to transmit TLV, data: %u", + value[0]); + return NULL; + } + + return g_steal_pointer(&st_res); +} + +static gboolean +fu_legion_hid2_iap_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + g_autoptr(FuStructLegionIapTlv) st_req = fu_struct_legion_iap_tlv_new(); + g_autoptr(FuStructLegionIapTlv) st_res = NULL; + g_autoptr(GError) error_attach = NULL; + + fu_struct_legion_iap_tlv_set_tag(st_req, FU_LEGION_IAP_HOST_TAG_IAP_RESTART); + st_res = + fu_legion_hid2_iap_device_tlv(FU_LEGION_HID2_IAP_DEVICE(device), st_req, &error_attach); + if (st_res == NULL) + g_debug("failed to attach: %s", error_attach->message); + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + return TRUE; +} + +static gboolean +fu_legion_hid2_iap_device_unlock_flash(FuLegionHid2IapDevice *self, GError **error) +{ + g_autoptr(FuStructLegionIapTlv) st_req = fu_struct_legion_iap_tlv_new(); + g_autoptr(FuStructLegionIapTlv) st_res = NULL; + + fu_struct_legion_iap_tlv_set_tag(st_req, FU_LEGION_IAP_HOST_TAG_IAP_UNLOCK); + + st_res = fu_legion_hid2_iap_device_tlv(self, st_req, error); + if (st_res == NULL) { + g_prefix_error_literal(error, "failed to unlock: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_legion_hid2_iap_device_verify_signature(FuLegionHid2IapDevice *self, GError **error) +{ + g_autoptr(FuStructLegionIapTlv) st_req = fu_struct_legion_iap_tlv_new(); + g_autoptr(FuStructLegionIapTlv) st_res = NULL; + + fu_struct_legion_iap_tlv_set_tag(st_req, FU_LEGION_IAP_HOST_TAG_IAP_UPDATE); + + st_res = fu_legion_hid2_iap_device_tlv(self, st_req, error); + if (st_res == NULL) { + g_prefix_error_literal(error, "failed to verify signature: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_legion_hid2_iap_device_verify_code(FuLegionHid2IapDevice *self, GError **error) +{ + g_autoptr(FuStructLegionIapTlv) st_req = fu_struct_legion_iap_tlv_new(); + g_autoptr(FuStructLegionIapTlv) st_res = NULL; + + fu_struct_legion_iap_tlv_set_tag(st_req, FU_LEGION_IAP_HOST_TAG_IAP_VERIFY); + + st_res = fu_legion_hid2_iap_device_tlv(self, st_req, error); + if (st_res == NULL) { + g_prefix_error_literal(error, "failed to verify code: "); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_legion_hid2_iap_device_write_data_chunks(FuLegionHid2IapDevice *self, + FuChunkArray *chunks, + FuProgress *progress, + guint16 tag, + GError **error) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); + for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { + g_autoptr(FuChunk) chk = NULL; + g_autoptr(FuStructLegionIapTlv) st_req = fu_struct_legion_iap_tlv_new(); + g_autoptr(FuStructLegionIapTlv) st_res = NULL; + + fu_struct_legion_iap_tlv_set_tag(st_req, tag); + + chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) + return FALSE; + + if (!fu_struct_legion_iap_tlv_set_value(st_req, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + + fu_struct_legion_iap_tlv_set_length(st_req, fu_chunk_get_data_sz(chk)); + + st_res = fu_legion_hid2_iap_device_tlv(self, st_req, error); + if (st_res == NULL) { + g_prefix_error_literal(error, "failed to write data chunks: "); + return FALSE; + } + + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_legion_hid2_iap_device_wait_for_complete_cb(FuDevice *device, gpointer user_data, GError **error) +{ + g_autoptr(FuStructLegionIapTlv) st_req = fu_struct_legion_iap_tlv_new(); + g_autoptr(FuStructLegionIapTlv) st_res = NULL; + const guint8 *value; + + fu_struct_legion_iap_tlv_set_tag(st_req, FU_LEGION_IAP_HOST_TAG_IAP_CARRY); + + st_res = fu_legion_hid2_iap_device_tlv(FU_LEGION_HID2_IAP_DEVICE(device), st_req, error); + if (st_res == NULL) { + g_prefix_error_literal(error, "failed to verify code: "); + return FALSE; + } + value = fu_struct_legion_iap_tlv_get_value(st_res, NULL); + if (value[1] < 100) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_BUSY, + "device is %d percent done", + value[1]); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_legion_hid2_iap_device_write_data(FuLegionHid2IapDevice *self, + FuFirmware *firmware, + FuProgress *progress, + GError **error) +{ + g_autoptr(GInputStream) stream = NULL; + g_autoptr(FuChunkArray) chunks = NULL; + + stream = fu_firmware_get_image_by_id_stream(firmware, FU_FIRMWARE_ID_PAYLOAD, error); + if (stream == NULL) + return FALSE; + + chunks = fu_chunk_array_new_from_stream(stream, + FU_CHUNK_ADDR_OFFSET_NONE, + FU_CHUNK_PAGESZ_NONE, + FU_STRUCT_LEGION_IAP_TLV_SIZE_VALUE, + error); + if (chunks == NULL) + return FALSE; + return fu_legion_hid2_iap_device_write_data_chunks(self, + chunks, + progress, + FU_LEGION_IAP_HOST_TAG_IAP_DATA, + error); +} + +static gboolean +fu_legion_hid2_iap_device_write_sig(FuLegionHid2IapDevice *self, + FuFirmware *firmware, + FuProgress *progress, + GError **error) +{ + g_autoptr(GInputStream) stream = NULL; + g_autoptr(FuChunkArray) chunks = NULL; + + stream = fu_firmware_get_image_by_id_stream(firmware, FU_FIRMWARE_ID_SIGNATURE, error); + if (stream == NULL) + return FALSE; + + chunks = fu_chunk_array_new_from_stream(stream, + FU_CHUNK_ADDR_OFFSET_NONE, + FU_CHUNK_PAGESZ_NONE, + FU_STRUCT_LEGION_IAP_TLV_SIZE_VALUE, + error); + if (chunks == NULL) + return FALSE; + return fu_legion_hid2_iap_device_write_data_chunks(self, + chunks, + progress, + FU_LEGION_IAP_HOST_TAG_IAP_SIGNATURE, + error); +} + +static gboolean +fu_legion_hid2_iap_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLegionHid2IapDevice *self = FU_LEGION_HID2_IAP_DEVICE(device); + + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 29, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 29, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 19, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 19, NULL); + + if (!fu_legion_hid2_iap_device_unlock_flash(self, error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_legion_hid2_iap_device_write_data(self, + firmware, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_legion_hid2_iap_device_write_sig(self, + firmware, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_legion_hid2_iap_device_verify_signature(self, error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_device_retry_full(device, + fu_legion_hid2_iap_device_wait_for_complete_cb, + 50, + 200, + NULL, + error)) + return FALSE; + fu_progress_step_done(progress); + + if (!fu_legion_hid2_iap_device_verify_code(self, error)) + return FALSE; + fu_progress_step_done(progress); + + /* restart dev is moved to attach command! */ + + return TRUE; +} + +static void +fu_legion_hid2_iap_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 6, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 76, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 17, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_legion_hid2_iap_device_init(FuLegionHid2IapDevice *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_protocol(FU_DEVICE(self), "com.lenovo.legion-hid2"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_LEGION_HID2_FIRMWARE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); +} + +static void +fu_legion_hid2_iap_device_class_init(FuLegionHid2IapDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->write_firmware = fu_legion_hid2_iap_device_write_firmware; + device_class->attach = fu_legion_hid2_iap_device_attach; + device_class->set_progress = fu_legion_hid2_iap_device_set_progress; +} diff -Nru fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-iap-device.h fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-iap-device.h --- fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-iap-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-iap-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_LEGION_HID2_IAP_DEVICE (fu_legion_hid2_iap_device_get_type()) +G_DECLARE_FINAL_TYPE(FuLegionHid2IapDevice, + fu_legion_hid2_iap_device, + FU, + LEGION_HID2_IAP_DEVICE, + FuHidrawDevice) diff -Nru fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-plugin.c fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-plugin.c --- fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -5,9 +5,12 @@ #include "config.h" +#include "fu-legion-hid2-bl-device.h" #include "fu-legion-hid2-device.h" #include "fu-legion-hid2-firmware.h" +#include "fu-legion-hid2-iap-device.h" #include "fu-legion-hid2-plugin.h" +#include "fu-legion-hid2-sipo-device.h" struct _FuLegionHid2Plugin { FuPlugin parent_instance; @@ -25,7 +28,12 @@ { FuPlugin *plugin = FU_PLUGIN(obj); fu_plugin_add_device_gtype(plugin, FU_TYPE_LEGION_HID2_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_LEGION_HID2_IAP_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_LEGION_HID2_SIPO_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_LEGION_HID2_BL_DEVICE); + fu_plugin_set_device_gtype_default(plugin, FU_TYPE_LEGION_HID2_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_LEGION_HID2_FIRMWARE); + fu_plugin_add_udev_subsystem(plugin, "hidraw"); } static void diff -Nru fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-sipo-device.c fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-sipo-device.c --- fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-sipo-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-sipo-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,81 @@ +/* + * Copyright 2025 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-legion-hid2-device.h" +#include "fu-legion-hid2-sipo-device.h" +#include "fu-legion-hid2-struct.h" + +struct _FuLegionHid2SipoDevice { + FuDevice parent_instance; +}; + +G_DEFINE_TYPE(FuLegionHid2SipoDevice, fu_legion_hid2_sipo_device, FU_TYPE_DEVICE) + +static gboolean +fu_legion_hid2_sipo_device_probe(FuDevice *device, GError **error) +{ + return fu_device_build_instance_id(device, NULL, "USB", "VID", "PID", "TP", NULL); +} + +static gboolean +fu_legion_hid2_sipo_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLegionHid2SipoDevice *self = FU_LEGION_HID2_SIPO_DEVICE(device); + FuLegionHid2Device *proxy; + + proxy = FU_LEGION_HID2_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not yet implemented for SIPO touchpads"); + return FALSE; +} + +static gchar * +fu_legion_hid2_sipo_device_convert_version(FuDevice *device, guint64 version_raw) +{ + return fu_version_from_uint32(version_raw, fu_device_get_version_format(device)); +} + +static void +fu_legion_hid2_sipo_device_init(FuLegionHid2SipoDevice *self) +{ + fu_device_set_name(FU_DEVICE(self), "Touchpad"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FALLBACK); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY); + fu_device_add_protocol(FU_DEVICE(self), "com.lenovo.legion-hid2"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER); + fu_device_set_logical_id(FU_DEVICE(self), "touchpad"); + fu_device_set_vendor(FU_DEVICE(self), "SIPO"); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_LEGION_HID2_DEVICE); + fu_device_add_instance_strsafe(FU_DEVICE(self), "TP", "SIPO"); +} + +static void +fu_legion_hid2_sipo_device_class_init(FuLegionHid2SipoDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->probe = fu_legion_hid2_sipo_device_probe; + device_class->write_firmware = fu_legion_hid2_sipo_device_write_firmware; + device_class->convert_version = fu_legion_hid2_sipo_device_convert_version; +} + +FuDevice * +fu_legion_hid2_sipo_device_new(FuDevice *proxy) +{ + return g_object_new(FU_TYPE_LEGION_HID2_SIPO_DEVICE, "proxy", proxy, NULL); +} diff -Nru fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-sipo-device.h fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-sipo-device.h --- fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2-sipo-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2-sipo-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +/* + * Copyright 2025 Mario Limonciello + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_LEGION_HID2_SIPO_DEVICE (fu_legion_hid2_sipo_device_get_type()) +G_DECLARE_FINAL_TYPE(FuLegionHid2SipoDevice, + fu_legion_hid2_sipo_device, + FU, + LEGION_HID2_SIPO_DEVICE, + FuDevice) + +FuDevice * +fu_legion_hid2_sipo_device_new(FuDevice *proxy) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2.rs fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2.rs --- fwupd-2.0.8/plugins/legion-hid2/fu-legion-hid2.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/fu-legion-hid2.rs 2026-02-26 11:36:18.000000000 +0000 @@ -5,12 +5,26 @@ #[repr(u32)] enum FuLegionHid2Command { GetVersion = 0x01, - GetMcuId = 0x02, + GetPlTest = 0xDF, StartIap = 0xE1, IcReset = 0xEF, } #[repr(u8)] +enum FuLegionHid2PlTest { + TpManufacturer = 0x2, + AgSensorManufacturer = 0x3, + TpVersion = 0x4, +} + +#[repr(u8)] +enum FuLegionHid2TpMan { + None = 0x0, + BetterLife = 0x1, + Sipo = 0x2, +} + +#[repr(u8)] enum FuLegionHid2ReportId { Iap = 0x1, Communication = 0x4, @@ -20,6 +34,7 @@ #[repr(C, packed)] struct FuStructLegionGetVersion { cmd: u8 == 0x01, + reserved: [u8; 63], } #[derive(New, Getters)] @@ -30,17 +45,28 @@ reserved: [u8; 59], } +//Get MCU ID, unused by fwupd +//#[derive(New, Getters)] +//#[repr(C, packed)] +//struct FuStructLegionMcuId { +// id: [u8; 12], +// reserved: [u8; 52], +//} + #[derive(New, Default)] #[repr(C, packed)] -struct FuStructLegionGetMcuId { - cmd: u8 == 0x02, +struct FuStructLegionGetPlTest { + cmd: u8 == 0xDF, + index: u8, } #[derive(New, Getters)] #[repr(C, packed)] -struct FuStructLegionMcuId { - id: [u8; 12], - reserved: [u8; 52], +struct FuStructLegionGetPlTestResult { + cmd: u8, + index: u8, + content: u8, + reserved: [u8; 61], } #[derive(New, Default)] @@ -48,7 +74,8 @@ struct FuStructLegionStartIap { cmd: u8 == 0xE1, data: [char; 7] == "UPGRADE", - reserved: [u8; 57], + reset: u8 == 0x01, // 0x01 for reset, 0x00 for no reset + reserved: [u8; 56], } #[derive(New, Getters)] diff -Nru fwupd-2.0.8/plugins/legion-hid2/legion-hid2.quirk fwupd-2.0.20/plugins/legion-hid2/legion-hid2.quirk --- fwupd-2.0.8/plugins/legion-hid2/legion-hid2.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/legion-hid2.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -1,24 +1,28 @@ # Xinput -[USB\VID_1A86&PID_E310] +[HIDRAW\VEN_1A86&DEV_E310] Plugin = legion_hid2 GType = FuLegionHid2Device -CounterpartGuid = USB\VID_1A86&PID_FE10 +CounterpartGuid = HIDRAW\VEN_1A86&DEV_FE10 FirmwareSizeMax = 0x2C000 Flags = use-runtime-version InstallDuration = 15 +Name = Legion Go S # Dinput -[USB\VID_1A86&PID_E311] +[HIDRAW\VEN_1A86&DEV_E311] Plugin = legion_hid2 GType = FuLegionHid2Device -CounterpartGuid = USB\VID_1A86&PID_FE10 +CounterpartGuid = HIDRAW\VEN_1A86&DEV_FE10 FirmwareSizeMax = 0x2C000 Flags = use-runtime-version InstallDuration = 15 +Name = Legion Go S # IAP -[USB\VID_1A86&PID_FE10] +[HIDRAW\VEN_1A86&DEV_FE10] Plugin = legion_hid2 +GType = FuLegionHid2IapDevice Flags = is-bootloader FirmwareSizeMax = 0x2C000 InstallDuration = 15 +Name = Legion Go S IAP diff -Nru fwupd-2.0.8/plugins/legion-hid2/meson.build fwupd-2.0.20/plugins/legion-hid2/meson.build --- fwupd-2.0.8/plugins/legion-hid2/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -6,6 +6,9 @@ rustgen.process('fu-legion-hid2.rs'), sources: [ 'fu-legion-hid2-device.c', + 'fu-legion-hid2-iap-device.c', + 'fu-legion-hid2-sipo-device.c', + 'fu-legion-hid2-bl-device.c', 'fu-legion-hid2-plugin.c', 'fu-legion-hid2-firmware.c', ], diff -Nru fwupd-2.0.8/plugins/legion-hid2/tests/legion-hid2.json fwupd-2.0.20/plugins/legion-hid2/tests/legion-hid2.json --- fwupd-2.0.8/plugins/legion-hid2/tests/legion-hid2.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/legion-hid2/tests/legion-hid2.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,11 +3,23 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/76ea95f3d3fa893a70de6716a9b34115365f4cb3ac57f290190ce9de20935f9d-firmware.cab", - "emulation-url": "https://fwupd.org/downloads/05162d6d3cf6c595da613fcd5c57415022c08e57a76454b7c19de533492bc3f2-legion-hid2.zip", + "url": "3275a3f8b393025003943b40bba39145c2c93a3208039055a02ee49ca1a02eb4-0_0_3_8.cab", + "emulation-url": "23bd1f020956963a10badd4c705877844ae1ecec533d8558706bbf7720f9d634-no_legos_0038_hidraw.zip", "components": [ { - "version": "0.0.2.4", + "version": "0.0.3.8", + "guids": [ + "65619675-fec6-5035-801d-7f5e59fd9749" + ] + } + ] + }, + { + "url": "3275a3f8b393025003943b40bba39145c2c93a3208039055a02ee49ca1a02eb4-0_0_3_8.cab", + "emulation-url": "ac963a09da109f6896e5e7f8c5dd3366fc2f6dbdd7689758509fd30cf14e1942-legos_0038_hidraw.zip", + "components": [ + { + "version": "0.0.3.8", "guids": [ "65619675-fec6-5035-801d-7f5e59fd9749" ] diff -Nru fwupd-2.0.8/plugins/lenovo-thinklmi/fu-self-test.c fwupd-2.0.20/plugins/lenovo-thinklmi/fu-self-test.c --- fwupd-2.0.8/plugins/lenovo-thinklmi/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/lenovo-thinklmi/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -38,40 +38,40 @@ return log_level >= G_LOG_LEVEL_MESSAGE; } -static gboolean -fu_test_self_init(FuTest *self, GError **error) +static void +fu_test_self_init(FuTest *self) { gboolean ret; g_autoptr(FuContext) ctx = fu_context_new(); g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; g_test_log_set_fatal_handler(fu_test_fatal_handler_cb, NULL); ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE | FU_QUIRKS_LOAD_FLAG_NO_VERIFY, - error); - g_assert_no_error(*error); + &error); + g_assert_no_error(error); g_assert_true(ret); - ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_LOAD_CONFIG, error); - g_assert_no_error(*error); + ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_LOAD_CONFIG, &error); + g_assert_no_error(error); g_assert_true(ret); - ret = fu_context_reload_bios_settings(ctx, error); - g_assert_no_error(*error); + ret = fu_context_reload_bios_settings(ctx, &error); + g_assert_no_error(error); g_assert_true(ret); self->plugin_uefi_capsule = fu_plugin_new_from_gtype(fu_uefi_capsule_plugin_get_type(), ctx); - ret = fu_plugin_runner_startup(self->plugin_uefi_capsule, progress, error); - g_assert_no_error(*error); + ret = fu_plugin_runner_startup(self->plugin_uefi_capsule, progress, &error); + g_assert_no_error(error); g_assert_true(ret); self->plugin_lenovo_thinklmi = fu_plugin_new_from_gtype(fu_lenovo_thinklmi_plugin_get_type(), ctx); - ret = fu_plugin_runner_startup(self->plugin_lenovo_thinklmi, progress, error); - g_assert_no_error(*error); + ret = fu_plugin_runner_startup(self->plugin_lenovo_thinklmi, progress, &error); + g_assert_no_error(error); g_assert_true(ret); self->ctx = fu_plugin_get_context(self->plugin_lenovo_thinklmi); - return TRUE; } static FuDevice * @@ -181,7 +181,6 @@ g_autofree gchar *confdir = NULL; g_autofree gchar *test_dir = NULL; g_autoptr(FuTest) self = g_new0(FuTest, 1); - g_autoptr(GError) error = NULL; (void)g_setenv("G_TEST_SRCDIR", SRCDIR, FALSE); g_test_init(&argc, &argv, NULL); @@ -210,10 +209,7 @@ g_assert_cmpint(g_mkdir_with_parents("/tmp/fwupd-self-test/var/lib/fwupd", 0755), ==, 0); /* tests go here */ - if (!fu_test_self_init(self, &error)) { - g_test_skip(error->message); - return 0; - } + fu_test_self_init(self); g_test_add_data_func("/fwupd/plugin{lenovo-think-lmi:bootorder-locked}", self, fu_plugin_lenovo_thinklmi_bootorder_locked); diff -Nru fwupd-2.0.8/plugins/lenovo-thinklmi/meson.build fwupd-2.0.20/plugins/lenovo-thinklmi/meson.build --- fwupd-2.0.8/plugins/lenovo-thinklmi/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/lenovo-thinklmi/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if allow_uefi_capsule +allow_uefi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginLenovoThinkLmi"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -40,4 +41,3 @@ ) test('lenovo-thinklmi-self-test', e, env: env) endif -endif diff -Nru fwupd-2.0.8/plugins/linux-display/fu-linux-display-plugin.c fwupd-2.0.20/plugins/linux-display/fu-linux-display-plugin.c --- fwupd-2.0.8/plugins/linux-display/fu-linux-display-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/linux-display/fu-linux-display-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -51,6 +51,11 @@ GError **error) { FuLinuxDisplayPlugin *self = FU_LINUX_DISPLAY_PLUGIN(plugin); + + /* not us */ + if (!FU_IS_DRM_DEVICE(device)) + return TRUE; + if (fu_drm_device_get_edid(FU_DRM_DEVICE(device)) != NULL) { if (!fu_device_setup(device, error)) return FALSE; diff -Nru fwupd-2.0.8/plugins/linux-display/meson.build fwupd-2.0.20/plugins/linux-display/meson.build --- fwupd-2.0.8/plugins/linux-display/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/linux-display/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxDisplay"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -11,4 +12,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/linux-lockdown/fu-linux-lockdown-plugin.c fwupd-2.0.20/plugins/linux-lockdown/fu-linux-lockdown-plugin.c --- fwupd-2.0.8/plugins/linux-lockdown/fu-linux-lockdown-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/linux-lockdown/fu-linux-lockdown-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -61,11 +61,9 @@ fu_linux_lockdown_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) { FuLinuxLockdownPlugin *self = FU_LINUX_LOCKDOWN_PLUGIN(plugin); - g_autofree gchar *path = NULL; g_autofree gchar *fn = NULL; - path = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_SECURITY); - fn = g_build_filename(path, "lockdown", NULL); + fn = fu_path_build(FU_PATH_KIND_SYSFSDIR_SECURITY, "lockdown", NULL); if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { g_set_error_literal(error, FWUPD_ERROR, diff -Nru fwupd-2.0.8/plugins/linux-lockdown/meson.build fwupd-2.0.20/plugins/linux-lockdown/meson.build --- fwupd-2.0.8/plugins/linux-lockdown/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/linux-lockdown/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if host_machine.system() == 'linux' and hsi +host_machine.system() == 'linux' or subdir_done() +hsi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxLockdown"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -12,4 +14,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/linux-sleep/fu-linux-sleep-plugin.c fwupd-2.0.20/plugins/linux-sleep/fu-linux-sleep-plugin.c --- fwupd-2.0.8/plugins/linux-sleep/fu-linux-sleep-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/linux-sleep/fu-linux-sleep-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,8 +19,7 @@ { gsize bufsz = 0; g_autofree gchar *buf = NULL; - g_autofree gchar *sysfsdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR); - g_autofree gchar *fn = g_build_filename(sysfsdir, "power", "mem_sleep", NULL); + g_autofree gchar *fn = fu_path_build(FU_PATH_KIND_SYSFSDIR, "power", "mem_sleep", NULL); g_autoptr(FwupdSecurityAttr) attr = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(GFile) file = NULL; diff -Nru fwupd-2.0.8/plugins/linux-sleep/meson.build fwupd-2.0.20/plugins/linux-sleep/meson.build --- fwupd-2.0.8/plugins/linux-sleep/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/linux-sleep/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,7 @@ -if host_machine.system() == 'linux' and hsi and (host_cpu == 'x86' or host_cpu == 'x86_64') +host_machine.system() == 'linux' or subdir_done() +hsi or subdir_done() +host_cpu in ['x86', 'x86_64'] or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxSleep"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -11,4 +14,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/linux-swap/fu-linux-swap-plugin.c fwupd-2.0.20/plugins/linux-swap/fu-linux-swap-plugin.c --- fwupd-2.0.8/plugins/linux-swap/fu-linux-swap-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/linux-swap/fu-linux-swap-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -34,10 +34,8 @@ { FuLinuxSwapPlugin *self = FU_LINUX_SWAP_PLUGIN(plugin); g_autofree gchar *fn = NULL; - g_autofree gchar *procfs = NULL; - procfs = fu_path_from_kind(FU_PATH_KIND_PROCFS); - fn = g_build_filename(procfs, "swaps", NULL); + fn = fu_path_build(FU_PATH_KIND_PROCFS, "swaps", NULL); if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { g_set_error_literal(error, FWUPD_ERROR, diff -Nru fwupd-2.0.8/plugins/linux-swap/fu-linux-swap.c fwupd-2.0.20/plugins/linux-swap/fu-linux-swap.c --- fwupd-2.0.8/plugins/linux-swap/fu-linux-swap.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/linux-swap/fu-linux-swap.c 2026-02-26 11:36:18.000000000 +0000 @@ -119,12 +119,10 @@ if (!fu_linux_swap_verify_partition(self, fn, error)) return NULL; } else if (g_strcmp0(ty, "file") == 0) { - g_autofree gchar *base = NULL; g_autofree gchar *path = NULL; /* get the path to the file */ - base = fu_path_from_kind(FU_PATH_KIND_HOSTFS_ROOT); - path = g_build_filename(base, fn, NULL); + path = fu_path_build(FU_PATH_KIND_HOSTFS_ROOT, fn, NULL); self->enabled_cnt++; if (!fu_linux_swap_verify_file(self, path, error)) diff -Nru fwupd-2.0.8/plugins/linux-swap/meson.build fwupd-2.0.20/plugins/linux-swap/meson.build --- fwupd-2.0.8/plugins/linux-swap/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/linux-swap/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if host_machine.system() == 'linux' and hsi +host_machine.system() == 'linux' or subdir_done() +hsi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxSwap"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -31,6 +33,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', @@ -38,4 +41,3 @@ ) test('linux-swap-self-test', e, env: env) # added to installed-tests endif -endif diff -Nru fwupd-2.0.8/plugins/linux-tainted/fu-linux-tainted-plugin.c fwupd-2.0.20/plugins/linux-tainted/fu-linux-tainted-plugin.c --- fwupd-2.0.8/plugins/linux-tainted/fu-linux-tainted-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/linux-tainted/fu-linux-tainted-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -53,10 +53,8 @@ { FuLinuxTaintedPlugin *self = FU_LINUX_TAINTED_PLUGIN(plugin); g_autofree gchar *fn = NULL; - g_autofree gchar *procfs = NULL; - procfs = fu_path_from_kind(FU_PATH_KIND_PROCFS); - fn = g_build_filename(procfs, "sys", "kernel", "tainted", NULL); + fn = fu_path_build(FU_PATH_KIND_PROCFS, "sys", "kernel", "tainted", NULL); self->file = g_file_new_for_path(fn); self->monitor = g_file_monitor(self->file, G_FILE_MONITOR_NONE, NULL, error); if (self->monitor == NULL) diff -Nru fwupd-2.0.8/plugins/linux-tainted/meson.build fwupd-2.0.20/plugins/linux-tainted/meson.build --- fwupd-2.0.8/plugins/linux-tainted/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/linux-tainted/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if host_machine.system() == 'linux' and hsi +host_machine.system() == 'linux' or subdir_done() +hsi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginLinuxTainted"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -11,4 +13,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/logind/fu-logind-plugin.c fwupd-2.0.20/plugins/logind/fu-logind-plugin.c --- fwupd-2.0.8/plugins/logind/fu-logind-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logind/fu-logind-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -42,7 +42,7 @@ NULL, error); if (self->logind_proxy == NULL) { - g_prefix_error(error, "failed to connect to logind: "); + g_prefix_error_literal(error, "failed to connect to logind: "); return FALSE; } name_owner = g_dbus_proxy_get_name_owner(self->logind_proxy); diff -Nru fwupd-2.0.8/plugins/logind/meson.build fwupd-2.0.20/plugins/logind/meson.build --- fwupd-2.0.8/plugins/logind/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logind/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if libsystemd.found() +libsystemd.found() or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginLogind"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -11,4 +12,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/README.md fwupd-2.0.20/plugins/logitech-bulkcontroller/README.md --- fwupd-2.0.8/plugins/logitech-bulkcontroller/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -90,10 +90,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.7.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Sanjay Sheth: @vcdmp diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-child.c fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-child.c --- fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-child.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-child.c 2026-02-26 11:36:18.000000000 +0000 @@ -7,6 +7,7 @@ #include "config.h" #include "fu-logitech-bulkcontroller-child.h" +#include "fu-logitech-bulkcontroller-device.h" struct _FuLogitechBulkcontrollerChild { FuDevice parent_instance; @@ -21,12 +22,20 @@ FwupdInstallFlags flags, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + /* + * set the flag, to let parent know that firmware update is for child, no need to wait for + * replug event, after child firmware is updated + */ + fu_device_add_private_flag(proxy, + FU_LOGITECH_BULKCONTROLLER_DEVICE_FLAG_PHERIPHERAL_UPDATE); return fu_device_write_firmware(proxy, firmware, progress, flags, error); } static void -fu_logitech_bulkcontroller_child_set_progress(FuDevice *self, FuProgress *progress) +fu_logitech_bulkcontroller_child_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -41,9 +50,11 @@ { fu_device_add_protocol(FU_DEVICE(self), "com.logitech.vc.proto"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_LOGITECH_BULKCONTROLLER_DEVICE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); - fu_device_add_icon(FU_DEVICE(self), "camera-web"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_WEB_CAMERA); } static void diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-child.h fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-child.h --- fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-child.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-child.h 2026-02-26 11:36:18.000000000 +0000 @@ -14,3 +14,5 @@ FU, LOGITECH_BULKCONTROLLER_CHILD, FuDevice) + +#define FU_LOGITECH_BULKCONTROLLER_DEVICE_FLAG_PHERIPHERAL_UPDATE "pheripheral-update" diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.c fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.c --- fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,7 +13,7 @@ #include "usb_msg.pb-c.h" static void -fu_logitech_bulkcontroller_proto_manager_set_header(FuDevice *device, +fu_logitech_bulkcontroller_proto_manager_set_header(FuLogitechBulkcontrollerDevice *self, Logi__Device__Proto__Header *header_msg) { gint64 timestamp_tv; @@ -21,8 +21,8 @@ g_return_if_fail(header_msg != NULL); /* make predictable */ - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED) || - fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATION_TAG)) { + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || + fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATION_TAG)) { header_msg->id = 0; header_msg->timestamp = g_strdup("0"); return; @@ -34,7 +34,8 @@ } GByteArray * -fu_logitech_bulkcontroller_proto_manager_generate_get_device_info_request(FuDevice *device) +fu_logitech_bulkcontroller_proto_manager_generate_get_device_info_request( + FuLogitechBulkcontrollerDevice *self) { GByteArray *buf = g_byte_array_new(); Logi__Device__Proto__Header header_msg = LOGI__DEVICE__PROTO__HEADER__INIT; @@ -45,7 +46,7 @@ request_msg.payload_case = LOGI__DEVICE__PROTO__REQUEST__PAYLOAD_GET_DEVICE_INFO_REQUEST; request_msg.get_device_info_request = &get_deviceinfo_msg; - fu_logitech_bulkcontroller_proto_manager_set_header(device, &header_msg); + fu_logitech_bulkcontroller_proto_manager_set_header(self, &header_msg); usb_msg.header = &header_msg; usb_msg.message_case = LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_REQUEST; usb_msg.request = &request_msg; @@ -59,7 +60,7 @@ GByteArray * fu_logitech_bulkcontroller_proto_manager_generate_transition_to_device_mode_request( - FuDevice *device) + FuLogitechBulkcontrollerDevice *self) { GByteArray *buf = g_byte_array_new(); Logi__Device__Proto__Header header_msg = LOGI__DEVICE__PROTO__HEADER__INIT; @@ -71,7 +72,7 @@ LOGI__DEVICE__PROTO__REQUEST__PAYLOAD_TRANSITION_TO_DEVICEMODE_REQUEST; request_msg.transition_to_devicemode_request = &transition_to_device_mode_msg; - fu_logitech_bulkcontroller_proto_manager_set_header(device, &header_msg); + fu_logitech_bulkcontroller_proto_manager_set_header(self, &header_msg); usb_msg.header = &header_msg; usb_msg.message_case = LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_REQUEST; usb_msg.request = &request_msg; @@ -84,8 +85,9 @@ } GByteArray * -fu_logitech_bulkcontroller_proto_manager_generate_set_device_time_request(FuDevice *device, - GError **error) +fu_logitech_bulkcontroller_proto_manager_generate_set_device_time_request( + FuLogitechBulkcontrollerDevice *self, + GError **error) { g_autofree gchar *olson_location = NULL; g_autoptr(GByteArray) buf = g_byte_array_new(); @@ -96,8 +98,8 @@ Logi__Device__Proto__Request request_msg = LOGI__DEVICE__PROTO__REQUEST__INIT; /* the device expects an olson_location, not a timezone */ - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED) || - fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATION_TAG)) { + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || + fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATION_TAG)) { olson_location = g_strdup("Europe/London"); } else { olson_location = fu_common_get_olson_timezone_id(error); @@ -109,14 +111,14 @@ request_msg.set_device_time_request = &set_devicetime_msg; /* make predictable */ - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED) || - fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATION_TAG)) { + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || + fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATION_TAG)) { set_devicetime_msg.ts = 0; } else { set_devicetime_msg.ts = (g_get_real_time() / 1000) + SET_TIME_DELAY_MS; } set_devicetime_msg.time_zone = olson_location; - fu_logitech_bulkcontroller_proto_manager_set_header(device, &header_msg); + fu_logitech_bulkcontroller_proto_manager_set_header(self, &header_msg); usb_msg.header = &header_msg; usb_msg.message_case = LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_REQUEST; usb_msg.request = &request_msg; @@ -128,15 +130,97 @@ return g_steal_pointer(&buf); } +static GByteArray * +fu_logitech_bulkcontroller_usb_msg_parse_msg_response(Logi__Device__Proto__UsbMsg *usb_msg, + FuLogitechBulkcontrollerProtoId *proto_id, + GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + + if (usb_msg->response == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "no USB response"); + return NULL; + } + switch (usb_msg->response->payload_case) { + case LOGI__DEVICE__PROTO__RESPONSE__PAYLOAD_GET_DEVICE_INFO_RESPONSE: + if (usb_msg->response->get_device_info_response) { + const gchar *tmp = usb_msg->response->get_device_info_response->payload; + *proto_id = kProtoId_GetDeviceInfoResponse; + if (tmp != NULL) + g_byte_array_append(buf, (const guint8 *)tmp, strlen(tmp)); + } + break; + case LOGI__DEVICE__PROTO__RESPONSE__PAYLOAD_TRANSITION_TO_DEVICEMODE_RESPONSE: + if (usb_msg->response->transition_to_devicemode_response) { + *proto_id = kProtoId_TransitionToDeviceModeResponse; + if (!usb_msg->response->transition_to_devicemode_response->success) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "transition mode request failed. error: %u", + (guint)usb_msg->response + ->transition_to_devicemode_response->error); + return NULL; + } + } + break; + default: + break; + }; + + /* success */ + return g_steal_pointer(&buf); +} + +static GByteArray * +fu_logitech_bulkcontroller_usb_msg_parse_msg_event(Logi__Device__Proto__UsbMsg *usb_msg, + FuLogitechBulkcontrollerProtoId *proto_id, + GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + + if (usb_msg->response == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "no USB event"); + return NULL; + } + switch (usb_msg->event->payload_case) { + case LOGI__DEVICE__PROTO__EVENT__PAYLOAD_KONG_EVENT: + if (usb_msg->event->kong_event) { + const gchar *tmp = usb_msg->event->kong_event->mqtt_event; + *proto_id = kProtoId_KongEvent; + if (tmp != NULL) + g_byte_array_append(buf, (const guint8 *)tmp, strlen(tmp)); + } + break; + case LOGI__DEVICE__PROTO__EVENT__PAYLOAD_HANDSHAKE_EVENT: + if (usb_msg->event->handshake_event) + *proto_id = kProtoId_HandshakeEvent; + break; + case LOGI__DEVICE__PROTO__EVENT__PAYLOAD_CRASH_DUMP_AVAILABLE_EVENT: + *proto_id = kProtoId_CrashDumpAvailableEvent; + break; + default: + break; + }; + + /* success */ + return g_steal_pointer(&buf); +} + GByteArray * fu_logitech_bulkcontroller_proto_manager_decode_message(const guint8 *data, guint32 len, FuLogitechBulkcontrollerProtoId *proto_id, GError **error) { - g_autoptr(GByteArray) buf_decoded = g_byte_array_new(); + g_autoptr(GByteArray) buf = NULL; Logi__Device__Proto__UsbMsg *usb_msg = logi__device__proto__usb_msg__unpack(NULL, len, (const unsigned char *)data); + + /* sanity check */ if (usb_msg == NULL) { g_set_error_literal(error, FWUPD_ERROR, @@ -147,81 +231,25 @@ switch (usb_msg->message_case) { case LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_ACK: + buf = g_byte_array_new(); *proto_id = kProtoId_Ack; break; case LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_RESPONSE: - if (!usb_msg->response) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "no USB response"); + buf = + fu_logitech_bulkcontroller_usb_msg_parse_msg_response(usb_msg, proto_id, error); + if (buf == NULL) return NULL; - } - switch (usb_msg->response->payload_case) { - case LOGI__DEVICE__PROTO__RESPONSE__PAYLOAD_GET_DEVICE_INFO_RESPONSE: - if (usb_msg->response->get_device_info_response) { - const gchar *tmp = - usb_msg->response->get_device_info_response->payload; - *proto_id = kProtoId_GetDeviceInfoResponse; - if (tmp != NULL) - g_byte_array_append(buf_decoded, - (const guint8 *)tmp, - strlen(tmp)); - } - break; - case LOGI__DEVICE__PROTO__RESPONSE__PAYLOAD_TRANSITION_TO_DEVICEMODE_RESPONSE: - if (usb_msg->response->transition_to_devicemode_response) { - *proto_id = kProtoId_TransitionToDeviceModeResponse; - if (!usb_msg->response->transition_to_devicemode_response - ->success) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "transition mode request failed. error: %u", - (guint)usb_msg->response - ->transition_to_devicemode_response->error); - return NULL; - } - } - break; - default: - break; - }; break; case LOGI__DEVICE__PROTO__USB_MSG__MESSAGE_EVENT: - if (!usb_msg->response) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "no USB event"); + buf = fu_logitech_bulkcontroller_usb_msg_parse_msg_event(usb_msg, proto_id, error); + if (buf == NULL) return NULL; - } - switch (usb_msg->event->payload_case) { - case LOGI__DEVICE__PROTO__EVENT__PAYLOAD_KONG_EVENT: - if (usb_msg->event->kong_event) { - const gchar *tmp = usb_msg->event->kong_event->mqtt_event; - *proto_id = kProtoId_KongEvent; - if (tmp != NULL) - g_byte_array_append(buf_decoded, - (const guint8 *)tmp, - strlen(tmp)); - } - break; - case LOGI__DEVICE__PROTO__EVENT__PAYLOAD_HANDSHAKE_EVENT: - if (usb_msg->event->handshake_event) { - *proto_id = kProtoId_HandshakeEvent; - } - break; - case LOGI__DEVICE__PROTO__EVENT__PAYLOAD_CRASH_DUMP_AVAILABLE_EVENT: - *proto_id = kProtoId_CrashDumpAvailableEvent; - break; - default: - break; - }; break; default: + buf = g_byte_array_new(); + g_debug("ignoring invalid message case 0x%x", usb_msg->message_case); break; }; logi__device__proto__usb_msg__free_unpacked(usb_msg, NULL); - return g_steal_pointer(&buf_decoded); + return g_steal_pointer(&buf); } diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.h fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.h --- fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -7,8 +7,7 @@ #pragma once -#include - +#include "fu-logitech-bulkcontroller-device.h" #include "usb_msg.pb-c.h" #define SET_TIME_DELAY_MS 500 /* send future time to keep PC & device time as close as possible */ @@ -24,15 +23,15 @@ } FuLogitechBulkcontrollerProtoId; GByteArray * -fu_logitech_bulkcontroller_proto_manager_generate_get_device_info_request(FuDevice *device) - G_GNUC_NON_NULL(1); +fu_logitech_bulkcontroller_proto_manager_generate_get_device_info_request( + FuLogitechBulkcontrollerDevice *self) G_GNUC_NON_NULL(1); GByteArray * fu_logitech_bulkcontroller_proto_manager_generate_transition_to_device_mode_request( - FuDevice *device) G_GNUC_NON_NULL(1); + FuLogitechBulkcontrollerDevice *self) G_GNUC_NON_NULL(1); GByteArray * -fu_logitech_bulkcontroller_proto_manager_generate_set_device_time_request(FuDevice *device, - GError **error) - G_GNUC_NON_NULL(1); +fu_logitech_bulkcontroller_proto_manager_generate_set_device_time_request( + FuLogitechBulkcontrollerDevice *self, + GError **error) G_GNUC_NON_NULL(1); GByteArray * fu_logitech_bulkcontroller_proto_manager_decode_message(const guint8 *data, guint32 len, diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.c fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.c --- fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,6 +19,7 @@ #define UPD_INTERFACE_SUBPROTOCOL_ID 117 #define SYNC_INTERFACE_SUBPROTOCOL_ID 118 #define BULK_TRANSFER_TIMEOUT 2500 +#define BULK_TRANSFER_FLUSH_TIMEOUT 5 #define HASH_VALUE_SIZE 16 #define MAX_RETRIES 5 #define MAX_SETUP_RETRIES 50 @@ -36,7 +37,7 @@ FuLogitechBulkcontrollerDeviceState status; FuLogitechBulkcontrollerUpdateState update_status; guint update_progress; /* percentage value */ - gboolean is_sync_transfer_in_progress; + gboolean is_sync_flush_events_in_progress; GString *device_info_response_json; gsize transfer_bufsz; guint32 sequence_id; @@ -125,6 +126,7 @@ FuLogitechBulkcontrollerCmd cmd; guint32 sequence_id; GByteArray *data; + guint timeout; } FuLogitechBulkcontrollerResponse; static FuLogitechBulkcontrollerResponse * @@ -150,6 +152,7 @@ fu_logitech_bulkcontroller_device_sync_send_cmd(FuLogitechBulkcontrollerDevice *self, FuLogitechBulkcontrollerCmd cmd, GByteArray *buf, + guint timeout, GError **error) { g_autoptr(FuStructLogitechBulkcontrollerSendSyncReq) st_req = @@ -165,20 +168,20 @@ if (buf != NULL) { fu_struct_logitech_bulkcontroller_send_sync_req_set_payload_length(st_req, buf->len); - g_byte_array_append(st_req, buf->data, buf->len); + g_byte_array_append(st_req->buf, buf->data, buf->len); } str = fu_struct_logitech_bulkcontroller_send_sync_req_to_string(st_req); g_debug("sending: %s", str); if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), self->sync_ep[EP_OUT], - st_req->data, - st_req->len, + st_req->buf->data, + st_req->buf->len, NULL, /* transferred */ - BULK_TRANSFER_TIMEOUT, + timeout, NULL, error)) { - g_prefix_error(error, "failed to send sync bulk transfer: "); + g_prefix_error_literal(error, "failed to send sync bulk transfer: "); return FALSE; } @@ -189,6 +192,7 @@ static gboolean fu_logitech_bulkcontroller_device_sync_send_ack(FuLogitechBulkcontrollerDevice *self, FuLogitechBulkcontrollerCmd cmd, + guint timeout, GError **error) { g_autoptr(GByteArray) buf_ack = g_byte_array_new(); @@ -196,6 +200,7 @@ if (!fu_logitech_bulkcontroller_device_sync_send_cmd(self, FU_LOGITECH_BULKCONTROLLER_CMD_ACK, buf_ack, + timeout, error)) { g_prefix_error(error, "failed to send ack for %s: ", @@ -207,6 +212,7 @@ static FuLogitechBulkcontrollerResponse * fu_logitech_bulkcontroller_device_sync_wait_any(FuLogitechBulkcontrollerDevice *self, + guint timeout, GError **error) { gsize actual_length = 0; @@ -220,10 +226,10 @@ buf, self->transfer_bufsz, &actual_length, - BULK_TRANSFER_TIMEOUT, + timeout, NULL, error)) { - g_prefix_error(error, "failed to receive: "); + g_prefix_error_literal(error, "failed to receive: "); return NULL; } fu_dump_raw(G_LOG_DOMAIN, "response", buf, MIN(actual_length, 12)); @@ -236,9 +242,14 @@ return NULL; response->cmd = fu_struct_logitech_bulkcontroller_send_sync_res_get_cmd(st); response->sequence_id = fu_struct_logitech_bulkcontroller_send_sync_res_get_sequence_id(st); - g_byte_array_append(response->data, - buf + st->len, - fu_struct_logitech_bulkcontroller_send_sync_res_get_payload_length(st)); + if (!fu_byte_array_append_safe( + response->data, + buf, + self->transfer_bufsz, + st->buf->len, /* offset */ + fu_struct_logitech_bulkcontroller_send_sync_res_get_payload_length(st), + error)) + return NULL; /* no payload for UninitBuffer, skip check */ if ((response->cmd != FU_LOGITECH_BULKCONTROLLER_CMD_UNINIT_BUFFER) && (response->data->len == 0)) { @@ -255,11 +266,12 @@ fu_logitech_bulkcontroller_device_sync_wait_cmd(FuLogitechBulkcontrollerDevice *self, FuLogitechBulkcontrollerCmd cmd, guint32 sequence_id, + guint timeout, GError **error) { g_autoptr(FuLogitechBulkcontrollerResponse) response = NULL; - response = fu_logitech_bulkcontroller_device_sync_wait_any(self, error); + response = fu_logitech_bulkcontroller_device_sync_wait_any(self, timeout, error); if (response == NULL) return NULL; if (response->cmd != cmd) { @@ -298,6 +310,7 @@ helper->data = fu_logitech_bulkcontroller_device_sync_wait_cmd(self, helper->cmd, helper->sequence_id, + helper->timeout, error); if (helper->data == NULL) return FALSE; @@ -310,9 +323,12 @@ fu_logitech_bulkcontroller_device_sync_wait_cmd_retry(FuLogitechBulkcontrollerDevice *self, FuLogitechBulkcontrollerCmd cmd, guint32 sequence_id, + guint timeout, GError **error) { - FuLogitechBulkcontrollerResponse helper = {.cmd = cmd, .sequence_id = sequence_id}; + FuLogitechBulkcontrollerResponse helper = {.cmd = cmd, + .sequence_id = sequence_id, + .timeout = timeout}; if (!fu_device_retry(FU_DEVICE(self), fu_logitech_bulkcontroller_device_sync_wait_cmd_retry_cb, MAX_RETRIES, @@ -339,7 +355,7 @@ 0x0, sizeof(ack_payload) - 1, error)) { - g_prefix_error(error, "failed to copy ack payload: "); + g_prefix_error_literal(error, "failed to copy ack payload: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, "ack_payload", (guint8 *)ack_payload, sizeof(ack_payload)); @@ -349,7 +365,7 @@ G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "failed to parse ack payload cmd: "); + g_prefix_error_literal(error, "failed to parse ack payload cmd: "); return FALSE; } g_debug("ack_cmd: %s [0x%x]", @@ -381,6 +397,7 @@ buf = fu_logitech_bulkcontroller_device_sync_wait_cmd(self, FU_LOGITECH_BULKCONTROLLER_CMD_ACK, self->sequence_id, + helper->timeout, error); if (buf == NULL) return FALSE; @@ -395,9 +412,10 @@ static gboolean fu_logitech_bulkcontroller_device_sync_wait_ack(FuLogitechBulkcontrollerDevice *self, FuLogitechBulkcontrollerCmd cmd, + guint timeout, GError **error) { - FuLogitechBulkcontrollerResponse helper = {.cmd = cmd}; + FuLogitechBulkcontrollerResponse helper = {.cmd = cmd, .timeout = timeout}; return fu_device_retry_full(FU_DEVICE(self), fu_logitech_bulkcontroller_device_sync_wait_ack_cb, 10, @@ -439,8 +457,9 @@ self, FU_LOGITECH_BULKCONTROLLER_CMD_BUFFER_WRITE, req, + BULK_TRANSFER_TIMEOUT, error)) { - g_prefix_error(error, "failed to send request: "); + g_prefix_error_literal(error, "failed to send request: "); return NULL; } @@ -448,8 +467,9 @@ if (!fu_logitech_bulkcontroller_device_sync_wait_ack( self, FU_LOGITECH_BULKCONTROLLER_CMD_BUFFER_WRITE, + BULK_TRANSFER_TIMEOUT, error)) { - g_prefix_error(error, "failed to wait for ack: "); + g_prefix_error_literal(error, "failed to wait for ack: "); return NULL; } @@ -458,17 +478,21 @@ self, FU_LOGITECH_BULKCONTROLLER_CMD_UNINIT_BUFFER, NULL, + BULK_TRANSFER_TIMEOUT, error)) { - g_prefix_error(error, "failed to uninit buffer: "); + g_prefix_error_literal(error, "failed to uninit buffer: "); return NULL; } /* wait device->host buffer-read|ack */ do { g_autoptr(FuLogitechBulkcontrollerResponse) response_tmp = NULL; - response_tmp = fu_logitech_bulkcontroller_device_sync_wait_any(self, error); + response_tmp = + fu_logitech_bulkcontroller_device_sync_wait_any(self, + BULK_TRANSFER_TIMEOUT, + error); if (response_tmp == NULL) { - g_prefix_error(error, "failed to wait for any: "); + g_prefix_error_literal(error, "failed to wait for any: "); return NULL; } if (response_tmp->cmd == FU_LOGITECH_BULKCONTROLLER_CMD_ACK) { @@ -484,7 +508,7 @@ response_tmp, FU_LOGITECH_BULKCONTROLLER_CMD_UNINIT_BUFFER, error)) { - g_prefix_error(error, "failed to check uninit buffer: "); + g_prefix_error_literal(error, "failed to check uninit buffer: "); return NULL; } res_ack = g_steal_pointer(&response_tmp->data); @@ -504,8 +528,9 @@ if (!fu_logitech_bulkcontroller_device_sync_send_ack( self, FU_LOGITECH_BULKCONTROLLER_CMD_BUFFER_READ, + BULK_TRANSFER_TIMEOUT, error)) { - g_prefix_error(error, "failed to ack read buffer: "); + g_prefix_error_literal(error, "failed to ack read buffer: "); return NULL; } @@ -514,9 +539,10 @@ self, FU_LOGITECH_BULKCONTROLLER_CMD_UNINIT_BUFFER, 0x0, /* why? */ + BULK_TRANSFER_TIMEOUT, error); if (buf == NULL) { - g_prefix_error(error, "failed to wait for uninit buffer: "); + g_prefix_error_literal(error, "failed to wait for uninit buffer: "); return NULL; } @@ -524,8 +550,9 @@ if (!fu_logitech_bulkcontroller_device_sync_send_ack( self, FU_LOGITECH_BULKCONTROLLER_CMD_UNINIT_BUFFER, + BULK_TRANSFER_TIMEOUT, error)) { - g_prefix_error(error, "failed to ack uninit buffer: "); + g_prefix_error_literal(error, "failed to ack uninit buffer: "); return NULL; } @@ -542,29 +569,29 @@ { gsize actual_length = 0; g_autofree guint8 *buf_tmp = g_malloc0(self->transfer_bufsz); - GByteArray buf_ack = {.data = buf_tmp, .len = self->transfer_bufsz}; - g_autoptr(FuStructLogitechBulkcontrollerUpdateReq) buf_pkt = + g_autoptr(FuStructLogitechBulkcontrollerUpdateRes) st_res = NULL; + g_autoptr(FuStructLogitechBulkcontrollerUpdateReq) st_pkt = fu_struct_logitech_bulkcontroller_update_req_new(); - fu_struct_logitech_bulkcontroller_update_req_set_cmd(buf_pkt, cmd); + fu_struct_logitech_bulkcontroller_update_req_set_cmd(st_pkt, cmd); if (buf != NULL) { fu_struct_logitech_bulkcontroller_update_req_set_payload_length( - buf_pkt, + st_pkt, g_bytes_get_size(buf)); - fu_byte_array_append_bytes(buf_pkt, buf); + fu_byte_array_append_bytes(st_pkt->buf, buf); } - fu_dump_raw(G_LOG_DOMAIN, "request", buf_pkt->data, MIN(buf_pkt->len, 12)); + fu_dump_raw(G_LOG_DOMAIN, "request", st_pkt->buf->data, MIN(st_pkt->buf->len, 12)); if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), self->update_ep[EP_OUT], - buf_pkt->data, - buf_pkt->len, + st_pkt->buf->data, + st_pkt->buf->len, NULL, /* transferred */ BULK_TRANSFER_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to send upd bulk transfer: "); - fu_error_convert(error); + g_prefix_error_literal(error, "failed to send upd bulk transfer: "); + fwupd_error_convert(error); return FALSE; } @@ -577,30 +604,35 @@ timeout, NULL, error)) { - g_prefix_error(error, "failed to receive: "); + g_prefix_error_literal(error, "failed to receive: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, "response", buf_tmp, MIN(actual_length, 12)); - if (fu_struct_logitech_bulkcontroller_update_res_get_cmd(&buf_ack) != + st_res = fu_struct_logitech_bulkcontroller_update_res_parse(buf_tmp, + self->transfer_bufsz, + 0x0, + error); + if (st_res == NULL) + return FALSE; + if (fu_struct_logitech_bulkcontroller_update_res_get_cmd(st_res) != FU_LOGITECH_BULKCONTROLLER_CMD_ACK) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "not CMD_ACK, got %s", fu_logitech_bulkcontroller_cmd_to_string( - fu_struct_logitech_bulkcontroller_update_res_get_cmd(&buf_ack))); + fu_struct_logitech_bulkcontroller_update_res_get_cmd(st_res))); return FALSE; } - if (fu_struct_logitech_bulkcontroller_update_res_get_cmd_req(&buf_ack) != cmd) { - g_set_error( - error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid upd message received, expected %s, got %s", - fu_logitech_bulkcontroller_cmd_to_string(cmd), - fu_logitech_bulkcontroller_cmd_to_string( - fu_struct_logitech_bulkcontroller_update_res_get_cmd_req(&buf_ack))); + if (fu_struct_logitech_bulkcontroller_update_res_get_cmd_req(st_res) != cmd) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "invalid upd message received, expected %s, got %s", + fu_logitech_bulkcontroller_cmd_to_string(cmd), + fu_logitech_bulkcontroller_cmd_to_string( + fu_struct_logitech_bulkcontroller_update_res_get_cmd_req(st_res))); return FALSE; } return TRUE; @@ -714,7 +746,7 @@ (const gchar *)decoded_pkt->data, decoded_pkt->len, error)) { - g_prefix_error(error, "failed to parse json data: "); + g_prefix_error_literal(error, "failed to parse json data: "); return FALSE; } json_root = json_parser_get_root(json_parser); @@ -795,7 +827,7 @@ &proto_id, error); if (decoded_pkt == NULL) { - g_prefix_error(error, "failed to unpack packet for device info request: "); + g_prefix_error_literal(error, "failed to unpack packet for device info request: "); return FALSE; } bufstr = fu_strsafe((const gchar *)decoded_pkt->data, decoded_pkt->len); @@ -836,8 +868,7 @@ */ if (send_req) { g_autoptr(GByteArray) device_request = - fu_logitech_bulkcontroller_proto_manager_generate_get_device_info_request( - device); + fu_logitech_bulkcontroller_proto_manager_generate_get_device_info_request(self); buf = fu_logitech_bulkcontroller_device_sync_write(self, device_request, error); if (buf == NULL) return FALSE; @@ -847,6 +878,7 @@ self, FU_LOGITECH_BULKCONTROLLER_CMD_BUFFER_READ, 0x0, /* sequence_id */ + BULK_TRANSFER_TIMEOUT, error); if (buf == NULL) return FALSE; @@ -927,19 +959,63 @@ FuProgress *progress = FU_PROGRESS(user_data); g_autoptr(GError) error_local = NULL; g_autoptr(GByteArray) buf = NULL; + g_autoptr(GByteArray) uninit_buf = NULL; - /* poll the out interface */ + /* + * poll the out interface. Device mandates following read flow on SYNC interface + * Device->Host FU_LOGITECH_BULKCONTROLLER_CMD_BUFFER_READ + * Host->Device FU_LOGITECH_BULKCONTROLLER_CMD_ACK + * Device->Host FU_LOGITECH_BULKCONTROLLER_CMD_UNINIT_BUFFER + * Host->Device FU_LOGITECH_BULKCONTROLLER_CMD_ACK + * + * use lower timeout to quickly flush any kUpdateStateDownloading progress events, + * accumulated while host busy sending firmware image. Device queue stores upto 100 events. + * These events are not removed from the queue, unless properly acknowledged + */ buf = fu_logitech_bulkcontroller_device_sync_wait_cmd( self, FU_LOGITECH_BULKCONTROLLER_CMD_BUFFER_READ, 0x0, /* sequence_id */ - &error_local); + (self->is_sync_flush_events_in_progress) ? BULK_TRANSFER_FLUSH_TIMEOUT + : BULK_TRANSFER_TIMEOUT, + error); + if (buf == NULL) + return FALSE; + + /* send host->device ack */ + if (!fu_logitech_bulkcontroller_device_sync_send_ack( + self, + FU_LOGITECH_BULKCONTROLLER_CMD_BUFFER_READ, + (self->is_sync_flush_events_in_progress) ? BULK_TRANSFER_FLUSH_TIMEOUT + : BULK_TRANSFER_TIMEOUT, + error)) + return FALSE; + + /* wait device->host uninit */ + uninit_buf = fu_logitech_bulkcontroller_device_sync_wait_cmd_retry( + self, + FU_LOGITECH_BULKCONTROLLER_CMD_UNINIT_BUFFER, + 0x0, /* why? */ + (self->is_sync_flush_events_in_progress) ? BULK_TRANSFER_FLUSH_TIMEOUT + : BULK_TRANSFER_TIMEOUT, + error); + if (uninit_buf == NULL) + return FALSE; + + /* send host->device ack */ + if (!fu_logitech_bulkcontroller_device_sync_send_ack( + self, + FU_LOGITECH_BULKCONTROLLER_CMD_UNINIT_BUFFER, + (self->is_sync_flush_events_in_progress) ? BULK_TRANSFER_FLUSH_TIMEOUT + : BULK_TRANSFER_TIMEOUT, + error)) + return FALSE; + if (buf == NULL) { g_autoptr(GByteArray) device_request = NULL; g_debug("manually requesting as no pending request: %s", error_local->message); device_request = - fu_logitech_bulkcontroller_proto_manager_generate_get_device_info_request( - device); + fu_logitech_bulkcontroller_proto_manager_generate_get_device_info_request(self); buf = fu_logitech_bulkcontroller_device_sync_write(self, device_request, error); if (buf == NULL) return FALSE; @@ -950,6 +1026,19 @@ g_debug("firmware update status: %s, progress: %u", fu_logitech_bulkcontroller_update_state_to_string(self->update_status), self->update_progress); + + /* events are not sorted, so stale downloading events can appear anytime */ + if (self->update_status == FU_LOGITECH_BULKCONTROLLER_UPDATE_STATE_DOWNLOADING) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_BUSY, + "waiting for download to finish"); + self->is_sync_flush_events_in_progress = TRUE; + return FALSE; + } else { + self->is_sync_flush_events_in_progress = FALSE; + } + fu_progress_set_status( progress, fu_logitech_bulkcontroller_device_update_state_to_status(self->update_status)); @@ -1015,8 +1104,9 @@ MAX_RETRIES, NULL, error)) { - g_prefix_error(error, - "failed to write init transfer packet: please reboot the device: "); + g_prefix_error_literal( + error, + "failed to write init transfer packet: please reboot the device: "); return FALSE; } @@ -1031,7 +1121,7 @@ start_pkt_blob, BULK_TRANSFER_TIMEOUT, error)) { - g_prefix_error(error, "failed to write start transfer packet: "); + g_prefix_error_literal(error, "failed to write start transfer packet: "); return FALSE; } fu_progress_step_done(progress); @@ -1041,7 +1131,7 @@ stream, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write firmware: "); + g_prefix_error_literal(error, "failed to write firmware: "); return FALSE; } fu_progress_step_done(progress); @@ -1061,7 +1151,7 @@ end_pkt_blob, HASH_TIMEOUT, error)) { - g_prefix_error(error, "failed to write end transfer packet: "); + g_prefix_error_literal(error, "failed to write end transfer packet: "); return FALSE; } fu_progress_step_done(progress); @@ -1072,7 +1162,7 @@ NULL, BULK_TRANSFER_TIMEOUT, error)) { - g_prefix_error(error, "failed to write finish transfer packet: "); + g_prefix_error_literal(error, "failed to write finish transfer packet: "); return FALSE; } fu_progress_step_done(progress); @@ -1080,6 +1170,8 @@ /* * image file pushed. Device validates and uploads new image on inactive partition. * Restart sync cb, to get the update progress + * Flush any kUpdateStateDownloading progress events, accumulated while busy sending + * firmware image. Peripheral device needs higher delay * Normally status changes as follows: * While image being pushed: kUpdateStateCurrent->kUpdateStateDownloading (~5minutes) * After image push is complete: kUpdateStateDownloading->kUpdateStateReady @@ -1088,10 +1180,15 @@ * Upload finished: kUpdateStateUpdating->kUpdateStateCurrent (~5minutes) * After upload is finished, device reboots itself */ + if (fu_device_has_private_flag(device, + FU_LOGITECH_BULKCONTROLLER_DEVICE_FLAG_PHERIPHERAL_UPDATE)) + self->is_sync_flush_events_in_progress = FALSE; + else + self->is_sync_flush_events_in_progress = TRUE; if (!fu_device_retry_full(device, fu_logitech_bulkcontroller_device_verify_cb, - 500, /* over 10 minutes */ - 2500, /* ms */ + 5000, /* over 10 minutes */ + (self->is_sync_flush_events_in_progress) ? 5 : 250, /* ms */ fu_progress_get_child(progress), error)) return FALSE; @@ -1105,12 +1202,24 @@ fu_progress_step_done(progress); /* success! */ - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + if (fu_device_has_private_flag(device, + FU_LOGITECH_BULKCONTROLLER_DEVICE_FLAG_PHERIPHERAL_UPDATE)) { + /* skip replug if updating child. parent waits till child reboot with new firmware + */ + g_debug("child firmware updated"); + fu_device_remove_private_flag( + device, + FU_LOGITECH_BULKCONTROLLER_DEVICE_FLAG_PHERIPHERAL_UPDATE); + } else { + fu_device_set_remove_delay(FU_DEVICE(self), + 10 * 60 * 1000); /* >1 min to finish init */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } return TRUE; } static gboolean -fu_logitech_bulkcontroller_device_set_time_cb(FuDevice *device, gpointer user_data, GError **error) +fu_logitech_bulkcontroller_device_send_time_cb(FuDevice *device, gpointer user_data, GError **error) { FuLogitechBulkcontrollerDevice *self = FU_LOGITECH_BULKCONTROLLER_DEVICE(device); FuLogitechBulkcontrollerProtoId proto_id = kProtoId_UnknownId; @@ -1121,8 +1230,7 @@ /* send SetDeviceTimeRequest to sync device clock with host */ device_request = - fu_logitech_bulkcontroller_proto_manager_generate_set_device_time_request(device, - error); + fu_logitech_bulkcontroller_proto_manager_generate_set_device_time_request(self, error); if (device_request == NULL) return FALSE; buf = fu_logitech_bulkcontroller_device_sync_write(self, device_request, error); @@ -1133,7 +1241,7 @@ &proto_id, error); if (decoded_pkt == NULL) { - g_prefix_error(error, "failed to unpack packet: "); + g_prefix_error_literal(error, "failed to unpack packet: "); return FALSE; } bufstr = fu_strsafe((const gchar *)decoded_pkt->data, decoded_pkt->len); @@ -1154,10 +1262,10 @@ } static gboolean -fu_logitech_bulkcontroller_device_set_time(FuLogitechBulkcontrollerDevice *self, GError **error) +fu_logitech_bulkcontroller_device_send_time(FuLogitechBulkcontrollerDevice *self, GError **error) { return fu_device_retry(FU_DEVICE(self), - fu_logitech_bulkcontroller_device_set_time_cb, + fu_logitech_bulkcontroller_device_send_time_cb, MAX_SETUP_RETRIES, NULL, error); @@ -1175,7 +1283,7 @@ g_autoptr(GByteArray) decoded_pkt = NULL; req = fu_logitech_bulkcontroller_proto_manager_generate_transition_to_device_mode_request( - device); + self); res = fu_logitech_bulkcontroller_device_sync_write(self, req, error); if (res == NULL) return FALSE; @@ -1185,7 +1293,7 @@ &proto_id, error); if (decoded_pkt == NULL) { - g_prefix_error(error, "failed to unpack packet: "); + g_prefix_error_literal(error, "failed to unpack packet: "); return FALSE; } g_debug("received transition mode response: id: %u, length %u", proto_id, res->len); @@ -1239,7 +1347,10 @@ } /* failed */ - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "got valid data, so keep going"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "got valid data, so keep going"); return FALSE; } @@ -1265,14 +1376,16 @@ self, FU_LOGITECH_BULKCONTROLLER_CMD_CHECK_BUFFERSIZE, NULL, /* data */ + BULK_TRANSFER_TIMEOUT, error)) { - g_prefix_error(error, "failed to send request: "); + g_prefix_error_literal(error, "failed to send request: "); return FALSE; } buf = fu_logitech_bulkcontroller_device_sync_wait_cmd_retry( self, FU_LOGITECH_BULKCONTROLLER_CMD_CHECK_BUFFERSIZE, 0x0, /* always zero */ + BULK_TRANSFER_TIMEOUT, &error_local); if (buf != NULL) { self->transfer_bufsz = 16 * 1024; @@ -1300,13 +1413,13 @@ /* FuUsbDevice->setup */ if (!FU_DEVICE_CLASS(fu_logitech_bulkcontroller_device_parent_class) ->setup(device, error)) { - g_prefix_error(error, "failed to FuUsbDevice->setup: "); + g_prefix_error_literal(error, "failed to FuUsbDevice->setup: "); return FALSE; } /* empty the queue */ if (!fu_logitech_bulkcontroller_device_clear_queue(self, error)) { - g_prefix_error(error, "failed to clear queue: "); + g_prefix_error_literal(error, "failed to clear queue: "); return FALSE; } @@ -1314,26 +1427,26 @@ if (fu_device_has_private_flag(device, FU_LOGITECH_BULKCONTROLLER_DEVICE_FLAG_CHECK_BUFFER_SIZE)) { if (!fu_logitech_bulkcontroller_device_check_buffer_size(self, error)) { - g_prefix_error(error, "failed to check buffer size: "); + g_prefix_error_literal(error, "failed to check buffer size: "); return FALSE; } } /* device supports modes of Device (supported), Appliance and BYOD (both unsupported) */ if (!fu_logitech_bulkcontroller_device_transition_to_device_mode(self, error)) { - g_prefix_error(error, "failed to transition to device_mode: "); + g_prefix_error_literal(error, "failed to transition to device_mode: "); return FALSE; } /* set device time */ - if (!fu_logitech_bulkcontroller_device_set_time(self, error)) { - g_prefix_error(error, "failed to set time: "); + if (!fu_logitech_bulkcontroller_device_send_time(self, error)) { + g_prefix_error_literal(error, "failed to set time: "); return FALSE; } /* load current device data */ if (!fu_logitech_bulkcontroller_device_ensure_info(self, TRUE, error)) { - g_prefix_error(error, "failed to ensure info: "); + g_prefix_error_literal(error, "failed to ensure info: "); return FALSE; } @@ -1342,7 +1455,7 @@ } static void -fu_logitech_bulkcontroller_device_set_progress(FuDevice *self, FuProgress *progress) +fu_logitech_bulkcontroller_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -1361,15 +1474,15 @@ fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_INSTALL_PARENT_FIRST); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_RETRY_OPEN); fu_usb_device_set_claim_retry_count(FU_USB_DEVICE(self), 100); fu_device_retry_set_delay(FU_DEVICE(self), 1000); - fu_device_set_remove_delay(FU_DEVICE(self), 10 * 60 * 1000); /* >1 min to finish init */ fu_device_register_private_flag(FU_DEVICE(self), FU_LOGITECH_BULKCONTROLLER_DEVICE_FLAG_CHECK_BUFFER_SIZE); fu_device_register_private_flag(FU_DEVICE(self), FU_LOGITECH_BULKCONTROLLER_DEVICE_FLAG_POST_INSTALL); + fu_device_register_private_flag(FU_DEVICE(self), + FU_LOGITECH_BULKCONTROLLER_DEVICE_FLAG_PHERIPHERAL_UPDATE); /* these are unrecoverable */ fu_device_retry_add_recovery(FU_DEVICE(self), FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, NULL); diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-plugin.c fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-plugin.c --- fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -65,6 +65,7 @@ fu_logitech_bulkcontroller_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_set_device_gtype_default(plugin, FU_TYPE_LOGITECH_BULKCONTROLLER_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_LOGITECH_BULKCONTROLLER_CHILD); /* coverage */ } diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller.rs fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller.rs --- fwupd-2.0.8/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/fu-logitech-bulkcontroller.rs 2026-02-26 11:36:18.000000000 +0000 @@ -65,7 +65,7 @@ payload_length: u32le, } -#[derive(Getters)] +#[derive(Parse)] #[repr(C, packed)] struct FuStructLogitechBulkcontrollerUpdateRes { cmd: FuLogitechBulkcontrollerCmd, diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/logitech-bulkcontroller.quirk fwupd-2.0.20/plugins/logitech-bulkcontroller/logitech-bulkcontroller.quirk --- fwupd-2.0.8/plugins/logitech-bulkcontroller/logitech-bulkcontroller.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/logitech-bulkcontroller.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -20,8 +20,3 @@ # Sight [LOGI\MODEL_VU0067] InstallDuration = 1500 - -# Sight as Standalone device -[USB\VID_046D&PID_087A] -Plugin = logitech_bulkcontroller -InstallDuration = 1500 diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/meson.build fwupd-2.0.20/plugins/logitech-bulkcontroller/meson.build --- fwupd-2.0.8/plugins/logitech-bulkcontroller/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if protobufc.found() and protoc.found() +protobufc.found() or subdir_done() +protoc.found() or subdir_done() cargs = ['-DG_LOG_DOMAIN="FuPluginLogitechBulkController"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -23,11 +24,7 @@ enumeration_data += files( 'tests/logi-rally-bar-setup.json', - 'tests/logi-sight-setup.json', ) device_tests += files( 'tests/logi-rally-bar.json', - 'tests/logi-sight.json', ) - -endif diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/tests/logi-rally-bar.json fwupd-2.0.20/plugins/logitech-bulkcontroller/tests/logi-rally-bar.json --- fwupd-2.0.8/plugins/logitech-bulkcontroller/tests/logi-rally-bar.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/tests/logi-rally-bar.json 2026-02-26 11:36:18.000000000 +0000 @@ -10,13 +10,6 @@ "guids": [ "9608c52f-e60f-597d-a813-84c6f2177a89" ] - }, - { - "name": "Sight", - "version": "0.5.0", - "guids": [ - "79ca7ba2-bb52-5c6e-968c-94a006482562" - ] } ] } diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/tests/logi-sight-setup.json fwupd-2.0.20/plugins/logitech-bulkcontroller/tests/logi-sight-setup.json --- fwupd-2.0.8/plugins/logitech-bulkcontroller/tests/logi-sight-setup.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/tests/logi-sight-setup.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,306 +0,0 @@ -{ - "UsbDevices": [ - { - "GType": "FuUsbDevice", - "PlatformId": "1-6", - "Created": "2024-11-13T10:54:38.026097Z", - "IdVendor": 1133, - "IdProduct": 2170, - "Device": 547, - "USB": 528, - "Manufacturer": 1, - "DeviceClass": 239, - "DeviceSubClass": 2, - "DeviceProtocol": 1, - "Product": 2, - "SerialNumber": 3, - "UsbBosDescriptors": [ - { - "DevCapabilityType": 42, - "ExtraData": "AAM=" - }, - { - "DevCapabilityType": 2, - "ExtraData": "AAAAAA==" - }, - { - "DevCapabilityType": 3, - "ExtraData": "AA8AAQH0AQ==" - }, - { - "DevCapabilityType": 10, - "ExtraData": "AAEAAAAAEQAAMEAKALBACgA=" - } - ], - "UsbConfigDescriptors": [ - { - "Configuration": 4, - "ConfigurationValue": 1 - } - ], - "UsbInterfaces": [ - { - "Length": 9, - "DescriptorType": 4, - "InterfaceClass": 14, - "InterfaceSubClass": 1, - "Interface": 5, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 129, - "Interval": 8, - "MaxPacketSize": 16 - } - ] - }, - { - "Length": 9, - "DescriptorType": 4, - "InterfaceNumber": 1, - "InterfaceClass": 14, - "InterfaceSubClass": 2, - "Interface": 6 - }, - { - "Length": 9, - "DescriptorType": 4, - "InterfaceNumber": 1, - "AlternateSetting": 1, - "InterfaceClass": 14, - "InterfaceSubClass": 2, - "Interface": 6, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 130, - "Interval": 1, - "MaxPacketSize": 1024 - } - ] - }, - { - "Length": 9, - "DescriptorType": 4, - "InterfaceNumber": 1, - "AlternateSetting": 2, - "InterfaceClass": 14, - "InterfaceSubClass": 2, - "Interface": 6, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 130, - "Interval": 1, - "MaxPacketSize": 5120 - } - ] - }, - { - "Length": 9, - "DescriptorType": 4, - "InterfaceNumber": 2, - "InterfaceClass": 255, - "InterfaceSubClass": 117, - "InterfaceProtocol": 1, - "Interface": 8, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 1, - "MaxPacketSize": 512 - }, - { - "DescriptorType": 5, - "EndpointAddress": 131, - "MaxPacketSize": 512 - } - ] - }, - { - "Length": 9, - "DescriptorType": 4, - "InterfaceNumber": 3, - "InterfaceClass": 255, - "InterfaceSubClass": 118, - "InterfaceProtocol": 1, - "Interface": 9, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 2, - "MaxPacketSize": 512 - }, - { - "DescriptorType": 5, - "EndpointAddress": 132, - "MaxPacketSize": 512 - } - ] - }, - { - "Length": 9, - "DescriptorType": 4, - "InterfaceNumber": 4, - "InterfaceClass": 3, - "Interface": 10, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 133, - "Interval": 4, - "MaxPacketSize": 64 - } - ] - }, - { - "Length": 9, - "DescriptorType": 4, - "InterfaceNumber": 5, - "InterfaceClass": 255, - "InterfaceSubClass": 66, - "InterfaceProtocol": 1, - "Interface": 12, - "UsbEndpoints": [ - { - "DescriptorType": 5, - "EndpointAddress": 3, - "MaxPacketSize": 512 - }, - { - "DescriptorType": 5, - "EndpointAddress": 134, - "MaxPacketSize": 512 - } - ] - } - ], - "UsbEvents": [ - { - "Id": "#4693935e", - "Data": "001" - }, - { - "Id": "#bddbca22", - "Data": "MAJOR=189\nMINOR=101\nDEVNAME=bus/usb/001/102\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=46d/87a/223\nTYPE=239/2/1\nBUSNUM=001\nDEVNUM=102" - }, - { - "Id": "#1ab3ae0a", - "Data": "102" - }, - { - "Id": "#1fcf122d", - "Data": "TG9naSBTaWdodAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" - }, - { - "Id": "#028c3a0e", - "Data": "MjMyNUxaNTFYWTU4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" - }, - { - "Id": "#45324a3b", - "Error": -7 - }, - { - "Id": "#084996a0", - "Data": "B8wAAAkAAAABAAAACgMSATAaAioA" - }, - { - "Id": "#45324a3b", - "Data": "Af8AAAUAAAABAAAANTIyMzE=" - }, - { - "Id": "#0d787287", - "Data": "CMwAAAAAAAACAAAA" - }, - { - "Id": "#45324a3b", - "Data": "BswAAEgAAAAAAAAACkAKJDlmNmU1MjkzLTBiM2MtNDZiMC05NWQ0LTIyYzRhZTQ5ZmQ5OBIYMjAyNC0xMS0xM1QxMDo1NDozOC41MDhaIgQqAggB" - }, - { - "Id": "#45324a3b", - "Data": "Af8AAAUAAAACAAAANTIyMzI=" - }, - { - "Id": "#d61f7af4", - "Data": "Af8AAAQAAAADAAAABswAAA==" - }, - { - "Id": "#45324a3b", - "Data": "CMwAAAAAAAA=" - }, - { - "Id": "#a280e80a", - "Data": "Af8AAAQAAAAEAAAACMwAAA==" - }, - { - "Id": "#5091e4b7", - "Data": "B8wAABgAAAAFAAAACgMSATAaEVIPEg1FdXJvcGUvTG9uZG9u" - }, - { - "Id": "#45324a3b", - "Data": "Af8AAAUAAAAFAAAANTIyMzE=" - }, - { - "Id": "#9ec27604", - "Data": "CMwAAAAAAAAGAAAA" - }, - { - "Id": "#45324a3b", - "Data": "BswAAEYAAAAAAAAACkAKJDdmYzlkZjE1LWEwOWQtNDA1My1hZmJjLWEwNGQ5Y2I3YWY0ZhIYMjAyNC0xMS0xM1QxMDo1NDozOC41MTNaEgIQAQ==" - }, - { - "Id": "#45324a3b", - "Data": "Af8AAAUAAAAGAAAANTIyMzI=" - }, - { - "Id": "#f9b0be46", - "Data": "Af8AAAQAAAAHAAAABswAAA==" - }, - { - "Id": "#45324a3b", - "Data": "CMwAAAAAAAA=" - }, - { - "Id": "#ea4acbcb", - "Data": "Af8AAAQAAAAIAAAACMwAAA==" - }, - { - "Id": "#0bdae261", - "Data": "B8wAAAkAAAAJAAAACgMSATAaAhIA" - }, - { - "Id": "#45324a3b", - "Data": "Af8AAAUAAAAJAAAANTIyMzE=" - }, - { - "Id": "#e7e001c1", - "Data": "CMwAAAAAAAAKAAAA" - }, - { - "Id": "#45324a3b", - "Data": "Af8AAAUAAAAKAAAANTIyMzI=" - }, - { - "Id": "#45324a3b", - "Data": "BswAAP0DAAAAAAAACkAKJDZjNTc1ZGE2LTNmNzgtNDQ5ZS04NzEzLWQzMjQzMmEyYzU3ZBIYMjAyNC0xMS0xM1QxMDo1NDozOC41MThaIrgHErUHCrIHeyJpZCI6Ijc4OTg5OTQxLTU0MTItNDlmMy05MGVmLTE5MDJjMDAxMjc3MSIsInRzIjoiMTczMTQ5NTI3ODUyMSIsInR6T2Zmc2V0IjowLCJ0eXBlIjoyLCJwYXlsb2FkIjp7ImRldmljZXMiOlt7ImF1ZGlvU2V0dGluZ3MiOnsiZGVSZXZlcmJNb2RlIjoxLCJtaWNFUSI6MSwibm9pc2VSZWR1Y3Rpb24iOjEsInNwZWFrZXJCb29zdCI6MSwic3BlYWtlckVRIjoxLCJ0cyI6MTczMTQ5NTI3ODUxOH0sImF2IjoiMC45MDEuNTAiLCJib290U3RhdHVzIjowLCJidCI6eyJvbiI6MCwidHMiOjE3MzE0OTUyNzg1MTh9LCJidWlsZFR5cGUiOiJ1c2VyIiwiZGlzcGxheXMiOltdLCJodyI6Im10ODE5NS40LjAiLCJpcCI6IiIsImtleXNUeXBlIjoicmVsZWFzZS1rZXlzIiwibWFrZSI6IkxvZ2l0ZWNoIiwibWFuaWZlc3QiOiIwLjAuMCIsIm1vZGVsIjoiVlUwMDY3IiwibmFtZSI6IlNpZ2h0Iiwib3MiOiJMb2dpIENvbGxhYk9TIiwib3N2IjoiMC45MDEuNTAiLCJwcm9jIjoiYXJtNjQtdjhhIiwicmFtIjoiMy4wIiwicmVib290Ijp7InJlYXNvbiI6ImNvbGQsY2hhcmdlciIsInRzIjoxNjg0ODc2NzUxNDUwfSwicmVnaW9uYWxTZXR0aW5ncyI6eyJ0cyI6IjE2ODQ4NzY3NTgzMTIifSwicmlnaHRTaWdodCI6eyJncm91cEZyYW1pbmdTcGVlZCI6MSwibW9kZSI6MCwib24iOjAsInBpcCI6dHJ1ZSwic3BlYWtlckRldGVjdGlvblNwZWVkIjoxLCJzcGVha2VyRnJhbWluZ1NwZWVkIjoxLCJzdGF0dXMiOjEsInRyYWNraW5nTW9kZSI6MCwidHMiOjE2ODQ4NzY3NTA2NDcsInZlcnNpb24iOjZ9LCJyb29tUGVvcGxlQ291bnQiOiIwIiwic2VyaWFsIjoiMjMyNUxaNTFYWTU4Iiwic3RhdHVzIjowLCJzdyI6IjAuNS4wIiwidHlwZSI6IlNlbnRpbmVsIiwidWlkIjoiMDI6MDA6MDA6MDA6MDA6MDAiLCJ1cGRhdGVTdGF0dXMiOjAsInZpZCI6IjB4MDQ2ZCIsInZ2IjoiMC45MDEuNTAifV19fQ==" - }, - { - "Id": "#fd4a2ae1", - "Data": "Af8AAAQAAAALAAAABswAAA==" - }, - { - "Id": "#45324a3b", - "Data": "CMwAAAAAAAA=" - }, - { - "Id": "#6dadfaf2", - "Data": "Af8AAAQAAAAMAAAACMwAAA==" - }, - { - "Id": "#0f6d472e", - "Error": -1 - } - ] - } - ] -} diff -Nru fwupd-2.0.8/plugins/logitech-bulkcontroller/tests/logi-sight.json fwupd-2.0.20/plugins/logitech-bulkcontroller/tests/logi-sight.json --- fwupd-2.0.8/plugins/logitech-bulkcontroller/tests/logi-sight.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-bulkcontroller/tests/logi-sight.json 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -{ - "name": "Logi Sight (standalone)", - "interactive": false, - "steps": [ - { - "emulation-file": "@enumeration_datadir@/logi-sight-setup.json", - "components": [ - { - "version": "0.5.0", - "guids": [ - "8064915d-8c91-5f1e-a7a9-cd95f6538bfe" - ] - } - ] - } - ] -} diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-nordic.c fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-nordic.c --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-nordic.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-nordic.c 2026-02-26 11:36:18.000000000 +0000 @@ -28,7 +28,7 @@ fu_logitech_hidpp_bootloader_request_new(); req->cmd = FU_LOGITECH_HIDPP_BOOTLOADER_CMD_GET_HW_PLATFORM_ID; if (!fu_logitech_hidpp_bootloader_request(self, req, error)) { - g_prefix_error(error, "failed to get HW ID: "); + g_prefix_error_literal(error, "failed to get HW ID: "); return NULL; } return g_strndup((const gchar *)req->data, req->len); @@ -43,7 +43,7 @@ fu_logitech_hidpp_bootloader_request_new(); req->cmd = FU_LOGITECH_HIDPP_BOOTLOADER_CMD_GET_FW_VERSION; if (!fu_logitech_hidpp_bootloader_request(self, req, error)) { - g_prefix_error(error, "failed to get firmware version: "); + g_prefix_error_literal(error, "failed to get firmware version: "); return NULL; } diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-texas.c fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-texas.c --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-texas.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader-texas.c 2026-02-26 11:36:18.000000000 +0000 @@ -27,7 +27,7 @@ req->len = 0x01; /* magic number */ req->data[0] = 0x00; /* magic number */ if (!fu_logitech_hidpp_bootloader_request(self, req, error)) { - g_prefix_error(error, "failed to erase all pages: "); + g_prefix_error_literal(error, "failed to erase all pages: "); return FALSE; } return TRUE; @@ -43,7 +43,7 @@ req->len = 0x01; /* magic number */ req->data[0] = 0x03; /* magic number */ if (!fu_logitech_hidpp_bootloader_request(self, req, error)) { - g_prefix_error(error, "failed to compute and test CRC: "); + g_prefix_error_literal(error, "failed to compute and test CRC: "); return FALSE; } if (req->cmd == FU_LOGITECH_HIDPP_BOOTLOADER_CMD_FLASH_RAM_WRONG_CRC) { diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader.c fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader.c --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-bootloader.c 2026-02-26 11:36:18.000000000 +0000 @@ -103,11 +103,11 @@ if (!fu_firmware_strparse_uint16_safe(tmp, linesz, 0x09, &offset, error)) return NULL; if (offset != 0x0000) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "extended linear addresses with offset different from " - "0 are not supported"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "extended linear addresses with offset " + "different from 0 are not supported"); return NULL; } continue; @@ -216,7 +216,7 @@ fu_logitech_hidpp_bootloader_request_new(); req->cmd = FU_LOGITECH_HIDPP_BOOTLOADER_CMD_REBOOT; if (!fu_logitech_hidpp_bootloader_request(self, req, error)) { - g_prefix_error(error, "failed to attach back to runtime: "); + g_prefix_error_literal(error, "failed to attach back to runtime: "); return FALSE; } fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); @@ -224,7 +224,7 @@ } static gboolean -fu_logitech_hidpp_bootloader_set_bl_version(FuLogitechHidppBootloader *self, GError **error) +fu_logitech_hidpp_bootloader_ensure_bl_version(FuLogitechHidppBootloader *self, GError **error) { guint16 build; guint8 major; @@ -236,7 +236,7 @@ /* call into hardware */ req->cmd = FU_LOGITECH_HIDPP_BOOTLOADER_CMD_GET_BL_VERSION; if (!fu_logitech_hidpp_bootloader_request(self, req, error)) { - g_prefix_error(error, "failed to get firmware version: "); + g_prefix_error_literal(error, "failed to get firmware version: "); return FALSE; } @@ -248,7 +248,10 @@ minor = fu_logitech_hidpp_buffer_read_uint8((const gchar *)req->data + 6); version = fu_logitech_hidpp_format_version("BOT", major, minor, build); if (version == NULL) { - g_prefix_error(error, "failed to format firmware version: "); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to format firmware version"); return FALSE; } fu_device_set_version_bootloader(FU_DEVICE(self), version); @@ -278,7 +281,7 @@ /* get memory map */ req->cmd = FU_LOGITECH_HIDPP_BOOTLOADER_CMD_GET_MEMINFO; if (!fu_logitech_hidpp_bootloader_request(self, req, error)) { - g_prefix_error(error, "failed to get meminfo: "); + g_prefix_error_literal(error, "failed to get meminfo: "); return FALSE; } if (req->len != 0x06) { @@ -296,7 +299,7 @@ priv->flash_blocksize = fu_memread_uint16(req->data + 4, G_BIG_ENDIAN); /* get bootloader version */ - return fu_logitech_hidpp_bootloader_set_bl_version(self, error); + return fu_logitech_hidpp_bootloader_ensure_bl_version(self, error); } gboolean @@ -305,11 +308,10 @@ GError **error) { gsize actual_length = 0; - guint8 buf_request[32]; - guint8 buf_response[32]; + guint8 buf_request[32] = {0}; + guint8 buf_response[32] = {0}; /* build packet */ - memset(buf_request, 0x00, sizeof(buf_request)); buf_request[0x00] = req->cmd; buf_request[0x01] = req->addr >> 8; buf_request[0x02] = req->addr & 0xff; @@ -333,7 +335,7 @@ FU_LOGITECH_HIDPP_DEVICE_TIMEOUT_MS, FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to send data: "); + g_prefix_error_literal(error, "failed to send data: "); return FALSE; } @@ -356,7 +358,6 @@ } /* get response */ - memset(buf_response, 0x00, sizeof(buf_response)); if (!fu_usb_device_interrupt_transfer(FU_USB_DEVICE(self), FU_LOGITECH_HIDPP_DEVICE_EP1, buf_response, @@ -365,7 +366,7 @@ FU_LOGITECH_HIDPP_DEVICE_TIMEOUT_MS, NULL, error)) { - g_prefix_error(error, "failed to get data: "); + g_prefix_error_literal(error, "failed to get data: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, "device->host", buf_response, actual_length); @@ -410,7 +411,7 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); - fu_device_add_icon(FU_DEVICE(self), "preferences-desktop-keyboard"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_USB_RECEIVER); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); fu_device_set_name(FU_DEVICE(self), "Unifying Receiver"); fu_device_set_summary(FU_DEVICE(self), "Miniaturised USB wireless receiver (bootloader)"); diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-common.h fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-common.h --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -21,10 +21,10 @@ #define FU_LOGITECH_HIDPP_DEVICE_TIMEOUT_MS 30000 /* Polling intervals (ms) */ -#define FU_HIDPP_DEVICE_POLLING_INTERVAL 30000 -#define FU_HIDPP_RECEIVER_RUNTIME_POLLING_INTERVAL 5000 +#define FU_LOGITECH_HIDPP_DEVICE_POLLING_INTERVAL 30000 +#define FU_LOGITECH_HIDPP_RECEIVER_RUNTIME_POLLING_INTERVAL 5000 -#define FU_HIDPP_VERSION_BLE 0xFE +#define FU_LOGITECH_HIDPP_VERSION_BLE 0xFE guint8 fu_logitech_hidpp_buffer_read_uint8(const gchar *str); diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-device.c fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-device.c --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,8 +10,10 @@ #include "fu-logitech-hidpp-device.h" #include "fu-logitech-hidpp-hidpp.h" #include "fu-logitech-hidpp-radio.h" +#include "fu-logitech-hidpp-rdfu-struct.h" #include "fu-logitech-hidpp-runtime-bolt.h" #include "fu-logitech-hidpp-struct.h" +#include "fu-logitech-rdfu-firmware.h" typedef struct { guint8 cached_fw_entity; @@ -25,6 +27,11 @@ guint8 hidpp_version; gchar *model_id; GPtrArray *feature_index; /* of FuLogitechHidppHidppMap */ + FuLogitechHidppRdfuState rdfu_state; + guint8 rdfu_capabilities; + guint16 rdfu_block; + guint32 rdfu_pkt; + guint32 rdfu_wait; } FuLogitechHidppDevicePrivate; typedef struct { @@ -32,27 +39,31 @@ guint16 feature; } FuLogitechHidppHidppMap; +#define FU_LOGITECH_HIDPP_DEVICE_DATA_PKT_LONG 16 +/* max attempts to resume after non-critical errors */ +#define FU_LOGITECH_HIDPP_DEVICE_RDFU_MAX_RETRIES 10 + G_DEFINE_TYPE_WITH_PRIVATE(FuLogitechHidppDevice, fu_logitech_hidpp_device, FU_TYPE_UDEV_DEVICE) #define GET_PRIVATE(o) (fu_logitech_hidpp_device_get_instance_private(o)) typedef enum { - FU_HIDPP_DEVICE_KIND_KEYBOARD, - FU_HIDPP_DEVICE_KIND_REMOTE_CONTROL, - FU_HIDPP_DEVICE_KIND_NUMPAD, - FU_HIDPP_DEVICE_KIND_MOUSE, - FU_HIDPP_DEVICE_KIND_TOUCHPAD, - FU_HIDPP_DEVICE_KIND_TRACKBALL, - FU_HIDPP_DEVICE_KIND_PRESENTER, - FU_HIDPP_DEVICE_KIND_RECEIVER, - FU_HIDPP_DEVICE_KIND_LAST + FU_LOGITECH_HIDPP_DEVICE_KIND_KEYBOARD, + FU_LOGITECH_HIDPP_DEVICE_KIND_REMOTE_CONTROL, + FU_LOGITECH_HIDPP_DEVICE_KIND_NUMPAD, + FU_LOGITECH_HIDPP_DEVICE_KIND_MOUSE, + FU_LOGITECH_HIDPP_DEVICE_KIND_TOUCHPAD, + FU_LOGITECH_HIDPP_DEVICE_KIND_TRACKBALL, + FU_LOGITECH_HIDPP_DEVICE_KIND_PRESENTER, + FU_LOGITECH_HIDPP_DEVICE_KIND_RECEIVER, + FU_LOGITECH_HIDPP_DEVICE_KIND_LAST } FuLogitechHidppDeviceKind; void fu_logitech_hidpp_device_set_device_idx(FuLogitechHidppDevice *self, guint8 device_idx) { FuLogitechHidppDevicePrivate *priv; - g_return_if_fail(FU_IS_HIDPP_DEVICE(self)); + g_return_if_fail(FU_IS_LOGITECH_HIDPP_DEVICE(self)); priv = GET_PRIVATE(self); priv->device_idx = device_idx; } @@ -61,7 +72,7 @@ fu_logitech_hidpp_device_get_hidpp_pid(FuLogitechHidppDevice *self) { FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FU_IS_HIDPP_DEVICE(self), G_MAXUINT16); + g_return_val_if_fail(FU_IS_LOGITECH_HIDPP_DEVICE(self), G_MAXUINT16); return priv->hidpp_pid; } @@ -69,7 +80,7 @@ fu_logitech_hidpp_device_set_hidpp_pid(FuLogitechHidppDevice *self, guint16 hidpp_pid) { FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); - g_return_if_fail(FU_IS_HIDPP_DEVICE(self)); + g_return_if_fail(FU_IS_LOGITECH_HIDPP_DEVICE(self)); priv->hidpp_pid = hidpp_pid; } @@ -77,7 +88,7 @@ fu_logitech_hidpp_device_set_model_id(FuLogitechHidppDevice *self, const gchar *model_id) { FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); - g_return_if_fail(FU_IS_HIDPP_DEVICE(self)); + g_return_if_fail(FU_IS_LOGITECH_HIDPP_DEVICE(self)); if (g_strcmp0(priv->model_id, model_id) == 0) return; g_free(priv->model_id); @@ -87,43 +98,43 @@ static const gchar * fu_logitech_hidpp_device_get_icon(FuLogitechHidppDeviceKind kind) { - if (kind == FU_HIDPP_DEVICE_KIND_KEYBOARD) - return "input-keyboard"; - if (kind == FU_HIDPP_DEVICE_KIND_REMOTE_CONTROL) - return "pda"; // ish - if (kind == FU_HIDPP_DEVICE_KIND_NUMPAD) - return "input-dialpad"; - if (kind == FU_HIDPP_DEVICE_KIND_MOUSE) - return "input-mouse"; - if (kind == FU_HIDPP_DEVICE_KIND_TOUCHPAD) - return "input-touchpad"; - if (kind == FU_HIDPP_DEVICE_KIND_TRACKBALL) - return "input-mouse"; // ish - if (kind == FU_HIDPP_DEVICE_KIND_PRESENTER) - return "pda"; // ish - if (kind == FU_HIDPP_DEVICE_KIND_RECEIVER) - return "preferences-desktop-keyboard"; + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_KEYBOARD) + return FU_DEVICE_ICON_INPUT_KEYBOARD; + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_REMOTE_CONTROL) + return FU_DEVICE_ICON_PDA; /* ish */ + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_NUMPAD) + return FU_DEVICE_ICON_INPUT_DIALPAD; + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_MOUSE) + return FU_DEVICE_ICON_INPUT_MOUSE; + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_TOUCHPAD) + return FU_DEVICE_ICON_INPUT_TOUCHPAD; + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_TRACKBALL) + return FU_DEVICE_ICON_INPUT_MOUSE; /* ish */ + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_PRESENTER) + return FU_DEVICE_ICON_PDA; /* ish */ + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_RECEIVER) + return FU_DEVICE_ICON_USB_RECEIVER; return NULL; } static const gchar * fu_logitech_hidpp_device_get_summary(FuLogitechHidppDeviceKind kind) { - if (kind == FU_HIDPP_DEVICE_KIND_KEYBOARD) + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_KEYBOARD) return "Unifying Keyboard"; - if (kind == FU_HIDPP_DEVICE_KIND_REMOTE_CONTROL) + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_REMOTE_CONTROL) return "Unifying Remote Control"; - if (kind == FU_HIDPP_DEVICE_KIND_NUMPAD) + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_NUMPAD) return "Unifying Number Pad"; - if (kind == FU_HIDPP_DEVICE_KIND_MOUSE) + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_MOUSE) return "Unifying Mouse"; - if (kind == FU_HIDPP_DEVICE_KIND_TOUCHPAD) + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_TOUCHPAD) return "Unifying Touchpad"; - if (kind == FU_HIDPP_DEVICE_KIND_TRACKBALL) + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_TRACKBALL) return "Unifying Trackball"; - if (kind == FU_HIDPP_DEVICE_KIND_PRESENTER) + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_PRESENTER) return "Unifying Presenter"; - if (kind == FU_HIDPP_DEVICE_KIND_RECEIVER) + if (kind == FU_LOGITECH_HIDPP_DEVICE_KIND_RECEIVER) return "Unifying Receiver"; return NULL; } @@ -133,19 +144,19 @@ { FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); g_autoptr(GError) error_local = NULL; - g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(FuLogitechHidppHidppMsg) st_req = fu_logitech_hidpp_msg_new(); GPtrArray *children = NULL; /* handle failure */ - msg->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; - msg->device_id = priv->device_idx; - msg->sub_id = 0x00; /* rootIndex */ - msg->function_id = 0x01 << 4; /* ping */ - msg->data[0] = 0x00; - msg->data[1] = 0x00; - msg->data[2] = 0xaa; /* user-selected value */ - msg->hidpp_version = priv->hidpp_version; - if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, &error_local)) { + st_req->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; + st_req->device_id = priv->device_idx; + st_req->sub_id = 0x00; /* rootIndex */ + st_req->function_id = 0x01 << 4; /* ping */ + st_req->data[0] = 0x00; + st_req->data[1] = 0x00; + st_req->data[2] = 0xaa; /* user-selected value */ + st_req->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), st_req, &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { priv->hidpp_version = 1; return TRUE; @@ -168,15 +179,15 @@ /* if the device index is unset, grab it from the reply */ if (priv->device_idx == FU_LOGITECH_HIDPP_DEVICE_IDX_WIRED && - msg->device_id != FU_LOGITECH_HIDPP_DEVICE_IDX_WIRED) { - priv->device_idx = msg->device_id; + st_req->device_id != FU_LOGITECH_HIDPP_DEVICE_IDX_WIRED) { + priv->device_idx = st_req->device_id; g_debug("device index is %02x", priv->device_idx); } /* format version in BCD format */ - if (priv->hidpp_version != FU_HIDPP_VERSION_BLE) { - /* minor version is in msg->data[1] */; - priv->hidpp_version = msg->data[0]; + if (priv->hidpp_version != FU_LOGITECH_HIDPP_VERSION_BLE) { + /* minor version is in st_req->data[1] */; + priv->hidpp_version = st_req->data[0]; } /* success */ @@ -186,22 +197,22 @@ static gboolean fu_logitech_hidpp_device_poll(FuDevice *device, GError **error) { - FuLogitechHidppDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidppDevice *self = FU_LOGITECH_HIDPP_DEVICE(device); FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); const guint timeout = 1; /* ms */ g_autoptr(GError) error_local = NULL; - g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(FuLogitechHidppHidppMsg) st_req = fu_logitech_hidpp_msg_new(); g_autoptr(FuDeviceLocker) locker = NULL; /* open */ - locker = fu_device_locker_new(self, error); + locker = fu_device_locker_new(device, error); if (locker == NULL) return FALSE; /* flush pending data */ - msg->device_id = priv->device_idx; - msg->hidpp_version = priv->hidpp_version; - if (!fu_logitech_hidpp_receive(FU_UDEV_DEVICE(self), msg, timeout, &error_local)) { + st_req->device_id = priv->device_idx; + st_req->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_receive(FU_UDEV_DEVICE(self), st_req, timeout, &error_local)) { if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT)) { g_warning("failed to get pending read: %s", error_local->message); return TRUE; @@ -244,7 +255,7 @@ static void fu_logitech_hidpp_device_to_string(FuDevice *device, guint idt, GString *str) { - FuLogitechHidppDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidppDevice *self = FU_LOGITECH_HIDPP_DEVICE(device); FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); fwupd_codec_string_append_int(str, idt, "HidppVersion", priv->hidpp_version); fwupd_codec_string_append_int(str, idt, "HidppPid", priv->hidpp_pid); @@ -332,9 +343,9 @@ static gboolean fu_logitech_hidpp_device_fetch_firmware_info(FuLogitechHidppDevice *self, GError **error) { + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); guint8 idx; guint8 entity_count; - FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); g_autoptr(FuLogitechHidppHidppMsg) msg_count = fu_logitech_hidpp_msg_new(); gboolean radio_ok = FALSE; gboolean app_ok = FALSE; @@ -352,7 +363,7 @@ msg_count->function_id = 0x00 << 4; /* getCount */ msg_count->hidpp_version = priv->hidpp_version; if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg_count, error)) { - g_prefix_error(error, "failed to get firmware count: "); + g_prefix_error_literal(error, "failed to get firmware count: "); return FALSE; } entity_count = msg_count->data[0]; @@ -363,47 +374,48 @@ guint16 build; g_autofree gchar *version = NULL; g_autofree gchar *name = NULL; - g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(FuLogitechHidppHidppMsg) st_req = fu_logitech_hidpp_msg_new(); - msg->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; - msg->device_id = priv->device_idx; - msg->sub_id = idx; - msg->function_id = 0x01 << 4; /* getInfo */ - msg->hidpp_version = priv->hidpp_version; - msg->data[0] = i; - if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error)) { - g_prefix_error(error, "failed to get firmware info: "); + st_req->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; + st_req->device_id = priv->device_idx; + st_req->sub_id = idx; + st_req->function_id = 0x01 << 4; /* getInfo */ + st_req->hidpp_version = priv->hidpp_version; + st_req->data[0] = i; + if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), st_req, error)) { + g_prefix_error_literal(error, "failed to get firmware info: "); return FALSE; } /* use the single available slot, otherwise -- the slot which is not active */ - if (msg->data[0] == 0) { - if (!app_ok || FU_BIT_IS_CLEAR(msg->data[8], 0)) + if (st_req->data[0] == 0) { + if (!app_ok || FU_BIT_IS_CLEAR(st_req->data[8], 0)) priv->cached_fw_entity = i; app_ok = TRUE; } - if (msg->data[1] == 0x00 && msg->data[2] == 0x00 && msg->data[3] == 0x00 && - msg->data[4] == 0x00 && msg->data[5] == 0x00 && msg->data[6] == 0x00 && - msg->data[7] == 0x00) { + if (st_req->data[1] == 0x00 && st_req->data[2] == 0x00 && st_req->data[3] == 0x00 && + st_req->data[4] == 0x00 && st_req->data[5] == 0x00 && st_req->data[6] == 0x00 && + st_req->data[7] == 0x00) { g_debug("no version set for entity %u", i); continue; } - name = g_strdup_printf("%c%c%c", msg->data[1], msg->data[2], msg->data[3]); - build = ((guint16)msg->data[6]) << 8 | msg->data[7]; - version = fu_logitech_hidpp_format_version(name, msg->data[4], msg->data[5], build); + name = g_strdup_printf("%c%c%c", st_req->data[1], st_req->data[2], st_req->data[3]); + build = ((guint16)st_req->data[6]) << 8 | st_req->data[7]; + version = + fu_logitech_hidpp_format_version(name, st_req->data[4], st_req->data[5], build); g_debug("firmware entity 0x%02x version is %s", i, version); - if (msg->data[0] == 0) { + if (st_req->data[0] == 0) { /* set version from the active entity */ - if (FU_BIT_IS_SET(msg->data[8], 0)) + if (FU_BIT_IS_SET(st_req->data[8], 0)) fu_device_set_version(FU_DEVICE(self), version); - } else if (msg->data[0] == 1) { + } else if (st_req->data[0] == 1) { fu_device_set_version_bootloader(FU_DEVICE(self), version); - } else if (msg->data[0] == 2) { + } else if (st_req->data[0] == 2) { fu_device_set_metadata(FU_DEVICE(self), "version-hw", version); - } else if (msg->data[0] == 5 && + } else if (st_req->data[0] == 5 && fu_device_has_private_flag(FU_DEVICE(self), FU_LOGITECH_HIDPP_DEVICE_FLAG_ADD_RADIO)) { if (!fu_logitech_hidpp_device_create_radio_child(self, i, build, error)) { - g_prefix_error(error, "failed to create radio: "); + g_prefix_error_literal(error, "failed to create radio: "); return FALSE; } radio_ok = TRUE; @@ -412,10 +424,11 @@ /* the device is probably in bootloader mode and the last SoftDevice FW upgrade failed */ if (fu_device_has_private_flag(FU_DEVICE(self), FU_LOGITECH_HIDPP_DEVICE_FLAG_ADD_RADIO) && + !fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_RDFU) && !radio_ok) { g_debug("no radio found, creating a fake one for recovery"); if (!fu_logitech_hidpp_device_create_radio_child(self, 1, 0, error)) { - g_prefix_error(error, "failed to create radio: "); + g_prefix_error_literal(error, "failed to create radio: "); return FALSE; } } @@ -430,7 +443,7 @@ FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); guint8 idx; guint64 pid_tmp = 0; - g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); + g_autoptr(FuLogitechHidppHidppMsg) st_req = fu_logitech_hidpp_msg_new(); g_autoptr(GString) str = g_string_new(NULL); /* get the (optional) feature index */ @@ -439,25 +452,25 @@ if (idx == 0x00) return TRUE; - msg->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; - msg->device_id = priv->device_idx; - msg->sub_id = idx; - msg->function_id = 0x00 << 4; /* getDeviceInfo */ - msg->hidpp_version = priv->hidpp_version; - if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error)) { - g_prefix_error(error, "failed to get the model ID: "); + st_req->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; + st_req->device_id = priv->device_idx; + st_req->sub_id = idx; + st_req->function_id = 0x00 << 4; /* getDeviceInfo */ + st_req->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), st_req, error)) { + g_prefix_error_literal(error, "failed to get the model ID: "); return FALSE; } /* ignore extendedModelID in data[13] */ for (guint i = 7; i < 13; i++) - g_string_append_printf(str, "%02X", msg->data[i]); + g_string_append_printf(str, "%02X", st_req->data[i]); fu_logitech_hidpp_device_set_model_id(self, str->str); /* truncate to 4 chars and convert to a PID */ g_string_set_size(str, 4); if (!fu_strtoull(str->str, &pid_tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_16, error)) { - g_prefix_error(error, "failed to parse the model ID: "); + g_prefix_error_literal(error, "failed to parse the model ID: "); return FALSE; } fu_device_set_pid(FU_DEVICE(self), (guint32)pid_tmp); @@ -483,27 +496,27 @@ FU_LOGITECH_HIDPP_FEATURE_UNIFIED_BATTERY); if (idx != 0x00) { gboolean socc = FALSE; /* state of charge capability */ - g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); - msg->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; - msg->device_id = priv->device_idx; - msg->sub_id = idx; - msg->function_id = 0x00 << 4; /* get_capabilities */ - msg->hidpp_version = priv->hidpp_version; - if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error)) { - g_prefix_error(error, "failed to get battery info: "); + g_autoptr(FuLogitechHidppHidppMsg) st_req = fu_logitech_hidpp_msg_new(); + st_req->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; + st_req->device_id = priv->device_idx; + st_req->sub_id = idx; + st_req->function_id = 0x00 << 4; /* get_capabilities */ + st_req->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), st_req, error)) { + g_prefix_error_literal(error, "failed to get battery info: "); return FALSE; } - if (msg->data[1] & 0x02) + if (st_req->data[1] & 0x02) socc = TRUE; - msg->function_id = 0x01 << 4; /* get_status */ - if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error)) { - g_prefix_error(error, "failed to get battery info: "); + st_req->function_id = 0x01 << 4; /* get_status */ + if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), st_req, error)) { + g_prefix_error_literal(error, "failed to get battery info: "); return FALSE; } if (socc) { - fu_device_set_battery_level(FU_DEVICE(self), msg->data[0]); + fu_device_set_battery_level(FU_DEVICE(self), st_req->data[0]); } else { - switch (msg->data[1]) { + switch (st_req->data[1]) { case 1: /* critical */ fu_device_set_battery_level(FU_DEVICE(self), 5); break; @@ -517,7 +530,7 @@ fu_device_set_battery_level(FU_DEVICE(self), 90); break; default: - g_warning("unknown battery level: 0x%02x", msg->data[1]); + g_warning("unknown battery level: 0x%02x", st_req->data[1]); break; } } @@ -529,42 +542,42 @@ self, FU_LOGITECH_HIDPP_FEATURE_BATTERY_LEVEL_STATUS); if (idx != 0x00) { - g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); - msg->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; - msg->device_id = priv->device_idx; - msg->sub_id = idx; - msg->function_id = 0x00 << 4; /* GetBatteryLevelStatus */ - msg->hidpp_version = priv->hidpp_version; - if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error)) { - g_prefix_error(error, "failed to get battery info: "); + g_autoptr(FuLogitechHidppHidppMsg) st_req = fu_logitech_hidpp_msg_new(); + st_req->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; + st_req->device_id = priv->device_idx; + st_req->sub_id = idx; + st_req->function_id = 0x00 << 4; /* GetBatteryLevelStatus */ + st_req->hidpp_version = priv->hidpp_version; + if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), st_req, error)) { + g_prefix_error_literal(error, "failed to get battery info: "); return FALSE; } - if (msg->data[0] != 0x00) - fu_device_set_battery_level(FU_DEVICE(self), msg->data[0]); + if (st_req->data[0] != 0x00) + fu_device_set_battery_level(FU_DEVICE(self), st_req->data[0]); return TRUE; } } /* try HID++1.0 battery mileage */ if (priv->hidpp_version == 1) { - g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); - msg->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; - msg->device_id = priv->device_idx; - msg->sub_id = FU_LOGITECH_HIDPP_SUBID_GET_REGISTER; - msg->function_id = FU_LOGITECH_HIDPP_REGISTER_BATTERY_MILEAGE << 4; - msg->hidpp_version = priv->hidpp_version; - if (fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, NULL)) { - if (msg->data[0] != 0x7F) - fu_device_set_battery_level(FU_DEVICE(self), msg->data[0]); + g_autoptr(FuLogitechHidppHidppMsg) st_req = fu_logitech_hidpp_msg_new(); + st_req->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; + st_req->device_id = priv->device_idx; + st_req->sub_id = FU_LOGITECH_HIDPP_SUBID_GET_REGISTER; + st_req->function_id = FU_LOGITECH_HIDPP_REGISTER_BATTERY_MILEAGE << 4; + st_req->hidpp_version = priv->hidpp_version; + if (fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), st_req, NULL)) { + if (st_req->data[0] != 0x7F) + fu_device_set_battery_level(FU_DEVICE(self), st_req->data[0]); else - g_warning("unknown battery level: 0x%02x", msg->data[0]); + g_warning("unknown battery level: 0x%02x", st_req->data[0]); return TRUE; } /* try HID++1.0 battery status instead */ - msg->function_id = FU_LOGITECH_HIDPP_REGISTER_BATTERY_STATUS << 4; - if (fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, NULL)) { - switch (msg->data[0]) { + st_req->function_id = FU_LOGITECH_HIDPP_REGISTER_BATTERY_STATUS << 4; + if (fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), st_req, NULL)) { + switch (st_req->data[0]) { case 1: /* 0 - 10 */ fu_device_set_battery_level(FU_DEVICE(self), 5); break; @@ -578,7 +591,7 @@ fu_device_set_battery_level(FU_DEVICE(self), 90); break; default: - g_warning("unknown battery percentage: 0x%02x", msg->data[0]); + g_warning("unknown battery percentage: 0x%02x", st_req->data[0]); break; } return TRUE; @@ -589,6 +602,527 @@ return TRUE; } +/* wrapper function to reuse the pre-rustgen communication */ +static gboolean +fu_logitech_hidpp_device_transfer_msg(FuLogitechHidppDevice *self, + GByteArray *st_req, + GError **error) +{ + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); + FuLogitechHidppHidppMsg *hidpp_msg = NULL; + + g_return_val_if_fail(st_req != NULL, FALSE); + + /* enlarge the size since fu_logitech_hidpp_transfer() returns the answer in the + * same message */ + fu_byte_array_set_size(st_req, sizeof(FuLogitechHidppHidppMsg), 0); + + hidpp_msg = (FuLogitechHidppHidppMsg *)st_req->data; + hidpp_msg->hidpp_version = priv->hidpp_version; + + if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), hidpp_msg, error)) + return FALSE; + + /* validate and cleanup the function_id from Application ID */ + if ((hidpp_msg->function_id & 0x0f) != FU_LOGITECH_HIDPP_HIDPP_MSG_SW_ID) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "expected application ID = %i, got %u", + FU_LOGITECH_HIDPP_HIDPP_MSG_SW_ID, + (guint)(hidpp_msg->function_id & 0x0f)); + return FALSE; + } + hidpp_msg->function_id &= 0xf0; + + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_rdfu_get_capabilities(FuLogitechHidppDevice *self, GError **error) +{ + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); + guint8 idx; + g_autoptr(FuStructLogitechHidppRdfuGetCapabilities) st_req = + fu_struct_logitech_hidpp_rdfu_get_capabilities_new(); + g_autoptr(FuStructLogitechHidppRdfuCapabilities) st_res = NULL; + + priv->rdfu_capabilities = 0; /* set empty */ + + /* get the feature index */ + idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_RDFU); + if (idx == 0x00) + return TRUE; + + fu_struct_logitech_hidpp_rdfu_get_capabilities_set_device_id(st_req, priv->device_idx); + fu_struct_logitech_hidpp_rdfu_get_capabilities_set_sub_id(st_req, idx); + + g_debug("read capabilities"); + if (!fu_logitech_hidpp_device_transfer_msg(self, st_req->buf, error)) { + g_prefix_error_literal(error, "failed to get capabilities: "); + return FALSE; + } + + st_res = fu_struct_logitech_hidpp_rdfu_capabilities_parse(st_req->buf->data, + st_req->buf->len, + 0, + error); + if (st_res == NULL) + return FALSE; + priv->rdfu_capabilities = + fu_struct_logitech_hidpp_rdfu_capabilities_get_capabilities(st_res); + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_rdfu_start_dfu(FuLogitechHidppDevice *self, + GByteArray *magic, + GError **error) +{ + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); + guint8 idx; + guint8 status; + g_autoptr(FuStructLogitechHidppRdfuStartDfu) st_req = + fu_struct_logitech_hidpp_rdfu_start_dfu_new(); + g_autoptr(FuStructLogitechHidppRdfuStartDfuResponse) st_res = NULL; + + idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_RDFU); + if (idx == 0x00) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "RDFU feature is required for startDfu"); + return FALSE; + } + + fu_struct_logitech_hidpp_rdfu_start_dfu_set_device_id(st_req, priv->device_idx); + fu_struct_logitech_hidpp_rdfu_start_dfu_set_sub_id(st_req, idx); + fu_struct_logitech_hidpp_rdfu_start_dfu_set_fw_entity(st_req, priv->cached_fw_entity); + if (!fu_struct_logitech_hidpp_rdfu_start_dfu_set_magic(st_req, + magic->data, + magic->len, + error)) + return FALSE; + + g_debug("startDfu"); + if (!fu_logitech_hidpp_device_transfer_msg(self, st_req->buf, error)) { + g_prefix_error_literal(error, "startDfu failed: "); + return FALSE; + } + + st_res = fu_struct_logitech_hidpp_rdfu_start_dfu_response_parse(st_req->buf->data, + st_req->buf->len, + 0, + error); + if (st_res == NULL) + return FALSE; + status = fu_struct_logitech_hidpp_rdfu_start_dfu_response_get_status_code(st_res); + if (status != FU_LOGITECH_HIDPP_RDFU_RESPONSE_CODE_DATA_TRANSFER_READY) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR, + "unexpected status 0x%x = %s", + status, + fu_logitech_hidpp_rdfu_response_code_to_string(status)); + + return FALSE; + } + + priv->rdfu_state = FU_LOGITECH_HIDPP_RDFU_STATE_TRANSFER; + + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_rdfu_check_status(FuLogitechHidppDevice *self, + FuStructLogitechHidppRdfuResponse *st_res, + GError **error); + +static void +fu_logitech_hidpp_device_rdfu_set_state(FuLogitechHidppDevice *self, FuLogitechHidppRdfuState state) +{ + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); + priv->rdfu_state = state; + priv->rdfu_block = 0; + priv->rdfu_pkt = 0; +} + +static gboolean +fu_logitech_hidpp_device_rdfu_status_data_transfer_ready(FuLogitechHidppDevice *self, + FuStructLogitechHidppRdfuResponse *st_res, + GError **error) +{ + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); + guint16 block; + g_autoptr(FuStructLogitechHidppRdfuDataTransferReady) st_params = NULL; + + priv->rdfu_state = FU_LOGITECH_HIDPP_RDFU_STATE_TRANSFER; + st_params = fu_struct_logitech_hidpp_rdfu_data_transfer_ready_parse( + st_res->buf->data, + st_res->buf->len, + FU_STRUCT_LOGITECH_HIDPP_RDFU_RESPONSE_OFFSET_STATUS_CODE, + error); + if (st_params == NULL) + return FALSE; + + block = fu_struct_logitech_hidpp_rdfu_data_transfer_ready_get_block_id(st_params); + if (block != 0 && block <= priv->rdfu_block) { + /* additional protection from misbehaviored devices */ + fu_logitech_hidpp_device_rdfu_set_state(self, + FU_LOGITECH_HIDPP_RDFU_STATE_RESUME_DFU); + return TRUE; + } + + priv->rdfu_block = block; + priv->rdfu_pkt = 0; + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_rdfu_status_data_transfer_wait(FuLogitechHidppDevice *self, + FuStructLogitechHidppRdfuResponse *st_res, + GError **error) +{ + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); + guint retry = 0; + g_autoptr(FuStructLogitechHidppRdfuDataTransferWait) st_params = NULL; + + st_params = fu_struct_logitech_hidpp_rdfu_data_transfer_wait_parse( + st_res->buf->data, + st_res->buf->len, + FU_STRUCT_LOGITECH_HIDPP_RDFU_RESPONSE_OFFSET_STATUS_CODE, + error); + if (st_params == NULL) + return FALSE; + + /* set the delay to wait the next event */ + priv->rdfu_wait = fu_struct_logitech_hidpp_rdfu_data_transfer_wait_get_delay_ms(st_params); + + /* if we already in waiting loop just leave to avoid recursion */ + if (priv->rdfu_state == FU_LOGITECH_HIDPP_RDFU_STATE_WAIT) + return TRUE; + + priv->rdfu_state = FU_LOGITECH_HIDPP_RDFU_STATE_WAIT; + + while (priv->rdfu_state == FU_LOGITECH_HIDPP_RDFU_STATE_WAIT) { + g_autoptr(FuStructLogitechHidppRdfuResponse) st_wait_res = NULL; + g_autoptr(FuLogitechHidppHidppMsg) msg_in_wait = fu_logitech_hidpp_msg_new(); + g_autoptr(GError) error_local = NULL; + + /* wait for requested time or event */ + /* NB: waiting longer than `rdfu_wait` breaks the specification; + * unfortunately, some early firmware versions require this. */ + if (!fu_logitech_hidpp_receive(FU_UDEV_DEVICE(self), + msg_in_wait, + priv->rdfu_wait * 3, /* be tolerant */ + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT)) { + g_debug("ignored error: %s", error_local->message); + /* let's try to reset with getDfuStatus */ + fu_logitech_hidpp_device_rdfu_set_state( + self, + FU_LOGITECH_HIDPP_RDFU_STATE_RESUME_DFU); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + + st_wait_res = fu_struct_logitech_hidpp_rdfu_response_parse( + (guint8 *)msg_in_wait, + fu_logitech_hidpp_msg_get_payload_length(msg_in_wait), + 0, + error); + if (st_wait_res == NULL) + return FALSE; + + /* check the message and set the new delay if requested additional wait */ + if (!fu_logitech_hidpp_device_rdfu_check_status(self, st_wait_res, error)) + return FALSE; + + /* too lot attempts in a raw, let's fail everything */ + if (retry++ > FU_LOGITECH_HIDPP_DEVICE_RDFU_MAX_RETRIES) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "too lot of wait requests in a raw"); + return FALSE; + } + } + + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_rdfu_status_pkt_ack(FuLogitechHidppDevice *self, + FuStructLogitechHidppRdfuResponse *st_res, + GError **error) +{ + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); + guint32 pkt; + g_autoptr(FuStructLogitechHidppRdfuDfuTransferPktAck) st_params = NULL; + + priv->rdfu_state = FU_LOGITECH_HIDPP_RDFU_STATE_TRANSFER; + st_params = fu_struct_logitech_hidpp_rdfu_dfu_transfer_pkt_ack_parse( + st_res->buf->data, + st_res->buf->len, + FU_STRUCT_LOGITECH_HIDPP_RDFU_RESPONSE_OFFSET_STATUS_CODE, + error); + if (st_params == NULL) + return FALSE; + + pkt = fu_struct_logitech_hidpp_rdfu_dfu_transfer_pkt_ack_get_pkt_number(st_params); + /* expecting monotonic increase */ + if (pkt != priv->rdfu_pkt + 1) { + /* additional protection from misbehaviored devices */ + if (pkt <= priv->rdfu_pkt) { + fu_logitech_hidpp_device_rdfu_set_state( + self, + FU_LOGITECH_HIDPP_RDFU_STATE_NOT_STARTED); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "expecting ack %u for block %u, got %u", + priv->rdfu_pkt + 1, + priv->rdfu_block, + pkt); + return FALSE; + } + /* probably skipped the ACK, try to resume */ + fu_logitech_hidpp_device_rdfu_set_state(self, + FU_LOGITECH_HIDPP_RDFU_STATE_RESUME_DFU); + return TRUE; + } + + priv->rdfu_pkt = pkt; + + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_rdfu_check_status(FuLogitechHidppDevice *self, + FuStructLogitechHidppRdfuResponse *st_res, + GError **error) +{ + guint8 status = fu_struct_logitech_hidpp_rdfu_response_get_status_code(st_res); + + switch (status) { + case FU_LOGITECH_HIDPP_RDFU_RESPONSE_CODE_DFU_NOT_STARTED: + fu_logitech_hidpp_device_rdfu_set_state(self, + FU_LOGITECH_HIDPP_RDFU_STATE_NOT_STARTED); + break; + case FU_LOGITECH_HIDPP_RDFU_RESPONSE_CODE_DATA_TRANSFER_READY: + /* ready for transfer, next block requested */ + if (!fu_logitech_hidpp_device_rdfu_status_data_transfer_ready(self, st_res, error)) + return FALSE; + break; + case FU_LOGITECH_HIDPP_RDFU_RESPONSE_CODE_DATA_TRANSFER_WAIT: + /* device requested to wait */ + if (!fu_logitech_hidpp_device_rdfu_status_data_transfer_wait(self, st_res, error)) + return FALSE; + break; + case FU_LOGITECH_HIDPP_RDFU_RESPONSE_CODE_DFU_TRANSFER_PKT_ACK: + /* ok to transfer next packet */ + if (!fu_logitech_hidpp_device_rdfu_status_pkt_ack(self, st_res, error)) + return FALSE; + break; + case FU_LOGITECH_HIDPP_RDFU_RESPONSE_CODE_DFU_TRANSFER_COMPLETE: + /* success! Apply and reboot */ + fu_logitech_hidpp_device_rdfu_set_state(self, FU_LOGITECH_HIDPP_RDFU_STATE_APPLY); + break; + case FU_LOGITECH_HIDPP_RDFU_RESPONSE_CODE_INVALID_BLOCK: + case FU_LOGITECH_HIDPP_RDFU_RESPONSE_CODE_DFU_STATE_ERROR: + /* let's try to reset with getDfuStatus */ + fu_logitech_hidpp_device_rdfu_set_state(self, + FU_LOGITECH_HIDPP_RDFU_STATE_RESUME_DFU); + break; + case FU_LOGITECH_HIDPP_RDFU_RESPONSE_CODE_DFU_APPLY_PENDING: + /* device is already waiting to apply the unknown deferred update. + * Let's restart the update.*/ + default: + /* reset state */ + fu_logitech_hidpp_device_rdfu_set_state(self, + FU_LOGITECH_HIDPP_RDFU_STATE_NOT_STARTED); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "unexpected status 0x%x (%s)", + status, + fu_logitech_hidpp_rdfu_response_code_to_string(status)); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_rdfu_get_dfu_status(FuLogitechHidppDevice *self, GError **error) +{ + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); + guint8 idx; + g_autoptr(FuStructLogitechHidppRdfuGetDfuStatus) st_req = + fu_struct_logitech_hidpp_rdfu_get_dfu_status_new(); + g_autoptr(FuStructLogitechHidppRdfuResponse) st_res = NULL; + + idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_RDFU); + if (idx == 0x00) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "RDFU feature is required for getDfuStatus"); + return FALSE; + } + + fu_struct_logitech_hidpp_rdfu_get_dfu_status_set_device_id(st_req, priv->device_idx); + fu_struct_logitech_hidpp_rdfu_get_dfu_status_set_sub_id(st_req, idx); + fu_struct_logitech_hidpp_rdfu_get_dfu_status_set_fw_entity(st_req, priv->cached_fw_entity); + + g_debug("getDfuStatus for entity %u", priv->cached_fw_entity); + if (!fu_logitech_hidpp_device_transfer_msg(self, st_req->buf, error)) { + g_prefix_error_literal(error, "getDfuStatus failed: "); + return FALSE; + } + + st_res = fu_struct_logitech_hidpp_rdfu_response_parse(st_req->buf->data, + st_req->buf->len, + 0, + error); + if (st_res == NULL) + return FALSE; + + return fu_logitech_hidpp_device_rdfu_check_status(self, st_res, error); +} + +static gboolean +fu_logitech_hidpp_device_rdfu_apply_dfu(FuLogitechHidppDevice *self, + guint8 fw_entity, + GError **error) +{ + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); + guint8 idx; + guint8 flags = FU_LOGITECH_HIDPP_RDFU_APPLY_FLAG_FORCE_DFU_BIT; + g_autoptr(FuStructLogitechHidppRdfuApplyDfu) st_req = + fu_struct_logitech_hidpp_rdfu_apply_dfu_new(); + FuLogitechHidppHidppMsg *hidpp_msg = NULL; + + idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_RDFU); + if (idx == 0x00) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "RDFU feature is required for startDfu"); + return FALSE; + } + + if (priv->rdfu_state != FU_LOGITECH_HIDPP_RDFU_STATE_APPLY) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unable to apply the update"); + return FALSE; + } + + g_debug("applyDfu for entity %u", fw_entity); + + fu_struct_logitech_hidpp_rdfu_apply_dfu_set_device_id(st_req, priv->device_idx); + fu_struct_logitech_hidpp_rdfu_apply_dfu_set_sub_id(st_req, idx); + fu_struct_logitech_hidpp_rdfu_apply_dfu_set_fw_entity(st_req, fw_entity); + fu_struct_logitech_hidpp_rdfu_apply_dfu_set_flags(st_req, flags); + + /* reuse pre-rustgen send */ + hidpp_msg = (FuLogitechHidppHidppMsg *)st_req->buf->data; + hidpp_msg->hidpp_version = priv->hidpp_version; + /* don't expect the reply for forced applyDfu */ + if (!fu_logitech_hidpp_send(FU_UDEV_DEVICE(self), + hidpp_msg, + FU_LOGITECH_HIDPP_DEVICE_TIMEOUT_MS, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_rdfu_transfer_pkt(FuLogitechHidppDevice *self, + const guint8 *data, + const gsize datasz, + GError **error) +{ + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); + guint8 idx; + g_autoptr(FuStructLogitechHidppRdfuTransferDfuData) st_req = + fu_struct_logitech_hidpp_rdfu_transfer_dfu_data_new(); + g_autoptr(FuStructLogitechHidppRdfuResponse) st_res = NULL; + + idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_RDFU); + if (idx == 0x00) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "RDFU feature is required for startDfu"); + return FALSE; + } + + fu_struct_logitech_hidpp_rdfu_transfer_dfu_data_set_device_id(st_req, priv->device_idx); + fu_struct_logitech_hidpp_rdfu_transfer_dfu_data_set_sub_id(st_req, idx); + if (!fu_struct_logitech_hidpp_rdfu_transfer_dfu_data_set_data(st_req, data, datasz, error)) + return FALSE; + + g_debug("transferDfuData"); + if (!fu_logitech_hidpp_device_transfer_msg(self, st_req->buf, error)) { + g_prefix_error_literal(error, "transferDfuData failed: "); + return FALSE; + } + + st_res = fu_struct_logitech_hidpp_rdfu_response_parse(st_req->buf->data, + st_req->buf->len, + 0, + error); + if (st_res == NULL) + return FALSE; + + return fu_logitech_hidpp_device_rdfu_check_status(self, st_res, error); +} + +static gboolean +fu_logitech_hidpp_device_rdfu_transfer_data(FuLogitechHidppDevice *self, + GPtrArray *blocks, + GError **error) +{ + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); + const GByteArray *block = NULL; + g_autofree guint8 *data = g_malloc0(FU_LOGITECH_HIDPP_DEVICE_DATA_PKT_LONG); + + g_debug("send: block=%u, pkt=%04x", priv->rdfu_block, priv->rdfu_pkt); + + if (blocks->len < priv->rdfu_block) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "requested invalid block %u", + priv->rdfu_block); + return FALSE; + } + block = (GByteArray *)blocks->pdata[priv->rdfu_block]; + + if (!fu_memcpy_safe(data, + FU_LOGITECH_HIDPP_DEVICE_DATA_PKT_LONG, + 0, + block->data, + block->len, + priv->rdfu_pkt * FU_LOGITECH_HIDPP_DEVICE_DATA_PKT_LONG, + FU_LOGITECH_HIDPP_DEVICE_DATA_PKT_LONG, + error)) { + return FALSE; + } + + return fu_logitech_hidpp_device_rdfu_transfer_pkt(self, + data, + FU_LOGITECH_HIDPP_DEVICE_DATA_PKT_LONG, + error); +} + static gboolean fu_logitech_hidpp_device_feature_search(FuLogitechHidppDevice *self, guint16 feature, @@ -641,7 +1175,7 @@ static gboolean fu_logitech_hidpp_device_probe(FuDevice *device, GError **error) { - FuLogitechHidppDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidppDevice *self = FU_LOGITECH_HIDPP_DEVICE(device); FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); /* nearly... */ @@ -676,9 +1210,16 @@ } static gboolean +fu_logitech_hidpp_device_write_firmware_pkt(FuLogitechHidppDevice *self, + guint8 idx, + guint8 cmd, + const guint8 *data, + GError **error); + +static gboolean fu_logitech_hidpp_device_setup(FuDevice *device, GError **error) { - FuLogitechHidppDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidppDevice *self = FU_LOGITECH_HIDPP_DEVICE(device); FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); guint8 idx; const guint16 map_features[] = {FU_LOGITECH_HIDPP_FEATURE_GET_DEVICE_NAME_TYPE, @@ -689,10 +1230,11 @@ FU_LOGITECH_HIDPP_FEATURE_DFU_CONTROL_SIGNED, FU_LOGITECH_HIDPP_FEATURE_DFU_CONTROL_BOLT, FU_LOGITECH_HIDPP_FEATURE_DFU, + FU_LOGITECH_HIDPP_FEATURE_RDFU, FU_LOGITECH_HIDPP_FEATURE_ROOT}; if (fu_device_has_private_flag(device, FU_LOGITECH_HIDPP_DEVICE_FLAG_BLE)) { - priv->hidpp_version = FU_HIDPP_VERSION_BLE; + priv->hidpp_version = FU_LOGITECH_HIDPP_VERSION_BLE; priv->device_idx = FU_LOGITECH_HIDPP_DEVICE_IDX_RECEIVER; /* * BLE devices might not be ready for ping right after @@ -752,7 +1294,7 @@ msg->function_id = 0x02 << 4; /* getDeviceType */ msg->hidpp_version = priv->hidpp_version; if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error)) { - g_prefix_error(error, "failed to get device type: "); + g_prefix_error_literal(error, "failed to get device type: "); return FALSE; } @@ -786,7 +1328,7 @@ msg->function_id = 0x00 << 4; /* getDfuStatus */ msg->hidpp_version = priv->hidpp_version; if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error)) { - g_prefix_error(error, "failed to get DFU status: "); + g_prefix_error_literal(error, "failed to get DFU status: "); return FALSE; } if ((msg->data[2] & 0x01) > 0) { @@ -821,19 +1363,35 @@ if (!fu_logitech_hidpp_device_fetch_battery_level(self, error)) return FALSE; + idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_RDFU); + if (idx != 0x00) { + /* get RDFU capabilities */ + if (!fu_logitech_hidpp_device_rdfu_get_capabilities(self, error)) + return FALSE; + fu_device_add_protocol(FU_DEVICE(self), "com.logitech.rdfu"); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_LOGITECH_RDFU_FIRMWARE); + fu_device_add_flag(FU_DEVICE(device), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + } /* poll for pings to track active state */ - fu_device_set_poll_interval(device, FU_HIDPP_DEVICE_POLLING_INTERVAL); + fu_device_set_poll_interval(device, FU_LOGITECH_HIDPP_DEVICE_POLLING_INTERVAL); return TRUE; } static gboolean fu_logitech_hidpp_device_detach(FuDevice *device, FuProgress *progress, GError **error) { - FuLogitechHidppDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidppDevice *self = FU_LOGITECH_HIDPP_DEVICE(device); FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); guint8 idx; g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); + idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_RDFU); + if (idx != 0x00) { + g_debug("RDFU supported, no need to switch to bootloader mode"); + return TRUE; + } + /* sanity check */ if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { g_debug("already in bootloader mode, skipping"); @@ -881,7 +1439,7 @@ fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); /* so we detect off then on */ - parent = fu_device_get_parent(device); + parent = fu_device_get_parent(device, NULL); if (parent != NULL) fu_device_set_poll_interval(parent, 500); @@ -925,7 +1483,7 @@ msg->data[6] = 'U'; msg->flags = FU_LOGITECH_HIDPP_HIDPP_MSG_FLAG_IGNORE_SUB_ID; if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error)) { - g_prefix_error(error, "failed to put device into DFU mode: "); + g_prefix_error_literal(error, "failed to put device into DFU mode: "); return FALSE; } fu_device_sleep(device, 200); /* ms */ @@ -1027,7 +1585,7 @@ error)) return FALSE; if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error)) { - g_prefix_error(error, "failed to supply program data: "); + g_prefix_error_literal(error, "failed to supply program data: "); return FALSE; } @@ -1064,7 +1622,7 @@ } return TRUE; } - g_debug("got wrong packet, continue to wait..."); + g_debug("got wrong packet, continue to wait…"); } /* nothing in the queue */ @@ -1076,13 +1634,12 @@ } static gboolean -fu_logitech_hidpp_device_write_firmware(FuDevice *device, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) +fu_logitech_hidpp_device_write_firmware_dfu(FuLogitechHidppDevice *self, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) { - FuLogitechHidppDevice *self = FU_HIDPP_DEVICE(device); FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); gsize sz = 0; const guint8 *data; @@ -1093,10 +1650,10 @@ /* if we're in bootloader mode, we should be able to get this feature */ idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_DFU); if (idx == 0x00) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no DFU feature available"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no DFU feature available"); return FALSE; } @@ -1135,6 +1692,146 @@ } static gboolean +fu_logitech_hidpp_device_write_firmware_rdfu(FuLogitechHidppDevice *self, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); + FuLogitechRdfuFirmware *entity_fw = NULL; + guint8 idx; + guint retry = 0; + g_autoptr(GByteArray) magic = NULL; + g_autoptr(GPtrArray) blocks = NULL; + g_autoptr(GError) error_local = NULL; + g_autofree gchar *entity_id = g_strdup_printf("%u", priv->cached_fw_entity); + g_autofree gchar *model_id = NULL; + + idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_RDFU); + if (idx == 0x00) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no RDFU feature available"); + return FALSE; + } + + entity_fw = + (FuLogitechRdfuFirmware *)fu_firmware_get_image_by_id(firmware, entity_id, error); + if (entity_fw == NULL) + return FALSE; + + model_id = fu_logitech_rdfu_firmware_get_model_id(entity_fw, error); + if (model_id == NULL) + return FALSE; + + if (g_strcmp0(priv->model_id, model_id) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware for model %s, but the target is %s", + model_id, + priv->model_id); + return FALSE; + } + + magic = fu_logitech_rdfu_firmware_get_magic(entity_fw, error); + if (magic == NULL) + return FALSE; + + blocks = fu_logitech_rdfu_firmware_get_blocks(entity_fw, error); + if (blocks == NULL) + return FALSE; + + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + fu_progress_set_id(progress, G_STRLOC); + + /* check if we in update mode already */ + if (!fu_logitech_hidpp_device_rdfu_get_dfu_status(self, &error_local)) { + if (error_local->message != NULL) + g_debug("forcing startDFU, reason %s", error_local->message); + /* try to drop the inner state at device */ + fu_logitech_hidpp_device_rdfu_set_state(self, + FU_LOGITECH_HIDPP_RDFU_STATE_NOT_STARTED); + } + /* device requested to start or restart for some reason */ + if (priv->rdfu_state == FU_LOGITECH_HIDPP_RDFU_STATE_NOT_STARTED) { + if (!fu_logitech_hidpp_device_rdfu_start_dfu(self, magic, error)) + return FALSE; + } + + while (priv->rdfu_state == FU_LOGITECH_HIDPP_RDFU_STATE_TRANSFER) { + /* update progress-bar here to avoid jumps caused dfu-transfer-complete */ + fu_progress_set_percentage_full(progress, priv->rdfu_block, blocks->len); + + /* send packet and wait for reply */ + if (!fu_logitech_hidpp_device_rdfu_transfer_data(self, blocks, error)) + return FALSE; + + /* additional protection from misbehaviored devices */ + if (priv->rdfu_state != FU_LOGITECH_HIDPP_RDFU_STATE_TRANSFER) { + if (!fu_logitech_hidpp_device_rdfu_get_dfu_status(self, error)) + return FALSE; + + /* too many soft restarts, let's fail everything */ + if (retry++ > FU_LOGITECH_HIDPP_DEVICE_RDFU_MAX_RETRIES) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "too lot recover attempts"); + return FALSE; + } + } + } + + g_debug("RDFU supported, applying the update"); + if (!fu_logitech_hidpp_device_rdfu_apply_dfu(self, priv->cached_fw_entity, error)) + return FALSE; + + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + return TRUE; +} + +static gboolean +fu_logitech_hidpp_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuLogitechHidppDevice *self = FU_LOGITECH_HIDPP_DEVICE(device); + guint8 idx; + + /* device should support either RDFU or DFU mode */ + idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_RDFU); + if (idx != 0x00) { + return fu_logitech_hidpp_device_write_firmware_rdfu(self, + firmware, + progress, + flags, + error); + } + + /* if we're in bootloader mode, we should be able to get this feature */ + idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_DFU); + if (idx != 0x00) { + return fu_logitech_hidpp_device_write_firmware_dfu(self, + firmware, + progress, + flags, + error); + } + + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no DFU or RDFU feature available"); + return FALSE; +} + +static gboolean fu_logitech_hidpp_device_reprobe_cb(FuDevice *device, gpointer user_data, GError **error) { return fu_logitech_hidpp_device_setup(device, error); @@ -1152,41 +1849,45 @@ g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); g_autoptr(GError) error_local = NULL; - /* sanity check */ - if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { - g_debug("already in runtime mode, skipping"); - return TRUE; - } - - /* if we're in bootloader mode, we should be able to get this feature */ - idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_DFU); + idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_RDFU); if (idx == 0x00) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no DFU feature available"); - return FALSE; - } + /* sanity check for DFU*/ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } - /* reboot back into firmware mode */ - msg->report_id = FU_LOGITECH_HIDPP_REPORT_ID_LONG; - msg->device_id = priv->device_idx; - msg->sub_id = idx; - msg->function_id = 0x05 << 4; /* restart */ - msg->data[0] = entity; /* fwEntity */ - msg->hidpp_version = priv->hidpp_version; - msg->flags = FU_LOGITECH_HIDPP_HIDPP_MSG_FLAG_IGNORE_SUB_ID | - FU_LOGITECH_HIDPP_HIDPP_MSG_FLAG_IGNORE_SWID | // inferred? - FU_LOGITECH_HIDPP_HIDPP_MSG_FLAG_LONGER_TIMEOUT; - if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, &error_local)) { - if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_READ) || - g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { - g_debug("ignoring '%s' on reset", error_local->message); - } else { - g_prefix_error(&error_local, "failed to restart device: "); - g_propagate_error(error, g_steal_pointer(&error_local)); + /* if we're in bootloader mode, we should be able to get this feature */ + idx = fu_logitech_hidpp_device_feature_get_idx(self, FU_LOGITECH_HIDPP_FEATURE_DFU); + if (idx == 0x00) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no DFU feature available"); return FALSE; } + + /* reboot back into firmware mode */ + msg->report_id = FU_LOGITECH_HIDPP_REPORT_ID_LONG; + msg->device_id = priv->device_idx; + msg->sub_id = idx; + msg->function_id = 0x05 << 4; /* restart */ + msg->data[0] = entity; /* fwEntity */ + msg->hidpp_version = priv->hidpp_version; + msg->flags = FU_LOGITECH_HIDPP_HIDPP_MSG_FLAG_IGNORE_SUB_ID | + FU_LOGITECH_HIDPP_HIDPP_MSG_FLAG_IGNORE_SWID | /* inferred? */ + FU_LOGITECH_HIDPP_HIDPP_MSG_FLAG_LONGER_TIMEOUT; + if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_READ) || + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("ignoring '%s' on reset", error_local->message); + } else { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to restart device: "); + return FALSE; + } + } } if (fu_device_has_private_flag(device, FU_LOGITECH_HIDPP_DEVICE_FLAG_REBIND_ATTACH)) { @@ -1205,7 +1906,7 @@ static gboolean fu_logitech_hidpp_device_attach_cached(FuDevice *device, FuProgress *progress, GError **error) { - FuLogitechHidppDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidppDevice *self = FU_LOGITECH_HIDPP_DEVICE(device); FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); if (fu_device_has_private_flag(device, FU_LOGITECH_HIDPP_DEVICE_FLAG_REBIND_ATTACH)) @@ -1219,7 +1920,7 @@ const gchar *value, GError **error) { - FuLogitechHidppDevice *self = FU_HIDPP_DEVICE(device); + FuLogitechHidppDevice *self = FU_LOGITECH_HIDPP_DEVICE(device); if (g_strcmp0(key, "LogitechHidppModelId") == 0) { fu_logitech_hidpp_device_set_model_id(self, value); return TRUE; @@ -1232,7 +1933,7 @@ } static void -fu_logitech_hidpp_device_set_progress(FuDevice *self, FuProgress *progress) +fu_logitech_hidpp_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -1245,7 +1946,7 @@ static void fu_logitech_hidpp_device_finalize(GObject *object) { - FuLogitechHidppDevice *self = FU_HIDPP_DEVICE(object); + FuLogitechHidppDevice *self = FU_LOGITECH_HIDPP_DEVICE(object); FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); g_ptr_array_unref(priv->feature_index); g_free(priv->model_id); @@ -1258,10 +1959,11 @@ FwupdInstallFlags flags, GError **error) { - FuDevice *parent = fu_device_get_parent(device); - if (parent != NULL) - fu_device_set_poll_interval(parent, FU_HIDPP_RECEIVER_RUNTIME_POLLING_INTERVAL); - + FuDevice *parent = fu_device_get_parent(device, NULL); + if (parent != NULL) { + fu_device_set_poll_interval(parent, + FU_LOGITECH_HIDPP_RECEIVER_RUNTIME_POLLING_INTERVAL); + } return TRUE; } @@ -1290,11 +1992,11 @@ FuLogitechHidppDevicePrivate *priv = GET_PRIVATE(self); priv->device_idx = FU_LOGITECH_HIDPP_DEVICE_IDX_WIRED; priv->feature_index = g_ptr_array_new_with_free_func(g_free); + fu_logitech_hidpp_device_rdfu_set_state(self, FU_LOGITECH_HIDPP_RDFU_STATE_NOT_STARTED); fu_device_add_request_flag(FU_DEVICE(self), FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE); fu_device_set_vid(FU_DEVICE(self), FU_LOGITECH_HIDPP_DEVICE_VID); fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); - fu_device_set_vendor(FU_DEVICE(self), "Logitech"); fu_device_retry_set_delay(FU_DEVICE(self), 1000); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); @@ -1321,7 +2023,7 @@ FuLogitechHidppDevice * fu_logitech_hidpp_device_new(FuUdevDevice *parent) { - return g_object_new(FU_TYPE_HIDPP_DEVICE, + return g_object_new(FU_TYPE_LOGITECH_HIDPP_DEVICE, "proxy", parent, "device-file", diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-device.h fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-device.h --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,11 +8,11 @@ #include -#define FU_TYPE_HIDPP_DEVICE (fu_logitech_hidpp_device_get_type()) +#define FU_TYPE_LOGITECH_HIDPP_DEVICE (fu_logitech_hidpp_device_get_type()) G_DECLARE_DERIVABLE_TYPE(FuLogitechHidppDevice, fu_logitech_hidpp_device, FU, - HIDPP_DEVICE, + LOGITECH_HIDPP_DEVICE, FuUdevDevice) struct _FuLogitechHidppDeviceClass { diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.c fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.c --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.c 2026-02-26 11:36:18.000000000 +0000 @@ -79,73 +79,41 @@ { g_return_val_if_fail(msg != NULL, FALSE); if (msg->sub_id == FU_LOGITECH_HIDPP_SUBID_ERROR_MSG) { - const gchar *text = fu_logitech_hidpp_err_to_string(msg->data[1]); - switch (msg->data[1]) { - case FU_LOGITECH_HIDPP_ERR_INVALID_SUBID: - case FU_LOGITECH_HIDPP_ERR_TOO_MANY_DEVICES: - case FU_LOGITECH_HIDPP_ERR_REQUEST_UNAVAILABLE: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, text); - break; - case FU_LOGITECH_HIDPP_ERR_INVALID_ADDRESS: - case FU_LOGITECH_HIDPP_ERR_INVALID_VALUE: - case FU_LOGITECH_HIDPP_ERR_ALREADY_EXISTS: - case FU_LOGITECH_HIDPP_ERR_INVALID_PARAM_VALUE: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, text); - break; - case FU_LOGITECH_HIDPP_ERR_CONNECT_FAIL: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, text); - break; - case FU_LOGITECH_HIDPP_ERR_BUSY: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, text); - break; - case FU_LOGITECH_HIDPP_ERR_UNKNOWN_DEVICE: - case FU_LOGITECH_HIDPP_ERR_RESOURCE_ERROR: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, text); - break; - case FU_LOGITECH_HIDPP_ERR_WRONG_PIN_CODE: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_AUTH_FAILED, - "the pin code was wrong"); - break; - default: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "generic failure"); - } - return FALSE; + const gchar *str = fu_logitech_hidpp_err_to_string(msg->data[1]); + const FuErrorMapEntry entries[] = { + {FU_LOGITECH_HIDPP_ERR_INVALID_SUBID, FWUPD_ERROR_NOT_SUPPORTED, str}, + {FU_LOGITECH_HIDPP_ERR_TOO_MANY_DEVICES, FWUPD_ERROR_NOT_SUPPORTED, str}, + {FU_LOGITECH_HIDPP_ERR_REQUEST_UNAVAILABLE, FWUPD_ERROR_NOT_SUPPORTED, str}, + {FU_LOGITECH_HIDPP_ERR_INVALID_ADDRESS, FWUPD_ERROR_INVALID_DATA, str}, + {FU_LOGITECH_HIDPP_ERR_INVALID_VALUE, FWUPD_ERROR_INVALID_DATA, str}, + {FU_LOGITECH_HIDPP_ERR_ALREADY_EXISTS, FWUPD_ERROR_INVALID_DATA, str}, + {FU_LOGITECH_HIDPP_ERR_INVALID_PARAM_VALUE, FWUPD_ERROR_INVALID_DATA, str}, + {FU_LOGITECH_HIDPP_ERR_CONNECT_FAIL, FWUPD_ERROR_INTERNAL, str}, + {FU_LOGITECH_HIDPP_ERR_BUSY, FWUPD_ERROR_BUSY, str}, + {FU_LOGITECH_HIDPP_ERR_UNKNOWN_DEVICE, FWUPD_ERROR_NOT_FOUND, str}, + {FU_LOGITECH_HIDPP_ERR_RESOURCE_ERROR, FWUPD_ERROR_NOT_FOUND, str}, + {FU_LOGITECH_HIDPP_ERR_WRONG_PIN_CODE, FWUPD_ERROR_AUTH_FAILED, str}, + }; + return fu_error_map_entry_to_gerror(msg->data[1], + entries, + G_N_ELEMENTS(entries), + error); } if (msg->sub_id == FU_LOGITECH_HIDPP_SUBID_ERROR_MSG_20) { - const gchar *text = fu_logitech_hidpp_err2_to_string(msg->data[1]); - switch (msg->data[1]) { - case FU_LOGITECH_HIDPP_ERR2_INVALID_ARGUMENT: - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "Invalid argument 0x%02x", - msg->data[2]); - break; - case FU_LOGITECH_HIDPP_ERR2_OUT_OF_RANGE: - case FU_LOGITECH_HIDPP_ERR2_HW_ERROR: - case FU_LOGITECH_HIDPP_ERR2_INVALID_FEATURE_INDEX: - case FU_LOGITECH_HIDPP_ERR2_INVALID_FUNCTION_ID: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, text); - break; - case FU_LOGITECH_HIDPP_ERR2_BUSY: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "busy"); - break; - case FU_LOGITECH_HIDPP_ERR2_UNSUPPORTED: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, text); - break; - default: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "generic failure"); - break; - } - return FALSE; + const gchar *str = fu_logitech_hidpp_err2_to_string(msg->data[1]); + const FuErrorMapEntry entries[] = { + {FU_LOGITECH_HIDPP_ERR2_INVALID_ARGUMENT, FWUPD_ERROR_INVALID_DATA, str}, + {FU_LOGITECH_HIDPP_ERR2_OUT_OF_RANGE, FWUPD_ERROR_INVALID_DATA, str}, + {FU_LOGITECH_HIDPP_ERR2_HW_ERROR, FWUPD_ERROR_INVALID_DATA, str}, + {FU_LOGITECH_HIDPP_ERR2_INVALID_FEATURE_INDEX, FWUPD_ERROR_INVALID_DATA, str}, + {FU_LOGITECH_HIDPP_ERR2_INVALID_FUNCTION_ID, FWUPD_ERROR_INVALID_DATA, str}, + {FU_LOGITECH_HIDPP_ERR2_BUSY, FWUPD_ERROR_BUSY, str}, + {FU_LOGITECH_HIDPP_ERR2_UNSUPPORTED, FWUPD_ERROR_NOT_SUPPORTED, str}, + }; + return fu_error_map_entry_to_gerror(msg->data[1], + entries, + G_N_ELEMENTS(entries), + error); } return TRUE; } diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.h fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.h --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp-msg.h 2026-02-26 11:36:18.000000000 +0000 @@ -6,7 +6,7 @@ #pragma once -#include +#include typedef enum { FU_LOGITECH_HIDPP_HIDPP_MSG_FLAG_NONE, @@ -17,7 +17,7 @@ FU_LOGITECH_HIDPP_HIDPP_MSG_FLAG_RETRY_STUCK = 1 << 4, /*< private >*/ FU_LOGITECH_HIDPP_HIDPP_MSG_FLAG_LAST -} FuLogitechHidppHidppMsgFlags; +} G_GNUC_FLAG_ENUM FuLogitechHidppHidppMsgFlags; typedef struct __attribute__((packed)) { /* nocheck:blocked */ guint8 report_id; diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp.c fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp.c --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-hidpp.c 2026-02-26 11:36:18.000000000 +0000 @@ -50,9 +50,8 @@ "function-id: %02x [%s]\n", msg->function_id, fu_logitech_hidpp_msg_fcn_id_to_string(msg)); - if (!fu_logitech_hidpp_msg_is_error(msg, &error)) { + if (!fu_logitech_hidpp_msg_is_error(msg, &error)) g_string_append_printf(str, "error: %s\n", error->message); - } if (str->len > 0) g_string_truncate(str, str->len - 1); return g_string_free(g_steal_pointer(&str), FALSE); @@ -73,7 +72,7 @@ msg->function_id |= FU_LOGITECH_HIDPP_HIDPP_MSG_SW_ID; /* force long reports for BLE-direct devices */ - if (msg->hidpp_version == FU_HIDPP_VERSION_BLE) { + if (msg->hidpp_version == FU_LOGITECH_HIDPP_VERSION_BLE) { msg->report_id = FU_LOGITECH_HIDPP_REPORT_ID_LONG; len = 20; } @@ -94,7 +93,7 @@ timeout, write_flags, error)) { - g_prefix_error(error, "failed to send: "); + g_prefix_error_literal(error, "failed to send: "); return FALSE; } @@ -118,7 +117,7 @@ timeout, FU_IO_CHANNEL_FLAG_SINGLE_SHOT, error)) { - g_prefix_error(error, "failed to receive: "); + g_prefix_error_literal(error, "failed to receive: "); return FALSE; } @@ -172,13 +171,13 @@ msg_tmp, timeout, error)) { - g_prefix_error(error, "failed to receive: "); + g_prefix_error_literal(error, "failed to receive: "); return FALSE; } } } else { if (!fu_logitech_hidpp_receive(udev_device, msg_tmp, timeout, error)) { - g_prefix_error(error, "failed to receive: "); + g_prefix_error_literal(error, "failed to receive: "); return FALSE; } } @@ -222,10 +221,10 @@ /* hardware not responding */ if (ignore_cnt++ > 10) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "too many messages to ignore"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "too many messages to ignore"); return FALSE; } diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-plugin.c fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-plugin.c --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,6 +13,7 @@ #include "fu-logitech-hidpp-plugin.h" #include "fu-logitech-hidpp-runtime-bolt.h" #include "fu-logitech-hidpp-runtime-unifying.h" +#include "fu-logitech-rdfu-firmware.h" struct _FuLogitechHidppPlugin { FuPlugin parent_instance; @@ -23,6 +24,7 @@ static void fu_logitech_hidpp_plugin_init(FuLogitechHidppPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void @@ -32,12 +34,14 @@ FuContext *ctx = fu_plugin_get_context(plugin); fu_context_add_quirk_key(ctx, "LogitechHidppModelId"); fu_plugin_add_udev_subsystem(plugin, "hidraw"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "unifying"); fu_plugin_add_device_gtype(plugin, FU_TYPE_LOGITECH_HIDPP_BOOTLOADER_NORDIC); fu_plugin_add_device_gtype(plugin, FU_TYPE_LOGITECH_HIDPP_BOOTLOADER_TEXAS); - fu_plugin_add_device_gtype(plugin, FU_TYPE_HIDPP_RUNTIME_UNIFYING); - fu_plugin_add_device_gtype(plugin, FU_TYPE_HIDPP_DEVICE); - fu_plugin_add_device_gtype(plugin, FU_TYPE_HIDPP_RUNTIME_BOLT); + fu_plugin_add_device_gtype(plugin, FU_TYPE_LOGITECH_HIDPP_RUNTIME_UNIFYING); + fu_plugin_add_device_gtype(plugin, FU_TYPE_LOGITECH_HIDPP_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_LOGITECH_HIDPP_RUNTIME_BOLT); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_LOGITECH_RDFU_FIRMWARE); } static void diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-radio.c fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-radio.c --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-radio.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-radio.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,24 +19,27 @@ static void fu_logitech_hidpp_radio_to_string(FuDevice *device, guint idt, GString *str) { - FuLogitechHidppRadio *self = FU_HIDPP_RADIO(device); + FuLogitechHidppRadio *self = FU_LOGITECH_HIDPP_RADIO(device); fwupd_codec_string_append_hex(str, idt, "Entity", self->entity); } static gboolean fu_logitech_hidpp_radio_attach(FuDevice *device, FuProgress *progress, GError **error) { - FuLogitechHidppRadio *self = FU_HIDPP_RADIO(device); - FuDevice *parent = fu_device_get_parent(device); + FuLogitechHidppRadio *self = FU_LOGITECH_HIDPP_RADIO(device); + FuDevice *parent; g_autoptr(FuDeviceLocker) locker = NULL; /* open */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - return fu_logitech_hidpp_device_attach(FU_HIDPP_DEVICE(parent), + return fu_logitech_hidpp_device_attach(FU_LOGITECH_HIDPP_DEVICE(parent), self->entity, progress, error); @@ -45,10 +48,13 @@ static gboolean fu_logitech_hidpp_radio_detach(FuDevice *device, FuProgress *progress, GError **error) { - FuDevice *parent = fu_device_get_parent(device); + FuDevice *parent; g_autoptr(FuDeviceLocker) locker = NULL; /* open */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -65,10 +71,13 @@ FwupdInstallFlags flags, GError **error) { - FuDevice *parent = fu_device_get_parent(device); + FuDevice *parent; g_autoptr(FuDeviceLocker) locker = NULL; /* open */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -76,7 +85,7 @@ } static void -fu_logitech_hidpp_radio_set_progress(FuDevice *self, FuProgress *progress) +fu_logitech_hidpp_radio_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -96,6 +105,7 @@ fu_device_set_install_duration(FU_DEVICE(self), 270); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_BATTERY); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); fu_device_add_protocol(FU_DEVICE(self), "com.logitech.unifyingsigned"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); } diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-radio.h fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-radio.h --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-radio.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-radio.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,11 @@ #include #define FU_TYPE_LOGITECH_HIDPP_RADIO (fu_logitech_hidpp_radio_get_type()) -G_DECLARE_FINAL_TYPE(FuLogitechHidppRadio, fu_logitech_hidpp_radio, FU, HIDPP_RADIO, FuDevice) +G_DECLARE_FINAL_TYPE(FuLogitechHidppRadio, + fu_logitech_hidpp_radio, + FU, + LOGITECH_HIDPP_RADIO, + FuDevice) FuLogitechHidppRadio * fu_logitech_hidpp_radio_new(FuContext *ctx, guint8 entity); diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-rdfu.rs fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-rdfu.rs --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-rdfu.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-rdfu.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,165 @@ +// Copyright 2024 Denis Pynkin +// SPDX-License-Identifier: LGPL-2.1-or-later + +// the same as FuLogitechHidppReportId, need for the header +#[repr(u8)] +enum FuLogitechHidppRdfuReportId { + Short = 0x10, + Long = 0x11, + VeryLong = 0x12, + Notification = 0x01, +} + + +#[repr(u8)] +enum FuLogitechHidppRdfuState { + NotStarted, + Transfer, + Wait, // waiting the event from the device + ResumeDfu, // for soft recover + Apply, +} + +#[repr(u8)] +enum FuLogitechHidppRdfuFunc { + GetCapabilities = 0, + StartDfu = 1 << 4, + GetDfuStatus = 2 << 4, + ApplyDfu = 3 << 4, + TransferDfuData = 4 << 4, +} + +// For both status and error codes +#[derive(ToString)] +#[repr(u8)] +enum FuLogitechHidppRdfuResponseCode { +// Status Codes + DfuNotStarted = 0x01, + DataTransferReady = 0x02, + DataTransferWait = 0x03, + DfuTransferComplete = 0x04, + DfuApplyPending = 0x05, + DfuTransferPktAck = 0x06, + DfuAbort = 0x07, +// Error Codes + InvalidMagicString = 0x80, + InvalidFwEntity = 0x81, + DeviceBusy = 0x82, + DeviceOperationFailure = 0x83, + NotSupported = 0x84, + DfuStateError = 0x85, + InvalidBlock = 0x86, + GenericError = 0xFF, +} + +#[derive(Default, Parse)] +struct FuStructLogitechHidppRdfuResponse { + report_id: FuLogitechHidppRdfuReportId == Long, + device_id: u8, + sub_id: u8, + function_id: FuLogitechHidppRdfuFunc, + fw_entity: u8, + status_code: FuLogitechHidppRdfuResponseCode, + parameters: [u8; 14], +} + +#[repr(u8)] +enum FuLogitechHidppRdfuCapabilities { + ResumableDfuBit = 1, + DeferrableDfuBit = 1 << 1, + ForcibleDfuBit = 1 << 2, +} + +#[derive(New, Default)] +struct FuStructLogitechHidppRdfuGetCapabilities { + report_id: FuLogitechHidppRdfuReportId == Long, + device_id: u8, + sub_id: u8, + function_id: FuLogitechHidppRdfuFunc == GetCapabilities, + data: [u8; 16], +} + +#[derive(Default, Parse)] +struct FuStructLogitechHidppRdfuCapabilities { + report_id: FuLogitechHidppRdfuReportId == Long, + device_id: u8, + sub_id: u8, + function_id: FuLogitechHidppRdfuFunc == GetCapabilities, + capabilities: u8, + data: [u8; 15], +} + +#[derive(New, Default)] +struct FuStructLogitechHidppRdfuGetDfuStatus { + report_id: FuLogitechHidppRdfuReportId == Long, + device_id: u8, + sub_id: u8, + function_id: FuLogitechHidppRdfuFunc == GetDfuStatus, + fw_entity: u8, +} + +#[derive(New, Default)] +struct FuStructLogitechHidppRdfuStartDfu { + report_id: FuLogitechHidppRdfuReportId == Long, + device_id: u8, + sub_id: u8, + function_id: FuLogitechHidppRdfuFunc == StartDfu, + fw_entity: u8, + magic: [u8; 10], +} + +#[derive(Default, Parse)] +struct FuStructLogitechHidppRdfuStartDfuResponse { + report_id: FuLogitechHidppRdfuReportId == Long, + device_id: u8, + sub_id: u8, + function_id: FuLogitechHidppRdfuFunc == StartDfu, + fw_entity: u8, + status_code: FuLogitechHidppRdfuResponseCode, + status_params: u8, + additional_status_params: u8, +} + +#[derive(New, Default)] +struct FuStructLogitechHidppRdfuTransferDfuData { + report_id: FuLogitechHidppRdfuReportId == Long, + device_id: u8, + sub_id: u8, + function_id: FuLogitechHidppRdfuFunc == TransferDfuData, + data: [u8; 16], +} + + +#[derive(Default, Parse)] +struct FuStructLogitechHidppRdfuDataTransferReady { + status_code: FuLogitechHidppRdfuResponseCode == DataTransferReady, + block_id: u16be, +} + +#[derive(Default, Parse)] +struct FuStructLogitechHidppRdfuDataTransferWait { + status_code: FuLogitechHidppRdfuResponseCode == DataTransferWait, + delay_ms: u16be, +} + +#[derive(Default, Parse)] +struct FuStructLogitechHidppRdfuDfuTransferPktAck { + status_code: FuLogitechHidppRdfuResponseCode == DfuTransferPktAck, + pkt_number: u32be, +} + +#[repr(u8)] +enum FuLogitechHidppRdfuApplyFlags { + DeferDfuBit = 1, + ForceDfuBit = 1 << 1, +} + +#[derive(New, Default)] +struct FuStructLogitechHidppRdfuApplyDfu { + report_id: FuLogitechHidppRdfuReportId == Long, + device_id: u8, + sub_id: u8, + function_id: FuLogitechHidppRdfuFunc == ApplyDfu, + fw_entity: u8, + flags: u8, +} diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.c fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.c --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,12 +18,14 @@ guint8 pairing_slots; }; -G_DEFINE_TYPE(FuLogitechHidppRuntimeBolt, fu_logitech_hidpp_runtime_bolt, FU_TYPE_HIDPP_RUNTIME) +G_DEFINE_TYPE(FuLogitechHidppRuntimeBolt, + fu_logitech_hidpp_runtime_bolt, + FU_TYPE_LOGITECH_HIDPP_RUNTIME) static gboolean fu_logitech_hidpp_runtime_bolt_detach(FuDevice *device, FuProgress *progress, GError **error) { - FuLogitechHidppRuntime *self = FU_HIDPP_RUNTIME(device); + FuLogitechHidppRuntime *self = FU_LOGITECH_HIDPP_RUNTIME(device); g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); g_autoptr(GError) error_local = NULL; @@ -31,7 +33,7 @@ msg->device_id = FU_LOGITECH_HIDPP_DEVICE_IDX_RECEIVER; msg->sub_id = FU_LOGITECH_HIDPP_SUBID_SET_LONG_REGISTER; msg->function_id = BOLT_REGISTER_DFU_CONTROL; - msg->data[0] = 1; /* Enable DFU */ + msg->data[0] = 1; /* enable DFU */ msg->data[4] = 'P'; msg->data[5] = 'R'; msg->data[6] = 'E'; @@ -45,7 +47,7 @@ g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { g_debug("failed to detach to bootloader: %s", error_local->message); } else { - g_prefix_error(&error_local, "failed to detach to bootloader: "); + g_prefix_error_literal(&error_local, "failed to detach to bootloader: "); g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } @@ -57,20 +59,22 @@ static void fu_logitech_hidpp_runtime_bolt_to_string(FuDevice *device, guint idt, GString *str) { - FuLogitechHidppRuntimeBolt *self = FU_HIDPP_RUNTIME_BOLT(device); + FuLogitechHidppRuntimeBolt *self = FU_LOGITECH_HIDPP_RUNTIME_BOLT(device); fwupd_codec_string_append_int(str, idt, "PairingSlots", self->pairing_slots); } static FuLogitechHidppDevice * -fu_logitech_hidpp_runtime_bolt_find_paired_device(FuDevice *device, guint16 hidpp_pid) +fu_logitech_hidpp_runtime_bolt_find_paired_device(FuLogitechHidppRuntimeBolt *self, + guint16 hidpp_pid) { - GPtrArray *children = fu_device_get_children(device); + GPtrArray *children = fu_device_get_children(FU_DEVICE(self)); for (guint i = 0; i < children->len; i++) { FuDevice *child = g_ptr_array_index(children, i); - if (FU_IS_HIDPP_DEVICE(child) && - fu_logitech_hidpp_device_get_hidpp_pid(FU_HIDPP_DEVICE(child)) == hidpp_pid) - return FU_HIDPP_DEVICE(g_object_ref(child)); + if (FU_IS_LOGITECH_HIDPP_DEVICE(child) && + fu_logitech_hidpp_device_get_hidpp_pid(FU_LOGITECH_HIDPP_DEVICE(child)) == + hidpp_pid) + return FU_LOGITECH_HIDPP_DEVICE(g_object_ref(child)); } return NULL; @@ -107,7 +111,7 @@ FuLogitechHidppHidppMsg *msg, GError **error) { - FuLogitechHidppRuntime *runtime = FU_HIDPP_RUNTIME(self); + FuLogitechHidppRuntime *runtime = FU_LOGITECH_HIDPP_RUNTIME(self); gboolean reachable = FALSE; guint16 hidpp_pid; g_autoptr(FuLogitechHidppDevice) child = NULL; @@ -116,12 +120,10 @@ reachable = TRUE; hidpp_pid = (msg->data[1] << 8) | msg->data[2]; - child = fu_logitech_hidpp_runtime_bolt_find_paired_device(FU_DEVICE(self), hidpp_pid); + child = fu_logitech_hidpp_runtime_bolt_find_paired_device(self, hidpp_pid); if (child != NULL) { - g_debug("%s [%s] is reachable:%i", - fu_device_get_name(FU_DEVICE(child)), - fu_device_get_name(FU_DEVICE(child)), - reachable); + g_autofree gchar *id_display = fu_device_get_id_display(FU_DEVICE(child)); + g_debug("%s is reachable:%i", id_display, reachable); if (reachable) { g_autoptr(FuDeviceLocker) locker = NULL; @@ -129,7 +131,7 @@ fu_device_probe_invalidate(FU_DEVICE(child)); locker = fu_device_locker_new(FU_DEVICE(child), error); if (locker == NULL) { - g_prefix_error(error, "cannot rescan paired device: "); + g_prefix_error_literal(error, "cannot rescan paired device: "); return FALSE; } fu_device_remove_flag(FU_DEVICE(child), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); @@ -174,10 +176,9 @@ } static void -fu_logitech_hidpp_runtime_bolt_poll_peripherals(FuDevice *device) +fu_logitech_hidpp_runtime_bolt_poll_peripherals(FuLogitechHidppRuntime *self) { - FuLogitechHidppRuntime *self = FU_HIDPP_RUNTIME(device); - FuLogitechHidppRuntimeBolt *bolt = FU_HIDPP_RUNTIME_BOLT(device); + FuLogitechHidppRuntimeBolt *bolt = FU_LOGITECH_HIDPP_RUNTIME_BOLT(self); for (guint i = 1; i <= bolt->pairing_slots; i++) { g_autofree gchar *name = NULL; @@ -203,7 +204,7 @@ if ((msg->data[1] & 0x40) == 0) { /* paired device is reachable */ g_autoptr(FuLogitechHidppDevice) child = NULL; - child = fu_logitech_hidpp_device_new(FU_UDEV_DEVICE(device)); + child = fu_logitech_hidpp_device_new(FU_UDEV_DEVICE(self)); fu_device_set_install_duration(FU_DEVICE(child), 270); fu_device_add_private_flag(FU_DEVICE(child), FU_LOGITECH_HIDPP_DEVICE_FLAG_ADD_RADIO); @@ -216,7 +217,7 @@ continue; if (!fu_device_setup(FU_DEVICE(child), &error_local)) continue; - fu_device_add_child(device, FU_DEVICE(child)); + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(child)); } } } @@ -276,8 +277,8 @@ static gboolean fu_logitech_hidpp_runtime_bolt_poll(FuDevice *device, GError **error) { - FuLogitechHidppRuntime *runtime = FU_HIDPP_RUNTIME(device); - FuLogitechHidppRuntimeBolt *self = FU_HIDPP_RUNTIME_BOLT(device); + FuLogitechHidppRuntime *runtime = FU_LOGITECH_HIDPP_RUNTIME(device); + FuLogitechHidppRuntimeBolt *self = FU_LOGITECH_HIDPP_RUNTIME_BOLT(device); const guint timeout = 1; /* ms */ g_autoptr(GPtrArray) msgs = g_ptr_array_new_with_free_func(g_free); @@ -332,11 +333,10 @@ } static gboolean -fu_logitech_hidpp_runtime_bolt_setup_internal(FuDevice *device, GError **error) +fu_logitech_hidpp_runtime_bolt_setup_internal(FuLogitechHidppRuntime *self, GError **error) { - FuContext *ctx = fu_device_get_context(device); - FuLogitechHidppRuntime *self = FU_HIDPP_RUNTIME(device); - FuLogitechHidppRuntimeBolt *bolt = FU_HIDPP_RUNTIME_BOLT(device); + FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); + FuLogitechHidppRuntimeBolt *bolt = FU_LOGITECH_HIDPP_RUNTIME_BOLT(self); g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); msg->report_id = FU_LOGITECH_HIDPP_REPORT_ID_SHORT; @@ -346,7 +346,7 @@ msg->data[0] = 0x02; /* FW Version (contains the number of pairing slots) */ msg->hidpp_version = 1; if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error)) { - g_prefix_error(error, "failed to fetch the number of pairing slots: "); + g_prefix_error_literal(error, "failed to fetch the number of pairing slots: "); return FALSE; } bolt->pairing_slots = msg->data[8]; @@ -368,7 +368,7 @@ msg->data[0] = i; msg->hidpp_version = 1; if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error)) { - g_prefix_error(error, "failed to read device config: "); + g_prefix_error_literal(error, "failed to read device config: "); return FALSE; } @@ -386,7 +386,7 @@ msg->data[1], msg->data[2], version_raw); - fu_device_set_version(device, version); + fu_device_set_version(FU_DEVICE(self), version); break; case 1: /* bootloader */ @@ -401,7 +401,7 @@ msg->data[1], msg->data[2], version_raw); - fu_device_set_version_bootloader(device, version); + fu_device_set_version_bootloader(FU_DEVICE(self), version); break; case 5: /* SoftDevice */ @@ -409,13 +409,13 @@ radio = fu_logitech_hidpp_radio_new(ctx, i); fu_device_add_instance_u16(FU_DEVICE(radio), "VEN", - fu_device_get_vid(device)); + fu_device_get_vid(FU_DEVICE(self))); fu_device_add_instance_u16(FU_DEVICE(radio), "DEV", - fu_device_get_pid(device)); + fu_device_get_pid(FU_DEVICE(self))); fu_device_add_instance_u8(FU_DEVICE(radio), "ENT", msg->data[0]); fu_device_incorporate(FU_DEVICE(radio), - FU_DEVICE(device), + FU_DEVICE(self), FU_DEVICE_INCORPORATE_FLAG_PHYSICAL_ID); fu_device_set_logical_id(FU_DEVICE(radio), "Receiver_SoftDevice"); if (!fu_device_build_instance_id(FU_DEVICE(radio), @@ -435,7 +435,7 @@ return FALSE; g_string_append_printf(radio_version, "0x%.4x", version_raw); fu_device_set_version(FU_DEVICE(radio), radio_version->str); - fu_device_add_child(device, FU_DEVICE(radio)); + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(radio)); break; default: break; @@ -444,10 +444,10 @@ /* enable HID++ notifications */ if (!fu_logitech_hidpp_runtime_enable_notifications(self, error)) { - g_prefix_error(error, "failed to enable notifications: "); + g_prefix_error_literal(error, "failed to enable notifications: "); return FALSE; } - fu_logitech_hidpp_runtime_bolt_poll_peripherals(device); + fu_logitech_hidpp_runtime_bolt_poll_peripherals(self); /* success */ return TRUE; @@ -456,6 +456,7 @@ static gboolean fu_logitech_hidpp_runtime_bolt_setup(FuDevice *device, GError **error) { + FuLogitechHidppRuntime *self = FU_LOGITECH_HIDPP_RUNTIME(device); g_autoptr(GError) error_local = NULL; for (guint i = 0; i < 5; i++) { g_clear_error(&error_local); @@ -463,7 +464,7 @@ * the device first -- we can't use the SwID as this is a * HID++2.0 feature */ fu_device_sleep(device, 200); /* ms */ - if (fu_logitech_hidpp_runtime_bolt_setup_internal(device, &error_local)) + if (fu_logitech_hidpp_runtime_bolt_setup_internal(self, &error_local)) return TRUE; if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA)) { g_propagate_error(error, g_steal_pointer(&error_local)); @@ -490,7 +491,6 @@ { fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_USER_REPLUG); - fu_device_set_vendor(FU_DEVICE(self), "Logitech"); fu_device_set_name(FU_DEVICE(self), "Bolt Receiver"); fu_device_add_protocol(FU_DEVICE(self), "com.logitech.unifyingsigned"); } diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.h fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.h --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-bolt.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,9 +8,9 @@ #include "fu-logitech-hidpp-runtime.h" -#define FU_TYPE_HIDPP_RUNTIME_BOLT (fu_logitech_hidpp_runtime_bolt_get_type()) +#define FU_TYPE_LOGITECH_HIDPP_RUNTIME_BOLT (fu_logitech_hidpp_runtime_bolt_get_type()) G_DECLARE_FINAL_TYPE(FuLogitechHidppRuntimeBolt, fu_logitech_hidpp_runtime_bolt, FU, - HIDPP_RUNTIME_BOLT, + LOGITECH_HIDPP_RUNTIME_BOLT, FuLogitechHidppRuntime) diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.c fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.c --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,13 +17,13 @@ G_DEFINE_TYPE(FuLogitechHidppRuntimeUnifying, fu_logitech_hidpp_runtime_unifying, - FU_TYPE_HIDPP_RUNTIME) + FU_TYPE_LOGITECH_HIDPP_RUNTIME) #define GET_PRIVATE(o) (fu_logitech_hidpp_runtime_unifying_get_instance_private(o)) static gboolean fu_logitech_hidpp_runtime_unifying_detach(FuDevice *device, FuProgress *progress, GError **error) { - FuLogitechHidppRuntime *self = FU_HIDPP_RUNTIME(device); + FuLogitechHidppRuntime *self = FU_LOGITECH_HIDPP_RUNTIME(device); g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); g_autoptr(GError) error_local = NULL; @@ -44,7 +44,7 @@ g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { g_debug("failed to detach to bootloader: %s", error_local->message); } else { - g_prefix_error(&error_local, "failed to detach to bootloader: "); + g_prefix_error_literal(&error_local, "failed to detach to bootloader: "); g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } @@ -54,14 +54,12 @@ } static gboolean -fu_logitech_hidpp_runtime_unifying_setup_internal(FuDevice *device, GError **error) +fu_logitech_hidpp_runtime_unifying_setup_internal(FuLogitechHidppRuntime *self, GError **error) { - FuLogitechHidppRuntime *self = FU_HIDPP_RUNTIME(device); - guint8 config[10]; + guint8 config[10] = {0}; g_autofree gchar *version_fw = NULL; /* read all 10 bytes of the version register */ - memset(config, 0x00, sizeof(config)); for (guint i = 0x01; i < 0x05; i++) { g_autoptr(FuLogitechHidppHidppMsg) msg = fu_logitech_hidpp_msg_new(); @@ -77,7 +75,7 @@ msg->data[0] = i; msg->hidpp_version = 1; if (!fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error)) { - g_prefix_error(error, "failed to read device config: "); + g_prefix_error_literal(error, "failed to read device config: "); return FALSE; } if (!fu_memcpy_safe(config, @@ -96,7 +94,7 @@ config[2], config[3], (guint16)config[4] << 8 | config[5]); - fu_device_set_version(device, version_fw); + fu_device_set_version(FU_DEVICE(self), version_fw); /* get bootloader version */ if (fu_logitech_hidpp_runtime_get_version_bl_major(self) > 0) { @@ -106,25 +104,25 @@ fu_logitech_hidpp_runtime_get_version_bl_major(self), config[8], config[9]); - fu_device_set_version_bootloader(FU_DEVICE(device), version_bl); + fu_device_set_version_bootloader(FU_DEVICE(self), version_bl); /* is the USB receiver expecting signed firmware */ if ((fu_logitech_hidpp_runtime_get_version_bl_major(self) == 0x01 && config[8] >= 0x04) || (fu_logitech_hidpp_runtime_get_version_bl_major(self) == 0x03 && config[8] >= 0x02)) { - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); - fu_device_add_protocol(device, "com.logitech.unifyingsigned"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_add_protocol(FU_DEVICE(self), "com.logitech.unifyingsigned"); } } - if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD)) { - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - fu_device_add_protocol(device, "com.logitech.unifying"); + if (!fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD)) { + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_protocol(FU_DEVICE(self), "com.logitech.unifying"); } /* enable HID++ notifications */ if (!fu_logitech_hidpp_runtime_enable_notifications(self, error)) { - g_prefix_error(error, "failed to enable notifications: "); + g_prefix_error_literal(error, "failed to enable notifications: "); return FALSE; } @@ -135,6 +133,7 @@ static gboolean fu_logitech_hidpp_runtime_unifying_setup(FuDevice *device, GError **error) { + FuLogitechHidppRuntime *self = FU_LOGITECH_HIDPP_RUNTIME(device); g_autoptr(GError) error_local = NULL; for (guint i = 0; i < 5; i++) { g_clear_error(&error_local); @@ -142,7 +141,7 @@ * the device first -- we can't use the SwID as this is a * HID++2.0 feature */ fu_device_sleep(device, 200); /* ms */ - if (fu_logitech_hidpp_runtime_unifying_setup_internal(device, &error_local)) + if (fu_logitech_hidpp_runtime_unifying_setup_internal(self, &error_local)) return TRUE; if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA)) { g_propagate_error(error, g_steal_pointer(&error_local)); @@ -154,7 +153,7 @@ } static void -fu_logitech_hidpp_runtime_unifying_set_progress(FuDevice *self, FuProgress *progress) +fu_logitech_hidpp_runtime_unifying_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.h fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.h --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime-unifying.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,9 +8,9 @@ #include "fu-logitech-hidpp-runtime.h" -#define FU_TYPE_HIDPP_RUNTIME_UNIFYING (fu_logitech_hidpp_runtime_unifying_get_type()) +#define FU_TYPE_LOGITECH_HIDPP_RUNTIME_UNIFYING (fu_logitech_hidpp_runtime_unifying_get_type()) G_DECLARE_FINAL_TYPE(FuLogitechHidppRuntimeUnifying, fu_logitech_hidpp_runtime_unifying, FU, - HIDPP_RUNTIME_UNIFYING, + LOGITECH_HIDPP_RUNTIME_UNIFYING, FuLogitechHidppRuntime) diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.c fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.c --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.c 2026-02-26 11:36:18.000000000 +0000 @@ -23,7 +23,7 @@ fu_logitech_hidpp_runtime_get_version_bl_major(FuLogitechHidppRuntime *self) { FuLogitechHidppRuntimePrivate *priv; - g_return_val_if_fail(FU_IS_HIDPP_RUNTIME(self), 0); + g_return_val_if_fail(FU_IS_LOGITECH_HIDPP_RUNTIME(self), 0); priv = GET_PRIVATE(self); return priv->version_bl_major; } @@ -38,7 +38,7 @@ msg->sub_id = FU_LOGITECH_HIDPP_SUBID_SET_REGISTER; msg->function_id = FU_LOGITECH_HIDPP_REGISTER_HIDPP_NOTIFICATIONS; msg->data[0] = 0x00; - msg->data[1] = 0x05; /* Wireless + SoftwarePresent */ + msg->data[1] = 0x05; /* wireless + softwarepresent */ msg->data[2] = 0x00; msg->hidpp_version = 1; return fu_logitech_hidpp_transfer(FU_UDEV_DEVICE(self), msg, error); @@ -47,7 +47,7 @@ static gboolean fu_logitech_hidpp_runtime_probe(FuDevice *device, GError **error) { - FuLogitechHidppRuntime *self = FU_HIDPP_RUNTIME(device); + FuLogitechHidppRuntime *self = FU_LOGITECH_HIDPP_RUNTIME(device); FuLogitechHidppRuntimePrivate *priv = GET_PRIVATE(self); g_autoptr(FuDevice) device_usb = NULL; g_autoptr(FuDevice) device_usb_iface = NULL; @@ -66,7 +66,7 @@ return FALSE; switch (fu_usb_device_get_release(FU_USB_DEVICE(device_usb)) & 0xFF00) { case 0x1200: - /* Nordic */ + /* nordic */ devid2 = g_strdup_printf("USB\\VID_%04X&PID_%04X", fu_device_get_vid(device), @@ -79,7 +79,7 @@ priv->version_bl_major = 0x01; break; case 0x2400: - /* Texas */ + /* texas */ devid2 = g_strdup_printf("USB\\VID_%04X&PID_%04X", fu_device_get_vid(device), @@ -92,7 +92,7 @@ priv->version_bl_major = 0x03; break; case 0x0500: - /* Bolt */ + /* bolt */ device_usb_iface = fu_device_get_backend_parent_with_subsystem(device, "usb:usb_interface", @@ -106,10 +106,10 @@ if (prop_interface == NULL) return FALSE; if (g_strcmp0(prop_interface, "3/0/0") != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "skipping hidraw device"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "skipping hidraw device"); return FALSE; } devid2 = @@ -165,11 +165,12 @@ fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); fu_device_set_vid(FU_DEVICE(self), FU_LOGITECH_HIDPP_DEVICE_VID); - fu_device_add_icon(FU_DEVICE(self), "usb-receiver"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_USB_RECEIVER); fu_device_set_name(FU_DEVICE(self), "Unifying Receiver"); fu_device_set_summary(FU_DEVICE(self), "Miniaturised USB wireless receiver"); fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); - fu_device_set_poll_interval(FU_DEVICE(self), FU_HIDPP_RECEIVER_RUNTIME_POLLING_INTERVAL); + fu_device_set_poll_interval(FU_DEVICE(self), + FU_LOGITECH_HIDPP_RECEIVER_RUNTIME_POLLING_INTERVAL); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); g_signal_connect(FU_DEVICE(self), diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.h fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.h --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp-runtime.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,11 +8,11 @@ #include -#define FU_TYPE_HIDPP_RUNTIME (fu_logitech_hidpp_runtime_get_type()) +#define FU_TYPE_LOGITECH_HIDPP_RUNTIME (fu_logitech_hidpp_runtime_get_type()) G_DECLARE_DERIVABLE_TYPE(FuLogitechHidppRuntime, fu_logitech_hidpp_runtime, FU, - HIDPP_RUNTIME, + LOGITECH_HIDPP_RUNTIME, FuHidrawDevice) struct _FuLogitechHidppRuntimeClass { diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp.rs fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp.rs --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-hidpp.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-hidpp.rs 2026-02-26 11:36:18.000000000 +0000 @@ -11,6 +11,7 @@ DfuControlSigned = 0x00C2, DfuControlBolt = 0x00C3, Dfu = 0x00D0, + Rdfu = 0x00D1, BatteryLevelStatus = 0x1000, UnifiedBattery = 0x1004, KbdReprogrammableKeys = 0x1B00, diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-rdfu-firmware.c fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-rdfu-firmware.c --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-rdfu-firmware.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-rdfu-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,417 @@ +/* + * Copyright 2024 Denis Pynkin + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-logitech-rdfu-firmware.h" +#include "json-glib/json-glib.h" + +#define FU_LOGITECH_RDFU_FIRMWARE_VERSION 1 +#define FU_LOGITECH_RDFU_MAGIC_ASCII_SIZE 22 /* 0x + 10 hex */ + +struct _FuLogitechRdfuFirmware { + FuFirmware parent_instance; + gchar *payload_name; + gchar *model_id; + GByteArray *magic; + GPtrArray *blocks; /* GByteArray */ +}; + +G_DEFINE_TYPE(FuLogitechRdfuFirmware, fu_logitech_rdfu_firmware, FU_TYPE_FIRMWARE) + +gchar * +fu_logitech_rdfu_firmware_get_model_id(FuLogitechRdfuFirmware *self, GError **error) +{ + return g_strdup(self->model_id); +} + +GByteArray * +fu_logitech_rdfu_firmware_get_magic(FuLogitechRdfuFirmware *self, GError **error) +{ + return g_byte_array_ref(self->magic); +} + +GPtrArray * +fu_logitech_rdfu_firmware_get_blocks(FuLogitechRdfuFirmware *self, GError **error) +{ + return g_ptr_array_ref(self->blocks); +} + +static gboolean +fu_logitech_rdfu_firmware_block_add(FuLogitechRdfuFirmware *self, JsonNode *node, GError **error) +{ + JsonObject *json_obj; + const gchar *block_str; + g_autoptr(GByteArray) block = NULL; + g_autoptr(GError) error_local = NULL; + + if (node == NULL || !JSON_NODE_HOLDS_OBJECT(node)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "RDFU firmware contains incorrect payloads node"); + return FALSE; + } + + json_obj = json_node_get_object(node); + if (!json_object_has_member(json_obj, "data")) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "payload %s has no data key", + self->payload_name); + return FALSE; + } + + block_str = json_object_get_string_member_with_default(json_obj, "data", NULL); + if (block_str == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "payload %s has no data", + self->payload_name); + return FALSE; + } + + if (strlen(block_str) % 2) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "payload %s has incorrect size %u", + self->payload_name, + (guint)strlen(block_str)); + return FALSE; + } + + block = fu_byte_array_from_string(block_str, &error_local); + if (block == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "unable to serialize payload %s", + self->payload_name); + return FALSE; + } + + g_ptr_array_add(self->blocks, g_steal_pointer(&block)); + return TRUE; +} + +static gboolean +fu_logitech_rdfu_firmware_entry_add(FuLogitechRdfuFirmware *self, JsonNode *node, GError **error) + +{ + JsonObject *json_obj; + guint str_offset = 0; + guint64 entity; + guint64 revision; + guint64 build; + const gchar *entity_str; + const gchar *magic_str; + const gchar *payload_str; + const gchar *model_id_str; + const gchar *name_str; + const gchar *revision_str; + const gchar *build_str; + g_autofree gchar *version = NULL; + g_autofree gchar *tmp = NULL; + g_autoptr(GByteArray) model_id = NULL; + + if (node == NULL || !JSON_NODE_HOLDS_OBJECT(node)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "incorrect contents node"); + return FALSE; + } + + json_obj = json_node_get_object(node); + if (!json_object_has_member(json_obj, "entity")) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "has no entity"); + return FALSE; + } + entity_str = json_object_get_string_member_with_default(json_obj, "entity", NULL); + if (!fu_strtoull(entity_str, &entity, 0, G_MAXUINT8, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + + fu_firmware_set_id(FU_FIRMWARE(self), entity_str); + + magic_str = json_object_get_string_member_with_default(json_obj, "magicStr", NULL); + if (magic_str == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "has no magic"); + return FALSE; + } + if (strlen(magic_str) != FU_LOGITECH_RDFU_MAGIC_ASCII_SIZE) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "has incorrect magic"); + return FALSE; + } + + payload_str = json_object_get_string_member_with_default(json_obj, "payload", NULL); + if (payload_str == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "has no payload"); + return FALSE; + } + g_debug("RDFU firmware for entity %u payload = %s", (guint)entity, payload_str); + + if (!json_object_has_member(json_obj, "modelId")) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "has no modelId"); + return FALSE; + } + model_id_str = json_object_get_string_member_with_default(json_obj, "modelId", NULL); + /* just to validate if modelId is in a hexadecimal string format */ + if (g_str_has_prefix(model_id_str, "0x")) + str_offset = 2; + model_id = fu_byte_array_from_string(model_id_str + str_offset, error); + if (model_id == NULL) + return FALSE; + + if (!json_object_has_member(json_obj, "name")) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "has no name"); + return FALSE; + } + name_str = json_object_get_string_member_with_default(json_obj, "name", NULL); + + if (!json_object_has_member(json_obj, "revision")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "has no revision"); + return FALSE; + } + revision_str = json_object_get_string_member_with_default(json_obj, "revision", NULL); + if (!fu_strtoull(revision_str, &revision, 0, G_MAXUINT8, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + + if (!json_object_has_member(json_obj, "build")) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "has no build"); + return FALSE; + } + build_str = json_object_get_string_member_with_default(json_obj, "build", NULL); + /* should be in BCD format already but let's be tolerant to absent leading 0 */ + if (!fu_strtoull(build_str, &build, 0, G_MAXUINT16, FU_INTEGER_BASE_16, error)) + return FALSE; + + /* skip "0x" prefix */ + self->magic = fu_byte_array_from_string(magic_str + 2, error); + if (self->magic == NULL) + return FALSE; + /* model id should be in same format as returned for device by getFwInfo */ + tmp = fu_byte_array_to_string(model_id); + self->model_id = g_ascii_strup(tmp, -1); + self->payload_name = g_strdup(payload_str); + self->blocks = g_ptr_array_new_with_free_func((GDestroyNotify)g_byte_array_unref); + version = g_strdup_printf("%s.%02x_B%04x", name_str, (guint)revision, (guint)build); + fu_firmware_set_version(FU_FIRMWARE(self), version); + + return TRUE; +} + +static gboolean +fu_logitech_rdfu_firmware_compare_payload(gconstpointer item, gconstpointer payload) +{ + FuLogitechRdfuFirmware *entity_fw = (FuLogitechRdfuFirmware *)item; + + if (g_strcmp0(entity_fw->payload_name, payload) != 0) + return FALSE; + + return TRUE; +} + +static gboolean +fu_logitech_rdfu_firmware_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ + JsonNode *json_root_node; + JsonObject *json_obj; + JsonObject *json_payloads_obj; + JsonArray *contents; + const gchar *firmware_str; + guint64 firmware_ver; + gsize streamsz; + g_autoptr(JsonParser) parser = json_parser_new(); + g_autoptr(GList) payloads_list = NULL; + + if (!fu_input_stream_size(stream, &streamsz, error)) + return FALSE; + if (!g_seekable_seek(G_SEEKABLE(stream), 0, G_SEEK_SET, NULL, error)) { + g_prefix_error_literal(error, "seek to start: "); + return FALSE; + } + + if (!json_parser_load_from_stream(parser, stream, NULL, error)) { + g_prefix_error_literal(error, "failed to parse RDFU firmware: "); + return FALSE; + } + json_root_node = json_parser_get_root(parser); + if (json_root_node == NULL || !JSON_NODE_HOLDS_OBJECT(json_root_node)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "RDFU firmware has no root"); + return FALSE; + } + json_obj = json_node_get_object(json_root_node); + if (!json_object_has_member(json_obj, "fileVersion")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "RDFU firmware has invalid format"); + return FALSE; + } + firmware_str = json_object_get_string_member_with_default(json_obj, "fileVersion", "0"); + if (!fu_strtoull(firmware_str, + &firmware_ver, + 1, + FU_LOGITECH_RDFU_FIRMWARE_VERSION, + FU_INTEGER_BASE_AUTO, + error)) + return FALSE; + + if (!json_object_has_member(json_obj, "contents")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "unable to find contents"); + return FALSE; + } + contents = json_object_get_array_member(json_obj, "contents"); + if (json_array_get_length(contents) < 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "empty contents array"); + return FALSE; + } + /* adding blocks to the entity FW */ + for (guint i = 0; i < json_array_get_length(contents); i++) { + JsonNode *node = json_array_get_element(contents, i); + g_autoptr(FuLogitechRdfuFirmware) entity_fw = + FU_LOGITECH_RDFU_FIRMWARE(g_object_new(FU_TYPE_LOGITECH_RDFU_FIRMWARE, NULL)); + if (!fu_logitech_rdfu_firmware_entry_add(entity_fw, node, error)) { + g_prefix_error(error, "RDFU firmware contents[%u]: ", i); + return FALSE; + } + if (!fu_firmware_add_image(firmware, FU_FIRMWARE(entity_fw), error)) + return FALSE; + } + + if (!json_object_has_member(json_obj, "payloads")) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "unable to find payloads"); + return FALSE; + } + + json_payloads_obj = json_object_get_object_member(json_obj, "payloads"); + payloads_list = json_object_get_members(json_payloads_obj); + + for (GList *l = payloads_list; l != NULL; l = g_list_next(l)) { + guint index = 0; + JsonArray *blocks; + FuLogitechRdfuFirmware *entity_fw = NULL; + g_autoptr(GPtrArray) images = fu_firmware_get_images(firmware); + + if (!g_ptr_array_find_with_equal_func(images, + l->data, + fu_logitech_rdfu_firmware_compare_payload, + &index)) + continue; + + entity_fw = (FuLogitechRdfuFirmware *)images->pdata[index]; + g_debug("found payload %s for entity %s", + (gchar *)l->data, + fu_firmware_get_id(FU_FIRMWARE(entity_fw))); + + json_obj = json_object_get_object_member(json_payloads_obj, l->data); + + if (!json_object_has_member(json_obj, "blocks")) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "payload %s has no blocks", + (gchar *)l->data); + return FALSE; + } + blocks = json_object_get_array_member(json_obj, "blocks"); + if (json_array_get_length(contents) < 1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "empty blocks for payload %s", + (gchar *)l->data); + return FALSE; + } + /* adding blocks to the entity FW */ + for (guint i = 0; i < json_array_get_length(blocks); i++) { + JsonNode *block = json_array_get_element(blocks, i); + if (!fu_logitech_rdfu_firmware_block_add(entity_fw, block, error)) { + g_prefix_error(error, "unable to parse block %u: ", i); + return FALSE; + } + } + g_debug("added payload %s with %u blocks", + entity_fw->payload_name, + entity_fw->blocks->len); + } + + /* success */ + return TRUE; +} + +static void +fu_logitech_rdfu_firmware_export(FuFirmware *firmware, + FuFirmwareExportFlags flags, + XbBuilderNode *bn) +{ + FuLogitechRdfuFirmware *self = FU_LOGITECH_RDFU_FIRMWARE(firmware); + + if (self->model_id != NULL) + fu_xmlb_builder_insert_kv(bn, "modelId", self->model_id); + if (self->payload_name != NULL) + fu_xmlb_builder_insert_kv(bn, "payload", self->payload_name); + if (self->magic != NULL) { + g_autofree gchar *magic = fu_byte_array_to_string(self->magic); + fu_xmlb_builder_insert_kv(bn, "magic", magic); + } + if (self->blocks != NULL) + fu_xmlb_builder_insert_kx(bn, "blocks", self->blocks->len); +} + +static void +fu_logitech_rdfu_firmware_finalize(GObject *object) +{ + FuLogitechRdfuFirmware *self = FU_LOGITECH_RDFU_FIRMWARE(object); + + g_free(self->model_id); + g_free(self->payload_name); + if (self->magic != NULL) + g_byte_array_unref(self->magic); + if (self->blocks != NULL) + g_ptr_array_unref(self->blocks); + + G_OBJECT_CLASS(fu_logitech_rdfu_firmware_parent_class)->finalize(object); +} + +static void +fu_logitech_rdfu_firmware_init(FuLogitechRdfuFirmware *self) +{ +} + +static void +fu_logitech_rdfu_firmware_class_init(FuLogitechRdfuFirmwareClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + object_class->finalize = fu_logitech_rdfu_firmware_finalize; + firmware_class->parse = fu_logitech_rdfu_firmware_parse; + firmware_class->export = fu_logitech_rdfu_firmware_export; +} diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-rdfu-firmware.h fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-rdfu-firmware.h --- fwupd-2.0.8/plugins/logitech-hidpp/fu-logitech-rdfu-firmware.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/fu-logitech-rdfu-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,29 @@ +/* + * Copyright 2024 Denis Pynkin + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_LOGITECH_RDFU_FIRMWARE (fu_logitech_rdfu_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuLogitechRdfuFirmware, + fu_logitech_rdfu_firmware, + FU, + LOGITECH_RDFU_FIRMWARE, + FuFirmware) + +struct _FuLogitechRdfuFirmwareClass { + FuFirmwareClass parent_class; +}; + +gchar * +fu_logitech_rdfu_firmware_get_model_id(FuLogitechRdfuFirmware *self, GError **error); + +GByteArray * +fu_logitech_rdfu_firmware_get_magic(FuLogitechRdfuFirmware *self, GError **error); + +GPtrArray * +fu_logitech_rdfu_firmware_get_blocks(FuLogitechRdfuFirmware *self, GError **error); diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/logitech-hidpp.quirk fwupd-2.0.20/plugins/logitech-hidpp/logitech-hidpp.quirk --- fwupd-2.0.8/plugins/logitech-hidpp/logitech-hidpp.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/logitech-hidpp.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -16,7 +16,6 @@ [HIDRAW\VEN_046D&DEV_AB07] Plugin = logitech_hidpp Name = Bolt Receiver -Vendor = Logitech GType = FuLogitechHidppDevice CounterpartGuid = HIDRAW\VEN_046D&DEV_C548 InstallDuration = 30 @@ -195,3 +194,10 @@ GType = FuLogitechHidppDevice Flags = add-radio,ble,rebind-attach,no-request-required InstallDuration = 270 + +# MX Mechanical (BLE direct) +[HIDRAW\VEN_046D&DEV_B34E] +Plugin = logitech_hidpp +GType = FuLogitechHidppDevice +Flags = ~add-radio,ble,no-request-required +InstallDuration = 650 diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/meson.build fwupd-2.0.20/plugins/logitech-hidpp/meson.build --- fwupd-2.0.8/plugins/logitech-hidpp/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,10 +1,12 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginLogitechHidpp"'] plugins += {meson.current_source_dir().split('/')[-1]: true} plugin_quirks += files('logitech-hidpp.quirk') plugin_builtin_logitech_hidpp = static_library('fu_plugin_logitech_hidpp', rustgen.process('fu-logitech-hidpp.rs'), + rustgen.process('fu-logitech-hidpp-rdfu.rs'), sources: [ 'fu-logitech-hidpp-plugin.c', 'fu-logitech-hidpp-bootloader.c', @@ -18,6 +20,7 @@ 'fu-logitech-hidpp-runtime-unifying.c', 'fu-logitech-hidpp-runtime-bolt.c', 'fu-logitech-hidpp-radio.c', + 'fu-logitech-rdfu-firmware.c', ], include_directories: plugin_incdirs, link_with: plugin_libs, @@ -58,6 +61,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ cargs, @@ -66,4 +70,3 @@ ) test('logitech-hidpp-self-test', e, env: env) endif -endif diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-bolt-receiver.json fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-bolt-receiver.json --- fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-bolt-receiver.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-bolt-receiver.json 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,7 @@ ] }, { - "url": "https://fwupd.org/downloads/c35ade1237da4e1ff90d031cc2b2218c32ee53c01b92b014aae2d2226aed2e35-Logitech-MPR05-MPR05.00_B0009.cab", + "url": "c35ade1237da4e1ff90d031cc2b2218c32ee53c01b92b014aae2d2226aed2e35-Logitech-MPR05-MPR05.00_B0009.cab", "components": [ { "version": "MPR05.00_B0009", @@ -27,7 +27,7 @@ ] }, { - "url": "https://fwupd.org/downloads/f1e3ba268ae4e1d3c029392d4f203a976970b162521296a2e9a9a31f314c0c46-Logitech-MPR05-MPR05.01_B0010.cab", + "url": "f1e3ba268ae4e1d3c029392d4f203a976970b162521296a2e9a9a31f314c0c46-Logitech-MPR05-MPR05.01_B0010.cab", "components": [ { "version": "MPR05.01_B0010", diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-k780.json fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-k780.json --- fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-k780.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-k780.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/454f1034f5efeb531163d90852ef33afd3fafeeb-Logitech-K780-MPK01.02_B0021.cab", + "url": "454f1034f5efeb531163d90852ef33afd3fafeeb-Logitech-K780-MPK01.02_B0021.cab", "components": [ { "version": "MPK01.02_B0021", @@ -14,7 +14,7 @@ ] }, { - "url": "https://fwupd.org/downloads/b0dffe84c6d3681e7ae5f27509781bc1cf924dd7-Logitech-K780-MPK01.03_B0024.cab", + "url": "b0dffe84c6d3681e7ae5f27509781bc1cf924dd7-Logitech-K780-MPK01.03_B0024.cab", "components": [ { "version": "MPK01.03_B0024", diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-m650.json fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-m650.json --- fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-m650.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-m650.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/6531c7a26d54f9dacbc24f81e8b26e37dd630fe22a417cd2ce6e13ea3b6fd105-Logitech-RBM16-RBM16.00_B0009.cab", + "url": "6531c7a26d54f9dacbc24f81e8b26e37dd630fe22a417cd2ce6e13ea3b6fd105-Logitech-RBM16-RBM16.00_B0009.cab", "components": [ { "version": "RBM16.00_B0009", @@ -15,7 +15,7 @@ ] }, { - "url": "https://fwupd.org/downloads/422d8c719d859b4ef321d95aa9e21f8d0d899ac924b9ca3ed76b1d745224e8e4-Logitech-RBM16-RBM16.00_B0010.cab", + "url": "422d8c719d859b4ef321d95aa9e21f8d0d899ac924b9ca3ed76b1d745224e8e4-Logitech-RBM16-RBM16.00_B0010.cab", "components": [ { "version": "RBM16.00_B0010", diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-m750.json fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-m750.json --- fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-m750.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-m750.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/597a12cca95b9dcf19e82e5add970a45bf658de8c82dc329cc271a6c50d68752-Logitech-RBM18-RBM18.00_B0010.cab", + "url": "597a12cca95b9dcf19e82e5add970a45bf658de8c82dc329cc271a6c50d68752-Logitech-RBM18-RBM18.00_B0010.cab", "components": [ { "version": "RBM18.00_B0010", diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-mr0077.json fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-mr0077.json --- fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-mr0077.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-mr0077.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": true, "steps": [ { - "url": "https://fwupd.org/downloads/7b86a6cb5747607f38e78259734dcf0d1afc02d2116b7386ac00c15e70eece72-Logitech-RBM14-RBM14_00_B0007.cab", + "url": "7b86a6cb5747607f38e78259734dcf0d1afc02d2116b7386ac00c15e70eece72-Logitech-RBM14-RBM14_00_B0007.cab", "components": [ { "version": "RBM14.00_B0007", @@ -15,7 +15,7 @@ ] }, { - "url": "https://fwupd.org/downloads/1cec33151e0f206df9b353a56e318adebb9a9ba9f330b452336eef169f5b1f3c-Logitech-RBM14-RBM14_00_B0008.cab", + "url": "1cec33151e0f206df9b353a56e318adebb9a9ba9f330b452336eef169f5b1f3c-Logitech-RBM14-RBM14_00_B0008.cab", "components": [ { "version": "RBM14.00_B0008", diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-rqr12-signed.json fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-rqr12-signed.json --- fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-rqr12-signed.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-rqr12-signed.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/2443cef8b1dae48751d3c60dab22210733e57036-Logitech-Unifying-RQR12.11_B0032.cab", - "emulation-url": "https://fwupd.org/downloads/5c4e16b209915a41bd3b6c24da67e868c7147a38e49ca3abc70d3efd8bef4feb-Logitech-Unifying-RQR12.11_B0032.zip", + "url": "2443cef8b1dae48751d3c60dab22210733e57036-Logitech-Unifying-RQR12.11_B0032.cab", + "emulation-url": "5c4e16b209915a41bd3b6c24da67e868c7147a38e49ca3abc70d3efd8bef4feb-Logitech-Unifying-RQR12.11_B0032.zip", "components": [ { "version": "RQR12.11_B0032", diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-rqr12.json fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-rqr12.json --- fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-rqr12.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-rqr12.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/6e5ab5961ec4c577bff198ebb465106e979cf686-Logitech-Unifying-RQR12.05_B0028.cab", - "emulation-url": "https://fwupd.org/downloads/b87fe1c0b562780e4ab05459422af6ce4bd5d8914c9c3c519b793d9baf6c52b2-Logitech-Unifying-RQR12.05_B0028.zip", + "url": "6e5ab5961ec4c577bff198ebb465106e979cf686-Logitech-Unifying-RQR12.05_B0028.cab", + "emulation-url": "b87fe1c0b562780e4ab05459422af6ce4bd5d8914c9c3c519b793d9baf6c52b2-Logitech-Unifying-RQR12.05_B0028.zip", "components": [ { "version": "RQR12.05_B0028", @@ -16,8 +16,8 @@ ] }, { - "url": "https://fwupd.org/downloads/938fec082652c603a1cdafde7cd25d76baadc70d-Logitech-Unifying-RQR12.07_B0029.cab", - "emulation-url": "https://fwupd.org/downloads/af83dc257e32b7a18656b3e11b269ab5badf303d5897c11d5f8ceb2d3117b70e-Logitech-Unifying-RQR12.07_B0029.zip", + "url": "938fec082652c603a1cdafde7cd25d76baadc70d-Logitech-Unifying-RQR12.07_B0029.cab", + "emulation-url": "af83dc257e32b7a18656b3e11b269ab5badf303d5897c11d5f8ceb2d3117b70e-Logitech-Unifying-RQR12.07_B0029.zip", "components": [ { "version": "RQR12.07_B0029", diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-rqr24-signed.json fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-rqr24-signed.json --- fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-rqr24-signed.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-rqr24-signed.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/6a0134766c26c73cc08f3d30f0bb84d94d035b50f702e39506abdcab9a8f658f-Logitech-Unifying-RQR24.11_B0036.cab", - "emulation-url": "https://fwupd.org/downloads/67a9b3088294dc0f7b2d5397ba2eb6c5cd514e6c2899f84a77cb5930e5bb17ab-Logitech-Unifying-RQR24.11_B0036.zip", + "url": "6a0134766c26c73cc08f3d30f0bb84d94d035b50f702e39506abdcab9a8f658f-Logitech-Unifying-RQR24.11_B0036.cab", + "emulation-url": "67a9b3088294dc0f7b2d5397ba2eb6c5cd514e6c2899f84a77cb5930e5bb17ab-Logitech-Unifying-RQR24.11_B0036.zip", "components": [ { "version": "RQR24.11_B0036", diff -Nru fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-rqr24.json fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-rqr24.json --- fwupd-2.0.8/plugins/logitech-hidpp/tests/logitech-rqr24.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-hidpp/tests/logitech-rqr24.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/82b90b2614a9a4d0aced1ab8a4a99e228c95585c-Logitech-Unifying-RQ024.03_B0027.cab", - "emulation-url": "https://fwupd.org/downloads/a441bd637ef26e23f8ce2d184d8a88ea63a75123c1a8375fc41701231980c922-Logitech-Unifying-RQ024.03_B0027.zip", + "url": "82b90b2614a9a4d0aced1ab8a4a99e228c95585c-Logitech-Unifying-RQ024.03_B0027.cab", + "emulation-url": "a441bd637ef26e23f8ce2d184d8a88ea63a75123c1a8375fc41701231980c922-Logitech-Unifying-RQ024.03_B0027.zip", "components": [ { "version": "RQR24.03_B0027", @@ -16,8 +16,8 @@ ] }, { - "url": "https://fwupd.org/downloads/4511b9b0d123bdbe8a2007233318ab215a59dfe6-Logitech-Unifying-RQR24.05_B0029.cab", - "emulation-url": "https://fwupd.org/downloads/56c390da7584e0beefc8d92171e3b16c5e8f9406ef00e5eff3ade4ddcd13d455-Logitech-Unifying-RQR24.05_B0029.zip", + "url": "4511b9b0d123bdbe8a2007233318ab215a59dfe6-Logitech-Unifying-RQR24.05_B0029.cab", + "emulation-url": "56c390da7584e0beefc8d92171e3b16c5e8f9406ef00e5eff3ade4ddcd13d455-Logitech-Unifying-RQR24.05_B0029.zip", "components": [ { "version": "RQR24.05_B0029", diff -Nru fwupd-2.0.8/plugins/logitech-rallysystem/README.md fwupd-2.0.20/plugins/logitech-rallysystem/README.md --- fwupd-2.0.8/plugins/logitech-rallysystem/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-rallysystem/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -42,10 +42,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.9.7`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Sanjay Sheth: @vcdmp diff -Nru fwupd-2.0.8/plugins/logitech-rallysystem/fu-logitech-rallysystem-audio-device.c fwupd-2.0.20/plugins/logitech-rallysystem/fu-logitech-rallysystem-audio-device.c --- fwupd-2.0.8/plugins/logitech-rallysystem/fu-logitech-rallysystem-audio-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-rallysystem/fu-logitech-rallysystem-audio-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -22,8 +22,8 @@ FU_TYPE_HIDRAW_DEVICE) static gboolean -fu_logitech_rallysystem_audio_device_set_version(FuLogitechRallysystemAudioDevice *self, - GError **error) +fu_logitech_rallysystem_audio_device_ensure_version(FuLogitechRallysystemAudioDevice *self, + GError **error) { guint8 buf[TOPOLOGY_DATA_LEN] = {0x3E, 0x0}; guint32 fwversion = 0; @@ -51,13 +51,13 @@ } static gboolean -fu_logitech_rallysystem_audio_device_set_serial(FuLogitechRallysystemAudioDevice *self, - GError **error) +fu_logitech_rallysystem_audio_device_ensure_serial(FuLogitechRallysystemAudioDevice *self, + GError **error) { guint8 buf_req[SERIAL_NUMBER_REQUEST_DATA_LEN] = {0x28, 0x85, 0x08, 0xBB, 0x1B, 0x00, 0x01, 0x30, 0, 0, 0, 0x0C}; guint8 buf_res[SERIAL_NUMBER_RESPONSE_DATA_LEN] = {0x29, 0x0}; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructAudioSerialNumber) st = NULL; g_autoptr(GString) serial = g_string_new(NULL); /* setup HID report for serial number request */ @@ -91,7 +91,7 @@ fu_struct_audio_serial_number_get_month(st), fu_struct_audio_serial_number_get_day(st)); for (guint i = 0x0; i < FU_STRUCT_AUDIO_SERIAL_NUMBER_SIZE_MAC_ADDRESS; i++) - g_string_append_printf(serial, "%02x", st->data[i]); + g_string_append_printf(serial, "%02x", st->buf->data[i]); fu_device_set_serial(FU_DEVICE(self), serial->str); /* success */ @@ -102,15 +102,15 @@ fu_logitech_rallysystem_audio_device_setup(FuDevice *device, GError **error) { FuLogitechRallysystemAudioDevice *self = FU_LOGITECH_RALLYSYSTEM_AUDIO_DEVICE(device); - if (!fu_logitech_rallysystem_audio_device_set_version(self, error)) + if (!fu_logitech_rallysystem_audio_device_ensure_version(self, error)) return FALSE; - if (!fu_logitech_rallysystem_audio_device_set_serial(self, error)) + if (!fu_logitech_rallysystem_audio_device_ensure_serial(self, error)) return FALSE; return TRUE; } static void -fu_logitech_rallysystem_audio_device_set_progress(FuDevice *self, FuProgress *progress) +fu_logitech_rallysystem_audio_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/logitech-rallysystem/fu-logitech-rallysystem-tablehub-device.c fwupd-2.0.20/plugins/logitech-rallysystem/fu-logitech-rallysystem-tablehub-device.c --- fwupd-2.0.8/plugins/logitech-rallysystem/fu-logitech-rallysystem-tablehub-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-rallysystem/fu-logitech-rallysystem-tablehub-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -85,7 +85,7 @@ FU_LOGITECH_RALLYSYSTEM_TABLEHUB_DEVICE_IOCTL_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to send using bulk transfer: "); + g_prefix_error_literal(error, "failed to send using bulk transfer: "); return FALSE; } if (bufsz != actual_length) { @@ -115,7 +115,7 @@ timeout, NULL, error)) { - g_prefix_error(error, "failed to receive using bulk transfer: "); + g_prefix_error_literal(error, "failed to receive using bulk transfer: "); return FALSE; } if (bufsz != actual_length) { @@ -175,7 +175,7 @@ { FuLogitechRallysystemTablehubDevice *self = FU_LOGITECH_RALLYSYSTEM_TABLEHUB_DEVICE(device); guint8 buf[FU_STRUCT_USB_PROGRESS_RESPONSE_SIZE] = {0x0}; - g_autoptr(GByteArray) st_res = NULL; + g_autoptr(FuStructUsbProgressResponse) st_res = NULL; if (!fu_logitech_rallysystem_tablehub_device_recv( self, @@ -183,7 +183,7 @@ sizeof(buf), FU_LOGITECH_RALLYSYSTEM_TABLEHUB_DEVICE_IOCTL_PROGRESS_TIMEOUT, error)) { - g_prefix_error(error, "failed to get progress report: "); + g_prefix_error_literal(error, "failed to get progress report: "); return FALSE; } st_res = fu_struct_usb_progress_response_parse(buf, sizeof(buf), 0x0, error); @@ -213,8 +213,9 @@ gsize streamsz = 0; guint8 buf[FU_STRUCT_USB_FIRMWARE_DOWNLOAD_RESPONSE_SIZE] = {0x0}; g_autoptr(GInputStream) stream = NULL; - g_autoptr(GByteArray) st_req = fu_struct_usb_firmware_download_request_new(); - g_autoptr(GByteArray) st_res = NULL; + g_autoptr(FuStructUsbFirmwareDownloadRequest) st_req = + fu_struct_usb_firmware_download_request_new(); + g_autoptr(FuStructUsbFirmwareDownloadResponse) st_res = NULL; /* progress */ fu_progress_set_id(progress, G_STRLOC); @@ -233,11 +234,14 @@ if (!fu_struct_usb_firmware_download_request_set_fw_version(st_req, fu_device_get_version(device), error)) { - g_prefix_error(error, "failed to copy download mode payload: "); + g_prefix_error_literal(error, "failed to copy download mode payload: "); return FALSE; } - if (!fu_logitech_rallysystem_tablehub_device_send(self, st_req->data, st_req->len, error)) { - g_prefix_error(error, "failed to set download mode: "); + if (!fu_logitech_rallysystem_tablehub_device_send(self, + st_req->buf->data, + st_req->buf->len, + error)) { + g_prefix_error_literal(error, "failed to set download mode: "); return FALSE; } if (!fu_logitech_rallysystem_tablehub_device_recv( @@ -246,7 +250,7 @@ sizeof(buf), FU_LOGITECH_RALLYSYSTEM_TABLEHUB_DEVICE_IOCTL_TIMEOUT, error)) { - g_prefix_error( + g_prefix_error_literal( error, "failed to receive set download mode response: please reboot the device: "); return FALSE; @@ -272,7 +276,7 @@ 1000, NULL, error)) { - g_prefix_error(error, "failed to wait for 100pc: "); + g_prefix_error_literal(error, "failed to wait for 100pc: "); return FALSE; } fu_progress_step_done(progress); @@ -294,11 +298,14 @@ { FuLogitechRallysystemTablehubDevice *self = FU_LOGITECH_RALLYSYSTEM_TABLEHUB_DEVICE(device); guint8 buf[FU_STRUCT_USB_INIT_RESPONSE_SIZE] = {0x0}; - g_autoptr(GByteArray) st_req = fu_struct_usb_init_request_new(); - g_autoptr(GByteArray) st_res = NULL; + g_autoptr(FuStructUsbInitRequest) st_req = fu_struct_usb_init_request_new(); + g_autoptr(FuStructUsbInitResponse) st_res = NULL; - if (!fu_logitech_rallysystem_tablehub_device_send(self, st_req->data, st_req->len, error)) { - g_prefix_error(error, "failed to send init packet: "); + if (!fu_logitech_rallysystem_tablehub_device_send(self, + st_req->buf->data, + st_req->buf->len, + error)) { + g_prefix_error_literal(error, "failed to send init packet: "); return FALSE; } if (!fu_logitech_rallysystem_tablehub_device_recv( @@ -307,12 +314,12 @@ sizeof(buf), FU_LOGITECH_RALLYSYSTEM_TABLEHUB_DEVICE_IOCTL_TIMEOUT, error)) { - g_prefix_error(error, "failed to receive init packet: "); + g_prefix_error_literal(error, "failed to receive init packet: "); return FALSE; } st_res = fu_struct_usb_init_response_parse(buf, sizeof(buf), 0x0, error); if (st_res == NULL) { - g_prefix_error(error, "failed to get correct init packet: "); + g_prefix_error_literal(error, "failed to get correct init packet: "); return FALSE; } @@ -326,8 +333,8 @@ FuLogitechRallysystemTablehubDevice *self = FU_LOGITECH_RALLYSYSTEM_TABLEHUB_DEVICE(device); guint8 buf[FU_STRUCT_USB_READ_VERSION_RESPONSE_SIZE] = {0x0}; g_autofree gchar *fw_version = NULL; - g_autoptr(GByteArray) st_req = fu_struct_usb_read_version_request_new(); - g_autoptr(GByteArray) st_res = NULL; + g_autoptr(FuStructUsbReadVersionRequest) st_req = fu_struct_usb_read_version_request_new(); + g_autoptr(FuStructUsbReadVersionResponse) st_res = NULL; /* FuUsbDevice->setup */ if (!FU_DEVICE_CLASS(fu_logitech_rallysystem_tablehub_device_parent_class) @@ -340,15 +347,19 @@ 5, NULL, error)) { - g_prefix_error(error, "failed to write init packet: please reboot the device: "); + g_prefix_error_literal(error, + "failed to write init packet: please reboot the device: "); return FALSE; } /* query tablehub firmware version */ - if (!fu_logitech_rallysystem_tablehub_device_send(self, st_req->data, st_req->len, error)) { - g_prefix_error(error, - "failed to send tablehub firmware version request: " - "please reboot the device: "); + if (!fu_logitech_rallysystem_tablehub_device_send(self, + st_req->buf->data, + st_req->buf->len, + error)) { + g_prefix_error_literal(error, + "failed to send tablehub firmware version request: " + "please reboot the device: "); return FALSE; } if (!fu_logitech_rallysystem_tablehub_device_recv( @@ -357,9 +368,9 @@ sizeof(buf), FU_LOGITECH_RALLYSYSTEM_TABLEHUB_DEVICE_IOCTL_TIMEOUT, error)) { - g_prefix_error(error, - "failed to get response for tablehub firmware " - "version request: please reboot the device: "); + g_prefix_error_literal(error, + "failed to get response for tablehub firmware " + "version request: please reboot the device: "); return FALSE; } st_res = fu_struct_usb_read_version_response_parse(buf, sizeof(buf), 0x0, error); @@ -373,7 +384,7 @@ } static void -fu_logitech_rallysystem_tablehub_device_set_progress(FuDevice *self, FuProgress *progress) +fu_logitech_rallysystem_tablehub_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/logitech-rallysystem/meson.build fwupd-2.0.20/plugins/logitech-rallysystem/meson.build --- fwupd-2.0.8/plugins/logitech-rallysystem/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-rallysystem/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginLogitechRallysystem"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -16,4 +17,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/logitech-scribe/README.md fwupd-2.0.20/plugins/logitech-scribe/README.md --- fwupd-2.0.8/plugins/logitech-scribe/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-scribe/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -45,10 +45,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.8.8`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Sanjay Sheth: @vcdmp diff -Nru fwupd-2.0.8/plugins/logitech-scribe/fu-logitech-scribe-device.c fwupd-2.0.20/plugins/logitech-scribe/fu-logitech-scribe-device.c --- fwupd-2.0.8/plugins/logitech-scribe/fu-logitech-scribe-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-scribe/fu-logitech-scribe-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -92,7 +92,7 @@ BULK_TRANSFER_TIMEOUT, cancellable, error)) { - g_prefix_error(error, "failed to send using bulk transfer: "); + g_prefix_error_literal(error, "failed to send using bulk transfer: "); return FALSE; } return TRUE; @@ -127,7 +127,7 @@ timeout, NULL, error)) { - g_prefix_error(error, "failed to receive: "); + g_prefix_error_literal(error, "failed to receive: "); return FALSE; } return TRUE; @@ -440,7 +440,7 @@ return FALSE; /* re-open with new device set */ - locker = fu_device_locker_new(usb_device, error); + locker = fu_device_locker_new(FU_DEVICE(usb_device), error); if (locker == NULL) return FALSE; @@ -483,6 +483,7 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "device-write-blocks"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "end-transfer"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "uninit"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "sleep"); /* get default image */ stream = fu_firmware_get_stream(firmware, error); @@ -495,8 +496,9 @@ MAX_RETRIES, usb_device, error)) { - g_prefix_error(error, - "failed to write init transfer packet: please reboot the device: "); + g_prefix_error_literal( + error, + "failed to write init transfer packet: please reboot the device: "); return FALSE; } fu_progress_step_done(progress); @@ -510,7 +512,7 @@ FU_LOGITECH_SCRIBE_USB_CMD_START_TRANSFER, start_pkt, error)) { - g_prefix_error(error, "failed to write start transfer packet: "); + g_prefix_error_literal(error, "failed to write start transfer packet: "); return FALSE; } fu_progress_step_done(progress); @@ -539,7 +541,7 @@ FU_LOGITECH_SCRIBE_USB_CMD_END_TRANSFER, end_pkt, error)) { - g_prefix_error(error, "failed to write end transfer packet: "); + g_prefix_error_literal(error, "failed to write end transfer packet: "); return FALSE; } fu_progress_step_done(progress); @@ -561,6 +563,9 @@ * image file pushed. Device validates and uploads new image on inactive partition. Reboots * wait for RemoveDelay duration */ + fu_device_sleep_full(FU_DEVICE(self), 60 * 1000, fu_progress_get_child(progress)); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + fu_progress_step_done(progress); /* success! */ return TRUE; @@ -593,7 +598,7 @@ error)) return FALSE; - /* little-endian data. MinorVersion byte 0, MajorVersion byte 1, BuildVersion byte 3 & 2 */ + /* nocheck:endian -- MinorVersion byte 0, MajorVersion byte 1, BuildVersion byte 3 & 2 */ fwversion = (query_data[1] << 24) + (query_data[0] << 16) + (query_data[3] << 8) + query_data[2]; fu_device_set_version_raw(FU_DEVICE(self), fwversion); @@ -627,7 +632,7 @@ } static void -fu_logitech_scribe_device_set_progress(FuDevice *self, FuProgress *progress) +fu_logitech_scribe_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -651,6 +656,8 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_retry_set_delay(FU_DEVICE(self), 1000); + fu_device_set_remove_delay(FU_DEVICE(self), 2 * 60 * 1000); + fu_device_set_install_duration(FU_DEVICE(self), 120); } static void diff -Nru fwupd-2.0.8/plugins/logitech-scribe/logitech-scribe.quirk fwupd-2.0.20/plugins/logitech-scribe/logitech-scribe.quirk --- fwupd-2.0.8/plugins/logitech-scribe/logitech-scribe.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-scribe/logitech-scribe.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,2 @@ [VIDEO4LINUX\VEN_046D&DEV_08E2] Plugin = logitech_scribe -InstallDuration = 120 -RemoveDelay = 120000 diff -Nru fwupd-2.0.8/plugins/logitech-scribe/meson.build fwupd-2.0.20/plugins/logitech-scribe/meson.build --- fwupd-2.0.8/plugins/logitech-scribe/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-scribe/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginLogitechScribe"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -15,4 +16,10 @@ c_args: cargs, dependencies: plugin_deps, ) -endif + +enumeration_data += files( + 'tests/logi-scribe-setup.json', +) +device_tests += files( + 'tests/logi-scribe.json', +) diff -Nru fwupd-2.0.8/plugins/logitech-scribe/tests/logi-scribe-setup.json fwupd-2.0.20/plugins/logitech-scribe/tests/logi-scribe-setup.json --- fwupd-2.0.8/plugins/logitech-scribe/tests/logi-scribe-setup.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-scribe/tests/logi-scribe-setup.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,94 @@ +{ + "FwupdVersion": "2.0.13", + "UsbDevices": [ + { + "Created": "2025-06-24T16:38:54.738341Z", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2/1-2:1.0/video4linux/video0", + "DeviceFile": "/dev/video0", + "Subsystem": "video4linux", + "Vendor": 1133, + "Model": 2274, + "Events": [ + { + "Id": "ReadAttr:Attr=name", + "Data": "Logitech Scribe" + }, + { + "Id": "ReadAttr:Attr=index", + "Data": "0" + }, + { + "Id": "GetBackendParent:Subsystem=usb:usb_device", + "GType": "FuUsbDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:14.0/usb1/1-2", + "PhysicalId": "1-2" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "usb" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "usb" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=10\nDEVNAME=bus/usb/001/011\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=46d/8e2/100\nTYPE=239/2/1\nBUSNUM=001\nDEVNUM=011" + }, + { + "Id": "ReadProp:Key=DEVNAME", + "Data": "bus/usb/001/011" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadProp:Key=BUSNUM", + "Data": "001" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=10\nDEVNAME=bus/usb/001/011\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=46d/8e2/100\nTYPE=239/2/1\nBUSNUM=001\nDEVNUM=011" + }, + { + "Id": "ReadProp:Key=DEVNUM", + "Data": "011" + }, + { + "Id": "LoadDescriptor:basename=descriptors", + "Data": "EgEAAu8CAUBtBOIIAAEBAgMBCQIJAgUBBID6CAsAAg4DAAAJBAAAAQ4BAAANJAEAAbkAAGzcAgEBEiQCAQECAAAAAAAAAAADAAAACyQFAgEAQAIAAAAJJAMDAQEAAgAdJAYF8l29qJgaTkeN0NkmctGU+g8BAgT/gwdAABokBgjkjmdpD0HbQKhQdCDX2CQOAgECASEAGiQGDUxlbVo1fk5NgQ0GnRXg95sBAQIBAQAbJAYOSvd8qXzEq0amrSVmuu+ApgkBAgL/AQAaJAYP2LxJQ2UptUWK0kYeYA4vKggBAgH/AAcFgQMQAAgFJQMQAAkEAQAADgIAAA8kAQKzAIIAAwAAAAEAABskBAECWVVZMgAAEACAAACqADibcRABAAAAAB4kBQEAgAJoAQDASwMAgJcGAAgHACosCgABKiwKAB4kBQIAAAXQAgAAyggAAMoIACAcACosCgABKiwKAAskBgICAQEAAAAAHiQHAQAABdACAAAvDQAAXhoAIBwAKiwKAAEqLAoAHiQHAgCABzgEAMCpHQCAUzsASD8AKiwKAAEqLAoABiQNAQEECQQBAQEOAgAABwWCBQAEAQkEAQIBDgIAAAcFggUAFAEJBAIAAv9lAQYHBQECAAIABwWDAgACAAkEAwAC/2YBBwcFAgIAAgAHBYQCAAIACQQEAAIDAAAICSEBAQABIngABwWFA0AABAcFAwNAAAQ=" + }, + { + "Id": "LoadDescriptor:basename=bos_descriptors" + }, + { + "Id": "Ioctl:Request=0x80685600,Data=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0x68", + "DataOut": "dXZjdmlkZW8AAAAAAAAAAExvZ2l0ZWNoIFNjcmliZQAAAAAAAAAAAAAAAAAAAAAAdXNiLTAwMDA6MDA6MTQuMC0yAAAAAAAAAAAAAAAAAAAMCAYAAQCghAEAIAQAAAAAAAAAAAAAAAA=" + }, + { + "Id": "Ioctl:Request=0xc0107521,Unit=0x08,Selector=0x01,Query=0x85,Data=AAA=,Length=0x2", + "DataOut": "BAA=" + }, + { + "Id": "Ioctl:Request=0xc0107521,Unit=0x08,Selector=0x01,Query=0x81,Data=AAAAAA==,Length=0x4", + "DataOut": "AQECAA==" + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/logitech-scribe/tests/logi-scribe.json fwupd-2.0.20/plugins/logitech-scribe/tests/logi-scribe.json --- fwupd-2.0.8/plugins/logitech-scribe/tests/logi-scribe.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-scribe/tests/logi-scribe.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,20 @@ +{ + "name": "Logi Scribe", + "interactive": false, + "platform-architectures": [ + "x86_64" + ], + "steps": [ + { + "emulation-file": "@enumeration_datadir@/logi-scribe-setup.json", + "components": [ + { + "version": "1.1.2", + "guids": [ + "e06054e4-2d72-516c-938f-f0cb57e20c7d" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/logitech-tap/README.md fwupd-2.0.20/plugins/logitech-tap/README.md --- fwupd-2.0.8/plugins/logitech-tap/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-tap/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -50,10 +50,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.9.2`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Sanjay Sheth: @vcdmp diff -Nru fwupd-2.0.8/plugins/logitech-tap/fu-logitech-tap-hdmi-device.c fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-hdmi-device.c --- fwupd-2.0.8/plugins/logitech-tap/fu-logitech-tap-hdmi-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-hdmi-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,6 +12,7 @@ #include #include "fu-logitech-tap-hdmi-device.h" +#include "fu-logitech-tap-sensor-device.h" #define FU_LOGITECH_TAP_HDMI_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ #define XU_INPUT_DATA_LEN 8 @@ -224,7 +225,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "initiate query packet was too large at 0x%x bytes: ", + "initiate query packet was too large at 0x%x bytes", data_len); return FALSE; } @@ -443,7 +444,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "version query packet was too large at 0x%x bytes: ", + "version query packet was too large at 0x%x bytes", bufsz); return FALSE; } @@ -511,7 +512,7 @@ } static void -fu_logitech_tap_hdmi_device_set_progress(FuDevice *self, FuProgress *progress) +fu_logitech_tap_hdmi_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -526,6 +527,7 @@ { fu_device_add_protocol(FU_DEVICE(self), "com.logitech.hardware.tap"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_LOGITECH_TAP_SENSOR_DEVICE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_retry_set_delay(FU_DEVICE(self), 1000); diff -Nru fwupd-2.0.8/plugins/logitech-tap/fu-logitech-tap-plugin.c fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-plugin.c --- fwupd-2.0.8/plugins/logitech-tap/fu-logitech-tap-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -36,10 +36,12 @@ dev, FU_LOGITECH_TAP_HDMI_DEVICE_FLAG_SENSOR_NEEDS_REBOOT)) && self->hdmi_device != NULL) { + FuLogitechTapSensorDevice *proxy; g_debug("device needs reboot"); - if (!fu_logitech_tap_sensor_device_reboot_device( - FU_LOGITECH_TAP_SENSOR_DEVICE(fu_device_get_proxy(dev)), - error)) + proxy = FU_LOGITECH_TAP_SENSOR_DEVICE(fu_device_get_proxy(dev, error)); + if (proxy == NULL) + return FALSE; + if (!fu_logitech_tap_sensor_device_reboot_device(proxy, error)) return FALSE; fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); break; diff -Nru fwupd-2.0.8/plugins/logitech-tap/fu-logitech-tap-sensor-device.c fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-sensor-device.c --- fwupd-2.0.8/plugins/logitech-tap/fu-logitech-tap-sensor-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-sensor-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,23 +9,10 @@ #include #include #include -#ifdef HAVE_IOCTL_H -#include -#endif - -#include #include "fu-logitech-tap-sensor-device.h" #include "fu-logitech-tap-struct.h" -#define FU_LOGITECH_TAP_SENSOR_DEVICE_IOCTL_TIMEOUT 50000 /* ms */ - -#ifndef HIDIOCGINPUT -#define HIDIOCGINPUT(len) _IOC(_IOC_READ, 'H', 0x0A, len) -#endif - -const guint kLogiDefaultSensorSleepIntervalMs = 50; - struct _FuLogitechTapSensorDevice { FuHidrawDevice parent_instance; }; @@ -46,16 +33,12 @@ datasz, FU_IOCTL_FLAG_RETRY, &error_local)) { - g_autoptr(FuIoctl) ioctl = fu_udev_device_ioctl_new(FU_UDEV_DEVICE(self)); g_debug("failed to send get request, retrying: %s", error_local->message); - if (!fu_ioctl_execute(ioctl, - HIDIOCGINPUT(datasz), - data, - datasz, - NULL, - FU_LOGITECH_TAP_SENSOR_DEVICE_IOCTL_TIMEOUT, - FU_IOCTL_FLAG_RETRY, - error)) + if (!fu_hidraw_device_get_input(FU_HIDRAW_DEVICE(self), + data, + datasz, + FU_IOCTL_FLAG_RETRY, + error)) return FALSE; } @@ -64,7 +47,7 @@ } static gboolean -fu_logitech_tap_sensor_device_enable_tde(FuDevice *device, GError **error) +fu_logitech_tap_sensor_device_enable_tde_cb(FuDevice *device, GError **error) { FuLogitechTapSensorDevice *self = FU_LOGITECH_TAP_SENSOR_DEVICE(device); g_autoptr(FuStructLogitechTapSensorHidReq) st = fu_struct_logitech_tap_sensor_hid_req_new(); @@ -78,14 +61,14 @@ FU_LOGITECH_TAP_SENSOR_HID_TDE_MODE_ENABLE); return fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), - st->data, - st->len, + st->buf->data, + st->buf->len, FU_IOCTL_FLAG_RETRY, error); } static gboolean -fu_logitech_tap_sensor_device_disable_tde(FuDevice *device, GError **error) +fu_logitech_tap_sensor_device_disable_tde_cb(FuDevice *device, GError **error) { FuLogitechTapSensorDevice *self = FU_LOGITECH_TAP_SENSOR_DEVICE(device); g_autoptr(FuStructLogitechTapSensorHidReq) st = fu_struct_logitech_tap_sensor_hid_req_new(); @@ -99,8 +82,8 @@ FU_LOGITECH_TAP_SENSOR_HID_TDE_MODE_DISABLE); return fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), - st->data, - st->len, + st->buf->data, + st->buf->len, FU_IOCTL_FLAG_RETRY, error); } @@ -122,11 +105,10 @@ return FALSE; /* enable/disable TDE mode */ - locker = - fu_device_locker_new_full(FU_DEVICE(self), - (FuDeviceLockerFunc)fu_logitech_tap_sensor_device_enable_tde, - (FuDeviceLockerFunc)fu_logitech_tap_sensor_device_disable_tde, - error); + locker = fu_device_locker_new_full(FU_DEVICE(self), + fu_logitech_tap_sensor_device_enable_tde_cb, + fu_logitech_tap_sensor_device_disable_tde_cb, + error); if (locker == NULL) return FALSE; @@ -140,8 +122,8 @@ st, FU_LOGITECH_TAP_SENSOR_HID_REBOOT_PWR); if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), - st->data, - st->len, + st->buf->data, + st->buf->len, FU_IOCTL_FLAG_RETRY, error)) return FALSE; @@ -150,8 +132,8 @@ st, FU_LOGITECH_TAP_SENSOR_HID_REBOOT_RST); if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), - st->data, - st->len, + st->buf->data, + st->buf->len, FU_IOCTL_FLAG_RETRY, error)) return FALSE; @@ -165,8 +147,8 @@ st, FU_LOGITECH_TAP_SENSOR_HID_REBOOT_PWR); if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), - st->data, - st->len, + st->buf->data, + st->buf->len, FU_IOCTL_FLAG_RETRY, error)) return FALSE; @@ -177,8 +159,8 @@ st, FU_LOGITECH_TAP_SENSOR_HID_REBOOT_RST); if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), - st->data, - st->len, + st->buf->data, + st->buf->len, FU_IOCTL_FLAG_RETRY, error)) return FALSE; @@ -190,7 +172,7 @@ } static gboolean -fu_logitech_tap_sensor_device_set_version(FuLogitechTapSensorDevice *self, GError **error) +fu_logitech_tap_sensor_device_ensure_version(FuLogitechTapSensorDevice *self, GError **error) { guint32 version = 0; g_autoptr(FuStructLogitechTapSensorHidReq) st_req = @@ -207,20 +189,20 @@ FU_LOGITECH_TAP_SENSOR_HID_GET_CMD_VERSION); /* setup HID report to query current device version */ if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), - st_req->data, - st_req->len, + st_req->buf->data, + st_req->buf->len, FU_IOCTL_FLAG_RETRY, error)) return FALSE; if (!fu_logitech_tap_sensor_device_get_feature(self, - (guint8 *)st_res->data, - st_res->len, + (guint8 *)st_res->buf->data, + st_res->buf->len, error)) return FALSE; /* MinorVersion byte 3, MajorVersion byte 4, BuildVersion byte 2 & 1 */ - if (!fu_memread_uint32_safe((guint8 *)st_res->data, - st_res->len, + if (!fu_memread_uint32_safe((guint8 *)st_res->buf->data, + st_res->buf->len, 0x01, &version, G_LITTLE_ENDIAN, @@ -233,7 +215,7 @@ } static gboolean -fu_logitech_tap_sensor_device_set_serial(FuLogitechTapSensorDevice *self, GError **error) +fu_logitech_tap_sensor_device_ensure_serial(FuLogitechTapSensorDevice *self, GError **error) { g_autoptr(FuDeviceLocker) locker = NULL; g_autoptr(GString) serial_number = g_string_new(NULL); @@ -257,22 +239,22 @@ FU_LOGITECH_TAP_SENSOR_HID_SERIAL_NUMBER_SET_REPORT_BYTE4); /* enable/disable TDE mode */ - locker = - fu_device_locker_new_full(FU_DEVICE(self), - (FuDeviceLockerFunc)fu_logitech_tap_sensor_device_enable_tde, - (FuDeviceLockerFunc)fu_logitech_tap_sensor_device_disable_tde, - error); + locker = fu_device_locker_new_full( + FU_DEVICE(self), + (FuDeviceLockerFunc)fu_logitech_tap_sensor_device_enable_tde_cb, + (FuDeviceLockerFunc)fu_logitech_tap_sensor_device_disable_tde_cb, + error); if (locker == NULL) return FALSE; /* setup HID report for serial number */ if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), - st_req->data, - st_req->len, + st_req->buf->data, + st_req->buf->len, FU_IOCTL_FLAG_RETRY, error)) return FALSE; - fu_device_sleep(FU_DEVICE(self), kLogiDefaultSensorSleepIntervalMs); /* 50 ms */ + fu_device_sleep(FU_DEVICE(self), 50); /* ms */ /* serial number is a 12-byte-string that is stored in MCU */ /* each get request fetches 1 word (4 bytes), so iterate 3 times */ for (int index = 1; index <= 3; index++) { @@ -284,16 +266,16 @@ FU_LOGITECH_TAP_SENSOR_HID_GET_CMD_SERIAL_NUMBER); if (!fu_logitech_tap_sensor_device_get_feature(self, - (guint8 *)st_res->data, - st_res->len, + (guint8 *)st_res->buf->data, + st_res->buf->len, error)) return FALSE; g_string_append_printf(serial_number, "%c%c%c%c", - st_res->data[1], - st_res->data[2], - st_res->data[3], - st_res->data[4]); + st_res->buf->data[1], + st_res->buf->data[2], + st_res->buf->data[3], + st_res->buf->data[4]); } fu_device_set_serial(FU_DEVICE(self), serial_number->str); @@ -306,15 +288,15 @@ { FuLogitechTapSensorDevice *self = FU_LOGITECH_TAP_SENSOR_DEVICE(device); - if (!fu_logitech_tap_sensor_device_set_version(self, error)) + if (!fu_logitech_tap_sensor_device_ensure_version(self, error)) return FALSE; - if (!fu_logitech_tap_sensor_device_set_serial(self, error)) + if (!fu_logitech_tap_sensor_device_ensure_serial(self, error)) return FALSE; return TRUE; } static void -fu_logitech_tap_sensor_device_set_progress(FuDevice *self, FuProgress *progress) +fu_logitech_tap_sensor_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/logitech-tap/fu-logitech-tap-touch-device.c fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-touch-device.c --- fwupd-2.0.8/plugins/logitech-tap/fu-logitech-tap-touch-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-touch-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,13 +6,8 @@ #include "config.h" -#include #include #include -#ifdef HAVE_IOCTL_H -#include -#endif - #include #include "fu-logitech-tap-struct.h" @@ -41,9 +36,6 @@ #define FU_LOGITECH_TAP_TOUCH_SYSTEM_READY 0x50 /* wait and retry if device not ready */ -/* usb bus type */ -#define FU_LOGITECH_TAP_TOUCH_DEVICE_INFO_BUS_TYPE 0x03 - struct _FuLogitechTapTouchDevice { FuHidrawDevice parent_instance; }; @@ -69,7 +61,7 @@ 0x00U, &report_id, error)) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed to read report id: "); + g_prefix_error_literal(error, "failed to read report id: "); return FALSE; } if (!ret) { @@ -93,18 +85,18 @@ static gboolean fu_logitech_tap_touch_device_hid_transfer(FuLogitechTapTouchDevice *self, - GByteArray *st_req, + FuStructLogitechTapTouchHidReq *st_req, guint delay, GByteArray *buf_res, GError **error) { - fu_byte_array_set_size(st_req, FU_LOGITECH_TAP_TOUCH_HID_SET_DATA_LEN, 0x0); + fu_byte_array_set_size(st_req->buf, FU_LOGITECH_TAP_TOUCH_HID_SET_DATA_LEN, 0x0); if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), - st_req->data, - st_req->len, + st_req->buf->data, + st_req->buf->len, FU_IOCTL_FLAG_RETRY, error)) { - g_prefix_error(error, "failed to send packet to touch panel: "); + g_prefix_error_literal(error, "failed to send packet to touch panel: "); return FALSE; } /* check if there is a corresponding get report request. @@ -118,7 +110,8 @@ delay, buf_res, error)) { - g_prefix_error(error, "failed to receive packet from touch panel: "); + g_prefix_error_literal(error, + "failed to receive packet from touch panel: "); return FALSE; } } @@ -128,7 +121,7 @@ } static gboolean -fu_logitech_tap_touch_device_enable_tde(FuDevice *device, GError **error) +fu_logitech_tap_touch_device_enable_tde_cb(FuDevice *device, GError **error) { FuLogitechTapTouchDevice *self = FU_LOGITECH_TAP_TOUCH_DEVICE(device); g_autoptr(FuStructLogitechTapTouchHidReq) st = fu_struct_logitech_tap_touch_hid_req_new(); @@ -139,12 +132,12 @@ fu_struct_logitech_tap_touch_hid_req_set_cmd( st, FU_STRUCT_LOGITECH_TAP_TOUCH_HID_CMD_SET_TDE_TEST_MODE); - fu_byte_array_append_uint8(st, 0x01); + fu_byte_array_append_uint8(st->buf, 0x01); return fu_logitech_tap_touch_device_hid_transfer(self, st, 0, NULL, error); } static gboolean -fu_logitech_tap_touch_device_disable_tde(FuDevice *device, GError **error) +fu_logitech_tap_touch_device_disable_tde_cb(FuDevice *device, GError **error) { FuLogitechTapTouchDevice *self = FU_LOGITECH_TAP_TOUCH_DEVICE(device); g_autoptr(FuStructLogitechTapTouchHidReq) st = fu_struct_logitech_tap_touch_hid_req_new(); @@ -154,7 +147,7 @@ fu_struct_logitech_tap_touch_hid_req_set_cmd( st, FU_STRUCT_LOGITECH_TAP_TOUCH_HID_CMD_SET_TDE_TEST_MODE); - fu_byte_array_append_uint8(st, 0x00); + fu_byte_array_append_uint8(st->buf, 0x00); return fu_logitech_tap_touch_device_hid_transfer(self, st, 0, NULL, error); } @@ -180,12 +173,12 @@ fu_struct_logitech_tap_touch_hid_req_set_cmd( st, FU_STRUCT_LOGITECH_TAP_TOUCH_HID_CMD_WRITE_ENABLE); - fu_byte_array_append_uint8(st, 0x5A); - fu_byte_array_append_uint8(st, 0xA5); + fu_byte_array_append_uint8(st->buf, 0x5A); + fu_byte_array_append_uint8(st->buf, 0xA5); if (end > 0) { - fu_byte_array_append_uint8(st, write_ap ? 0x00 : 0x01); - fu_byte_array_append_uint24(st, end, G_BIG_ENDIAN); - fu_byte_array_append_uint24(st, checksum, G_BIG_ENDIAN); + fu_byte_array_append_uint8(st->buf, write_ap ? 0x00 : 0x01); + fu_byte_array_append_uint24(st->buf, end, G_BIG_ENDIAN); + fu_byte_array_append_uint24(st->buf, checksum, G_BIG_ENDIAN); } /* hid report to enable writing */ @@ -447,36 +440,29 @@ fu_logitech_tap_touch_device_setup(FuDevice *device, GError **error) { FuLogitechTapTouchDevice *self = FU_LOGITECH_TAP_TOUCH_DEVICE(device); - struct hidraw_devinfo hid_raw_info = {0x0}; g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(FuIoctl) ioctl = fu_udev_device_ioctl_new(FU_UDEV_DEVICE(self)); if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) g_debug("entering in BL MODE"); - if (!fu_ioctl_execute(ioctl, - HIDIOCGRAWINFO, - (guint8 *)&hid_raw_info, - sizeof(hid_raw_info), - NULL, - FU_LOGITECH_TAP_TOUCH_IOCTL_TIMEOUT, - FU_IOCTL_FLAG_NONE, - error)) + + /* FuHidrawDevice->setup */ + if (!FU_DEVICE_CLASS(fu_logitech_tap_touch_device_parent_class)->setup(device, error)) return FALSE; - if (hid_raw_info.bustype != FU_LOGITECH_TAP_TOUCH_DEVICE_INFO_BUS_TYPE) { + + if (fu_hidraw_device_get_bus_type(FU_HIDRAW_DEVICE(self)) != FU_HIDRAW_BUS_TYPE_USB) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "incorrect bustype=0x%x, expected usb", - hid_raw_info.bustype); + fu_hidraw_device_get_bus_type(FU_HIDRAW_DEVICE(self))); return FALSE; } /* enable/disable TDE mode */ - locker = - fu_device_locker_new_full(FU_DEVICE(self), - (FuDeviceLockerFunc)fu_logitech_tap_touch_device_enable_tde, - (FuDeviceLockerFunc)fu_logitech_tap_touch_device_disable_tde, - error); + locker = fu_device_locker_new_full(FU_DEVICE(self), + fu_logitech_tap_touch_device_enable_tde_cb, + fu_logitech_tap_touch_device_disable_tde_cb, + error); if (locker == NULL) return FALSE; @@ -506,7 +492,7 @@ } /* cannot use locker, device goes into bootloader mode here, looses connectivity */ - if (!fu_logitech_tap_touch_device_enable_tde(device, error)) + if (!fu_logitech_tap_touch_device_enable_tde_cb(device, error)) return FALSE; if (!fu_logitech_tap_touch_device_get_mcu_mode(self, &mcu_mode, error)) @@ -581,8 +567,8 @@ return FALSE; } - /* set the physical ID */ - return fu_udev_device_set_physical_id(FU_UDEV_DEVICE(device), "hid", error); + /* success */ + return TRUE; } static gboolean @@ -642,11 +628,11 @@ fu_struct_logitech_tap_touch_hid_req_set_cmd( st, FU_STRUCT_LOGITECH_TAP_TOUCH_HID_CMD_WRITE_DATA); - g_byte_array_append(st, chunk_buf->data, chunk_buf->len); + g_byte_array_append(st->buf, chunk_buf->data, chunk_buf->len); /* resize the last packet */ if ((i == (fu_chunk_array_length(chunks) - 1)) && (fu_chunk_get_data_sz(chk) < FU_LOGITECH_TAP_TOUCH_TRANSFER_BLOCK_SIZE)) - fu_byte_array_set_size(st, 37, in_ap ? 0xFF : 0x0); + fu_byte_array_set_size(st->buf, 37, in_ap ? 0xFF : 0x0); if (!fu_logitech_tap_touch_device_hid_transfer(self, st, 0, NULL, error)) return FALSE; fu_device_sleep(FU_DEVICE(self), 2); @@ -676,7 +662,7 @@ 5, NULL, error)) { - g_prefix_error(error, "failed to crc for %s, device busy", in_ap ? "AP" : "DF"); + g_prefix_error(error, "failed to crc for %s, device busy: ", in_ap ? "AP" : "DF"); return FALSE; } if (!fu_logitech_tap_touch_device_get_crc(self, &device_checksum, 4, error)) @@ -743,7 +729,7 @@ fu_struct_logitech_tap_touch_hid_req_set_cmd( st, FU_STRUCT_LOGITECH_TAP_TOUCH_HID_CMD_WRITE_DATA); - fu_byte_array_set_size(st, + fu_byte_array_set_size(st->buf, 37, 0xFF); /* 4 (req header) + 1 (cmd) + FU_LOGITECH_TAP_TOUCH_TRANSFER_BLOCK_SIZE (data buffer) */ @@ -790,11 +776,11 @@ g_autoptr(FuDeviceLocker) locker = NULL; /* enable/disable TDE mode */ - locker = - fu_device_locker_new_full(FU_DEVICE(self), - (FuDeviceLockerFunc)fu_logitech_tap_touch_device_enable_tde, - (FuDeviceLockerFunc)fu_logitech_tap_touch_device_disable_tde, - error); + locker = fu_device_locker_new_full( + FU_DEVICE(self), + (FuDeviceLockerFunc)fu_logitech_tap_touch_device_enable_tde_cb, + (FuDeviceLockerFunc)fu_logitech_tap_touch_device_disable_tde_cb, + error); if (locker == NULL) return FALSE; @@ -808,7 +794,7 @@ } static void -fu_logitech_tap_touch_device_set_progress(FuDevice *self, FuProgress *progress) +fu_logitech_tap_touch_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/logitech-tap/fu-logitech-tap-touch-firmware.c fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-touch-firmware.c --- fwupd-2.0.8/plugins/logitech-tap/fu-logitech-tap-touch-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-tap/fu-logitech-tap-touch-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -122,7 +122,7 @@ static gboolean fu_logitech_tap_touch_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuLogitechTapTouchFirmware *self = FU_LOGITECH_TAP_TOUCH_FIRMWARE(firmware); @@ -196,9 +196,10 @@ if (!fu_input_stream_find(stream, (const guint8 *)image_end_magic, strlen(image_end_magic), + 0x0, &ap_end_offset, error)) { - g_prefix_error(error, "failed to find anchor: "); + g_prefix_error_literal(error, "failed to find anchor: "); return FALSE; } ap_end = ap_end_offset + 32 + 2; @@ -220,7 +221,8 @@ fu_firmware_set_offset(ap_img, ap_end); if (!fu_firmware_set_stream(ap_img, ap_stream, error)) return FALSE; - fu_firmware_add_image(firmware, ap_img); + if (!fu_firmware_add_image(firmware, ap_img, error)) + return FALSE; /* calculate basic checksum for dataflash (DF) */ df_stream = fu_partial_input_stream_new(stream, df_start, df_end - df_start, error); @@ -235,7 +237,8 @@ fu_firmware_set_offset(df_img, df_end); if (!fu_firmware_set_stream(df_img, df_stream, error)) return FALSE; - fu_firmware_add_image(firmware, df_img); + if (!fu_firmware_add_image(firmware, df_img, error)) + return FALSE; /* success */ return TRUE; diff -Nru fwupd-2.0.8/plugins/logitech-tap/meson.build fwupd-2.0.20/plugins/logitech-tap/meson.build --- fwupd-2.0.8/plugins/logitech-tap/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/logitech-tap/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginLogitechTap"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -18,4 +19,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/mediatek-scaler/README.md fwupd-2.0.20/plugins/mediatek-scaler/README.md --- fwupd-2.0.8/plugins/mediatek-scaler/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mediatek-scaler/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -106,11 +106,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.9.6`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Crag Wang: @CragW -* Greg Lo: @GregLo007 diff -Nru fwupd-2.0.8/plugins/mediatek-scaler/fu-mediatek-scaler-device.c fwupd-2.0.20/plugins/mediatek-scaler/fu-mediatek-scaler-device.c --- fwupd-2.0.8/plugins/mediatek-scaler/fu-mediatek-scaler-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mediatek-scaler/fu-mediatek-scaler-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -59,17 +59,18 @@ static gboolean fu_mediatek_scaler_device_ddc_write(FuMediatekScalerDevice *self, - GByteArray *st_req, + FuStructDdcCmd *st_req, GError **error) { - FuI2cDevice *i2c_proxy = FU_I2C_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuI2cDevice *proxy; guint8 chksum = 0; g_autoptr(GByteArray) ddc_msgbox_write = g_byte_array_new(); - const guint8 ddc_wfmt[] = {FU_DDC_I2C_ADDR_HOST_DEVICE, st_req->len | DDC_DATA_LEN_DFT}; + const guint8 ddc_wfmt[] = {FU_DDC_I2C_ADDR_HOST_DEVICE, + st_req->buf->len | DDC_DATA_LEN_DFT}; /* write = addr_src, sizeof(cmd + op + data), cmd, op, data, checksum */ g_byte_array_append(ddc_msgbox_write, ddc_wfmt, sizeof(ddc_wfmt)); - g_byte_array_append(ddc_msgbox_write, st_req->data, st_req->len); + g_byte_array_append(ddc_msgbox_write, st_req->buf->data, st_req->buf->len); chksum ^= FU_DDC_I2C_ADDR_DISPLAY_DEVICE; for (gsize i = 0; i < ddc_msgbox_write->len; i++) @@ -82,13 +83,18 @@ ddc_msgbox_write->data, ddc_msgbox_write->len); - return fu_i2c_device_write(i2c_proxy, ddc_msgbox_write->data, ddc_msgbox_write->len, error); + proxy = FU_I2C_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + return fu_i2c_device_write(proxy, ddc_msgbox_write->data, ddc_msgbox_write->len, error); } static GByteArray * -fu_mediatek_scaler_device_ddc_read(FuMediatekScalerDevice *self, GByteArray *st_req, GError **error) +fu_mediatek_scaler_device_ddc_read(FuMediatekScalerDevice *self, + FuStructDdcCmd *st_req, + GError **error) { - FuI2cDevice *i2c_proxy = FU_I2C_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuI2cDevice *proxy; guint8 buf[0x40] = {0x00}; /* default 64 bytes */ gsize report_data_sz = 0; guint8 checksum = 0; @@ -103,7 +109,10 @@ fu_device_sleep(FU_DEVICE(self), FU_MEDIATEK_SCALER_DDC_MSG_DELAY_MS); /* read into tmp buffer */ - if (!fu_i2c_device_read(i2c_proxy, buf, sizeof(buf), error)) + proxy = FU_I2C_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + if (!fu_i2c_device_read(proxy, buf, sizeof(buf), error)) return NULL; /* read buffer = addr(src) + length + data + checksum */ @@ -160,7 +169,8 @@ } /* truncate the last byte which is the checksum value */ - g_byte_array_append(st_res, buf, report_data_sz + 2); + if (!fu_byte_array_append_safe(st_res, buf, sizeof(buf), 0x0, report_data_sz + 2, error)) + return NULL; /* print the raw data */ fu_dump_raw(G_LOG_DOMAIN, "DDC/CI read report", st_res->data, st_res->len); @@ -172,11 +182,11 @@ FuDdcciPriority priority, GError **error) { - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); g_autoptr(GError) error_local = NULL; fu_struct_ddc_cmd_set_vcp_code(st_req, FU_DDC_VCP_CODE_PRIORITY); - fu_byte_array_append_uint8(st_req, priority); + fu_byte_array_append_uint8(st_req->buf, priority); if (!fu_mediatek_scaler_device_ddc_write(self, st_req, &error_local)) { g_set_error(error, FWUPD_ERROR, @@ -194,8 +204,8 @@ static gboolean fu_mediatek_scaler_device_display_is_connected(FuMediatekScalerDevice *self, GError **error) { - FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self)); - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + FuDevice *proxy; + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); g_autoptr(GByteArray) st_res = NULL; g_autoptr(GError) error_local = NULL; guint8 randval_req = 0; @@ -203,8 +213,8 @@ guint8 randval2 = self->randval_cnt++; fu_struct_ddc_cmd_set_vcp_code(st_req, FU_DDC_VCP_CODE_SUM); - fu_byte_array_append_uint8(st_req, randval1); - fu_byte_array_append_uint8(st_req, randval2); + fu_byte_array_append_uint8(st_req->buf, randval1); + fu_byte_array_append_uint8(st_req->buf, randval2); st_res = fu_mediatek_scaler_device_ddc_read(self, st_req, &error_local); if (st_res == NULL) { g_set_error(error, @@ -228,6 +238,9 @@ return FALSE; } + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; g_info("found mediatek display controller: %s, i2c-dev: %s", fu_udev_device_get_device_file(FU_UDEV_DEVICE(self)), fu_udev_device_get_device_file(FU_UDEV_DEVICE(proxy))); @@ -244,16 +257,15 @@ } static gchar * -fu_mediatek_scaler_device_get_hardware_version(FuDevice *device, GError **error) +fu_mediatek_scaler_device_get_hardware_version(FuMediatekScalerDevice *self, GError **error) { - FuMediatekScalerDevice *self = FU_MEDIATEK_SCALER_DEVICE(device); - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); g_autoptr(GByteArray) st_res = NULL; guint8 verbuf[4] = {0}; /* get the hardware version */ fu_struct_ddc_cmd_set_vcp_code(st_req, FU_DDC_VCP_CODE_VERSION); - fu_byte_array_append_uint8(st_req, 0x00); + fu_byte_array_append_uint8(st_req->buf, 0x00); st_res = fu_mediatek_scaler_device_ddc_read(self, st_req, error); if (st_res == NULL) return NULL; @@ -273,11 +285,11 @@ { guint32 version_raw = 0x0; g_autoptr(GByteArray) st_res = NULL; - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); /* get the installed firmware version */ fu_struct_ddc_cmd_set_vcp_code(st_req, FU_DDC_VCP_CODE_VERSION); - fu_byte_array_append_uint8(st_req, 0x01); + fu_byte_array_append_uint8(st_req->buf, 0x01); st_res = fu_mediatek_scaler_device_ddc_read(self, st_req, error); if (st_res == NULL) return FALSE; @@ -296,17 +308,17 @@ fu_mediatek_scaler_device_open(FuDevice *device, GError **error) { FuMediatekScalerDevice *self = FU_MEDIATEK_SCALER_DEVICE(device); - FuI2cDevice *i2c_proxy = FU_I2C_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuI2cDevice *proxy; /* FuUdevDevice->open */ if (!FU_DEVICE_CLASS(fu_mediatek_scaler_device_parent_class)->open(device, error)) return FALSE; /* set the target address -- should be safe */ - if (!fu_i2c_device_set_address(i2c_proxy, - FU_DDC_I2C_ADDR_DISPLAY_DEVICE >> 1, - FALSE, - error)) + proxy = FU_I2C_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + if (!fu_i2c_device_set_address(proxy, FU_DDC_I2C_ADDR_DISPLAY_DEVICE >> 1, FALSE, error)) return FALSE; /* we know this is a Mediatek scaler now */ @@ -323,13 +335,13 @@ fu_mediatek_scaler_device_close(FuDevice *device, GError **error) { FuMediatekScalerDevice *self = FU_MEDIATEK_SCALER_DEVICE(device); - FuI2cDevice *i2c_proxy = FU_I2C_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuI2cDevice *proxy; /* set the target address */ - if (!fu_i2c_device_set_address(i2c_proxy, - FU_DDC_I2C_ADDR_DISPLAY_DEVICE >> 1, - FALSE, - error)) + proxy = FU_I2C_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + if (!fu_i2c_device_set_address(proxy, FU_DDC_I2C_ADDR_DISPLAY_DEVICE >> 1, FALSE, error)) return FALSE; /* reset DDC priority */ @@ -343,9 +355,8 @@ static gboolean fu_mediatek_scaler_device_verify_controller_type(FuMediatekScalerDevice *self, GError **error) { - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); g_autoptr(GByteArray) st_res = NULL; - g_autoptr(GError) error_local = NULL; guint32 controller_type = 0; fu_struct_ddc_cmd_set_opcode(st_req, FU_DDC_OPCODE_GET_VCP); @@ -384,7 +395,7 @@ /* verify the controller type */ if (!fu_mediatek_scaler_device_verify_controller_type(self, error)) { - g_prefix_error(error, "invalid controller type: "); + g_prefix_error_literal(error, "invalid controller type: "); return FALSE; } @@ -397,7 +408,7 @@ return FALSE; /* set hardware version */ - hw_ver = fu_mediatek_scaler_device_get_hardware_version(device, error); + hw_ver = fu_mediatek_scaler_device_get_hardware_version(self, error); if (hw_ver == NULL) return FALSE; fu_device_add_instance_str(device, "HWVER", hw_ver); @@ -413,20 +424,20 @@ } static gboolean -fu_mediatek_scaler_device_set_recv_info(FuDevice *device, gsize fw_sz, GError **error) +fu_mediatek_scaler_device_set_recv_info(FuMediatekScalerDevice *self, gsize fw_sz, GError **error) { - FuMediatekScalerDevice *self = FU_MEDIATEK_SCALER_DEVICE(device); - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); fu_struct_ddc_cmd_set_vcp_code(st_req, FU_DDC_VCP_CODE_UPDATE_PREP); - fu_byte_array_append_uint32(st_req, fw_sz, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(st_req->buf, fw_sz, G_LITTLE_ENDIAN); return fu_mediatek_scaler_device_ddc_write(self, st_req, error); } static gboolean -fu_mediatek_scaler_device_get_data_ack_size(FuDevice *device, guint32 *ack_sz, GError **error) +fu_mediatek_scaler_device_get_data_ack_size(FuMediatekScalerDevice *self, + guint32 *ack_sz, + GError **error) { - FuMediatekScalerDevice *self = FU_MEDIATEK_SCALER_DEVICE(device); - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); g_autoptr(GByteArray) st_res = NULL; fu_struct_ddc_cmd_set_vcp_code(st_req, FU_DDC_VCP_CODE_UPDATE_ACK); @@ -441,26 +452,26 @@ static gboolean fu_mediatek_scaler_device_prepare_update_cb(FuDevice *device, gpointer user_data, GError **error) { + FuMediatekScalerDevice *self = FU_MEDIATEK_SCALER_DEVICE(device); guint32 acksz = 0; gsize fw_sz = *(gsize *)user_data; /* set the file length that to be transmit*/ - if (!fu_mediatek_scaler_device_set_recv_info(device, fw_sz, error)) + if (!fu_mediatek_scaler_device_set_recv_info(self, fw_sz, error)) return FALSE; /* extra delay time needed */ - fu_device_sleep(device, 100); + fu_device_sleep(FU_DEVICE(self), 100); /* device accepted the file length for data transition */ - if (!fu_mediatek_scaler_device_get_data_ack_size(device, &acksz, error)) + if (!fu_mediatek_scaler_device_get_data_ack_size(self, &acksz, error)) return FALSE; if (fw_sz != (gsize)acksz) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "device nak the incoming filesize, requested: %" G_GSIZE_FORMAT - ", ack: %u", - fw_sz, + "device nak the incoming filesize, requested: 0x%x, ack: %u", + (guint)fw_sz, acksz); return FALSE; } @@ -469,15 +480,15 @@ } static gboolean -fu_mediatek_scaler_device_prepare_update(FuDevice *device, gsize fw_sz, GError **error) +fu_mediatek_scaler_device_prepare_update(FuMediatekScalerDevice *self, gsize fw_sz, GError **error) { - if (!fu_device_retry_full(device, + if (!fu_device_retry_full(FU_DEVICE(self), fu_mediatek_scaler_device_prepare_update_cb, DDC_RW_MAX_RETRY_CNT, 10, /* ms */ &fw_sz, error)) { - g_prefix_error(error, "failed to prepare update: "); + g_prefix_error_literal(error, "failed to prepare update: "); return FALSE; } return TRUE; @@ -489,24 +500,24 @@ g_autoptr(FuChunkArray) chk_slices = NULL; g_autoptr(GBytes) chk_bytes = fu_chunk_get_bytes(chk); - /* smaller slices to accodomate pch variants */ + /* smaller slices to accommodate pch variants */ chk_slices = fu_chunk_array_new_from_bytes(chk_bytes, FU_CHUNK_ADDR_OFFSET_NONE, FU_CHUNK_PAGESZ_NONE, DDC_DATA_FRAGEMENT_SIZE); for (guint i = 0; i < fu_chunk_array_length(chk_slices); i++) { g_autoptr(FuChunk) chk_slice = NULL; - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); chk_slice = fu_chunk_array_index(chk_slices, i, error); if (chk_slice == NULL) return FALSE; fu_struct_ddc_cmd_set_vcp_code(st_req, FU_DDC_VCP_CODE_SET_DATA); - g_byte_array_append(st_req, + g_byte_array_append(st_req->buf, fu_chunk_get_data(chk_slice), (guint)fu_chunk_get_data_sz(chk_slice)); if (!fu_mediatek_scaler_device_ddc_write(self, st_req, error)) { - g_prefix_error(error, "failed to send firmware to device: "); + g_prefix_error_literal(error, "failed to send firmware to device: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), FU_MEDIATEK_SCALER_CHUNK_SENT_DELAY_MS); @@ -520,7 +531,7 @@ guint32 *pktcnt, GError **error) { - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); g_autoptr(GByteArray) st_res = NULL; fu_struct_ddc_cmd_set_vcp_code(st_req, FU_DDC_VCP_CODE_GET_STAGED); @@ -545,7 +556,7 @@ guint32 pktcnt = 0; if (!fu_mediatek_scaler_device_get_staged_data(self, &chksum, &pktcnt, error)) { - g_prefix_error(error, "failed to get the staged data: "); + g_prefix_error_literal(error, "failed to get the staged data: "); return FALSE; } @@ -578,9 +589,9 @@ static gboolean fu_mediatek_scaler_device_run_isp(FuMediatekScalerDevice *self, guint16 chksum, GError **error) { - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); fu_struct_ddc_cmd_set_vcp_code(st_req, FU_DDC_VCP_CODE_COMMIT_FW); - fu_byte_array_append_uint16(st_req, chksum, G_LITTLE_ENDIAN); + fu_byte_array_append_uint16(st_req->buf, chksum, G_LITTLE_ENDIAN); return fu_mediatek_scaler_device_ddc_write(self, st_req, error); } @@ -595,16 +606,16 @@ return FALSE; if (!(fu_mediatek_scaler_device_run_isp(self, sum16, error))) { - g_prefix_error(error, "failed to commit firmware: "); + g_prefix_error_literal(error, "failed to commit firmware: "); return FALSE; } return TRUE; } static gboolean -fu_mediatek_scaler_device_set_isp_reboot(FuMediatekScalerDevice *self, GError **error) +fu_mediatek_scaler_device_isp_reboot(FuMediatekScalerDevice *self, GError **error) { - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); g_autoptr(GError) error_local = NULL; /* device will reboot after this, so the write will timed out fail */ @@ -625,7 +636,7 @@ guint8 *isp_status, GError **error) { - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); g_autoptr(GByteArray) st_res = NULL; fu_struct_ddc_cmd_set_vcp_code(st_req, FU_DDC_VCP_CODE_GET_ISP_MODE); @@ -663,9 +674,9 @@ } static gboolean -fu_mediatek_scaler_device_verify(FuDevice *device, GError **error) +fu_mediatek_scaler_device_verify(FuMediatekScalerDevice *self, GError **error) { - if (!fu_device_retry_full(device, + if (!fu_device_retry_full(FU_DEVICE(self), fu_mediatek_scaler_device_display_is_connected_cb, FU_MEDIATEK_SCALER_DEVICE_PRESENT_RETRY, FU_MEDIATEK_SCALER_DEVICE_POLL_INTERVAL, @@ -678,7 +689,7 @@ } /* ensure isp status */ - if (!fu_device_retry_full(device, + if (!fu_device_retry_full(FU_DEVICE(self), fu_mediatek_scaler_device_is_update_success_cb, FU_MEDIATEK_SCALER_DEVICE_PRESENT_RETRY, FU_MEDIATEK_SCALER_DEVICE_POLL_INTERVAL, @@ -704,14 +715,14 @@ guint32 sent_sz, GError **error) { - g_autoptr(GByteArray) st_req = fu_struct_ddc_cmd_new(); + g_autoptr(FuStructDdcCmd) st_req = fu_struct_ddc_cmd_new(); fu_struct_ddc_cmd_set_vcp_code(st_req, FU_DDC_VCP_CODE_SET_DATA_FF); - fu_byte_array_append_uint32(st_req, sent_sz, G_LITTLE_ENDIAN); + fu_byte_array_append_uint32(st_req->buf, sent_sz, G_LITTLE_ENDIAN); return fu_mediatek_scaler_device_ddc_write(self, st_req, error); } static gboolean -fu_mediatek_scaler_device_write_chunk(FuDevice *device, gpointer user_data, GError **error) +fu_mediatek_scaler_device_write_chunk_cb(FuDevice *device, gpointer user_data, GError **error) { FuMediatekScalerDevice *self = FU_MEDIATEK_SCALER_DEVICE(device); FuMediatekScalerWriteChunkHelper *helper = (FuMediatekScalerWriteChunkHelper *)user_data; @@ -778,12 +789,12 @@ helper_wchunk.chk = chk; helper_wchunk.sent_sz = sent_sz; if (!fu_device_retry_full(FU_DEVICE(self), - fu_mediatek_scaler_device_write_chunk, + fu_mediatek_scaler_device_write_chunk_cb, DDC_RW_MAX_RETRY_CNT, FU_MEDIATEK_SCALER_DDC_MSG_DELAY_MS, &helper_wchunk, error)) { - g_prefix_error(error, "writing chunk exceeded the maximum retries"); + g_prefix_error_literal(error, "writing chunk exceeded maximum retries: "); return FALSE; } @@ -823,7 +834,7 @@ /* prepare the device to accept firmware image */ if (!fu_input_stream_size(stream, &fw_size, error)) return FALSE; - if (!fu_mediatek_scaler_device_prepare_update(device, fw_size, error)) + if (!fu_mediatek_scaler_device_prepare_update(self, fw_size, error)) return FALSE; fu_progress_step_done(progress); @@ -838,14 +849,14 @@ fu_progress_step_done(progress); /* verify display and ISP status; for bank 1 devices 0xF8 will do self-reboot */ - if (!fu_mediatek_scaler_device_verify(device, error)) + if (!fu_mediatek_scaler_device_verify(self, error)) return FALSE; fu_progress_step_done(progress); /* for bank 2 update */ if (fu_device_has_private_flag(device, FWUPD_MEDIATEK_SCALER_FLAG_BANK2_ONLY)) { /* send reboot command to take effect immediately */ - if (!(fu_mediatek_scaler_device_set_isp_reboot(self, error))) + if (!(fu_mediatek_scaler_device_isp_reboot(self, error))) return FALSE; /* ensure device is back */ @@ -871,7 +882,7 @@ fu_mediatek_scaler_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) firmware = fu_mediatek_scaler_firmware_new(); @@ -886,13 +897,13 @@ } static gchar * -fu_mediatek_scaler_device_convert_version(FuDevice *self, guint64 version_raw) +fu_mediatek_scaler_device_convert_version(FuDevice *device, guint64 version_raw) { return fu_mediatek_scaler_version_to_string(version_raw); } static void -fu_mediatek_scaler_device_set_progress(FuDevice *self, FuProgress *progress) +fu_mediatek_scaler_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -914,8 +925,9 @@ fu_device_set_vendor(FU_DEVICE(self), "Mediatek"); fu_device_add_protocol(FU_DEVICE(self), "com.mediatek.scaler"); fu_device_set_name(FU_DEVICE(self), "Display Controller"); - fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_VIDEO_DISPLAY); fu_device_set_firmware_size_max(FU_DEVICE(self), FU_MEDIATEK_SCALER_FW_SIZE_MAX); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_I2C_DEVICE); fu_device_register_private_flag(FU_DEVICE(self), FWUPD_MEDIATEK_SCALER_FLAG_BANK2_ONLY); } diff -Nru fwupd-2.0.8/plugins/mediatek-scaler/fu-mediatek-scaler-firmware.c fwupd-2.0.20/plugins/mediatek-scaler/fu-mediatek-scaler-firmware.c --- fwupd-2.0.8/plugins/mediatek-scaler/fu-mediatek-scaler-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mediatek-scaler/fu-mediatek-scaler-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,7 +17,7 @@ #define MTK_FW_TIMESTAMP_TIME_SIZE 8 struct _FuMediatekScalerFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; }; G_DEFINE_TYPE(FuMediatekScalerFirmware, fu_mediatek_scaler_firmware, FU_TYPE_FIRMWARE) @@ -25,7 +25,7 @@ static gboolean fu_mediatek_scaler_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint32 ver_tmp = 0x0; @@ -72,6 +72,7 @@ static void fu_mediatek_scaler_firmware_init(FuMediatekScalerFirmware *self) { + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION); } static void diff -Nru fwupd-2.0.8/plugins/mediatek-scaler/mediatek-scaler.quirk fwupd-2.0.20/plugins/mediatek-scaler/mediatek-scaler.quirk --- fwupd-2.0.8/plugins/mediatek-scaler/mediatek-scaler.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mediatek-scaler/mediatek-scaler.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -4,3 +4,9 @@ [DRM\VEN_DEL&DEV_7430] Plugin = mediatek_scaler + +[DRM\VEN_DEL&DEV_9410] +Plugin = mediatek_scaler + +[DRM\VEN_DEL&DEV_9411] +Plugin = mediatek_scaler diff -Nru fwupd-2.0.8/plugins/mediatek-scaler/meson.build fwupd-2.0.20/plugins/mediatek-scaler/meson.build --- fwupd-2.0.8/plugins/mediatek-scaler/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mediatek-scaler/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginMediatekScaler"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -21,4 +22,3 @@ ], dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/meson.build fwupd-2.0.20/plugins/meson.build --- fwupd-2.0.8/plugins/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -35,16 +35,16 @@ 'ccgx': false, 'ccgx-dmc': false, 'cfu': false, - 'ch341a': false, - 'ch347': false, 'corsair': false, 'cpu': false, 'cros-ec': false, 'dell': false, 'dell-dock': false, 'dell-kestrel': false, + 'devlink': false, 'dfu': false, 'ebitdo': false, + 'egis-moc': false, 'elantp': false, 'elanfp': false, 'elan-kbd': false, @@ -54,25 +54,29 @@ 'flashrom': false, 'focalfp': false, 'fpc': false, + 'framework-qmk': false, 'fresco-pd': false, 'genesys': false, 'genesys-gl32xx': false, 'goodix-moc': false, 'goodix-tp': false, 'gpio': false, - 'hailuck': false, 'hpi-cfu': false, 'huddly-usb': false, 'hughski-colorhug': false, + 'ilitek-its': false, + 'intel-amt': false, 'intel-cvs': false, 'intel-gsc': false, - 'intel-me': false, + 'intel-mchi': false, + 'intel-mkhi': false, 'intel-usb4': false, 'iommu': false, 'jabra': false, 'jabra-file': false, 'jabra-gnp': false, 'kinetic-dp': false, + 'legion-hid': false, 'legion-hid2': false, 'lenovo-thinklmi': false, 'linux-display': false, @@ -92,13 +96,13 @@ 'mtd': false, 'nordic-hid': false, 'nvme': false, - 'optionrom': false, 'parade-lspcon': false, 'parade-usbhub': false, 'pci-bcr': false, 'pci-mei': false, 'pci-psp': false, 'pixart-rf': false, + 'pixart-tp': false, 'powerd': false, 'qc-firehose': false, 'qc-s5gen2': false, @@ -106,10 +110,10 @@ 'realtek-mst': false, 'redfish': false, 'rp-pico': false, - 'rts54hid': false, 'rts54hub': false, 'steelseries': false, 'scsi': false, + 'snapd-uefi': false, 'synaptics-cape': false, 'synaptics-cxaudio': false, 'synaptics-mst': false, @@ -118,7 +122,7 @@ 'synaptics-vmm9': false, 'system76-launch': false, 'test': false, - 'telink-dfu':false, + 'telink-dfu': false, 'thelio-io': false, 'thunderbolt': false, 'ti-tps6598x': false, @@ -138,6 +142,8 @@ 'vli': false, 'wacom-raw': false, 'wacom-usb': false, + 'wch-ch341a': false, + 'wch-ch347': false, 'wistron-dock': false, } @@ -145,7 +151,7 @@ umockdev_ioctls = [] device_tests = [] enumeration_data = [] -foreach plugin, enabled: plugins +foreach plugin, enabled : plugins subdir(plugin) endforeach @@ -159,19 +165,31 @@ envs.set('PYTHONPATH', join_paths(meson.project_source_root(), 'data', 'tests')) envs.set('STATE_DIRECTORY', join_paths(meson.project_build_root(), 'state')) - foreach suite: umockdev_tests - r = run_command(unittest_inspector, suite, - check: true, env: envs) + foreach suite : umockdev_tests + r = run_command( + unittest_inspector, + suite, + check: true, + env: envs, + ) unit_tests = r.stdout().strip().split('\n') - foreach ut: unit_tests - test(ut, python3, args: [suite, ut], is_parallel: false, env: envs) + foreach ut : unit_tests + test( + ut, + python3, + args: [suite, ut], + is_parallel: false, + env: envs, + ) endforeach - install_data(suite, + install_data( + suite, install_dir: installed_test_datadir, ) endforeach - foreach ioctl: umockdev_ioctls - install_data(ioctl, + foreach ioctl : umockdev_ioctls + install_data( + ioctl, install_dir: installed_test_datadir, ) endforeach diff -Nru fwupd-2.0.8/plugins/modem-manager/README.md fwupd-2.0.20/plugins/modem-manager/README.md --- fwupd-2.0.8/plugins/modem-manager/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -29,7 +29,7 @@ ### ModemManagerFirehoseProgFile -Firehose program file to use during the switch to EDL (Emergency Download) mode. +Firehose program file to use during the QCDM switch to EDL (Emergency Download) mode. Since: 1.8.10 @@ -43,16 +43,19 @@ Since: 1.9.8 -### `Flags=detach-at-fastboot-has-no-response` +### `Flags=make-serial-raw` -If no AT response is expected when entering fastboot mode. +When using a port that was not previously configured by ModemManager (an 'ignored' port), we need +to configure the serial port manually. -### `Flags=uninhibit-modemmanager-after-fastboot-reboot` +This is automatically added for any ports being used from `mm_modem_get_ignored_ports()` and should +not need to be added in quirk files. -After entering the fastboot state, the modem cannot execute the attach method in the MM plugin -plugin plugin. -The shadow device needs to be used to uninhibit the modem when `fu_mm_plugin_udev_uevent_cb` -detects it. +Since: 2.0.14 + +### `Flags=detach-at-fastboot-has-no-response` + +If no AT response is expected when entering fastboot mode. ## Vendor ID Security @@ -133,10 +136,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.2.6`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Aleksander Morgado: @aleksander0m diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-cinterion-fdl-updater.c fwupd-2.0.20/plugins/modem-manager/fu-cinterion-fdl-updater.c --- fwupd-2.0.8/plugins/modem-manager/fu-cinterion-fdl-updater.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-cinterion-fdl-updater.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,357 +0,0 @@ -/* - * Copyright 2024 TDT AG - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include -#include -#ifdef HAVE_TERMIOS_H -#include -#endif - -#include "fu-cinterion-fdl-updater-struct.h" -#include "fu-cinterion-fdl-updater.h" - -#ifdef HAVE_TERMIOS_H -#define FU_CINTERION_FDL_DEFAULT_BAUDRATE B115200 -#endif -#define FU_CINTERION_FDL_MAX_READ_RETRIES 100 -#define FU_CINTERION_FDL_MAX_WRITE_RETRIES 10 -#define FU_CINTERION_FDL_SIZE_BYTES 2 - -struct _FuCinterionFdlUpdater { - GObject parent_instance; - gchar *port; - FuIOChannel *io_channel; -}; - -G_DEFINE_TYPE(FuCinterionFdlUpdater, fu_cinterion_fdl_updater, G_TYPE_OBJECT) - -#if MM_CHECK_VERSION(1, 24, 0) -gboolean -fu_cinterion_fdl_updater_wait_ready(FuCinterionFdlUpdater *self, FuDevice *device, GError **error) -{ - guint8 byte = 0; - gsize bytes_read = 0; - - for (guint i = 0; i < FU_CINTERION_FDL_MAX_READ_RETRIES; i++) { - if (!fu_io_channel_read_raw(self->io_channel, - &byte, - 0x1, - &bytes_read, - 100, - FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, - error)) { - return FALSE; - } - if (bytes_read == 1 && byte == FU_CINTERION_FDL_RESPONSE_OK) { - g_debug("start signal read"); - return TRUE; - } - - fu_device_sleep(device, 100); - } - - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "no response from device after %d reads", - FU_CINTERION_FDL_MAX_READ_RETRIES); - return FALSE; -} - -static gboolean -fu_cinterion_fdl_updater_set_io_flags(FuCinterionFdlUpdater *self, GError **error) -{ -#ifdef HAVE_TERMIOS_H - struct termios tio; - gint fd; - - fd = fu_io_channel_unix_get_fd(self->io_channel); - - memset(&tio, 0, sizeof(tio)); - tio.c_cflag = CS8 | CREAD | CLOCAL | HUPCL | FU_CINTERION_FDL_DEFAULT_BAUDRATE; - - if (tcsetattr(fd, TCSANOW, &tio) != 0) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "could not set termios attributes"); - return FALSE; - } - - return TRUE; -#else - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Not supported as not found"); - return FALSE; -#endif -} - -gboolean -fu_cinterion_fdl_updater_open(FuCinterionFdlUpdater *self, GError **error) -{ - /* sanity check */ - if (self->port == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no port provided for update"); - return FALSE; - } - - self->io_channel = - fu_io_channel_new_file(self->port, - FU_IO_CHANNEL_OPEN_FLAG_READ | FU_IO_CHANNEL_OPEN_FLAG_WRITE, - error); - if (self->io_channel == NULL) - return FALSE; - - if (!fu_cinterion_fdl_updater_set_io_flags(self, error)) - return FALSE; - - return TRUE; -} - -gboolean -fu_cinterion_fdl_updater_close(FuCinterionFdlUpdater *self, GError **error) -{ - if (self->io_channel != NULL) { - g_debug("closing io port..."); - if (!fu_io_channel_shutdown(self->io_channel, error)) - return FALSE; - g_clear_object(&self->io_channel); - } - return TRUE; -} - -static gboolean -fu_cinterion_fdl_updater_write_chunk(FuCinterionFdlUpdater *self, - GBytes *size_bytes, - GBytes *chunk_bytes, - GError **error) -{ - if (!fu_io_channel_write_bytes(self->io_channel, - size_bytes, - 1500, - FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, - error)) { - return FALSE; - } - if (!fu_io_channel_write_bytes(self->io_channel, - chunk_bytes, - 1500, - FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, - error)) { - return FALSE; - } - - return TRUE; -} - -static gboolean -fu_cinterion_fdl_updater_read_response(FuCinterionFdlUpdater *self, - FuDevice *device, - FuCinterionFdlResponse *response, - GError **error) -{ - guint8 byte = 0; - gsize bytes_read = 0; - - for (guint i = 0; i < FU_CINTERION_FDL_MAX_READ_RETRIES; i++) { - if (!fu_io_channel_read_raw(self->io_channel, - &byte, - 0x1, - &bytes_read, - 100, - FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, - error)) { - return FALSE; - } - if (bytes_read != 1) { - /* retry until byte read */ - fu_device_sleep(device, 10); - continue; - } - - switch (byte) { - case FU_CINTERION_FDL_RESPONSE_OK: - *response = FU_CINTERION_FDL_RESPONSE_OK; - break; - case FU_CINTERION_FDL_RESPONSE_RETRY: - *response = FU_CINTERION_FDL_RESPONSE_RETRY; - break; - case FU_CINTERION_FDL_RESPONSE_BUSY: - *response = FU_CINTERION_FDL_RESPONSE_BUSY; - break; - default: - *response = FU_CINTERION_FDL_RESPONSE_UNKNOWN; - break; - } - - return TRUE; - } - - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "no response from device after %d reads", - FU_CINTERION_FDL_MAX_READ_RETRIES); - return FALSE; -} - -static gboolean -fu_cinterion_fdl_updater_write_chunk_retry(FuCinterionFdlUpdater *self, - FuDevice *device, - GBytes *size_bytes, - GBytes *chunk_bytes, - GError **error) -{ - guint write_retries = 0; - - while (write_retries < FU_CINTERION_FDL_MAX_WRITE_RETRIES) { - guint read_retries = 0; - FuCinterionFdlResponse response; - - if (!fu_cinterion_fdl_updater_write_chunk(self, size_bytes, chunk_bytes, error)) - return FALSE; - - while (read_retries < FU_CINTERION_FDL_MAX_READ_RETRIES) { - if (!fu_cinterion_fdl_updater_read_response(self, device, &response, error)) - return FALSE; - - if (response == FU_CINTERION_FDL_RESPONSE_OK) { - /* chunk written successfully, stop reading */ - return TRUE; - } - - if (response == FU_CINTERION_FDL_RESPONSE_BUSY) { - /* retry reading response */ - read_retries++; - } else if (response == FU_CINTERION_FDL_RESPONSE_RETRY) { - /* stop reading and retry write */ - break; - } else { - /* fatal response */ - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "received fatal response"); - return FALSE; - } - } - - if (read_retries >= FU_CINTERION_FDL_MAX_READ_RETRIES) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "no response from device after %d reads", - FU_CINTERION_FDL_MAX_READ_RETRIES); - return FALSE; - } - - write_retries++; - } - - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "failed writing chunk %d times", - FU_CINTERION_FDL_MAX_WRITE_RETRIES); - return FALSE; -} - -gboolean -fu_cinterion_fdl_updater_write(FuCinterionFdlUpdater *self, - FuProgress *progress, - FuDevice *device, - GBytes *fw, - GError **error) -{ - guint chunk = 0; - gsize offset = 0; - gsize fw_len = g_bytes_get_size(fw); - - while (offset < fw_len) { - g_autoptr(GBytes) size_bytes = NULL; - g_autoptr(GBytes) chunk_bytes = NULL; - guint16 chunk_size = 0; - - size_bytes = g_bytes_new_from_bytes(fw, offset, FU_CINTERION_FDL_SIZE_BYTES); - if (!fu_memread_uint16_safe(g_bytes_get_data(size_bytes, NULL), - FU_CINTERION_FDL_SIZE_BYTES, - 0x0, - &chunk_size, - G_LITTLE_ENDIAN, - error)) - return FALSE; - - offset += FU_CINTERION_FDL_SIZE_BYTES; - - chunk_bytes = g_bytes_new_from_bytes(fw, offset, chunk_size); - offset += chunk_size; - - if (!fu_cinterion_fdl_updater_write_chunk_retry(self, - device, - size_bytes, - chunk_bytes, - error)) { - g_prefix_error(error, "could not write chunk %u", chunk); - return FALSE; - } - if (chunk % 100 == 0) - g_debug("wrote chunk %u successfully", chunk); - - fu_progress_set_percentage_full(progress, offset, fw_len); - chunk++; - } - - if (fw_len != offset) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "expected %" G_GSIZE_FORMAT " bytes, but wrote %" G_GSIZE_FORMAT, - fw_len, - offset); - return FALSE; - } - - return TRUE; -} -#endif // MM_CHECK_VERSION(1, 24, 0) - -static void -fu_cinterion_fdl_updater_init(FuCinterionFdlUpdater *self) -{ -} - -static void -fu_cinterion_fdl_updater_finalize(GObject *object) -{ - FuCinterionFdlUpdater *self = FU_CINTERION_FDL_UPDATER(object); - g_free(self->port); - G_OBJECT_CLASS(fu_cinterion_fdl_updater_parent_class)->finalize(object); -} - -static void -fu_cinterion_fdl_updater_class_init(FuCinterionFdlUpdaterClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - - object_class->finalize = fu_cinterion_fdl_updater_finalize; -} - -#if MM_CHECK_VERSION(1, 24, 0) -FuCinterionFdlUpdater * -fu_cinterion_fdl_updater_new(const gchar *port) -{ - FuCinterionFdlUpdater *self = g_object_new(FU_TYPE_CINTERION_FDL_UPDATER, NULL); - self->port = g_strdup(port); - return self; -} -#endif // MM_CHECK_VERSION(1, 24, 0) diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-cinterion-fdl-updater.h fwupd-2.0.20/plugins/modem-manager/fu-cinterion-fdl-updater.h --- fwupd-2.0.8/plugins/modem-manager/fu-cinterion-fdl-updater.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-cinterion-fdl-updater.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,31 +0,0 @@ -/* - * Copyright 2024 TDT AG - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_CINTERION_FDL_UPDATER (fu_cinterion_fdl_updater_get_type()) -G_DECLARE_FINAL_TYPE(FuCinterionFdlUpdater, - fu_cinterion_fdl_updater, - FU, - CINTERION_FDL_UPDATER, - GObject) - -FuCinterionFdlUpdater * -fu_cinterion_fdl_updater_new(const gchar *port); -gboolean -fu_cinterion_fdl_updater_open(FuCinterionFdlUpdater *self, GError **error); -gboolean -fu_cinterion_fdl_updater_write(FuCinterionFdlUpdater *self, - FuProgress *progress, - FuDevice *device, - GBytes *fw, - GError **error); -gboolean -fu_cinterion_fdl_updater_close(FuCinterionFdlUpdater *self, GError **error); -gboolean -fu_cinterion_fdl_updater_wait_ready(FuCinterionFdlUpdater *self, FuDevice *device, GError **error); diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-cinterion-fdl-updater.rs fwupd-2.0.20/plugins/modem-manager/fu-cinterion-fdl-updater.rs --- fwupd-2.0.8/plugins/modem-manager/fu-cinterion-fdl-updater.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-cinterion-fdl-updater.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,13 +0,0 @@ -/* - * Copyright 2024 TDT AG - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#[repr(u8)] -enum FuCinterionFdlResponse { - Ok = 0x01, - Retry = 0x02, - Unknown = 0x03, - Busy = 0x04, -} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-dfota-updater.c fwupd-2.0.20/plugins/modem-manager/fu-dfota-updater.c --- fwupd-2.0.8/plugins/modem-manager/fu-dfota-updater.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-dfota-updater.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,404 +0,0 @@ -/* - * Copyright 2024 TDT AG - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include -#include - -#include "fu-dfota-updater.h" - -#define FU_DFOTA_UPDATER_TIMEOUT_SECS 5 -#define FU_DFOTA_UPDATER_FOTA_READ_TIMEOUT_SECS 90 - -struct _FuDfotaUpdater { - GObject parent_instance; - FuIOChannel *io_channel; -}; - -G_DEFINE_TYPE(FuDfotaUpdater, fu_dfota_updater, G_TYPE_OBJECT) - -#if MM_CHECK_VERSION(1, 24, 0) -static gchar * -fu_dfota_updater_compute_checksum(GBytes *fw) -{ - /* compute 16 bit checksum based on bitwise XOR */ - guint16 checksum = 0; - gsize size = 0; - const guint8 *bytes = g_bytes_get_data(fw, &size); - - for (guint i = 0; i < size; i += 2) { - guint16 word = bytes[i] << 8; - - if (i < size - 1) - word |= bytes[i + 1]; - - checksum ^= word; - } - - return g_strdup_printf("%x", checksum); -} - -static gboolean -fu_dfota_updater_upload_chunk(FuDfotaUpdater *self, FuChunk *chk, GError **error) -{ - g_autoptr(GBytes) ack_bytes = NULL; - g_autoptr(GRegex) ack_regex = NULL; - g_autoptr(GBytes) chunk_bytes = NULL; - const gchar *ack_result = NULL; - gsize ack_size; - gsize chunk_size; - gsize acks_expected; - - ack_regex = g_regex_new("^A+$", 0, 0, NULL); - chunk_size = g_bytes_get_size(fu_chunk_get_bytes(chk)); - /* expect one byte as response for every 1024 bytes sent */ - acks_expected = chunk_size / 1024; - /* pad every chunk to 2048 bytes to received correct amount of ACKs */ - chunk_bytes = fu_bytes_pad(fu_chunk_get_bytes(chk), 0x800, 0xFF); - - if (!fu_io_channel_write_bytes(self->io_channel, - chunk_bytes, - 1500, - FU_IO_CHANNEL_FLAG_NONE, - error)) { - g_prefix_error(error, "failed to upload firmware to the device: "); - return FALSE; - } - if (acks_expected == 0) - return TRUE; - - ack_bytes = fu_io_channel_read_bytes(self->io_channel, - acks_expected, - FU_DFOTA_UPDATER_TIMEOUT_SECS * 1000, - FU_IO_CHANNEL_FLAG_NONE, - error); - - if (ack_bytes == NULL) { - g_prefix_error(error, "failed to read response: "); - return FALSE; - } - - ack_result = g_bytes_get_data(ack_bytes, &ack_size); - if (ack_size != acks_expected) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "expected %" G_GSIZE_FORMAT " ACKs, got %" G_GSIZE_FORMAT, - acks_expected, - ack_size); - return FALSE; - } - if (!g_regex_match(ack_regex, ack_result, 0, NULL)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "expected ACKs (A), got %s", - ack_result); - return FALSE; - } - - return TRUE; -} - -static gboolean -fu_dfota_updater_parse_upload_result(FuDfotaUpdater *self, - gchar **checksum, - gsize *size, - GError **error) -{ - g_autoptr(GBytes) result_bytes = NULL; - g_autoptr(GRegex) result_regex = NULL; - g_autoptr(GMatchInfo) match_info = NULL; - g_autofree gchar *size_match = NULL; - g_autofree gchar *checksum_match = NULL; - const gchar *result = NULL; - - /* +QFUPL: , */ - result_regex = g_regex_new("\\r\\n\\+QFUPL:\\s*(\\d+),([0-9a-f]+)\\r\\n", 0, 0, error); - if (result_regex == NULL) { - g_prefix_error(error, "failed to build regex: "); - return FALSE; - } - - result_bytes = fu_io_channel_read_bytes(self->io_channel, - -1, - FU_DFOTA_UPDATER_TIMEOUT_SECS * 1000, - FU_IO_CHANNEL_FLAG_SINGLE_SHOT, - error); - if (result_bytes == NULL) { - g_prefix_error(error, "failed to read AT+QFUPL response: "); - return FALSE; - } - result = g_bytes_get_data(result_bytes, NULL); - - if (g_strrstr(result, "\r\nOK\r\n") == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "upload command exited with error"); - return FALSE; - } - if (!g_regex_match(result_regex, result, 0, &match_info)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "could not match QFUPL response"); - return FALSE; - } - if (!g_match_info_matches(match_info)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "could not match size and checksum"); - return FALSE; - } - - size_match = g_match_info_fetch(match_info, 1); - checksum_match = g_match_info_fetch(match_info, 2); - - g_debug("parsed checksum '%s' and size '%s'", checksum_match, size_match); - - if (!g_ascii_string_to_unsigned(size_match, 10, 0, G_MAXSIZE, size, error)) - return FALSE; - - *checksum = g_steal_pointer(&checksum_match); - - return TRUE; -} - -gboolean -fu_dfota_updater_upload_firmware(FuDfotaUpdater *self, GBytes *fw, GError **error) -{ - g_autoptr(FuChunkArray) chunks = fu_chunk_array_new_from_bytes(fw, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - 0x800); - guint chunk_count = fu_chunk_array_length(chunks); - g_autofree gchar *checksum = fu_dfota_updater_compute_checksum(fw); - g_autofree gchar *checksum_parsed = NULL; - gsize size = g_bytes_get_size(fw); - gsize size_parsed = 0; - - for (guint i = 0; i < chunk_count; i++) { - g_autoptr(FuChunk) chk = fu_chunk_array_index(chunks, i, error); - - if (chk == NULL) - return FALSE; - - if (!fu_dfota_updater_upload_chunk(self, chk, error)) { - g_prefix_error(error, "failed at chunk %u: ", i); - return FALSE; - } - if (i % 100 == 0) - g_debug("wrote chunk %u/%u", i, chunk_count - 1); - } - - if (!fu_dfota_updater_parse_upload_result(self, &checksum_parsed, &size_parsed, error)) - return FALSE; - - if (size != size_parsed) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "firmware size mismatch - expected %" G_GSIZE_FORMAT - ", but was %" G_GSIZE_FORMAT, - size, - size_parsed); - return FALSE; - } - if (g_strcmp0(checksum, checksum_parsed) != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "checksum mismatch - expected %s, but was %s", - checksum, - checksum_parsed); - return FALSE; - } - - return TRUE; -} - -gboolean -fu_dfota_updater_open(FuDfotaUpdater *self, GError **error) -{ - if (self->io_channel == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no channel provided for update"); - return FALSE; - } - - return TRUE; -} - -gboolean -fu_dfota_updater_close(FuDfotaUpdater *self, GError **error) -{ - /* closing will be handled by locker */ - return TRUE; -} - -static gboolean -fu_dfota_updater_parse_fota_response(gchar *response, - FuProgress *progress, - gboolean *finished, - GError **error) -{ - g_autoptr(GRegex) fota_regex = NULL; - g_autoptr(GMatchInfo) match_info = NULL; - g_autofree gchar *status_match = NULL; - g_autofree gchar *status_number_match = NULL; - guint64 status_number = 0; - - /* +QIND: "FOTA",""(,)? */ - fota_regex = g_regex_new("\\+QIND:\\s*\"FOTA\",\"([A-Z]+)\"(,(\\d+))?", 0, 0, error); - if (fota_regex == NULL) { - g_prefix_error(error, "failed to build regex: "); - return FALSE; - } - - if (!g_regex_match(fota_regex, response, 0, &match_info)) { - /* - * Log and continue on unexpected responses because devices - * may incorrectly return an incomplete status message 1-2 - * times. - */ - g_debug("got unexpected response '%s'", response); - return TRUE; - } - if (!g_match_info_matches(match_info)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "could not match fota status"); - return FALSE; - } - - status_match = g_match_info_fetch(match_info, 1); - - if (g_strcmp0(status_match, "START") == 0) { - g_debug("update started successfully"); - return TRUE; - } - - /* expect status and number, which means four matches in the above regex */ - if (g_match_info_get_match_count(match_info) != 4) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "badly formatted message '%s'", - response); - return FALSE; - } - - status_number_match = g_match_info_fetch(match_info, 3); - if (!g_ascii_string_to_unsigned(status_number_match, - 10, - 0, - G_MAXUINT, - &status_number, - error)) - return FALSE; - - if (g_strcmp0(status_match, "UPDATING") == 0) { - fu_progress_set_percentage(progress, (guint)status_number); - return TRUE; - } - if (g_strcmp0(status_match, "END") == 0) { - if (status_number != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "update exited with error code %" G_GUINT64_FORMAT, - status_number); - return FALSE; - } - - g_debug("updated finished successfully"); - *finished = TRUE; - return TRUE; - } - - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "unhandled fota status '%s'", - status_match); - return FALSE; -} - -gboolean -fu_dfota_updater_write(FuDfotaUpdater *self, FuProgress *progress, FuDevice *device, GError **error) -{ - gboolean finished = FALSE; - - while (!finished) { - g_autoptr(GBytes) bytes = NULL; - g_autofree gchar *result = NULL; - - bytes = fu_io_channel_read_bytes(self->io_channel, - -1, - FU_DFOTA_UPDATER_FOTA_READ_TIMEOUT_SECS * 1000, - FU_IO_CHANNEL_FLAG_SINGLE_SHOT, - error); - if (bytes == NULL) - return FALSE; - - result = g_strdup(g_bytes_get_data(bytes, NULL)); - if (result == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "no data read from device"); - return FALSE; - } - g_strstrip(result); - /* ignore empty responses */ - if (strlen(result) == 0) - continue; - - if (!fu_dfota_updater_parse_fota_response(result, progress, &finished, error)) - return FALSE; - } - - return TRUE; -} -#endif // MM_CHECK_VERSION(1, 24, 0) - -static void -fu_dfota_updater_init(FuDfotaUpdater *self) -{ -} - -static void -fu_dfota_updater_finalize(GObject *object) -{ - FuDfotaUpdater *self = FU_DFOTA_UPDATER(object); - if (self->io_channel != NULL) - g_object_unref(self->io_channel); - G_OBJECT_CLASS(fu_dfota_updater_parent_class)->finalize(object); -} - -static void -fu_dfota_updater_class_init(FuDfotaUpdaterClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - - object_class->finalize = fu_dfota_updater_finalize; -} - -#if MM_CHECK_VERSION(1, 24, 0) -FuDfotaUpdater * -fu_dfota_updater_new(FuIOChannel *io_channel) -{ - FuDfotaUpdater *self = g_object_new(FU_TYPE_DFOTA_UPDATER, NULL); - self->io_channel = g_object_ref(io_channel); - return self; -} -#endif // MM_CHECK_VERSION(1, 24, 0) diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-dfota-updater.h fwupd-2.0.20/plugins/modem-manager/fu-dfota-updater.h --- fwupd-2.0.8/plugins/modem-manager/fu-dfota-updater.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-dfota-updater.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -/* - * Copyright 2024 TDT AG - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_DFOTA_UPDATER (fu_dfota_updater_get_type()) -G_DECLARE_FINAL_TYPE(FuDfotaUpdater, fu_dfota_updater, FU, DFOTA_UPDATER, GObject) - -#define FU_DFOTA_UPDATER_FILENAME "dfota_update.bin" -#define FU_DFOTA_UPDATER_FOTA_RESTART_TIMEOUT_SECS 15 - -FuDfotaUpdater * -fu_dfota_updater_new(FuIOChannel *io_channel); -gboolean -fu_dfota_updater_open(FuDfotaUpdater *self, GError **error); -gboolean -fu_dfota_updater_write(FuDfotaUpdater *self, - FuProgress *progress, - FuDevice *device, - GError **error); -gboolean -fu_dfota_updater_close(FuDfotaUpdater *self, GError **error); -gboolean -fu_dfota_updater_upload_firmware(FuDfotaUpdater *self, GBytes *bytes, GError **error); diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-firehose-updater.c fwupd-2.0.20/plugins/modem-manager/fu-firehose-updater.c --- fwupd-2.0.8/plugins/modem-manager/fu-firehose-updater.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-firehose-updater.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,925 +0,0 @@ -/* - * Copyright 2020 Aleksander Morgado - * Copyright 2021 Ivan Mikhanchuk - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include - -#include "fu-firehose-updater.h" - -/* Maximum amount of non-"response" (e.g. "log") XML messages that can be - * received from the module when expecting a "response". This is just a safe - * upper limit to avoid reading forever. */ -#define MAX_RECV_MESSAGES 100 - -/* When initializing the conversation with the firehose interpreter, the - * first step is to receive and process a bunch of messages sent by the - * module. The initial timeout to receive the first message is longer in case - * the module needs some initialization time itself; all the messages after - * the first one are expected to be received much quicker. The default timeout - * value should not be extremely long because the initialization phase ends - * when we don't receive more messages, so it's expected that the timeout will - * fully elapse after the last message sent by the module. */ -#define INITIALIZE_INITIAL_TIMEOUT_MS 3000 -#define INITIALIZE_TIMEOUT_MS 250 - -/* Maximum amount of time to wait for a message from the module. */ -#define DEFAULT_RECV_TIMEOUT_MS 15000 - -/* The first configure attempt sent to the module will include all the defaults - * listed below. If the module replies with a NAK specifying a different - * (shorter) max payload size to use, the second configure attempt will be done - * with that new suggested max payload size value. Only 2 configure attempts are - * therefore expected. */ -#define MAX_CONFIGURE_ATTEMPTS 2 - -/* Defaults for the firehose configuration step. The max payload size to target - * in bytes may end up being a different if the module requests a shorter one. - */ -#define CONFIGURE_MEMORY_NAME "nand" -#define CONFIGURE_VERBOSE 0 -#define CONFIGURE_ALWAYS_VALIDATE 0 -#define CONFIGURE_MAX_DIGEST_TABLE_SIZE_IN_BYTES 2048 -#define CONFIGURE_MAX_PAYLOAD_SIZE_TO_TARGET_IN_BYTES 8192 -#define CONFIGURE_SKIP_STORAGE_INIT 0 - -struct _FuFirehoseUpdater { - GObject parent_instance; - gchar *port; - FuSaharaLoader *sahara; - FuIOChannel *io_channel; - gboolean supports_zlp; -}; - -G_DEFINE_TYPE(FuFirehoseUpdater, fu_firehose_updater, G_TYPE_OBJECT) - -static void -fu_firehose_updater_log_message(const gchar *action, GBytes *msg) -{ - const gchar *msg_data; - gsize msg_size; - g_autofree gchar *msg_strsafe = NULL; - - msg_data = (const gchar *)g_bytes_get_data(msg, &msg_size); - if (msg_size > G_MAXINT) - return; - - msg_strsafe = fu_strsafe(msg_data, msg_size); - g_debug("%s: %.*s", action, (gint)msg_size, msg_strsafe); -} - -static GBytes * -fu_firehose_updater_read(FuFirehoseUpdater *self, - guint timeout_ms, - FuIOChannelFlags flags, - GError **error) -{ - if (self->sahara != NULL) { - GByteArray *bytearr = fu_sahara_loader_qdl_read(self->sahara, error); - return bytearr == NULL ? NULL : g_bytes_new(bytearr->data, bytearr->len); - } - - return fu_io_channel_read_bytes(self->io_channel, -1, timeout_ms, flags, error); -} - -static gboolean -fu_firehose_updater_write_internal(FuFirehoseUpdater *self, - GBytes *bytes, - guint timeout_ms, - FuIOChannelFlags flags, - GError **error) -{ - if (self->sahara != NULL) - return fu_sahara_loader_qdl_write_bytes(self->sahara, bytes, error); - - return fu_io_channel_write_bytes(self->io_channel, bytes, timeout_ms, flags, error); -} - -static gboolean -fu_firehose_updater_validate_program_action(XbNode *program, FuArchive *archive, GError **error) -{ - const gchar *filename_attr; - gsize file_size; - guint64 computed_num_partition_sectors; - guint64 num_partition_sectors; - guint64 sector_size_in_bytes; - g_autoptr(GBytes) file = NULL; - - filename_attr = xb_node_get_attr(program, "filename"); - if (filename_attr == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "missing 'filename' attribute in 'program' action"); - return FALSE; - } - - /* contents of the CAB file are flat, no subdirectories; look for the - * exact filename */ - file = fu_archive_lookup_by_fn(archive, filename_attr, error); - if (file == NULL) - return FALSE; - - file_size = g_bytes_get_size(file); - - num_partition_sectors = xb_node_get_attr_as_uint(program, "num_partition_sectors"); - if (num_partition_sectors == G_MAXUINT64) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "missing 'num_partition_sectors' attribute in 'program' action for " - "filename '%s'", - filename_attr); - return FALSE; - } - sector_size_in_bytes = xb_node_get_attr_as_uint(program, "SECTOR_SIZE_IN_BYTES"); - if (sector_size_in_bytes == G_MAXUINT64) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "missing 'SECTOR_SIZE_IN_BYTES' attribute in 'program' action for " - "filename '%s'", - filename_attr); - return FALSE; - } - - computed_num_partition_sectors = file_size / sector_size_in_bytes; - if ((file_size % sector_size_in_bytes) != 0) - computed_num_partition_sectors++; - - if (computed_num_partition_sectors != num_partition_sectors) { - g_set_error( - error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid 'num_partition_sectors' in 'program' action for filename '%s': " - "expected %" G_GUINT64_FORMAT " instead of %" G_GUINT64_FORMAT " bytes", - filename_attr, - computed_num_partition_sectors, - num_partition_sectors); - return FALSE; - } - - xb_node_set_data(program, "fwupd:ProgramFile", file); - return TRUE; -} - -gboolean -fu_firehose_updater_validate_rawprogram(GBytes *rawprogram, - FuArchive *archive, - XbSilo **out_silo, - GPtrArray **out_action_nodes, - GError **error) -{ - g_autoptr(XbBuilder) builder = xb_builder_new(); - g_autoptr(XbBuilderSource) source = xb_builder_source_new(); - g_autoptr(XbSilo) silo = NULL; - g_autoptr(XbNode) data_node = NULL; - g_autoptr(GPtrArray) action_nodes = NULL; - - if (!xb_builder_source_load_bytes(source, rawprogram, XB_BUILDER_SOURCE_FLAG_NONE, error)) { - fwupd_error_convert(error); - return FALSE; - } - xb_builder_import_source(builder, source); - silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); - if (silo == NULL) { - fwupd_error_convert(error); - return FALSE; - } - - data_node = xb_silo_get_root(silo); - action_nodes = xb_node_get_children(data_node); - if (action_nodes == NULL || action_nodes->len == 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no actions given"); - return FALSE; - } - - for (guint i = 0; i < action_nodes->len; i++) { - XbNode *n = g_ptr_array_index(action_nodes, i); - if ((g_strcmp0(xb_node_get_element(n), "program") == 0) && - !fu_firehose_updater_validate_program_action(n, archive, error)) { - return FALSE; - } - } - - *out_silo = g_steal_pointer(&silo); - *out_action_nodes = g_steal_pointer(&action_nodes); - return TRUE; -} - -void -fu_firehose_updater_set_supports_zlp(FuFirehoseUpdater *self, gboolean supports_zlp) -{ - self->supports_zlp = supports_zlp; -} - -gboolean -fu_firehose_updater_open(FuFirehoseUpdater *self, GError **error) -{ - if (fu_sahara_loader_qdl_is_open(self->sahara)) { - g_debug("using sahara qdl port for firehose"); - return TRUE; - } - - /* sanity check */ - if (self->port == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no firehose port provided for filename"); - return FALSE; - } - - g_debug("opening firehose port..."); - - if (self->port != NULL) { - self->io_channel = fu_io_channel_new_file(self->port, - FU_IO_CHANNEL_OPEN_FLAG_READ | - FU_IO_CHANNEL_OPEN_FLAG_WRITE, - error); - return self->io_channel != NULL; - } - - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "no device to write firehose commands to"); - return FALSE; -} - -gboolean -fu_firehose_updater_close(FuFirehoseUpdater *self, GError **error) -{ - if (self->io_channel != NULL) { - g_debug("closing firehose port..."); - if (!fu_io_channel_shutdown(self->io_channel, error)) - return FALSE; - g_clear_object(&self->io_channel); - } - return TRUE; -} - -static gboolean -fu_firehose_updater_check_operation_result(XbNode *node, gboolean *out_rawmode) -{ - g_warn_if_fail(g_strcmp0(xb_node_get_element(node), "response") == 0); - if (g_strcmp0(xb_node_get_attr(node, "value"), "ACK") != 0) - return FALSE; - if (out_rawmode) - *out_rawmode = (g_strcmp0(xb_node_get_attr(node, "rawmode"), "true") == 0); - return TRUE; -} - -static gboolean -fu_firehose_updater_process_response(GBytes *rsp_bytes, - XbSilo **out_silo, - XbNode **out_response_node, - GError **error) -{ - g_autoptr(XbBuilder) builder = xb_builder_new(); - g_autoptr(XbBuilderSource) source = xb_builder_source_new(); - g_autoptr(XbSilo) silo = NULL; - g_autoptr(XbNode) data_node = NULL; - g_autoptr(GPtrArray) action_nodes = NULL; - - if (!xb_builder_source_load_bytes(source, rsp_bytes, XB_BUILDER_SOURCE_FLAG_NONE, error)) { - fwupd_error_convert(error); - return FALSE; - } - xb_builder_import_source(builder, source); - silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); - if (silo == NULL) - return FALSE; - - data_node = xb_silo_get_root(silo); - if (data_node == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "missing root data node"); - return FALSE; - } - - action_nodes = xb_node_get_children(data_node); - if (action_nodes != NULL) { - for (guint j = 0; j < action_nodes->len; j++) { - XbNode *node = g_ptr_array_index(action_nodes, j); - - if (g_strcmp0(xb_node_get_element(node), "response") == 0) { - if (out_silo) - *out_silo = g_steal_pointer(&silo); - if (out_response_node) - *out_response_node = g_object_ref(node); - return TRUE; - } - - if (g_strcmp0(xb_node_get_element(node), "log") == 0) { - const gchar *value_attr = xb_node_get_attr(node, "value"); - if (value_attr) - g_debug("device log: %s", value_attr); - } - } - } - - if (out_silo != NULL) - *out_silo = NULL; - if (out_response_node != NULL) - *out_response_node = NULL; - return TRUE; -} - -static gboolean -fu_firehose_updater_send_and_receive(FuFirehoseUpdater *self, - GByteArray *take_cmd_bytearray, - XbSilo **out_silo, - XbNode **out_response_node, - GError **error) -{ - if (take_cmd_bytearray) { - const gchar *cmd_header = "\n\n"; - const gchar *cmd_trailer = ""; - g_autoptr(GBytes) cmd_bytes = NULL; - - g_byte_array_prepend(take_cmd_bytearray, - (const guint8 *)cmd_header, - strlen(cmd_header)); - g_byte_array_append(take_cmd_bytearray, - (const guint8 *)cmd_trailer, - strlen(cmd_trailer)); - cmd_bytes = g_bytes_new(take_cmd_bytearray->data, take_cmd_bytearray->len); - - fu_firehose_updater_log_message("writing", cmd_bytes); - if (!fu_firehose_updater_write_internal(self, - cmd_bytes, - 1500, - FU_IO_CHANNEL_FLAG_FLUSH_INPUT, - error)) { - g_prefix_error(error, "Failed to write command: "); - return FALSE; - } - } - - for (guint i = 0; i < MAX_RECV_MESSAGES; i++) { - g_autoptr(GBytes) rsp_bytes = NULL; - g_autoptr(XbSilo) silo = NULL; - g_autoptr(XbNode) response_node = NULL; - - rsp_bytes = fu_firehose_updater_read(self, - DEFAULT_RECV_TIMEOUT_MS, - FU_IO_CHANNEL_FLAG_SINGLE_SHOT, - error); - if (rsp_bytes == NULL) { - g_prefix_error(error, "Failed to read XML message: "); - return FALSE; - } - - fu_firehose_updater_log_message("reading", rsp_bytes); - if (!fu_firehose_updater_process_response(rsp_bytes, - &silo, - &response_node, - error)) { - g_prefix_error(error, "Failed to parse XML message: "); - return FALSE; - } - - if (silo != NULL && response_node != NULL) { - *out_silo = g_steal_pointer(&silo); - *out_response_node = g_steal_pointer(&response_node); - return TRUE; - } - - /* continue until we get a 'response_node' */ - } - - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_TIMED_OUT, - "didn't get any response in the last %d messages", - MAX_RECV_MESSAGES); - return FALSE; -} - -static gboolean -fu_firehose_updater_initialize(FuFirehoseUpdater *self, GError **error) -{ - guint n_msg = 0; - - for (guint i = 0; i < MAX_RECV_MESSAGES; i++) { - g_autoptr(GBytes) rsp_bytes = NULL; - guint timeout = (i == 0 ? INITIALIZE_INITIAL_TIMEOUT_MS : INITIALIZE_TIMEOUT_MS); - - rsp_bytes = - fu_firehose_updater_read(self, timeout, FU_IO_CHANNEL_FLAG_SINGLE_SHOT, NULL); - if (rsp_bytes == NULL) - break; - - fu_firehose_updater_log_message("reading", rsp_bytes); - if (!fu_firehose_updater_process_response(rsp_bytes, NULL, NULL, error)) { - g_prefix_error(error, "Failed to parse XML message: "); - return FALSE; - } - - n_msg++; - } - - if (n_msg == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "couldn't read initial firehose messages from device"); - return FALSE; - } - - return TRUE; -} - -static guint -fu_firehose_updater_configure(FuFirehoseUpdater *self, GError **error) -{ - gint max_payload_size = CONFIGURE_MAX_PAYLOAD_SIZE_TO_TARGET_IN_BYTES; - - for (guint i = 0; i < MAX_CONFIGURE_ATTEMPTS; i++) { - GByteArray *cmd_bytearray = NULL; - g_autoptr(XbSilo) rsp_silo = NULL; - g_autoptr(XbNode) rsp_node = NULL; - GString *cmd_str = g_string_new(NULL); - - g_string_append_printf(cmd_str, "supports_zlp ? 1 : 0); - g_string_append_printf(cmd_str, - " SkipStorageInit=\"%d\"", - CONFIGURE_SKIP_STORAGE_INIT); - g_string_append_printf(cmd_str, "/>"); - - cmd_bytearray = g_bytes_unref_to_array(g_string_free_to_bytes(cmd_str)); - - if (!fu_firehose_updater_send_and_receive(self, - cmd_bytearray, - &rsp_silo, - &rsp_node, - error)) { - g_prefix_error(error, "Failed to run configure command: "); - return 0; - } - - /* retry if we're told to use a different max payload size */ - if (!fu_firehose_updater_check_operation_result(rsp_node, NULL)) { - guint64 suggested_max_payload_size; - g_autoptr(XbNode) root = NULL; - - root = xb_silo_get_root(rsp_silo); - suggested_max_payload_size = - xb_node_get_attr_as_uint(root, "MaxPayloadSizeToTargetInBytes"); - if ((suggested_max_payload_size > G_MAXINT) || - ((gint)suggested_max_payload_size == max_payload_size)) { - break; - } - - suggested_max_payload_size = max_payload_size; - continue; - } - - /* if operation is successful, return the max payload size we requested */ - return max_payload_size; - } - - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "configure operation failed"); - return 0; -} - -static gboolean -fu_firehose_updater_reset(FuFirehoseUpdater *self, GError **error) -{ - guint recv_cnt = 20; - const gchar *cmd_str = ""; - GByteArray *cmd_bytearray = NULL; - g_autoptr(XbSilo) rsp_silo = NULL; - g_autoptr(XbNode) rsp_node = NULL; - - cmd_bytearray = - g_byte_array_append(g_byte_array_new(), (const guint8 *)cmd_str, strlen(cmd_str)); - if (!fu_firehose_updater_send_and_receive(self, - cmd_bytearray, - &rsp_silo, - &rsp_node, - error)) { - g_prefix_error(error, "Failed to run reset command: "); - return FALSE; - } - - if (!fu_firehose_updater_check_operation_result(rsp_node, NULL)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "reset operation failed"); - return FALSE; - } - - /* read out all of the remaining messages. otherwise modem won't go into reset */ - while (--recv_cnt && - fu_firehose_updater_send_and_receive(self, NULL, &rsp_silo, &rsp_node, NULL)) - ; - - g_warn_if_fail(recv_cnt > 0); - - return TRUE; -} - -static gboolean -fu_firehose_updater_send_program_file(FuFirehoseUpdater *self, - const gchar *program_filename, - GBytes *program_file, - gsize payload_size, - gsize sector_size, - GError **error) -{ - g_autoptr(FuChunkArray) chunks = fu_chunk_array_new_from_bytes(program_file, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - payload_size); - - for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - g_autoptr(FuChunk) chk = NULL; - g_autoptr(GBytes) block = NULL; - g_autoptr(GBytes) block_padded = NULL; - gsize block_padded_sz; - - /* prepare chunk */ - chk = fu_chunk_array_index(chunks, i, error); - if (chk == NULL) - return FALSE; - - block = fu_chunk_get_bytes(chk); - /* block needs to be padded to the next sector_size, - * so that we always send full sectors */ - block_padded_sz = - ((g_bytes_get_size(block) + sector_size - 1) / sector_size) * sector_size; - block_padded = fu_bytes_pad(block, block_padded_sz, 0xFF); - - /* log only in blocks of 250 plus first/last */ - if (i == 0 || i == (fu_chunk_array_length(chunks) - 1) || (i + 1) % 250 == 0) - g_debug("sending %" G_GSIZE_FORMAT " bytes in block %u/%u of file '%s'", - block_padded_sz, - i + 1, - fu_chunk_array_length(chunks), - program_filename); - - if (!fu_firehose_updater_write_internal(self, - block_padded, - 1500, - FU_IO_CHANNEL_FLAG_FLUSH_INPUT, - error)) { - g_prefix_error(error, - "failed to write block %u/%u of file '%s': ", - i + 1, - fu_chunk_array_length(chunks), - program_filename); - return FALSE; - } - } - - return TRUE; -} - -static gboolean -fu_firehose_updater_actions_validate(GPtrArray *action_nodes, - guint max_payload_size, - GError **error) -{ - g_return_val_if_fail(action_nodes != NULL, FALSE); - - for (guint i = 0; i < action_nodes->len; i++) { - const gchar *name = NULL; - const gchar *program_filename = NULL; - GBytes *program_file = NULL; - guint64 program_sector_size_in_bytes = 0; - - XbNode *node = g_ptr_array_index(action_nodes, i); - const gchar *action = xb_node_get_element(node); - - if (g_strcmp0(action, "program") != 0) - continue; - - name = "fwupd:ProgramFile"; - program_file = xb_node_get_data(node, name); - if (program_file == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to validate program file: failed to get %s", - name); - return FALSE; - } - name = "filename"; - program_filename = xb_node_get_attr(node, name); - if (program_filename == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to validate program file: failed to get %s", - name); - return FALSE; - } - name = "SECTOR_SIZE_IN_BYTES"; - program_sector_size_in_bytes = xb_node_get_attr_as_uint(node, name); - if (program_sector_size_in_bytes > max_payload_size) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "failed to validate program file '%s' command: " - "requested sector size bigger (%" G_GUINT64_FORMAT " bytes) " - "than maximum payload size agreed with device (%u bytes)", - program_filename, - program_sector_size_in_bytes, - max_payload_size); - return FALSE; - } - } - - return TRUE; -} - -static gsize -fu_firehose_updater_actions_get_total_file_size(GPtrArray *action_nodes) -{ - gsize total_bytes = 0; - g_return_val_if_fail(action_nodes != NULL, 0); - - for (guint i = 0; i < action_nodes->len; i++) { - GBytes *program_file = NULL; - XbNode *node = g_ptr_array_index(action_nodes, i); - const gchar *action = xb_node_get_element(node); - - if (g_strcmp0(action, "program") != 0) - continue; - - program_file = xb_node_get_data(node, "fwupd:ProgramFile"); - - if (program_file != NULL) - total_bytes += g_bytes_get_size(program_file); - } - - return total_bytes; -} - -static gboolean -fu_firehose_updater_run_action_program(FuFirehoseUpdater *self, - XbNode *node, - gboolean rawmode, - guint max_payload_size, - gsize *sent_bytes, - GError **error) -{ - GBytes *program_file = NULL; - const gchar *program_filename = NULL; - guint64 program_sector_size = 0; - guint payload_size = 0; - g_autoptr(XbSilo) rsp_silo = NULL; - g_autoptr(XbNode) rsp_node = NULL; - - program_file = xb_node_get_data(node, "fwupd:ProgramFile"); - if (program_file == NULL) - return FALSE; - program_filename = xb_node_get_attr(node, "filename"); - if (program_filename == NULL) - return FALSE; - program_sector_size = xb_node_get_attr_as_uint(node, "SECTOR_SIZE_IN_BYTES"); - if (program_sector_size == G_MAXUINT64) - return FALSE; - - if (rawmode == FALSE) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to download program file '%s': rawmode not enabled", - program_filename); - return FALSE; - } - - while ((payload_size + (guint)program_sector_size) <= max_payload_size) - payload_size += (guint)program_sector_size; - if (payload_size == 0) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "payload size invalid"); - return FALSE; - } - - g_debug("sending program file '%s' (0x%x bytes)", - program_filename, - (guint)g_bytes_get_size(program_file)); - if (!fu_firehose_updater_send_program_file(self, - program_filename, - program_file, - payload_size, - program_sector_size, - error)) { - g_prefix_error(error, "failed to send program file '%s': ", program_filename); - return FALSE; - } - - g_debug("waiting for program file download confirmation..."); - if (!fu_firehose_updater_send_and_receive(self, NULL, &rsp_silo, &rsp_node, error)) { - g_prefix_error(error, - "download confirmation not received for file '%s': ", - program_filename); - return FALSE; - } - - if (!fu_firehose_updater_check_operation_result(rsp_node, &rawmode)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "download confirmation failed for file '%s'", - program_filename); - return FALSE; - } - - if (rawmode != FALSE) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "download confirmation failed for file '%s': rawmode still enabled", - program_filename); - return FALSE; - } - - if (sent_bytes != NULL) - *sent_bytes += g_bytes_get_size(program_file); - - return TRUE; -} - -static gboolean -fu_firehose_updater_run_action(FuFirehoseUpdater *self, - XbNode *node, - guint max_payload_size, - gsize *sent_bytes, - GError **error) -{ - const gchar *action; - gchar *cmd_str = NULL; - gboolean rawmode = FALSE; - GByteArray *cmd_bytearray = NULL; - g_autoptr(XbSilo) rsp_silo = NULL; - g_autoptr(XbNode) rsp_node = NULL; - - action = xb_node_get_element(node); - - cmd_str = xb_node_export(node, XB_NODE_EXPORT_FLAG_COLLAPSE_EMPTY, error); - if (cmd_str == NULL) - return FALSE; - cmd_bytearray = g_byte_array_new_take((guint8 *)cmd_str, strlen(cmd_str)); - - g_debug("running command '%s'...", action); - if (!fu_firehose_updater_send_and_receive(self, - cmd_bytearray, - &rsp_silo, - &rsp_node, - error)) { - g_prefix_error(error, "failed to run command '%s': ", action); - return FALSE; - } - - if (!fu_firehose_updater_check_operation_result(rsp_node, &rawmode)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "command '%s' failed", - action); - return FALSE; - } - - if (g_strcmp0(action, "program") == 0) - return fu_firehose_updater_run_action_program(self, - node, - rawmode, - max_payload_size, - sent_bytes, - error); - - return TRUE; -} - -static gboolean -fu_firehose_updater_run_actions(FuFirehoseUpdater *self, - XbSilo *silo, - GPtrArray *action_nodes, - guint max_payload_size, - FuProgress *progress, - GError **error) -{ - gsize sent_bytes = 0; - gsize total_bytes = 0; - - g_warn_if_fail(action_nodes->len > 0); - - if (!fu_firehose_updater_actions_validate(action_nodes, max_payload_size, error)) - return FALSE; - - total_bytes = fu_firehose_updater_actions_get_total_file_size(action_nodes); - - for (guint i = 0; i < action_nodes->len; i++) { - XbNode *node = g_ptr_array_index(action_nodes, i); - - if (!fu_firehose_updater_run_action(self, - node, - max_payload_size, - &sent_bytes, - error)) - return FALSE; - - fu_progress_set_percentage_full(progress, sent_bytes, total_bytes); - } - - return TRUE; -} - -gboolean -fu_firehose_updater_write(FuFirehoseUpdater *self, - XbSilo *silo, - GPtrArray *action_nodes, - FuProgress *progress, - GError **error) -{ - guint max_payload_size; - gboolean result; - g_autoptr(GError) error_local = NULL; - - if (!fu_firehose_updater_initialize(self, error)) - return FALSE; - - max_payload_size = fu_firehose_updater_configure(self, error); - if (max_payload_size == 0) - return FALSE; - - result = fu_firehose_updater_run_actions(self, - silo, - action_nodes, - max_payload_size, - progress, - error); - - if (!fu_firehose_updater_reset(self, &error_local)) { - if (result) - g_propagate_error(error, g_steal_pointer(&error_local)); - return FALSE; - } - - return result; -} - -static void -fu_firehose_updater_init(FuFirehoseUpdater *self) -{ - /* supported by most devices - enable by default */ - self->supports_zlp = TRUE; -} - -static void -fu_firehose_updater_finalize(GObject *object) -{ - FuFirehoseUpdater *self = FU_FIREHOSE_UPDATER(object); - g_warn_if_fail(self->io_channel == NULL); - g_free(self->port); - g_object_unref(self->sahara); - G_OBJECT_CLASS(fu_firehose_updater_parent_class)->finalize(object); -} - -static void -fu_firehose_updater_class_init(FuFirehoseUpdaterClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - object_class->finalize = fu_firehose_updater_finalize; -} - -FuFirehoseUpdater * -fu_firehose_updater_new(const gchar *port, FuSaharaLoader *sahara) -{ - FuFirehoseUpdater *self = g_object_new(FU_TYPE_FIREHOSE_UPDATER, NULL); - if (port != NULL) - self->port = g_strdup(port); - if (sahara != NULL) - self->sahara = g_object_ref(sahara); - return self; -} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-firehose-updater.h fwupd-2.0.20/plugins/modem-manager/fu-firehose-updater.h --- fwupd-2.0.8/plugins/modem-manager/fu-firehose-updater.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-firehose-updater.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,40 +0,0 @@ -/* - * Copyright 2020 Aleksander Morgado - * Copyright 2021 Ivan Mikhanchuk - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#include "fu-sahara-loader.h" - -#define FU_TYPE_FIREHOSE_UPDATER (fu_firehose_updater_get_type()) -G_DECLARE_FINAL_TYPE(FuFirehoseUpdater, fu_firehose_updater, FU, FIREHOSE_UPDATER, GObject) - -FuFirehoseUpdater * -fu_firehose_updater_new(const gchar *port, FuSaharaLoader *sahara); - -void -fu_firehose_updater_set_supports_zlp(FuFirehoseUpdater *self, gboolean supports_zlp); -gboolean -fu_firehose_updater_open(FuFirehoseUpdater *self, GError **error); -gboolean -fu_firehose_updater_write(FuFirehoseUpdater *self, - XbSilo *silo, - GPtrArray *action_nodes, - FuProgress *progress, - GError **error); -gboolean -fu_firehose_updater_close(FuFirehoseUpdater *self, GError **error); - -/* helpers */ - -gboolean -fu_firehose_updater_validate_rawprogram(GBytes *rawprogram, - FuArchive *archive, - XbSilo **out_silo, - GPtrArray **out_action_nodes, - GError **error); diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mbim-qdu-updater.c fwupd-2.0.20/plugins/modem-manager/fu-mbim-qdu-updater.c --- fwupd-2.0.8/plugins/modem-manager/fu-mbim-qdu-updater.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mbim-qdu-updater.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,527 +0,0 @@ -/* - * Copyright 2021 Jarvis Jiang - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include -#include -#include - -#include "fu-mbim-qdu-updater.h" - -#define FU_MBIM_QDU_MAX_OPEN_ATTEMPTS 8 - -struct _FuMbimQduUpdater { - GObject parent_instance; - gchar *mbim_port; - MbimDevice *mbim_device; -}; - -G_DEFINE_TYPE(FuMbimQduUpdater, fu_mbim_qdu_updater, G_TYPE_OBJECT) - -typedef struct { - GMainLoop *mainloop; - MbimDevice *mbim_device; - GError *error; - guint open_attempts; -} OpenContext; - -static void -fu_mbim_qdu_updater_mbim_device_open_attempt(OpenContext *ctx); - -static void -fu_mbim_qdu_updater_mbim_device_open_ready(GObject *mbim_device, - GAsyncResult *res, - gpointer user_data) -{ - OpenContext *ctx = (OpenContext *)user_data; - - if (ctx->open_attempts == 0) { - g_set_error_literal(&ctx->error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "no open attempts"); - return; - } - - if (!mbim_device_open_full_finish(MBIM_DEVICE(mbim_device), res, &ctx->error)) { - ctx->open_attempts--; - if (ctx->open_attempts == 0) { - g_clear_object(&ctx->mbim_device); - g_main_loop_quit(ctx->mainloop); - return; - } - - /* retry */ - g_debug("couldn't open mbim device: %s", ctx->error->message); - g_clear_error(&ctx->error); - fu_mbim_qdu_updater_mbim_device_open_attempt(ctx); - return; - } - - g_main_loop_quit(ctx->mainloop); -} - -static void -fu_mbim_qdu_updater_mbim_device_open_attempt(OpenContext *ctx) -{ - /* all communication through the proxy */ - MbimDeviceOpenFlags open_flags = MBIM_DEVICE_OPEN_FLAGS_PROXY; - - g_debug("trying to open MBIM device..."); - mbim_device_open_full(ctx->mbim_device, - open_flags, - 10, - NULL, - fu_mbim_qdu_updater_mbim_device_open_ready, - ctx); -} - -static void -fu_mbim_qdu_updater_mbim_device_new_ready(GObject *source, GAsyncResult *res, gpointer user_data) -{ - OpenContext *ctx = (OpenContext *)user_data; - - ctx->mbim_device = mbim_device_new_finish(res, &ctx->error); - if (ctx->mbim_device == NULL) { - g_main_loop_quit(ctx->mainloop); - return; - } - - fu_mbim_qdu_updater_mbim_device_open_attempt(ctx); -} - -gboolean -fu_mbim_qdu_updater_open(FuMbimQduUpdater *self, GError **error) -{ - g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); - g_autoptr(GFile) mbim_device_file = g_file_new_for_path(self->mbim_port); - OpenContext ctx = { - .mainloop = mainloop, - .mbim_device = NULL, - .error = NULL, - .open_attempts = FU_MBIM_QDU_MAX_OPEN_ATTEMPTS, - }; - - mbim_device_new(mbim_device_file, NULL, fu_mbim_qdu_updater_mbim_device_new_ready, &ctx); - g_main_loop_run(mainloop); - - /* either we have all device or otherwise error is set */ - if (ctx.mbim_device != NULL) { - g_warn_if_fail(ctx.error == NULL); - self->mbim_device = ctx.mbim_device; - /* success */ - return TRUE; - } - - g_warn_if_fail(ctx.error != NULL); - g_warn_if_fail(ctx.mbim_device == NULL); - g_propagate_error(error, ctx.error); - return FALSE; -} - -typedef struct { - GMainLoop *mainloop; - MbimDevice *mbim_device; - GError *error; -} CloseContext; - -static void -fu_mbim_qdu_updater_mbim_device_close_ready(GObject *mbim_device, - GAsyncResult *res, - gpointer user_data) -{ - CloseContext *ctx = (CloseContext *)user_data; - - /* ignore errors when closing */ - mbim_device_close_finish(MBIM_DEVICE(mbim_device), res, &ctx->error); - g_clear_object(&ctx->mbim_device); - g_main_loop_quit(ctx->mainloop); -} - -gboolean -fu_mbim_qdu_updater_close(FuMbimQduUpdater *self, GError **error) -{ - g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); - CloseContext ctx = { - .mainloop = mainloop, - .mbim_device = g_steal_pointer(&self->mbim_device), - .error = NULL, - }; - - if (ctx.mbim_device == NULL) - return TRUE; - - mbim_device_close(ctx.mbim_device, - 5, - NULL, - fu_mbim_qdu_updater_mbim_device_close_ready, - &ctx); - g_main_loop_run(mainloop); - - /* we should always have both device cleared, and optionally error set */ - g_warn_if_fail(ctx.mbim_device == NULL); - - if (ctx.error != NULL) { - g_propagate_error(error, ctx.error); - return FALSE; - } - - /* update attach right after this */ - return TRUE; -} - -typedef struct { - GMainLoop *mainloop; - GError *error; - gchar *firmware_version; -} GetFirmwareVersionContext; - -static void -fu_mbim_qdu_updater_caps_query_ready(MbimDevice *device, GAsyncResult *res, gpointer user_data) -{ - GetFirmwareVersionContext *ctx = user_data; - g_autofree gchar *firmware_version = NULL; - g_autoptr(MbimMessage) response = NULL; - - response = mbim_device_command_finish(device, res, &ctx->error); - if (!response || !mbim_message_response_get_result(response, - MBIM_MESSAGE_TYPE_COMMAND_DONE, - &ctx->error)) { - g_debug("operation failed: %s", ctx->error->message); - g_main_loop_quit(ctx->mainloop); - return; - } - - if (!mbim_message_device_caps_response_parse(response, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - &firmware_version, - NULL, - &ctx->error)) { - g_debug("couldn't parse response message: %s", ctx->error->message); - g_main_loop_quit(ctx->mainloop); - return; - } - - g_debug("[%s] Successfully request modem to query caps", - mbim_device_get_path_display(device)); - - ctx->firmware_version = g_strdup(firmware_version); - - g_main_loop_quit(ctx->mainloop); -} - -static void -fu_mbim_qdu_updater_caps_query(MbimDevice *device, GetFirmwareVersionContext *ctx) -{ - g_autoptr(MbimMessage) request = NULL; - - request = mbim_message_device_caps_query_new(NULL); - - mbim_device_command(device, - request, - 10, - NULL, - (GAsyncReadyCallback)fu_mbim_qdu_updater_caps_query_ready, - ctx); -} - -gchar * -fu_mbim_qdu_updater_check_ready(FuMbimQduUpdater *self, GError **error) -{ - g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); - GetFirmwareVersionContext ctx = { - .mainloop = mainloop, - .error = NULL, - .firmware_version = NULL, - }; - - fu_mbim_qdu_updater_caps_query(self->mbim_device, &ctx); - - g_main_loop_run(mainloop); - - if (ctx.error != NULL) { - g_propagate_error(error, ctx.error); - return NULL; - } - - return ctx.firmware_version; -} - -typedef struct { - GMainLoop *mainloop; - MbimDevice *mbim_device; - GError *error; - GBytes *blob; - GArray *digest; - FuChunkArray *chunks; - guint chunk_sent; - FuDevice *device; - FuProgress *progress; -} WriteContext; - -static void -fu_mbim_qdu_updater_file_write_ready(MbimDevice *device, GAsyncResult *res, gpointer user_data) -{ - WriteContext *ctx = user_data; - g_autoptr(MbimMessage) response = NULL; - - response = mbim_device_command_finish(device, res, &ctx->error); - if (!response || !mbim_message_response_get_result(response, - MBIM_MESSAGE_TYPE_COMMAND_DONE, - &ctx->error)) { - g_debug("operation failed: %s", ctx->error->message); - g_object_unref(ctx->chunks); - g_main_loop_quit(ctx->mainloop); - return; - } - - if (!mbim_message_qdu_file_write_response_parse(response, &ctx->error)) { - g_debug("couldn't parse response message: %s", ctx->error->message); - g_object_unref(ctx->chunks); - g_main_loop_quit(ctx->mainloop); - return; - } - - ctx->chunk_sent++; - fu_progress_set_percentage_full(ctx->progress, - (gsize)ctx->chunk_sent, - (gsize)fu_chunk_array_length(ctx->chunks)); - if (ctx->chunk_sent < fu_chunk_array_length(ctx->chunks)) { - g_autoptr(FuChunk) chk = NULL; - g_autoptr(MbimMessage) request = NULL; - - /* prepare chunk */ - chk = fu_chunk_array_index(ctx->chunks, ctx->chunk_sent, &ctx->error); - if (chk == NULL) { - g_main_loop_quit(ctx->mainloop); - return; - } - request = - mbim_message_qdu_file_write_set_new(fu_chunk_get_data_sz(chk), - (const guint8 *)fu_chunk_get_data(chk), - NULL); - mbim_device_command(ctx->mbim_device, - request, - 20, - NULL, - (GAsyncReadyCallback)fu_mbim_qdu_updater_file_write_ready, - ctx); - return; - } - - g_object_unref(ctx->chunks); - g_main_loop_quit(ctx->mainloop); -} - -static void -fu_mbim_qdu_updater_file_open_ready(MbimDevice *device, GAsyncResult *res, gpointer user_data) -{ - WriteContext *ctx = user_data; - guint32 out_max_transfer_size; - g_autoptr(FuChunk) chk = NULL; - g_autoptr(MbimMessage) request = NULL; - g_autoptr(MbimMessage) response = NULL; - - response = mbim_device_command_finish(device, res, &ctx->error); - if (!response || !mbim_message_response_get_result(response, - MBIM_MESSAGE_TYPE_COMMAND_DONE, - &ctx->error)) { - g_debug("operation failed: %s", ctx->error->message); - g_main_loop_quit(ctx->mainloop); - return; - } - - if (!mbim_message_qdu_file_open_response_parse(response, - &out_max_transfer_size, - NULL, - &ctx->error)) { - g_debug("couldn't parse response message: %s", ctx->error->message); - g_main_loop_quit(ctx->mainloop); - return; - } - - ctx->chunks = fu_chunk_array_new_from_bytes(ctx->blob, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - out_max_transfer_size); - chk = fu_chunk_array_index(ctx->chunks, 0, &ctx->error); - if (chk == NULL) { - g_main_loop_quit(ctx->mainloop); - return; - } - request = mbim_message_qdu_file_write_set_new(fu_chunk_get_data_sz(chk), - (const guint8 *)fu_chunk_get_data(chk), - NULL); - mbim_device_command(ctx->mbim_device, - request, - 10, - NULL, - (GAsyncReadyCallback)fu_mbim_qdu_updater_file_write_ready, - ctx); -} - -static void -fu_mbim_qdu_updater_session_ready(MbimDevice *device, GAsyncResult *res, gpointer user_data) -{ - WriteContext *ctx = user_data; - g_autoptr(MbimMessage) response = NULL; - g_autoptr(MbimMessage) request = NULL; - - response = mbim_device_command_finish(device, res, &ctx->error); - if (!response || !mbim_message_response_get_result(response, - MBIM_MESSAGE_TYPE_COMMAND_DONE, - &ctx->error)) { - g_debug("operation failed: %s", ctx->error->message); - g_main_loop_quit(ctx->mainloop); - return; - } - - if (!mbim_message_qdu_update_session_response_parse(response, - NULL, - NULL, - NULL, - NULL, - NULL, - NULL, - &ctx->error)) { - g_debug("couldn't parse response message: %s", ctx->error->message); - g_main_loop_quit(ctx->mainloop); - return; - } - - g_debug("[%s] Successfully request modem to update session", - mbim_device_get_path_display(device)); - - request = mbim_message_qdu_file_open_set_new(MBIM_QDU_FILE_TYPE_LITTLE_ENDIAN_PACKAGE, - g_bytes_get_size(ctx->blob), - NULL); - mbim_device_command(device, - request, - 10, - NULL, - (GAsyncReadyCallback)fu_mbim_qdu_updater_file_open_ready, - ctx); -} - -static void -fu_mbim_qdu_updater_set_update_session(MbimDevice *device, WriteContext *ctx) -{ - g_autoptr(MbimMessage) request = NULL; - - request = mbim_message_qdu_update_session_set_new(MBIM_QDU_SESSION_ACTION_START, - MBIM_QDU_SESSION_TYPE_LE, - NULL); - - mbim_device_command(device, - request, - 10, - NULL, - (GAsyncReadyCallback)fu_mbim_qdu_updater_session_ready, - ctx); -} - -static GArray * -fu_mbim_qdu_updater_get_checksum(GBytes *blob) -{ - gsize file_size; - gsize hash_size; - GArray *digest; - g_autoptr(GChecksum) checksum = NULL; - - /* get checksum, to be used as unique id */ - file_size = g_bytes_get_size(blob); - hash_size = g_checksum_type_get_length(G_CHECKSUM_SHA256); - checksum = g_checksum_new(G_CHECKSUM_SHA256); - g_checksum_update(checksum, g_bytes_get_data(blob, NULL), file_size); - - /* libqmi expects a GArray of bytes, not a GByteArray */ - digest = g_array_sized_new(FALSE, FALSE, sizeof(guint8), hash_size); - g_array_set_size(digest, hash_size); - g_checksum_get_digest(checksum, (guint8 *)digest->data, &hash_size); - - return digest; -} - -GArray * -fu_mbim_qdu_updater_write(FuMbimQduUpdater *self, - const gchar *filename, - GBytes *blob, - FuDevice *device, - FuProgress *progress, - GError **error) -{ - g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); - g_autoptr(GArray) digest = fu_mbim_qdu_updater_get_checksum(blob); - g_autoptr(FuChunkArray) chunks = NULL; - WriteContext ctx = { - .mainloop = mainloop, - .mbim_device = self->mbim_device, - .error = NULL, - .blob = blob, - .digest = digest, - .chunks = chunks, - .chunk_sent = 0, - .device = device, - .progress = progress, - }; - - fu_mbim_qdu_updater_set_update_session(self->mbim_device, &ctx); - - g_main_loop_run(mainloop); - - if (ctx.error != NULL) { - g_propagate_error(error, ctx.error); - return NULL; - } - - return g_steal_pointer(&digest); -} - -MbimDevice * -fu_mbim_qdu_updater_get_mbim_device(FuMbimQduUpdater *self) -{ - return self->mbim_device; -} - -static void -fu_mbim_qdu_updater_init(FuMbimQduUpdater *self) -{ -} - -static void -fu_mbim_qdu_updater_finalize(GObject *object) -{ - FuMbimQduUpdater *self = FU_MBIM_QDU_UPDATER(object); - g_warn_if_fail(self->mbim_device == NULL); - g_free(self->mbim_port); - G_OBJECT_CLASS(fu_mbim_qdu_updater_parent_class)->finalize(object); -} - -static void -fu_mbim_qdu_updater_class_init(FuMbimQduUpdaterClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - - object_class->finalize = fu_mbim_qdu_updater_finalize; -} - -FuMbimQduUpdater * -fu_mbim_qdu_updater_new(const gchar *mbim_port) -{ - FuMbimQduUpdater *self = g_object_new(FU_TYPE_MBIM_QDU_UPDATER, NULL); - self->mbim_port = g_strdup(mbim_port); - return self; -} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mbim-qdu-updater.h fwupd-2.0.20/plugins/modem-manager/fu-mbim-qdu-updater.h --- fwupd-2.0.8/plugins/modem-manager/fu-mbim-qdu-updater.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mbim-qdu-updater.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright 2021 Jarvis Jiang - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#include "fu-mm-device.h" - -#define FU_TYPE_MBIM_QDU_UPDATER (fu_mbim_qdu_updater_get_type()) -G_DECLARE_FINAL_TYPE(FuMbimQduUpdater, fu_mbim_qdu_updater, FU, MBIM_QDU_UPDATER, GObject) - -FuMbimQduUpdater * -fu_mbim_qdu_updater_new(const gchar *mbim_port); -gboolean -fu_mbim_qdu_updater_open(FuMbimQduUpdater *self, GError **error); -GArray * -fu_mbim_qdu_updater_write(FuMbimQduUpdater *self, - const gchar *filename, - GBytes *blob, - FuDevice *device, - FuProgress *progress, - GError **error); -gchar * -fu_mbim_qdu_updater_check_ready(FuMbimQduUpdater *self, GError **error); -gboolean -fu_mbim_qdu_updater_close(FuMbimQduUpdater *self, GError **error); -MbimDevice * -fu_mbim_qdu_updater_get_mbim_device(FuMbimQduUpdater *self); diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-backend.c fwupd-2.0.20/plugins/modem-manager/fu-mm-backend.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-backend.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-backend.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,528 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-mm-backend.h" +#include "fu-mm-common.h" +#include "fu-mm-dfota-device.h" +#include "fu-mm-fastboot-device.h" +#include "fu-mm-fdl-device.h" +#include "fu-mm-firehose-device.h" +#include "fu-mm-mbim-device.h" +#include "fu-mm-mhi-qcdm-device.h" +#include "fu-mm-qcdm-device.h" +#include "fu-mm-qdu-mbim-device.h" +#include "fu-mm-qmi-device.h" + +struct _FuMmBackend { + FuBackend parent_instance; + MMManager *manager; + gboolean manager_ready; + GFileMonitor *modem_power_monitor; +}; + +G_DEFINE_TYPE(FuMmBackend, fu_mm_backend, FU_TYPE_BACKEND) + +/* out-of-tree modem-power driver is unsupported */ +#define FU_MM_BACKEND_MODEM_POWER_SYSFS_PATH "/sys/class/modem-power" + +static void +fu_mm_backend_to_string(FuBackend *backend, guint idt, GString *str) +{ + FuMmBackend *self = FU_MM_BACKEND(backend); + fwupd_codec_string_append_bool(str, idt, "ManagerReady", self->manager_ready); +} + +static void +fu_mm_backend_device_inhibit(FuMmBackend *self, FuMmDevice *device) +{ + const gchar *inhibition_uid = fu_mm_device_get_inhibition_uid(FU_MM_DEVICE(device)); + g_autoptr(GError) error_local = NULL; + + if (inhibition_uid == NULL) + return; + g_debug("inhibit modemmanager device with uid %s", inhibition_uid); + if (!mm_manager_inhibit_device_sync(self->manager, inhibition_uid, NULL, &error_local)) + g_debug("ignoring: %s", error_local->message); +} + +static void +fu_mm_backend_device_uninhibit(FuMmBackend *self, FuMmDevice *device) +{ + const gchar *inhibition_uid = fu_mm_device_get_inhibition_uid(FU_MM_DEVICE(device)); + g_autoptr(GError) error_local = NULL; + + if (inhibition_uid == NULL) + return; + g_debug("uninhibit modemmanager device with uid %s", inhibition_uid); + if (!mm_manager_uninhibit_device_sync(self->manager, inhibition_uid, NULL, &error_local)) + g_debug("ignoring: %s", error_local->message); +} + +static void +fu_mm_backend_device_inhibited_notify_cb(FuDevice *device, GParamSpec *pspec, gpointer user_data) +{ + FuMmBackend *self = FU_MM_BACKEND(user_data); + if (fu_mm_device_get_inhibited(FU_MM_DEVICE(device))) { + fu_mm_backend_device_inhibit(self, FU_MM_DEVICE(device)); + return; + } + fu_mm_backend_device_uninhibit(self, FU_MM_DEVICE(device)); +} + +static FuDevice * +fu_mm_backend_probe_gtype(FuMmBackend *self, MMObject *omodem, GError **error) +{ + FuContext *ctx = fu_backend_get_context(FU_BACKEND(self)); + MMModemFirmware *modem_fw = mm_object_peek_modem_firmware(omodem); + const gchar **device_ids; + g_autofree gchar *device_ids_str = NULL; + g_autoptr(MMFirmwareUpdateSettings) update_settings = NULL; + + /* use the instance IDs provided by ModemManager to find the correct GType */ + update_settings = mm_modem_firmware_get_update_settings(modem_fw); + device_ids = mm_firmware_update_settings_get_device_ids(update_settings); + if (device_ids == NULL || device_ids[0] == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "modem did not specify any device IDs"); + return NULL; + } + for (guint i = 0; device_ids[i] != NULL; i++) { + g_autofree gchar *guid = fwupd_guid_hash_string(device_ids[i]); + const gchar *gtypestr = fu_context_lookup_quirk_by_id(ctx, guid, FU_QUIRKS_GTYPE); + if (gtypestr != NULL) { + GType gtype = g_type_from_name(gtypestr); + if (gtype == G_TYPE_INVALID) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unknown GType name %s", + gtypestr); + return NULL; + } + return g_object_new(gtype, "context", ctx, NULL); + } + } + + /* failed */ + device_ids_str = g_strjoinv(", ", (gchar **)device_ids); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no explicit GType for %s", + device_ids_str); + return NULL; +} + +static FuDevice * +fu_mm_backend_probe_gtype_fallback(FuMmBackend *self, MMObject *omodem, GError **error) +{ + MMModem *modem = mm_object_peek_modem(omodem); + FuContext *ctx = fu_backend_get_context(FU_BACKEND(self)); + MMModemFirmware *modem_fw; + MMModemFirmwareUpdateMethod update_methods; + MMModemPortInfo *used_ports = NULL; + guint n_used_ports = 0; +#if MM_CHECK_VERSION(1, 26, 0) + MMModemPortInfo *ignored_ports = NULL; + guint n_ignored_ports = 0; +#endif + const gchar **device_ids; + guint64 ports_bitmask = 0; + GType gtype = G_TYPE_INVALID; + g_autoptr(MMFirmwareUpdateSettings) update_settings = NULL; + struct { + GType gtype; + MMModemPortType port_type; + MMModemFirmwareUpdateMethod method; + } map[] = { + { + FU_TYPE_MM_QDU_MBIM_DEVICE, + MM_MODEM_PORT_TYPE_MBIM, + MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU, + }, + { + FU_TYPE_MM_MBIM_DEVICE, + MM_MODEM_PORT_TYPE_MBIM, + MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE | MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA, + }, + { + FU_TYPE_MM_FASTBOOT_DEVICE, + MM_MODEM_PORT_TYPE_AT, + MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT, + }, + { + FU_TYPE_MM_QMI_DEVICE, + MM_MODEM_PORT_TYPE_QMI, + MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC | MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT, + }, + { + FU_TYPE_MM_QCDM_DEVICE, + MM_MODEM_PORT_TYPE_QCDM, + MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU, + }, + { + FU_TYPE_MM_MHI_QCDM_DEVICE, + MM_MODEM_PORT_TYPE_QCDM, + MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE, + }, + { + FU_TYPE_MM_QCDM_DEVICE, + MM_MODEM_PORT_TYPE_QCDM, + MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE | MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA, + }, + { + FU_TYPE_MM_FIREHOSE_DEVICE, + MM_MODEM_PORT_TYPE_AT, + MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE | MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA, + }, + { + FU_TYPE_MM_FDL_DEVICE, + MM_MODEM_PORT_TYPE_AT, + MM_MODEM_FIRMWARE_UPDATE_METHOD_CINTERION_FDL, + }, + { + FU_TYPE_MM_DFOTA_DEVICE, + MM_MODEM_PORT_TYPE_AT, + MM_MODEM_FIRMWARE_UPDATE_METHOD_DFOTA, + }, + }; + + modem_fw = mm_object_peek_modem_firmware(omodem); + update_settings = mm_modem_firmware_get_update_settings(modem_fw); + update_methods = mm_firmware_update_settings_get_method(update_settings); + if (update_methods == MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "does not support firmware updates"); + return NULL; + } + + if (mm_modem_get_ports(modem, &used_ports, &n_used_ports)) { + for (guint i = 0; i < n_used_ports; i++) { + g_debug("found port %s: %s", + used_ports[i].name, + fu_mm_device_port_type_to_string(used_ports[i].type)); + FU_BIT_SET(ports_bitmask, used_ports[i].type); + } + mm_modem_port_info_array_free(used_ports, n_used_ports); + } + +#if MM_CHECK_VERSION(1, 26, 0) + if (mm_modem_get_ignored_ports(modem, &ignored_ports, &n_ignored_ports)) { + for (guint i = 0; i < n_ignored_ports; i++) { + g_debug("found port %s: %s", + ignored_ports[i].name, + fu_mm_device_port_type_to_string(ignored_ports[i].type)); + FU_BIT_SET(ports_bitmask, ignored_ports[i].type); + } + mm_modem_port_info_array_free(ignored_ports, n_ignored_ports); + } +#endif + + /* find the correct GType */ + for (guint i = 0; i < G_N_ELEMENTS(map); i++) { + if (FU_BIT_IS_SET(ports_bitmask, map[i].port_type) && + update_methods == map[i].method) { + gtype = map[i].gtype; + break; + } + } + if (gtype == G_TYPE_INVALID) { + g_autofree gchar *methods_str = + mm_modem_firmware_update_method_build_string_from_mask(update_methods); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "update method %s not supported", + methods_str); + return NULL; + } + + /* it's much better to be explicit, so ask the user to provide this information to us */ + device_ids = mm_firmware_update_settings_get_device_ids(update_settings); + if (device_ids != NULL && device_ids[0] != NULL) { + g_autofree gchar *device_ids_str = g_strjoinv(", ", (gchar **)device_ids); +#ifdef SUPPORTED_BUILD + g_debug("no explicit GType for %s, falling back to %s", + device_ids_str, + g_type_name(gtype)); +#else + g_warning("no explicit GType for %s, falling back to %s", + device_ids_str, + g_type_name(gtype)); + g_warning("Please see " + "https://github.com/fwupd/fwupd/wiki/Daemon-Warning:-FuMmDevice-GType"); +#endif + } + + /* success */ + return g_object_new(gtype, "context", ctx, NULL); +} + +static FuDevice * +fu_mm_backend_device_create_from_omodem(FuMmBackend *self, MMObject *omodem, GError **error) +{ + g_autoptr(FuDevice) device = NULL; + g_autoptr(GError) error_local = NULL; + + /* create device and probe */ + device = fu_mm_backend_probe_gtype(self, omodem, &error_local); + if (device == NULL) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("ignoring, and trying legacy fallback: %s", error_local->message); + device = fu_mm_backend_probe_gtype_fallback(self, omodem, error); + if (device == NULL) + return NULL; + } else { + g_propagate_error(error, g_steal_pointer(&error_local)); + return NULL; + } + } + if (!fu_mm_device_probe_from_omodem(FU_MM_DEVICE(device), omodem, error)) + return NULL; + + /* fastboot extra properties */ + if (FU_IS_MM_FASTBOOT_DEVICE(device)) { + MMModemFirmware *modem_fw = mm_object_peek_modem_firmware(omodem); + g_autoptr(MMFirmwareUpdateSettings) update_settings = + mm_modem_firmware_get_update_settings(modem_fw); + const gchar *tmp = mm_firmware_update_settings_get_fastboot_at(update_settings); + if (tmp == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "modem does not set fastboot command"); + return NULL; + } + fu_mm_fastboot_device_set_detach_at(FU_MM_FASTBOOT_DEVICE(device), tmp); + } + + /* success */ + return g_steal_pointer(&device); +} + +static void +fu_mm_backend_ensure_modem_power_inhibit(FuMmBackend *self, FuDevice *device) +{ + if (g_file_test(FU_MM_BACKEND_MODEM_POWER_SYSFS_PATH, G_FILE_TEST_EXISTS)) { + fu_device_inhibit(device, + "modem-power", + "The modem-power kernel driver cannot be used"); + } else { + fu_device_uninhibit(device, "modem-power"); + } +} + +static void +fu_mm_backend_device_add(FuMmBackend *self, MMObject *omodem) +{ + g_autoptr(GError) error = NULL; + g_autoptr(FuDevice) device = NULL; + + device = fu_mm_backend_device_create_from_omodem(self, omodem, &error); + if (device == NULL) { + g_debug("ignoring: %s", error->message); + return; + } + + /* inhibit MmManager when required */ + g_signal_connect(device, + "notify::inhibited", + G_CALLBACK(fu_mm_backend_device_inhibited_notify_cb), + self); + fu_mm_backend_ensure_modem_power_inhibit(self, device); + fu_backend_device_added(FU_BACKEND(self), device); +} + +static void +fu_mm_backend_device_added_cb(MMManager *manager, MMObject *omodem, FuMmBackend *self) +{ + FuDevice *device = fu_backend_lookup_by_id(FU_BACKEND(self), mm_object_get_path(omodem)); + if (device != NULL) { + g_autoptr(GError) error_local = NULL; + g_debug("modem came back, rescanning"); + if (!fu_mm_device_probe_from_omodem(FU_MM_DEVICE(device), omodem, &error_local)) + g_debug("ignoring: %s", error_local->message); + /* FIXME: perhaps need to mm_firmware_update_settings_get_fastboot_at() */ + } + fu_mm_backend_device_add(self, omodem); +} + +static void +fu_mm_backend_device_removed_cb(MMManager *manager, MMObject *omodem, FuBackend *backend) +{ + FuDevice *device = fu_backend_lookup_by_id(backend, mm_object_get_path(omodem)); + if (device == NULL) + return; + if (fu_mm_device_get_inhibited(FU_MM_DEVICE(device))) { + g_debug("inhibited modem %s, ignoring", fu_device_get_backend_id(device)); + return; + } + g_debug("removed modem: %s", fu_device_get_backend_id(device)); + fu_backend_device_removed(backend, device); +} + +static void +fu_mm_backend_modem_power_changed_cb(GFileMonitor *monitor, + GFile *file, + GFile *other_file, + GFileMonitorEvent event_type, + gpointer user_data) +{ + FuMmBackend *self = FU_MM_BACKEND(user_data); + GPtrArray *devices = fu_backend_get_devices(FU_BACKEND(self)); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + fu_mm_backend_ensure_modem_power_inhibit(self, device); + } +} + +static gboolean +fu_mm_backend_setup(FuBackend *backend, + FuBackendSetupFlags flags, + FuProgress *progress, + GError **error) +{ + FuMmBackend *self = FU_MM_BACKEND(backend); + g_autoptr(GDBusConnection) connection = NULL; + g_autoptr(GFile) file = g_file_new_for_path(FU_MM_BACKEND_MODEM_POWER_SYSFS_PATH); + + connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); + if (connection == NULL) + return FALSE; + self->manager = mm_manager_new_sync(connection, + G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, + NULL, + error); + if (self->manager == NULL) + return FALSE; + + /* detect presence of unsupported modem-power driver */ + self->modem_power_monitor = g_file_monitor(file, G_FILE_MONITOR_NONE, NULL, error); + if (self->modem_power_monitor == NULL) + return FALSE; + g_signal_connect(self->modem_power_monitor, + "changed", + G_CALLBACK(fu_mm_backend_modem_power_changed_cb), + self); + + /* success */ + return TRUE; +} + +static void +fu_mm_backend_teardown_manager(FuMmBackend *self) +{ + if (self->manager_ready) { + g_debug("ModemManager no longer available"); + g_signal_handlers_disconnect_by_func(self->manager, + G_CALLBACK(fu_mm_backend_device_added_cb), + self); + g_signal_handlers_disconnect_by_func(self->manager, + G_CALLBACK(fu_mm_backend_device_removed_cb), + self); + self->manager_ready = FALSE; + } +} + +static void +fu_mm_backend_setup_manager(FuMmBackend *self) +{ + const gchar *version = mm_manager_get_version(self->manager); + g_autolist(MMObject) list = NULL; + + if (fu_version_compare(version, MM_REQUIRED_VERSION, FWUPD_VERSION_FORMAT_TRIPLET) < 0) { + g_warning("ModemManager %s is available, but need at least %s", + version, + MM_REQUIRED_VERSION); + return; + } + + g_info("ModemManager %s is available", version); + g_signal_connect(G_DBUS_OBJECT_MANAGER(self->manager), + "object-added", + G_CALLBACK(fu_mm_backend_device_added_cb), + self); + g_signal_connect(G_DBUS_OBJECT_MANAGER(self->manager), + "object-removed", + G_CALLBACK(fu_mm_backend_device_removed_cb), + self); + + list = g_dbus_object_manager_get_objects(G_DBUS_OBJECT_MANAGER(self->manager)); + for (GList *l = list; l != NULL; l = g_list_next(l)) { + MMObject *modem = MM_OBJECT(l->data); + fu_mm_backend_device_add(self, modem); + } + + self->manager_ready = TRUE; +} + +static void +fu_mm_backend_name_owner_changed(FuMmBackend *self) +{ + g_autofree gchar *name_owner = g_dbus_object_manager_client_get_name_owner( + G_DBUS_OBJECT_MANAGER_CLIENT(self->manager)); + if (name_owner != NULL) + fu_mm_backend_setup_manager(self); + else + fu_mm_backend_teardown_manager(self); +} + +static void +fu_mm_backend_name_owner_notify_cb(MMManager *manager, GParamSpec *pspec, FuMmBackend *self) +{ + fu_mm_backend_name_owner_changed(self); +} + +static gboolean +fu_mm_backend_coldplug(FuBackend *backend, FuProgress *progress, GError **error) +{ + FuMmBackend *self = FU_MM_BACKEND(backend); + g_signal_connect(MM_MANAGER(self->manager), + "notify::name-owner", + G_CALLBACK(fu_mm_backend_name_owner_notify_cb), + self); + fu_mm_backend_name_owner_changed(self); + return TRUE; +} + +static void +fu_mm_backend_init(FuMmBackend *self) +{ +} + +static void +fu_mm_backend_finalize(GObject *object) +{ + FuMmBackend *self = FU_MM_BACKEND(object); + if (self->manager != NULL) + g_object_unref(self->manager); + if (self->modem_power_monitor != NULL) + g_object_unref(self->modem_power_monitor); + G_OBJECT_CLASS(fu_mm_backend_parent_class)->finalize(object); +} + +static void +fu_mm_backend_class_init(FuMmBackendClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuBackendClass *backend_class = FU_BACKEND_CLASS(klass); + object_class->finalize = fu_mm_backend_finalize; + backend_class->to_string = fu_mm_backend_to_string; + backend_class->setup = fu_mm_backend_setup; + backend_class->coldplug = fu_mm_backend_coldplug; +} + +FuBackend * +fu_mm_backend_new(FuContext *ctx) +{ + return g_object_new(FU_TYPE_MM_BACKEND, "name", "modem-manager", "context", ctx, NULL); +} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-backend.h fwupd-2.0.20/plugins/modem-manager/fu-mm-backend.h --- fwupd-2.0.8/plugins/modem-manager/fu-mm-backend.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-backend.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,15 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_MM_BACKEND (fu_mm_backend_get_type()) +G_DECLARE_FINAL_TYPE(FuMmBackend, fu_mm_backend, FU, MM_BACKEND, FuBackend) + +FuBackend * +fu_mm_backend_new(FuContext *ctx); diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-common.c fwupd-2.0.20/plugins/modem-manager/fu-mm-common.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,49 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-mm-common.h" + +const gchar * +fu_mm_device_port_type_to_string(MMModemPortType port_type) +{ + if (port_type == MM_MODEM_PORT_TYPE_NET) + return "net"; + if (port_type == MM_MODEM_PORT_TYPE_AT) + return "at"; + if (port_type == MM_MODEM_PORT_TYPE_QCDM) + return "qcdm"; + if (port_type == MM_MODEM_PORT_TYPE_GPS) + return "gps"; + if (port_type == MM_MODEM_PORT_TYPE_QMI) + return "qmi"; + if (port_type == MM_MODEM_PORT_TYPE_MBIM) + return "mbim"; + if (port_type == MM_MODEM_PORT_TYPE_IGNORED) + return "ignored"; + return NULL; +} + +MMModemPortType +fu_mm_device_port_type_from_string(const gchar *port_type) +{ + if (g_strcmp0(port_type, "net") == 0) + return MM_MODEM_PORT_TYPE_NET; + if (g_strcmp0(port_type, "at") == 0) + return MM_MODEM_PORT_TYPE_AT; + if (g_strcmp0(port_type, "qcdm") == 0) + return MM_MODEM_PORT_TYPE_QCDM; + if (g_strcmp0(port_type, "gps") == 0) + return MM_MODEM_PORT_TYPE_GPS; + if (g_strcmp0(port_type, "qmi") == 0) + return MM_MODEM_PORT_TYPE_QMI; + if (g_strcmp0(port_type, "mbim") == 0) + return MM_MODEM_PORT_TYPE_MBIM; + if (g_strcmp0(port_type, "ignored") == 0) + return MM_MODEM_PORT_TYPE_IGNORED; + return MM_MODEM_PORT_TYPE_UNKNOWN; +} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-common.h fwupd-2.0.20/plugins/modem-manager/fu-mm-common.h --- fwupd-2.0.8/plugins/modem-manager/fu-mm-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#include + +const gchar * +fu_mm_device_port_type_to_string(MMModemPortType port_type); +MMModemPortType +fu_mm_device_port_type_from_string(const gchar *port_type); diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-device.c fwupd-2.0.20/plugins/modem-manager/fu-mm-device.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,256 +6,392 @@ #include "config.h" -#include -#include -#include -#include - -#include "fu-cinterion-fdl-updater.h" -#include "fu-dfota-updater.h" -#include "fu-firehose-updater.h" -#include "fu-mbim-qdu-updater.h" -#include "fu-mm-device.h" -#include "fu-qmi-pdc-updater.h" -#include "fu-sahara-loader.h" - -/* Amount of time for the modem to boot in fastboot mode. */ -#define FU_MM_DEVICE_REMOVE_DELAY_RE_ENUMERATE 20000 /* ms */ - -/* Amount of time for the modem to be re-probed and exposed in MM after being - * uninhibited. The timeout is long enough to cover the worst case, where the - * modem boots without SIM card inserted (and therefore the initialization - * may be very slow) and also where carrier config switching is explicitly - * required (e.g. if switching from the default (DF) to generic (GC).*/ -#define FU_MM_DEVICE_REMOVE_DELAY_REPROBE 210000 /* ms */ +#ifdef HAVE_TERMIOS_H +#include +#endif -#define FU_MM_DEVICE_AT_RETRIES 3 -#define FU_MM_DEVICE_AT_DELAY 3000 /* ms */ +#include "fu-mm-common.h" +#include "fu-mm-device.h" -/* Amount of time for the modem to get firmware version */ -#define MAX_WAIT_TIME_SECS 240 /* s */ +/** + * FuMmDevice + * + * A modem manager device. + * + * See also: #FuUdevDevice + */ + +/* not strictly last, but the last we care about */ +#define MM_MODEM_PORT_TYPE_LAST (MM_MODEM_PORT_TYPE_IGNORED + 1) + +typedef enum { + FU_MM_DEVICE_PORT_FLAG_NONE = 0, + FU_MM_DEVICE_PORT_FLAG_MAKE_RAW = 1 << 0, +} G_GNUC_FLAG_ENUM FuMmDevicePortFlags; -struct _FuMmDevice { - FuDevice parent_instance; - MMManager *manager; - - /* ModemManager-based devices will have MMObject and inhibition_uid set, - * udev-based ones won't (as device is already inhibited) */ - MMObject *omodem; - gchar *inhibition_uid; +typedef struct { + MMModemPortType type; + gchar *device_file; + FuMmDevicePortFlags flags; +} FuMmDevicePort; - /* Properties read from the ModemManager-exposed modem, and to be - * propagated to plain udev-exposed modem objects. We assume that - * the firmware upgrade operation doesn't change the USB layout, and - * therefore the USB interface of the modem device that was an - * AT-capable TTY is assumed to be the same one after the upgrade. - */ - MMModemFirmwareUpdateMethod update_methods; - gchar *detach_fastboot_at; +typedef struct { + gboolean inhibited; gchar *branch_at; + gchar *inhibition_uid; + GPtrArray *ports; /* element-type FuMmDevicePort */ +} FuMmDevicePrivate; - /* fastboot detach handling */ - gchar *port_at; - FuIOChannel *io_channel; - - /* qmi-pdc update logic */ - gchar *port_qmi; - FuQmiPdcUpdater *qmi_pdc_updater; - GArray *qmi_pdc_active_id; - guint attach_idle; - - /* mbim-qdu update logic */ - gchar *port_mbim; - FuMbimQduUpdater *mbim_qdu_updater; - - /* firehose update handling */ - gchar *port_qcdm; - gchar *port_edl; - gchar *firehose_prog_file; - FuSaharaLoader *sahara_loader; - FuFirehoseUpdater *firehose_updater; - - /* for sahara */ - FuUdevDevice *udev_device; +G_DEFINE_TYPE_WITH_PRIVATE(FuMmDevice, fu_mm_device, FU_TYPE_UDEV_DEVICE); - /* cinterion-fdl update handling */ - FuCinterionFdlUpdater *cinterion_fdl_updater; +#define GET_PRIVATE(o) (fu_mm_device_get_instance_private(o)) - /* dfota update handling */ - FuDfotaUpdater *dfota_updater; +#define FU_MM_DEVICE_AT_RETRIES 3 - /* firmware path */ - gchar *firmware_path; -}; +#define FU_MM_DEVICE_AT_DELAY 3000 /* ms */ -enum { SIGNAL_ATTACH_FINISHED, SIGNAL_LAST }; +enum { PROP_0, PROP_INHIBITED, PROP_LAST }; -static guint signals[SIGNAL_LAST] = {0}; +static void +fu_mm_device_port_free(FuMmDevicePort *port) +{ + g_free(port->device_file); + g_free(port); +} -G_DEFINE_TYPE(FuMmDevice, fu_mm_device, FU_TYPE_DEVICE) +static void +fu_mm_device_set_branch_at(FuMmDevice *self, const gchar *branch_at) +{ + FuMmDevicePrivate *priv = GET_PRIVATE(self); + if (g_strcmp0(priv->branch_at, branch_at) == 0) + return; + g_free(priv->branch_at); + priv->branch_at = g_strdup(branch_at); +} static void fu_mm_device_to_string(FuDevice *device, guint idt, GString *str) { FuMmDevice *self = FU_MM_DEVICE(device); - fwupd_codec_string_append(str, idt, "AtPort", self->port_at); - fwupd_codec_string_append(str, idt, "QmiPort", self->port_qmi); - fwupd_codec_string_append(str, idt, "MbimPort", self->port_mbim); - fwupd_codec_string_append(str, idt, "QcdmPort", self->port_qcdm); + FuMmDevicePrivate *priv = GET_PRIVATE(self); + fwupd_codec_string_append(str, idt, "BranchAt", priv->branch_at); + fwupd_codec_string_append_bool(str, idt, "Inhibited", priv->inhibited); + fwupd_codec_string_append(str, idt, "InhibitionUid", priv->inhibition_uid); + for (guint i = 0; i < priv->ports->len; i++) { + FuMmDevicePort *port = g_ptr_array_index(priv->ports, i); + g_autofree gchar *title = + g_strdup_printf("Port[%s]", fu_mm_device_port_type_to_string(port->type)); + fwupd_codec_string_append(str, idt, title, port->device_file); + } } const gchar * -fu_mm_device_get_inhibition_uid(FuMmDevice *device) +fu_mm_device_get_inhibition_uid(FuMmDevice *self) { - g_return_val_if_fail(FU_IS_MM_DEVICE(device), NULL); - return device->inhibition_uid; + FuMmDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_MM_DEVICE(self), NULL); + return priv->inhibition_uid; } -MMModemFirmwareUpdateMethod -fu_mm_device_get_update_methods(FuMmDevice *device) +void +fu_mm_device_set_inhibited(FuMmDevice *self, gboolean inhibited) { - g_return_val_if_fail(FU_IS_MM_DEVICE(device), MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE); - return device->update_methods; + FuMmDevicePrivate *priv = GET_PRIVATE(self); + g_return_if_fail(FU_IS_MM_DEVICE(self)); + if (priv->inhibited == inhibited) + return; + priv->inhibited = inhibited; + g_object_notify(G_OBJECT(self), "inhibited"); } -static gboolean -fu_mm_device_validate_firmware_update_method(FuMmDevice *self, GError **error) +gboolean +fu_mm_device_get_inhibited(FuMmDevice *self) { - static const MMModemFirmwareUpdateMethod supported_combinations[] = { - MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT, - MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC | MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT, - MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU, - MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE, -#if MM_CHECK_VERSION(1, 19, 1) - MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE | MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA, -#endif -#if MM_CHECK_VERSION(1, 24, 0) - MM_MODEM_FIRMWARE_UPDATE_METHOD_CINTERION_FDL, - MM_MODEM_FIRMWARE_UPDATE_METHOD_DFOTA, -#endif - }; - g_autofree gchar *methods_str = NULL; + FuMmDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_MM_DEVICE(self), FALSE); + return priv->inhibited; +} - methods_str = mm_modem_firmware_update_method_build_string_from_mask(self->update_methods); - for (guint i = 0; i < G_N_ELEMENTS(supported_combinations); i++) { - if (supported_combinations[i] == self->update_methods) { - g_info("valid firmware update combination: %s", methods_str); +gboolean +fu_mm_device_set_device_file(FuMmDevice *self, MMModemPortType port_type, GError **error) +{ + FuMmDevicePrivate *priv = GET_PRIVATE(self); + g_return_val_if_fail(FU_IS_MM_DEVICE(self), FALSE); + + /* find port by type */ + for (guint i = 0; i < priv->ports->len; i++) { + FuMmDevicePort *port = g_ptr_array_index(priv->ports, i); + if (port_type == port->type) { + if (port->flags & FU_MM_DEVICE_PORT_FLAG_MAKE_RAW) { + fu_device_add_private_flag(FU_DEVICE(self), + FU_MM_DEVICE_FLAG_MAKE_SERIAL_RAW); + } + fu_udev_device_set_device_file(FU_UDEV_DEVICE(self), port->device_file); return TRUE; } } + /* not found */ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "invalid firmware update combination: %s", - methods_str); + "no port for %s", + fu_mm_device_port_type_to_string(port_type)); return FALSE; } -static void -fu_mm_device_add_instance_id(FuDevice *dev, const gchar *device_id) +gboolean +fu_mm_device_set_autosuspend_delay(FuMmDevice *self, guint timeout_ms, GError **error) { - if (g_pattern_match_simple("???\\VID_????", device_id)) { - fu_device_add_instance_id_full(dev, device_id, FU_DEVICE_INSTANCE_FLAG_QUIRKS); - return; - } - if (g_pattern_match_simple("???\\VID_????&PID_????", device_id) || - g_pattern_match_simple("???\\VID_????&PID_????&NAME_*", device_id)) { - fu_device_add_instance_id(dev, device_id); - return; - } - if (g_pattern_match_simple("???\\VID_????&PID_????&REV_????", device_id)) { - if (fu_device_has_private_flag(dev, FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV)) - fu_device_add_instance_id(dev, device_id); - return; - } - if (g_pattern_match_simple("???\\VID_????&PID_????&REV_????&CARRIER_*", device_id)) { - if (!fu_device_has_private_flag(dev, FU_MM_DEVICE_FLAG_USE_BRANCH)) - fu_device_add_instance_id(dev, device_id); - return; + g_autofree gchar *buf = g_strdup_printf("%u", timeout_ms); + g_autoptr(GError) error_local = NULL; + + if (!fu_udev_device_write_sysfs(FU_UDEV_DEVICE(self), + "power/autosuspend_delay_ms", + buf, + 1000, + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("autosuspend_delay_ms does not exist, so skipping"); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; } - if (g_pattern_match_simple("???\\SSVID_????&SSPID_????&REV_????&CARRIER_*", device_id)) { - if (!fu_device_has_private_flag(dev, FU_MM_DEVICE_FLAG_USE_BRANCH)) - fu_device_add_instance_id(dev, device_id); - return; + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_device_add_instance_id_vid(FuMmDevice *self, const gchar *value, GError **error) +{ + if (fu_device_get_vid(FU_DEVICE(self)) == 0x0) { + guint64 value_u64 = 0; + if (!fu_strtoull(value, &value_u64, 0x0, G_MAXUINT16, FU_INTEGER_BASE_16, error)) + return FALSE; + fu_device_set_vid(FU_DEVICE(self), (guint16)value_u64); } - g_warning("failed to add instance ID %s", device_id); + if (fu_device_get_instance_str(FU_DEVICE(self), "VID") == NULL) + fu_device_add_instance_str(FU_DEVICE(self), "VID", value); + return TRUE; } static gboolean -fu_mm_device_ensure_udev_device(FuMmDevice *self, GError **error) +fu_mm_device_add_instance_id_pid(FuMmDevice *self, const gchar *value, GError **error) +{ + if (fu_device_get_pid(FU_DEVICE(self)) == 0x0) { + guint64 value_u64 = 0; + if (!fu_strtoull(value, &value_u64, 0x0, G_MAXUINT16, FU_INTEGER_BASE_16, error)) + return FALSE; + fu_device_set_pid(FU_DEVICE(self), (guint16)value_u64); + } + if (fu_device_get_instance_str(FU_DEVICE(self), "PID") == NULL) + fu_device_add_instance_str(FU_DEVICE(self), "PID", value); + return TRUE; +} + +gboolean +fu_mm_device_add_instance_id(FuMmDevice *self, const gchar *device_id, GError **error) { - FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); - g_autoptr(FuBackend) backend = NULL; - g_autoptr(FuUdevDevice) udev_device = NULL; + g_autofree gchar *subsys_pid = NULL; + g_autofree gchar *subsys_vid = NULL; + g_auto(GStrv) instancestrs = NULL; + g_auto(GStrv) subsys_instancestr = NULL; - backend = fu_context_get_backend_by_name(ctx, "udev", error); - if (backend == NULL) - return FALSE; - udev_device = FU_UDEV_DEVICE( - fu_backend_create_device(backend, fu_device_get_physical_id(FU_DEVICE(self)), error)); - if (udev_device == NULL) { - g_prefix_error(error, - "failed to create udev device for %s: ", - fu_device_get_physical_id(FU_DEVICE(self))); - return FALSE; + /* add vendor ID */ + if (g_pattern_match_simple("???\\VID_????", device_id) || + g_pattern_match_simple("???\\VEN_????", device_id)) { + g_autofree gchar *prefix = g_strndup(device_id, 3); + g_autofree gchar *vendor_id = g_strdup_printf("%s:0x%s", prefix, device_id + 8); + fu_device_add_vendor_id(FU_DEVICE(self), vendor_id); } - if (!fu_device_probe(FU_DEVICE(udev_device), error)) - return FALSE; - fu_mm_device_set_udev_device(self, udev_device); - /* success */ + /* parse the ModemManager InstanceID lookalike */ + subsys_instancestr = g_strsplit(device_id, "\\", 2); + if (subsys_instancestr[1] == NULL) + return TRUE; + instancestrs = g_strsplit(subsys_instancestr[1], "&", -1); + for (guint i = 0; instancestrs[i] != NULL; i++) { + g_auto(GStrv) kv = g_strsplit(instancestrs[i], "_", 2); + if (g_strcmp0(kv[0], "VID") == 0) { + if (!fu_mm_device_add_instance_id_vid(self, kv[1], error)) + return FALSE; + } else if (g_strcmp0(kv[0], "PID") == 0) { + if (!fu_mm_device_add_instance_id_pid(self, kv[1], error)) + return FALSE; + } else if (g_strcmp0(kv[0], "REV") == 0 || g_strcmp0(kv[0], "NAME") == 0 || + g_strcmp0(kv[0], "CARRIER") == 0) { + fu_device_add_instance_str(FU_DEVICE(self), kv[0], kv[1]); + } else if (g_strcmp0(kv[0], "SSVID") == 0 && subsys_vid == NULL) { + subsys_vid = g_strdup(kv[1]); + } else if (g_strcmp0(kv[0], "SSPID") == 0 && subsys_pid == NULL) { + subsys_pid = g_strdup(kv[1]); + } else { + g_debug("ignoring instance attribute '%s'", instancestrs[i]); + } + } + + /* convert nonstandard SSVID+SSPID to SUBSYS */ + if (subsys_vid != NULL && subsys_pid != NULL) { + g_autofree gchar *subsys = g_strdup_printf("%s%s", subsys_vid, subsys_pid); + fu_device_add_instance_str(FU_DEVICE(self), "SUBSYS", subsys); + } + + /* add all possible instance IDs */ + fu_device_build_instance_id_full(FU_DEVICE(self), + FU_DEVICE_INSTANCE_FLAG_QUIRKS, + NULL, + subsys_instancestr[0], + "VID", + NULL); + fu_device_build_instance_id(FU_DEVICE(self), + NULL, + subsys_instancestr[0], + "VID", + "PID", + NULL); + fu_device_build_instance_id(FU_DEVICE(self), + NULL, + subsys_instancestr[0], + "VID", + "PID", + "NAME", + NULL); + fu_device_build_instance_id(FU_DEVICE(self), + NULL, + subsys_instancestr[0], + "VID", + "PID", + "SUBSYS", + NULL); + fu_device_build_instance_id(FU_DEVICE(self), + NULL, + subsys_instancestr[0], + "VID", + "PID", + "SUBSYS", + "NAME", + NULL); + if (fu_device_has_private_flag(FU_DEVICE(self), + FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV)) { + fu_device_build_instance_id(FU_DEVICE(self), + NULL, + subsys_instancestr[0], + "VID", + "PID", + "REV", + NULL); + fu_device_build_instance_id(FU_DEVICE(self), + NULL, + subsys_instancestr[0], + "VID", + "PID", + "REV", + "NAME", + NULL); + fu_device_build_instance_id(FU_DEVICE(self), + NULL, + subsys_instancestr[0], + "VID", + "PID", + "SUBSYS", + "REV", + NULL); + } + if (!fu_device_has_private_flag(FU_DEVICE(self), FU_MM_DEVICE_FLAG_USE_BRANCH)) { + fu_device_build_instance_id(FU_DEVICE(self), + NULL, + subsys_instancestr[0], + "VID", + "PID", + "CARRIER", + NULL); + if (fu_device_has_private_flag(FU_DEVICE(self), + FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV)) { + fu_device_build_instance_id(FU_DEVICE(self), + NULL, + subsys_instancestr[0], + "VID", + "PID", + "REV", + "CARRIER", + NULL); + fu_device_build_instance_id(FU_DEVICE(self), + NULL, + subsys_instancestr[0], + "VID", + "PID", + "SUBSYS", + "REV", + "CARRIER", + NULL); + } + } return TRUE; } -static gboolean -fu_mm_device_probe_default(FuDevice *device, GError **error) +static void +fu_mm_device_add_port(FuMmDevice *self, + MMModemPortType port_type, + const gchar *device_file, + FuMmDevicePortFlags flags) { - FuMmDevice *self = FU_MM_DEVICE(device); - MMModemFirmware *modem_fw; - MMModem *modem = mm_object_peek_modem(self->omodem); - MMModemPortInfo *ports = NULL; + FuMmDevicePrivate *priv = GET_PRIVATE(self); + FuMmDevicePort *port; + + if (port_type >= MM_MODEM_PORT_TYPE_LAST) + return; + + /* already exists */ + for (guint i = 0; i < priv->ports->len; i++) { + port = g_ptr_array_index(priv->ports, i); + if (port->type == port_type) + return; + } + + /* create new */ + port = g_new0(FuMmDevicePort, 1); + port->type = port_type; + port->flags = flags; + port->device_file = g_strdup(device_file); + g_ptr_array_add(priv->ports, port); +} + +gboolean +fu_mm_device_probe_from_omodem(FuMmDevice *self, MMObject *omodem, GError **error) +{ + FuMmDevicePrivate *priv = GET_PRIVATE(self); + MMModemFirmware *modem_fw = mm_object_peek_modem_firmware(omodem); + MMModem *modem = mm_object_peek_modem(omodem); + MMModemPortInfo *used_ports = NULL; + guint n_used_ports = 0; +#if MM_CHECK_VERSION(1, 26, 0) + MMModemPortInfo *ignored_ports = NULL; + guint n_ignored_ports = 0; +#endif const gchar **device_ids; + const gchar *sysfs_path; const gchar *version; - guint n_ports = 0; g_autoptr(MMFirmwareUpdateSettings) update_settings = NULL; - const gchar *sysfs_path; /* inhibition uid is the modem interface 'Device' property, which may * be the device sysfs path or a different user-provided id */ - self->inhibition_uid = mm_modem_dup_device(modem); + priv->inhibition_uid = mm_modem_dup_device(modem); - /* find out what update methods we should use */ - modem_fw = mm_object_peek_modem_firmware(self->omodem); - update_settings = mm_modem_firmware_get_update_settings(modem_fw); - self->update_methods = mm_firmware_update_settings_get_method(update_settings); - if (self->update_methods == MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE) { + /* get the sysfs path for the MM physical device */ + sysfs_path = mm_modem_get_physdev(modem); + if (sysfs_path == NULL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "modem cannot be put in programming mode"); + "no physdev set"); return FALSE; } - - /* make sure the combination is supported */ - if (!fu_mm_device_validate_firmware_update_method(self, error)) - return FALSE; - - /* various fastboot commands */ - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) { - const gchar *tmp; - tmp = mm_firmware_update_settings_get_fastboot_at(update_settings); - if (tmp == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "modem does not set fastboot command"); - return FALSE; - } - self->detach_fastboot_at = g_strdup(tmp); - } + fu_device_set_physical_id(FU_DEVICE(self), sysfs_path); /* get GUIDs */ + update_settings = mm_modem_firmware_get_update_settings(modem_fw); device_ids = mm_firmware_update_settings_get_device_ids(update_settings); if (device_ids == NULL || device_ids[0] == NULL) { g_set_error_literal(error, @@ -275,297 +411,74 @@ return FALSE; } + fu_device_set_backend_id(FU_DEVICE(self), mm_object_get_path(omodem)); + /* look for the AT and QMI/MBIM ports */ - if (!mm_modem_get_ports(modem, &ports, &n_ports)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to get port information"); - return FALSE; - } -#if MM_CHECK_VERSION(1, 24, 0) - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_CINTERION_FDL) { - for (guint i = 0; i < n_ports; i++) { - if (ports[i].type == MM_MODEM_PORT_TYPE_AT) { - self->port_at = g_strdup_printf("/dev/%s", ports[i].name); - break; - } - } - fu_device_add_protocol(device, "com.cinterion.fdl"); - } - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_DFOTA) { - for (guint i = 0; i < n_ports; i++) { - if (ports[i].type == MM_MODEM_PORT_TYPE_AT) { - self->port_at = g_strdup_printf("/dev/%s", ports[i].name); - break; - } - } - fu_device_add_protocol(device, "com.quectel.dfota"); - } -#endif // MM_CHECK_VERSION(1, 24, 0) - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) { - for (guint i = 0; i < n_ports; i++) { - if (ports[i].type == MM_MODEM_PORT_TYPE_AT) { - self->port_at = g_strdup_printf("/dev/%s", ports[i].name); - break; - } - } - fu_device_add_protocol(device, "com.google.fastboot"); - } - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC) { - for (guint i = 0; i < n_ports; i++) { - if ((ports[i].type == MM_MODEM_PORT_TYPE_QMI) || - (ports[i].type == MM_MODEM_PORT_TYPE_MBIM)) { - self->port_qmi = g_strdup_printf("/dev/%s", ports[i].name); - break; - } - } - /* only set if fastboot wasn't already set */ - if (fu_device_get_protocols(device)->len == 0) - fu_device_add_protocol(device, "com.qualcomm.qmi_pdc"); - } - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU) { - for (guint i = 0; i < n_ports; i++) { - if (ports[i].type == MM_MODEM_PORT_TYPE_MBIM) { - self->port_mbim = g_strdup_printf("/dev/%s", ports[i].name); - break; - } - } - fu_device_add_protocol(device, "com.qualcomm.mbim_qdu"); - } - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE) { - for (guint i = 0; i < n_ports; i++) { - if ((ports[i].type == MM_MODEM_PORT_TYPE_QCDM) || - (ports[i].type == MM_MODEM_PORT_TYPE_IGNORED && - g_strstr_len(ports[i].name, -1, "qcdm") != NULL)) - self->port_qcdm = g_strdup_printf("/dev/%s", ports[i].name); - else if (ports[i].type == MM_MODEM_PORT_TYPE_MBIM) - self->port_mbim = g_strdup_printf("/dev/%s", ports[i].name); - /* to read secboot status */ - else if (ports[i].type == MM_MODEM_PORT_TYPE_AT) - self->port_at = g_strdup_printf("/dev/%s", ports[i].name); + if (mm_modem_get_ports(modem, &used_ports, &n_used_ports)) { + for (guint i = 0; i < n_used_ports; i++) { + g_autofree gchar *device_file = + g_strdup_printf("/dev/%s", used_ports[i].name); + if (used_ports[i].type >= MM_MODEM_PORT_TYPE_LAST) + continue; + if (used_ports[i].type == MM_MODEM_PORT_TYPE_IGNORED && + g_pattern_match_simple("wwan*qcdm*", used_ports[i].name)) { + fu_mm_device_add_port(self, + MM_MODEM_PORT_TYPE_QCDM, + device_file, + FU_MM_DEVICE_PORT_FLAG_NONE); + } else { + fu_mm_device_add_port(self, + used_ports[i].type, + device_file, + FU_MM_DEVICE_PORT_FLAG_NONE); + } + } + mm_modem_port_info_array_free(used_ports, n_used_ports); + } + +#if MM_CHECK_VERSION(1, 26, 0) + if (mm_modem_get_ignored_ports(modem, &ignored_ports, &n_ignored_ports)) { + for (guint i = 0; i < n_ignored_ports; i++) { + g_autofree gchar *device_file = + g_strdup_printf("/dev/%s", ignored_ports[i].name); + if (ignored_ports[i].type >= MM_MODEM_PORT_TYPE_LAST) + continue; + fu_mm_device_add_port(self, + ignored_ports[i].type, + device_file, + FU_MM_DEVICE_PORT_FLAG_MAKE_RAW); } - fu_device_add_protocol(device, "com.qualcomm.firehose"); - } - - mm_modem_port_info_array_free(ports, n_ports); - - /* an at port is required for fastboot */ - if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) && - (self->port_at == NULL)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to find AT port"); - return FALSE; - } - -#if MM_CHECK_VERSION(1, 24, 0) - /* an at port is required for dfota */ - if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_DFOTA) && - (self->port_at == NULL)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to find AT port"); - return FALSE; - } -#endif // MM_CHECK_VERSION(1, 24, 0) - - /* a qmi port is required for qmi-pdc */ - if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC) && - (self->port_qmi == NULL)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to find QMI port"); - return FALSE; - } - - /* a mbim port is required for mbim-qdu */ - if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU) && - (self->port_mbim == NULL)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to find MBIM port"); - return FALSE; - } - - /* a qcdm or mbim port is required for firehose */ - if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE) && - (self->port_qcdm == NULL && self->port_mbim == NULL)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to find QCDM port"); - return FALSE; - } - -#if MM_CHECK_VERSION(1, 22, 0) - /* get the FuUdevDevice for the MM physical device */ - sysfs_path = mm_modem_get_physdev(modem); - if (sysfs_path == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no physdev set"); - return FALSE; + mm_modem_port_info_array_free(ignored_ports, n_ignored_ports); } -#else - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "physdev not supported on ModemManager < 1.22"); - return FALSE; #endif - fu_device_set_physical_id(device, sysfs_path); - if (!fu_mm_device_ensure_udev_device(self, error)) - return FALSE; /* add properties to fwupd device */ if (mm_modem_get_manufacturer(modem) != NULL) - fu_device_set_vendor(device, mm_modem_get_manufacturer(modem)); + fu_device_set_vendor(FU_DEVICE(self), mm_modem_get_manufacturer(modem)); if (mm_modem_get_model(modem) != NULL) - fu_device_set_name(device, mm_modem_get_model(modem)); + fu_device_set_name(FU_DEVICE(self), mm_modem_get_model(modem)); /* only for modems that opt-in */ - if (fu_device_has_private_flag(device, FU_MM_DEVICE_FLAG_USE_BRANCH)) - fu_device_set_branch(device, mm_modem_get_carrier_configuration(modem)); + if (fu_device_has_private_flag(FU_DEVICE(self), FU_MM_DEVICE_FLAG_USE_BRANCH)) + fu_device_set_branch(FU_DEVICE(self), mm_modem_get_carrier_configuration(modem)); - fu_device_set_version(device, version); + fu_device_set_version(FU_DEVICE(self), version); /* filter these */ - for (guint i = 0; device_ids[i] != NULL; i++) - fu_mm_device_add_instance_id(device, device_ids[i]); - - /* fix up vendor name */ - if (g_strcmp0(fu_device_get_vendor(device), "QUALCOMM INCORPORATED") == 0) - fu_device_set_vendor(device, "Qualcomm"); - - /* success */ - return TRUE; -} - -static gboolean -fu_mm_device_probe_udev(FuDevice *device, GError **error) -{ - FuMmDevice *self = FU_MM_DEVICE(device); - - /* an at port is required for fastboot */ - if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) && - (self->port_at == NULL)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to find AT port"); - return FALSE; - } - -#if MM_CHECK_VERSION(1, 24, 0) - if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_CINTERION_FDL || - self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_DFOTA) && - (self->port_at == NULL)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to find AT port"); - return FALSE; - } -#endif // MM_CHECK_VERSION(1, 24, 0) - - /* a qmi port is required for qmi-pdc */ - if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC) && - (self->port_qmi == NULL)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to find QMI port"); - return FALSE; - } - - return TRUE; -} - -static gboolean -fu_mm_device_probe(FuDevice *device, GError **error) -{ - FuMmDevice *self = FU_MM_DEVICE(device); - - if (self->omodem) - return fu_mm_device_probe_default(device, error); - return fu_mm_device_probe_udev(device, error); -} - -static gboolean -fu_mm_device_io_open_qcdm(FuMmDevice *self, GError **error) -{ - /* sanity check */ - if (self->port_qcdm == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no QCDM port provided for filename"); - return FALSE; + for (guint i = 0; device_ids[i] != NULL; i++) { + if (!fu_mm_device_add_instance_id(self, device_ids[i], error)) + return FALSE; } - /* open device */ - self->io_channel = - fu_io_channel_new_file(self->port_qcdm, - FU_IO_CHANNEL_OPEN_FLAG_READ | FU_IO_CHANNEL_OPEN_FLAG_WRITE, - error); - if (self->io_channel == NULL) - return FALSE; - /* success */ return TRUE; } -static gboolean -fu_mm_device_qcdm_cmd(FuMmDevice *self, const guint8 *cmd, gsize cmd_len, GError **error) -{ - g_autoptr(GBytes) qcdm_req = NULL; - g_autoptr(GBytes) qcdm_res = NULL; - - /* command */ - qcdm_req = g_bytes_new(cmd, cmd_len); - fu_dump_bytes(G_LOG_DOMAIN, "writing", qcdm_req); - if (!fu_io_channel_write_bytes(self->io_channel, - qcdm_req, - 1500, - FU_IO_CHANNEL_FLAG_FLUSH_INPUT, - error)) { - g_prefix_error(error, "failed to write qcdm command: "); - return FALSE; - } - - /* response */ - qcdm_res = fu_io_channel_read_bytes(self->io_channel, - -1, - 1500, - FU_IO_CHANNEL_FLAG_SINGLE_SHOT, - error); - if (qcdm_res == NULL) { - g_prefix_error(error, "failed to read qcdm response: "); - return FALSE; - } - fu_dump_bytes(G_LOG_DOMAIN, "read", qcdm_res); - - /* command == response */ - if (g_bytes_compare(qcdm_res, qcdm_req) != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "failed to read valid qcdm response"); - return FALSE; - } - - return TRUE; -} - typedef struct { const gchar *cmd; + gsize count; gboolean has_response; + GBytes *blob; } FuMmDeviceAtCmdHelper; static gboolean @@ -575,18 +488,19 @@ FuMmDeviceAtCmdHelper *helper = (FuMmDeviceAtCmdHelper *)user_data; const gchar *buf; gsize bufsz = 0; + g_autofree gchar *at_res_safe = NULL; + g_autofree gchar *cmd_cr = g_strdup_printf("%s\r\n", helper->cmd); g_autoptr(GBytes) at_req = NULL; g_autoptr(GBytes) at_res = NULL; - g_autofree gchar *cmd_cr = g_strdup_printf("%s\r\n", helper->cmd); /* command */ + g_debug("req: %s", helper->cmd); at_req = g_bytes_new(cmd_cr, strlen(cmd_cr)); - fu_dump_bytes(G_LOG_DOMAIN, "writing", at_req); - if (!fu_io_channel_write_bytes(self->io_channel, - at_req, - 1500, - FU_IO_CHANNEL_FLAG_FLUSH_INPUT, - error)) { + if (!fu_udev_device_write_bytes(FU_UDEV_DEVICE(self), + at_req, + 1500, + FU_IO_CHANNEL_FLAG_FLUSH_INPUT, + error)) { g_prefix_error(error, "failed to write %s: ", helper->cmd); return FALSE; } @@ -598,36 +512,36 @@ } /* response */ - at_res = fu_io_channel_read_bytes(self->io_channel, - -1, - 1500, - FU_IO_CHANNEL_FLAG_SINGLE_SHOT, - error); + at_res = fu_udev_device_read_bytes(FU_UDEV_DEVICE(self), + helper->count, + 1500, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error); if (at_res == NULL) { g_prefix_error(error, "failed to read response for %s: ", helper->cmd); return FALSE; } - fu_dump_bytes(G_LOG_DOMAIN, "read", at_res); - buf = g_bytes_get_data(at_res, &bufsz); + at_res_safe = fu_strsafe_bytes(at_res, 32); + g_debug("res: %s", at_res_safe); /* * the first time the modem returns may be the command itself with one \n missing. * this is because the modem AT has enabled echo */ - if (g_strrstr(buf, helper->cmd) != NULL && bufsz == strlen(helper->cmd) + 1) { + buf = g_bytes_get_data(at_res, &bufsz); + if (g_strrstr_len(buf, bufsz, helper->cmd) != NULL && bufsz == strlen(helper->cmd) + 1) { g_bytes_unref(at_res); - at_res = fu_io_channel_read_bytes(self->io_channel, - -1, - 1500, - FU_IO_CHANNEL_FLAG_SINGLE_SHOT, - error); + at_res = fu_udev_device_read_bytes(FU_UDEV_DEVICE(self), + helper->count, + 1500, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error); if (at_res == NULL) { g_prefix_error(error, "failed to read response for %s: ", helper->cmd); return FALSE; } buf = g_bytes_get_data(at_res, &bufsz); } - if (bufsz < 6) { g_set_error(error, FWUPD_ERROR, @@ -638,7 +552,8 @@ } /* return error if AT command failed */ - if (g_strrstr(buf, "\r\nOK\r\n") == NULL && g_strrstr(buf, "\r\nCONNECT\r\n") == NULL) { + if (g_strrstr_len(buf, bufsz, "\r\nOK\r\n") == NULL && + g_strrstr_len(buf, bufsz, "\r\nCONNECT\r\n") == NULL) { g_autofree gchar *tmp = g_strndup(buf + 2, bufsz - 4); g_set_error(error, FWUPD_ERROR, @@ -649,1184 +564,237 @@ return FALSE; } - /* set firmware branch if returned */ - if (self->branch_at != NULL && g_strcmp0(helper->cmd, self->branch_at) == 0) { - /* - * example AT+GETFWBRANCH response: - * - * \r\nFOSS-002 \r\n\r\nOK\r\n - * - * remove \r\n, and OK to get branch name - */ - g_auto(GStrv) parts = g_strsplit(buf, "\r\n", -1); - - for (int j = 0; parts[j] != NULL; j++) { - /* Ignore empty strings, and OK responses */ - if (g_strcmp0(parts[j], "") != 0 && g_strcmp0(parts[j], "OK") != 0) { - /* Set branch */ - fu_device_set_branch(FU_DEVICE(self), parts[j]); - g_info("firmware branch reported as '%s'", parts[j]); - break; - } - } - } - - if (g_strcmp0(helper->cmd, "AT+QSECBOOT=\"status\"") == 0) { - /* - * example AT+QSECBOOT="status" response: - * - * \r\n+QSECBOOT: "STATUS",1\r\n\r\nOK\r\n - * - * Secure boot status: - * 1 - enabled - * 0 - disabled - */ - g_auto(GStrv) parts = g_strsplit(buf, "\r\n", -1); - - for (int j = 0; parts[j] != NULL; j++) { - if (g_strcmp0(parts[j], "+QSECBOOT: \"status\",1") == 0) { - fu_device_add_flag(FU_DEVICE(self), - FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); - break; - } - if (g_strcmp0(parts[j], "+QSECBOOT: \"status\",0") == 0) { - fu_device_add_flag(FU_DEVICE(self), - FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - break; - } - } - } - - if (g_strcmp0(helper->cmd, "AT+QCFG=\"secbootstat\"") == 0) { - /* - * example AT+QSECBOOT="status" response: - * - * \r\n+QSECBOOT: "STATUS",1\r\n\r\nOK\r\n - * - * Secure boot status: - * 1 - enabled - * 0 - disabled - */ - g_auto(GStrv) parts = g_strsplit(buf, "\r\n", -1); - - for (int j = 0; parts[j] != NULL; j++) { - if (g_strcmp0(parts[j], "+QCFG: \"secbootstat\",1") == 0) { - fu_device_add_flag(FU_DEVICE(self), - FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); - break; - } - if (g_strcmp0(parts[j], "+QCFG: \"secbootstat\",0") == 0) { - fu_device_add_flag(FU_DEVICE(self), - FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - break; - } - } - } - /* success */ + helper->blob = g_steal_pointer(&at_res); return TRUE; } -static gboolean +gboolean fu_mm_device_at_cmd(FuMmDevice *self, const gchar *cmd, gboolean has_response, GError **error) { - FuMmDeviceAtCmdHelper helper = {.cmd = cmd, .has_response = has_response}; - return fu_device_retry_full(FU_DEVICE(self), - fu_mm_device_at_cmd_cb, - FU_MM_DEVICE_AT_RETRIES, - FU_MM_DEVICE_AT_DELAY, - &helper, - error); -} - -static gboolean -fu_mm_device_io_open(FuMmDevice *self, GError **error) -{ - /* sanity check */ - if (self->port_at == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no AT port provided for filename"); - return FALSE; - } - - /* open device */ - self->io_channel = - fu_io_channel_new_file(self->port_at, - FU_IO_CHANNEL_OPEN_FLAG_READ | FU_IO_CHANNEL_OPEN_FLAG_WRITE, - error); - if (self->io_channel == NULL) - return FALSE; - - /* success */ + FuMmDeviceAtCmdHelper helper = {.cmd = cmd, .count = 64, .has_response = has_response}; + if (!fu_device_retry_full(FU_DEVICE(self), + fu_mm_device_at_cmd_cb, + FU_MM_DEVICE_AT_RETRIES, + FU_MM_DEVICE_AT_DELAY, + &helper, + error)) + return FALSE; + + /* success, but remove the perhaps-unused buffer */ + if (helper.blob != NULL) + g_bytes_unref(helper.blob); return TRUE; } -static gboolean -fu_mm_device_io_close(FuMmDevice *self, GError **error) -{ - if (self->io_channel != NULL) { - if (!fu_io_channel_shutdown(self->io_channel, error)) - return FALSE; - g_clear_object(&self->io_channel); - } - return TRUE; -} - -#if MM_CHECK_VERSION(1, 24, 0) -static gboolean -fu_mm_device_cinterion_fdl_open(FuMmDevice *self, GError **error) -{ - self->cinterion_fdl_updater = fu_cinterion_fdl_updater_new(self->port_at); - return fu_cinterion_fdl_updater_open(self->cinterion_fdl_updater, error); -} - -static gboolean -fu_mm_device_cinterion_fdl_close(FuMmDevice *self, GError **error) -{ - g_autoptr(FuCinterionFdlUpdater) updater = NULL; - - updater = g_steal_pointer(&self->cinterion_fdl_updater); - return fu_cinterion_fdl_updater_close(updater, error); -} - -static gboolean -fu_mm_device_detach_fdl(FuDevice *device, FuProgress *progress, GError **error) -{ - FuMmDevice *self = FU_MM_DEVICE(device); - g_autoptr(FuDeviceLocker) locker = NULL; - - locker = fu_device_locker_new_full(device, - (FuDeviceLockerFunc)fu_mm_device_io_open, - (FuDeviceLockerFunc)fu_mm_device_io_close, - error); - - if (locker == NULL) - return FALSE; - if (!fu_mm_device_at_cmd(self, "AT", TRUE, error)) - return FALSE; - if (!fu_mm_device_at_cmd(self, "AT^SFDL", TRUE, error)) { - g_prefix_error(error, "enabling firmware download mode not supported: "); - return FALSE; - } - - if (!fu_device_locker_close(locker, error)) - return FALSE; - - /* wait 15 s before reopening port */ - fu_device_sleep(device, 15000); - - locker = fu_device_locker_new_full(self, - (FuDeviceLockerFunc)fu_mm_device_cinterion_fdl_open, - (FuDeviceLockerFunc)fu_mm_device_cinterion_fdl_close, - error); - if (locker == NULL) - return FALSE; - - return fu_cinterion_fdl_updater_wait_ready(self->cinterion_fdl_updater, device, error); -} - -static gboolean -fu_mm_device_io_open_dfota(FuMmDevice *self, GError **error) -{ - if (!fu_mm_device_io_open(self, error)) - return FALSE; - - self->dfota_updater = fu_dfota_updater_new(self->io_channel); - return fu_dfota_updater_open(self->dfota_updater, error); -} - -static gboolean -fu_mm_device_io_close_dfota(FuMmDevice *self, GError **error) -{ - g_autoptr(FuDfotaUpdater) updater = NULL; - - if (!fu_mm_device_io_close(self, error)) - return FALSE; - - updater = g_steal_pointer(&self->dfota_updater); - return fu_dfota_updater_close(updater, error); +static GBytes * +fu_mm_device_at_cmd_full(FuMmDevice *self, const gchar *cmd, gsize count, GError **error) +{ + FuMmDeviceAtCmdHelper helper = {.cmd = cmd, .count = count, .has_response = TRUE}; + if (!fu_device_retry_full(FU_DEVICE(self), + fu_mm_device_at_cmd_cb, + FU_MM_DEVICE_AT_RETRIES, + FU_MM_DEVICE_AT_DELAY, + &helper, + error)) + return NULL; + return helper.blob; } static gboolean -fu_mm_device_setup_firmware_file_dfota(FuDevice *device, GError **error) +fu_mm_device_ensure_branch(FuMmDevice *self, GError **error) { - /* remove firmware file from old update if exists */ - FuMmDevice *self = FU_MM_DEVICE(device); - g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(GError) inner_error = NULL; + FuMmDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GBytes) blob = NULL; + g_auto(GStrv) parts = NULL; - locker = fu_device_locker_new_full(device, - (FuDeviceLockerFunc)fu_mm_device_io_open, - (FuDeviceLockerFunc)fu_mm_device_io_close, - error); - - if (locker == NULL) - return FALSE; - if (!fu_mm_device_at_cmd(self, "AT+QFLST=?", TRUE, error)) { - g_prefix_error(error, "listing files not supported: "); - return FALSE; - } - /* if listing firmware file does not fail, there is an old firmware file to remove */ - if (!fu_mm_device_at_cmd(self, - "AT+QFLST=\"UFS:" FU_DFOTA_UPDATER_FILENAME "\"", - TRUE, - &inner_error)) { - g_debug("no old firmware found in filesystem: %s", inner_error->message); + /* nothing to do if there is no AT port available or + * ModemManagerBranchAtCommand quirk is not set */ + if (priv->branch_at == NULL) return TRUE; - } - - g_debug("found orphaned firmware file - trying to delete it."); - - if (!fu_mm_device_at_cmd(self, "AT+QFDEL=\"" FU_DFOTA_UPDATER_FILENAME "\"", TRUE, error)) { - g_prefix_error(error, "failed to delete existing firmware file: "); - return FALSE; - } - - return TRUE; -} -#endif // MM_CHECK_VERSION(1, 24, 0) - -static gboolean -fu_mm_device_detach_fastboot(FuDevice *device, GError **error) -{ - FuMmDevice *self = FU_MM_DEVICE(device); - g_autoptr(FuDeviceLocker) locker = NULL; - gboolean has_response = TRUE; - /* boot to fastboot mode */ - locker = fu_device_locker_new_full(device, - (FuDeviceLockerFunc)fu_mm_device_io_open, - (FuDeviceLockerFunc)fu_mm_device_io_close, - error); - - /* expect response for fastboot AT command */ - if (fu_device_has_private_flag(FU_DEVICE(self), - FU_MM_DEVICE_FLAG_DETACH_AT_FASTBOOT_HAS_NO_RESPONSE)) { - has_response = FALSE; - } + /* not supported if the devices is signed */ + if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD)) + return TRUE; - if (locker == NULL) - return FALSE; - if (!fu_mm_device_at_cmd(self, "AT", TRUE, error)) - return FALSE; - if (!fu_mm_device_at_cmd(self, self->detach_fastboot_at, has_response, error)) { - g_prefix_error(error, "rebooting into fastboot not supported: "); - return FALSE; + /* example AT+GETFWBRANCH response: "\r\nFOSS-002 \r\n\r\nOK\r\n" */ + blob = fu_mm_device_at_cmd_full(self, priv->branch_at, 64, error); + if (blob == NULL) + return FALSE; + parts = fu_strsplit_bytes(blob, "\r\n", -1); + for (guint i = 0; parts[i] != NULL; i++) { + if (g_strcmp0(parts[i], "") != 0 && g_strcmp0(parts[i], "OK") != 0) { + g_info("firmware branch reported as '%s'", parts[i]); + fu_device_set_branch(FU_DEVICE(self), parts[i]); + break; + } } /* success */ - fu_device_set_remove_delay(device, FU_MM_DEVICE_REMOVE_DELAY_RE_ENUMERATE); - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); return TRUE; } -static gboolean -fu_mm_device_mbim_open(FuMmDevice *self, GError **error) -{ - self->mbim_qdu_updater = fu_mbim_qdu_updater_new(self->port_mbim); - return fu_mbim_qdu_updater_open(self->mbim_qdu_updater, error); -} - -static gboolean -fu_mm_device_mbim_close(FuMmDevice *self, GError **error) -{ - g_autoptr(FuMbimQduUpdater) updater = NULL; - updater = g_steal_pointer(&self->mbim_qdu_updater); - return fu_mbim_qdu_updater_close(updater, error); -} - -#if MBIM_CHECK_VERSION(1, 27, 5) static void -fu_mm_device_switch_to_edl_mbim_ready(MbimDevice *device, GAsyncResult *res, gpointer user_data) -{ - /* No need to check for a response since MBIM - * port goes away without sending one */ - GMainLoop *loop = user_data; - - g_main_loop_quit(loop); -} - -static gboolean -fu_mm_device_mbim_switch_to_edl(FuDevice *device, GError **error) -{ - FuMmDevice *self = FU_MM_DEVICE(device); - g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(MbimMessage) message = NULL; - g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); - - locker = fu_device_locker_new_full(device, - (FuDeviceLockerFunc)fu_mm_device_mbim_open, - (FuDeviceLockerFunc)fu_mm_device_mbim_close, - error); - if (locker == NULL) - return FALSE; - - message = mbim_message_qdu_quectel_reboot_set_new(MBIM_QDU_QUECTEL_REBOOT_TYPE_EDL, NULL); - mbim_device_command(fu_mbim_qdu_updater_get_mbim_device(self->mbim_qdu_updater), - message, - 5, - NULL, - (GAsyncReadyCallback)fu_mm_device_switch_to_edl_mbim_ready, - mainloop); - - g_main_loop_run(mainloop); - - return TRUE; -} -#endif // MBIM_CHECK_VERSION(1, 27, 5) - -static gboolean -fu_mm_device_qcdm_switch_to_edl_cb(FuDevice *device, gpointer userdata, GError **error) -{ - FuMmDevice *self = FU_MM_DEVICE(device); - g_autoptr(FuDeviceLocker) locker = NULL; - const guint8 emergency_download[] = {0x4b, 0x65, 0x01, 0x00, 0x54, 0x0f, 0x7e}; - - /* when the QCDM port does not exist anymore, we are already detached */ - if (!g_file_test(self->port_qcdm, G_FILE_TEST_EXISTS)) - return TRUE; - - locker = fu_device_locker_new_full(self, - (FuDeviceLockerFunc)fu_mm_device_io_open_qcdm, - (FuDeviceLockerFunc)fu_mm_device_io_close, - error); - if (locker == NULL) - return FALSE; - - return fu_mm_device_qcdm_cmd(self, emergency_download, sizeof(emergency_download), error); -} - -static gboolean -fu_mm_device_qcdm_switch_to_edl(FuMmDevice *self, GError **error) +fu_mm_device_ensure_payload_quectel(FuMmDevice *self) { - /* retry up to 30 times until the QCDM port goes away */ - return fu_device_retry_full(FU_DEVICE(self), - fu_mm_device_qcdm_switch_to_edl_cb, - 30, - 1000, - NULL, - error); -} - -static gboolean -fu_mm_device_detach_sahara(FuDevice *device, GError **error) -{ - FuMmDevice *self = FU_MM_DEVICE(device); - - /* use special command for Quectel MBIM devices */ - if (fu_device_get_vid(device) == 0x2c7c && self->port_mbim != NULL) { -#if MBIM_CHECK_VERSION(1, 27, 5) - if (!fu_mm_device_mbim_switch_to_edl(device, error)) - return FALSE; -#else - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "libmbim >= 1.27.5 required for Quectel MBIM devices"); - return FALSE; -#endif - } else if (self->port_qcdm != NULL) { - if (!fu_mm_device_qcdm_switch_to_edl(self, error)) - return FALSE; + const gchar *version = fu_device_get_version(FU_DEVICE(self)); + g_autoptr(GError) error_qsec = NULL; + g_autoptr(GError) error_qcfg = NULL; + g_autoptr(GBytes) blob = NULL; + const gchar *signed_versions[] = {"EM05GFAR07A07M1G_01.005.01.005", + "EM05CEFCR08A16M1G_LNV"}; + + /* newer firmware */ + blob = fu_mm_device_at_cmd_full(self, "AT+QSECBOOT=\"status\"", 64, &error_qsec); + if (blob == NULL) { + g_debug("ignoring: %s", error_qsec->message); } else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "suitable port not found"); - return FALSE; - } - - fu_device_sleep(device, 1000); - - /* make sure the udev device is still present */ - if (!fu_mm_device_ensure_udev_device(self, error)) - return FALSE; - - self->port_edl = g_strdup(fu_udev_device_get_device_file(self->udev_device)); - - return TRUE; -} - -static gboolean -fu_mm_device_detach(FuDevice *device, FuProgress *progress, GError **error) -{ - FuMmDevice *self = FU_MM_DEVICE(device); - g_autoptr(FuDeviceLocker) locker = NULL; - - locker = fu_device_locker_new(device, error); - if (locker == NULL) - return FALSE; - - /* This plugin supports several methods to download firmware: - * fastboot, qmi-pdc, firehose. A modem may require one of those, - * or several, depending on the update type or the modem type. - * - * The first time this detach() method is executed is always for a - * FuMmDevice that was created from a MM-exposed modem, which is the - * moment when we're going to decide the amount of retries we need to - * flash all firmware. - * - * If the FuMmModem is created from a MM-exposed modem and... - * a) we only support fastboot, we just trigger the fastboot detach. - * b) we support both fastboot and qmi-pdc, we will set the - * ANOTHER_WRITE_REQUIRED flag in the device and we'll trigger - * the fastboot detach. - * c) we only support firehose, skip detach and switch to embedded - * downloader mode (EDL) during write_firmware. - * - * If the FuMmModem is created from udev events... - * c) it means we're in the extra required write that was flagged - * in an earlier detach(), and we need to perform the qmi-pdc - * update procedure at this time, so we just exit without any - * detach. - */ - - /* FuMmDevice created from MM... */ - if (self->omodem != NULL) { - /* both fastboot and qmi-pdc supported? another write required */ - if ((self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) && - (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC)) { - g_debug("both fastboot and qmi-pdc supported, so the upgrade requires " - "another write"); - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); + /* AT+QSECBOOT="status" response: `\r\n+QSECBOOT: "STATUS",1\r\n\r\nOK\r\n` */ + g_auto(GStrv) parts = fu_strsplit_bytes(blob, "\r\n", -1); + for (guint i = 0; parts[i] != NULL; i++) { + if (g_strcmp0(parts[i], "+QSECBOOT: \"status\",1") == 0) { + fu_device_add_flag(FU_DEVICE(self), + FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + break; + } + if (g_strcmp0(parts[i], "+QSECBOOT: \"status\",0") == 0) { + fu_device_add_flag(FU_DEVICE(self), + FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + break; + } } - /* fastboot */ - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FASTBOOT) - return fu_mm_device_detach_fastboot(device, error); -#if MM_CHECK_VERSION(1, 19, 1) - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA) - return fu_mm_device_detach_sahara(device, error); -#endif // MM_CHECK_VERSION(1, 19, 1) -#if MM_CHECK_VERSION(1, 24, 0) - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_CINTERION_FDL) - return fu_mm_device_detach_fdl(device, progress, error); -#endif // MM_CHECK_VERSION(1, 24, 0) - /* otherwise, assume we don't need any detach */ - return TRUE; + return; } - /* FuMmDevice created from udev... - * assume we don't need any detach */ - return TRUE; -} - -typedef struct { - gchar *filename; - GBytes *bytes; - GArray *digest; - gboolean active; -} FuMmFileInfo; - -static void -fu_mm_device_file_info_free(FuMmFileInfo *file_info) -{ - g_clear_pointer(&file_info->digest, g_array_unref); - g_free(file_info->filename); - g_bytes_unref(file_info->bytes); - g_free(file_info); -} - -typedef struct { - FuMmDevice *self; - GError *error; - GPtrArray *file_infos; -} FuMmArchiveIterateCtx; - -static gboolean -fu_mm_device_should_be_active(const gchar *version, const gchar *filename) -{ - g_auto(GStrv) split = NULL; - g_autofree gchar *carrier_id = NULL; - - /* The filename of the mcfg file is composed of a "mcfg." prefix, then the - * carrier code, followed by the carrier version, and finally a ".mbn" - * prefix. Here we try to guess, based on the carrier code, whether the - * specific mcfg file should be activated after the firmware upgrade - * operation. - * - * This logic requires that the previous device version includes the carrier - * code also embedded in the version string. E.g. "xxxx.VF.xxxx". If we find - * this match, we assume this is the active config to use. - */ - - split = g_strsplit(filename, ".", -1); - if (g_strv_length(split) < 4) - return FALSE; - if (g_strcmp0(split[0], "mcfg") != 0) - return FALSE; - - carrier_id = g_strdup_printf(".%s.", split[1]); - return (g_strstr_len(version, -1, carrier_id) != NULL); -} - -static gboolean -fu_mm_device_qmi_pdc_archive_iterate_mcfg(FuArchive *archive, - const gchar *filename, - GBytes *bytes, - gpointer user_data, - GError **error) -{ - FuMmArchiveIterateCtx *ctx = user_data; - FuMmFileInfo *file_info; - - /* filenames should be named as 'mcfg.*.mbn', e.g.: mcfg.A2.018.mbn */ - if (!g_str_has_prefix(filename, "mcfg.") || !g_str_has_suffix(filename, ".mbn")) - return TRUE; - - file_info = g_new0(FuMmFileInfo, 1); - file_info->filename = g_strdup(filename); - file_info->bytes = g_bytes_ref(bytes); - file_info->active = - fu_mm_device_should_be_active(fu_device_get_version(FU_DEVICE(ctx->self)), filename); - g_ptr_array_add(ctx->file_infos, file_info); - return TRUE; -} - -static gboolean -fu_mm_device_qmi_open(FuMmDevice *self, GError **error) -{ - self->qmi_pdc_updater = fu_qmi_pdc_updater_new(self->port_qmi); - return fu_qmi_pdc_updater_open(self->qmi_pdc_updater, error); -} - -static gboolean -fu_mm_device_qmi_close(FuMmDevice *self, GError **error) -{ - g_autoptr(FuQmiPdcUpdater) updater = NULL; - - updater = g_steal_pointer(&self->qmi_pdc_updater); - return fu_qmi_pdc_updater_close(updater, error); -} - -static gboolean -fu_mm_device_qmi_close_no_error(FuMmDevice *self, GError **error) -{ - g_autoptr(FuQmiPdcUpdater) updater = NULL; - - updater = g_steal_pointer(&self->qmi_pdc_updater); - fu_qmi_pdc_updater_close(updater, NULL); - return TRUE; -} - -static gboolean -fu_mm_device_write_firmware_qmi_pdc(FuMmDevice *self, - GBytes *fw, - GArray **active_id, - GError **error) -{ - g_autoptr(FuArchive) archive = NULL; - g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(GPtrArray) file_infos = - g_ptr_array_new_with_free_func((GDestroyNotify)fu_mm_device_file_info_free); - gint active_i = -1; - FuMmArchiveIterateCtx archive_context = { - .self = self, - .error = NULL, - .file_infos = file_infos, - }; - - /* decompress entire archive ahead of time */ - archive = fu_archive_new(fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error); - if (archive == NULL) - return FALSE; - - /* boot to fastboot mode */ - locker = fu_device_locker_new_full(FU_DEVICE(self), - (FuDeviceLockerFunc)fu_mm_device_qmi_open, - (FuDeviceLockerFunc)fu_mm_device_qmi_close, - error); - if (locker == NULL) - return FALSE; - - /* process the list of MCFG files to write */ - if (!fu_archive_iterate(archive, - fu_mm_device_qmi_pdc_archive_iterate_mcfg, - &archive_context, - error)) - return FALSE; - - for (guint i = 0; i < file_infos->len; i++) { - FuMmFileInfo *file_info = g_ptr_array_index(file_infos, i); - file_info->digest = fu_qmi_pdc_updater_write(archive_context.self->qmi_pdc_updater, - file_info->filename, - file_info->bytes, - &archive_context.error); - if (file_info->digest == NULL) { - g_prefix_error(&archive_context.error, - "Failed to write file '%s':", - file_info->filename); - break; + /* older firmware */ + blob = fu_mm_device_at_cmd_full(self, "AT+QCFG=\"secbootstat\"", 64, &error_qcfg); + if (blob == NULL) { + g_debug("ignoring: %s", error_qcfg->message); + } else { + g_auto(GStrv) parts = fu_strsplit_bytes(blob, "\r\n", -1); + for (guint i = 0; parts[i] != NULL; i++) { + if (g_strcmp0(parts[i], "+QCFG: \"secbootstat\",1") == 0) { + fu_device_add_flag(FU_DEVICE(self), + FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + break; + } + if (g_strcmp0(parts[i], "+QCFG: \"secbootstat\",0") == 0) { + fu_device_add_flag(FU_DEVICE(self), + FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + break; + } } - /* if we wrongly detect more than one, just assume the latest one; this - * is not critical, it may just take a bit more time to perform the - * automatic carrier config switching in ModemManager */ - if (file_info->active) - active_i = i; - } - - /* set expected active configuration */ - if (active_i >= 0 && active_id != NULL) { - FuMmFileInfo *file_info = g_ptr_array_index(file_infos, active_i); - *active_id = g_array_ref(file_info->digest); - } - - if (archive_context.error != NULL) { - g_propagate_error(error, archive_context.error); - return FALSE; + return; } - return TRUE; -} - -typedef struct { - FuMmDevice *self; /* no ref */ - GMainLoop *mainloop; - gchar *version; - GError *error; -} FuMmGetFirmwareVersionCtx; - -static gboolean -fu_mm_device_locker_new_timeout(gpointer user_data) -{ - FuMmGetFirmwareVersionCtx *ctx = user_data; - - g_main_loop_quit(ctx->mainloop); - - return G_SOURCE_REMOVE; -} - -static gboolean -fu_mm_device_get_firmware_version_mbim_timeout(gpointer user_data) -{ - FuMmGetFirmwareVersionCtx *ctx = user_data; - FuMmDevice *self = FU_MM_DEVICE(ctx->self); - - g_clear_error(&ctx->error); - ctx->version = fu_mbim_qdu_updater_check_ready(self->mbim_qdu_updater, &ctx->error); - g_main_loop_quit(ctx->mainloop); - - return G_SOURCE_REMOVE; -} - -static gchar * -fu_mm_device_get_firmware_version_mbim(FuMmDevice *self, GError **error) -{ - GTimer *timer = g_timer_new(); - g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); - FuMmGetFirmwareVersionCtx ctx = { - .self = self, - .mainloop = mainloop, - .version = NULL, - .error = NULL, - }; - - while (ctx.version == NULL && g_timer_elapsed(timer, NULL) < MAX_WAIT_TIME_SECS) { - g_autoptr(FuDeviceLocker) locker = NULL; - - g_clear_error(&ctx.error); - locker = fu_device_locker_new_full(FU_DEVICE(self), - (FuDeviceLockerFunc)fu_mm_device_mbim_open, - (FuDeviceLockerFunc)fu_mm_device_mbim_close, - &ctx.error); - - if (locker == NULL) { - g_timeout_add_seconds(20, fu_mm_device_locker_new_timeout, &ctx); - g_main_loop_run(mainloop); - continue; + /* find model name and compare with table from Quectel */ + if (version == NULL) + return; + for (guint i = 0; i < G_N_ELEMENTS(signed_versions); i++) { + if (strncmp(version, signed_versions[i], 6) == 0) { + if (fu_version_compare(version, + signed_versions[i], + FWUPD_VERSION_FORMAT_PLAIN) >= 0) { + fu_device_add_flag(FU_DEVICE(self), + FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + } else { + fu_device_add_flag(FU_DEVICE(self), + FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + } + return; } - - g_timeout_add_seconds(10, fu_mm_device_get_firmware_version_mbim_timeout, &ctx); - g_main_loop_run(mainloop); } - - g_timer_destroy(timer); - - if (ctx.version == NULL && ctx.error != NULL) { - g_propagate_error(error, ctx.error); - return NULL; - } - - return ctx.version; } -static gboolean -fu_mm_device_writeln(const gchar *fn, const gchar *buf, GError **error) -{ - g_autoptr(FuIOChannel) io = NULL; - io = fu_io_channel_new_file(fn, FU_IO_CHANNEL_OPEN_FLAG_WRITE, error); - if (io == NULL) - return FALSE; - return fu_io_channel_write_raw(io, - (const guint8 *)buf, - strlen(buf), - 1000, - FU_IO_CHANNEL_FLAG_NONE, - error); -} - -static gboolean -fu_mm_device_set_autosuspend_delay(FuMmDevice *self, guint timeout_ms, GError **error) +static void +fu_mm_device_ensure_payload(FuMmDevice *self) { - g_autofree gchar *autosuspend_delay_filename = NULL; - g_autofree gchar *buf = g_strdup_printf("%u", timeout_ms); - - /* autosuspend delay updated for a proper firmware update */ - autosuspend_delay_filename = g_build_filename(fu_device_get_physical_id(FU_DEVICE(self)), - "/power/autosuspend_delay_ms", - NULL); - return fu_mm_device_writeln(autosuspend_delay_filename, buf, error); -} - -static gboolean -fu_mm_device_write_firmware_mbim_qdu(FuDevice *device, - GBytes *fw, - FuProgress *progress, - GError **error) -{ - XbNode *part = NULL; - const gchar *filename = NULL; - const gchar *csum; - FuMmDevice *self = FU_MM_DEVICE(device); - g_autofree gchar *csum_actual = NULL; - g_autofree gchar *version = NULL; - g_autoptr(FuArchive) archive = NULL; - g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(GArray) digest = NULL; - g_autoptr(GBytes) data_xml = NULL; - g_autoptr(GBytes) data_part = NULL; - g_autoptr(XbBuilder) builder = xb_builder_new(); - g_autoptr(XbBuilderSource) source = xb_builder_source_new(); - g_autoptr(XbSilo) silo = NULL; - - /* decompress entire archive ahead of time */ - archive = fu_archive_new(fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error); - if (archive == NULL) - return FALSE; - - locker = fu_device_locker_new_full(device, - (FuDeviceLockerFunc)fu_mm_device_mbim_open, - (FuDeviceLockerFunc)fu_mm_device_mbim_close, - error); - if (locker == NULL) - return FALSE; - - /* load the manifest of operations */ - data_xml = fu_archive_lookup_by_fn(archive, "flashfile.xml", error); - if (data_xml == NULL) - return FALSE; - if (!xb_builder_source_load_bytes(source, data_xml, XB_BUILDER_SOURCE_FLAG_NONE, error)) { - fwupd_error_convert(error); - return FALSE; - } - xb_builder_import_source(builder, source); - silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); - if (silo == NULL) { - fwupd_error_convert(error); - return FALSE; - } - - part = xb_silo_query_first(silo, "parts/part", error); - if (part == NULL) { - fwupd_error_convert(error); - return FALSE; - } - filename = xb_node_get_attr(part, "filename"); - csum = xb_node_get_attr(part, "MD5"); - data_part = fu_archive_lookup_by_fn(archive, filename, error); - if (data_part == NULL) - return FALSE; - csum_actual = g_compute_checksum_for_bytes(G_CHECKSUM_MD5, data_part); - if (g_strcmp0(csum, csum_actual) != 0) { - g_debug("[%s] MD5 not matched", filename); - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "[%s] MD5 not matched", - filename); - return FALSE; + if (fu_device_has_vendor_id(FU_DEVICE(self), "USB:0x2C7C") || + fu_device_has_vendor_id(FU_DEVICE(self), "PCI:0x1EAC")) { + fu_mm_device_ensure_payload_quectel(self); + } else if (fu_device_has_vendor_id(FU_DEVICE(self), "USB:0x2CB7")) { + fu_device_add_private_flag(FU_DEVICE(self), + FU_DEVICE_PRIVATE_FLAG_SAVE_INTO_BACKUP_REMOTE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); } - g_debug("[%s] MD5 matched", filename); - - /* autosuspend delay updated for a proper firmware update */ - if (!fu_mm_device_set_autosuspend_delay(self, 20000, error)) - return FALSE; - - fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); - digest = fu_mbim_qdu_updater_write(self->mbim_qdu_updater, - filename, - data_part, - device, - progress, - error); - if (digest == NULL) - return FALSE; - if (!fu_device_locker_close(locker, error)) - return FALSE; - - fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); - version = fu_mm_device_get_firmware_version_mbim(self, error); - if (version == NULL) - return FALSE; - - return TRUE; } static gboolean -fu_mm_device_supports_zlp(FuMmDevice *self) +fu_mm_device_make_serial_raw(FuMmDevice *self, GError **error) { - return !fu_device_has_private_flag(FU_DEVICE(self), FU_MM_DEVICE_FLAG_DISABLE_ZLP); -} +#ifdef HAVE_TERMIOS_H + gint fd = fu_io_channel_unix_get_fd(fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self))); + struct termios tio; -static gboolean -fu_mm_device_firehose_open(FuMmDevice *self, GError **error) -{ - self->firehose_updater = fu_firehose_updater_new(self->port_edl, self->sahara_loader); - return fu_firehose_updater_open(self->firehose_updater, error); -} - -static gboolean -fu_mm_device_firehose_close(FuMmDevice *self, GError **error) -{ - g_autoptr(FuFirehoseUpdater) updater = NULL; - - updater = g_steal_pointer(&self->firehose_updater); - return fu_firehose_updater_close(updater, error); -} - -static gboolean -fu_mm_device_firehose_write(FuMmDevice *self, - XbSilo *rawprogram_silo, - GPtrArray *rawprogram_actions, - FuProgress *progress, - GError **error) -{ - g_autoptr(FuDeviceLocker) locker = NULL; - locker = fu_device_locker_new_full(self, - (FuDeviceLockerFunc)fu_mm_device_firehose_open, - (FuDeviceLockerFunc)fu_mm_device_firehose_close, - error); - if (locker == NULL) - return FALSE; - - /* set zero-length packet support */ - fu_firehose_updater_set_supports_zlp(self->firehose_updater, - fu_mm_device_supports_zlp(self)); - - return fu_firehose_updater_write(self->firehose_updater, - rawprogram_silo, - rawprogram_actions, - progress, - error); -} - -#if MM_CHECK_VERSION(1, 19, 1) -static gboolean -fu_mm_device_sahara_open(FuMmDevice *self, GError **error) -{ - self->sahara_loader = fu_sahara_loader_new(); - return fu_sahara_loader_open(self->sahara_loader, FU_USB_DEVICE(self->udev_device), error); -} - -static gboolean -fu_mm_device_sahara_close(FuMmDevice *self, GError **error) -{ - g_autoptr(FuSaharaLoader) loader = NULL; - - loader = g_steal_pointer(&self->sahara_loader); - return fu_sahara_loader_close(loader, error); -} -#endif // MM_CHECK_VERSION(1, 19, 1) - -static FuKernelSearchPathLocker * -fu_mm_device_search_path_locker_new(FuMmDevice *self, GError **error) -{ - g_autofree gchar *cachedir = NULL; - g_autofree gchar *mm_fw_dir = NULL; - g_autoptr(FuKernelSearchPathLocker) locker = NULL; - - /* create a directory to store firmware files for modem-manager plugin */ - cachedir = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); - mm_fw_dir = g_build_filename(cachedir, "modem-manager", "firmware", NULL); - if (g_mkdir_with_parents(mm_fw_dir, 0700) == -1) { + if (tcgetattr(fd, &tio) != 0) { g_set_error(error, FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Failed to create '%s': %s", - mm_fw_dir, - g_strerror(errno)); - return NULL; +#ifdef HAVE_ERRNO_H + g_io_error_from_errno(errno), +#else + G_IO_ERROR_FAILED, /* nocheck:blocked */ +#endif + "could not get termios attributes: %s", + fwupd_strerror(errno)); + return FALSE; } - locker = fu_kernel_search_path_locker_new(mm_fw_dir, error); - if (locker == NULL) - return NULL; - self->firmware_path = g_steal_pointer(&mm_fw_dir); - return g_steal_pointer(&locker); -} -static gboolean -fu_mm_device_copy_firehose_prog(FuMmDevice *self, GBytes *prog, GError **error) -{ - g_autofree gchar *qcom_fw_dir = NULL; - g_autofree gchar *firehose_file_path = NULL; + cfmakeraw(&tio); - if (self->firehose_prog_file == NULL) { + if (tcsetattr(fd, TCSANOW, &tio) != 0) { g_set_error(error, FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "Firehose prog filename is not set for the device"); +#ifdef HAVE_ERRNO_H + g_io_error_from_errno(errno), +#else + G_IO_ERROR_FAILED, /* nocheck:blocked */ +#endif + "could not set termios attributes: %s", + fwupd_strerror(errno)); return FALSE; } - qcom_fw_dir = g_build_filename(self->firmware_path, "qcom", NULL); - if (!fu_path_mkdir_parent(qcom_fw_dir, error)) - return FALSE; - - firehose_file_path = g_build_filename(qcom_fw_dir, self->firehose_prog_file, NULL); - - if (!fu_bytes_set_contents(firehose_file_path, prog, error)) - return FALSE; - return TRUE; -} - -static gboolean -fu_mm_device_write_firmware_firehose(FuDevice *device, - GBytes *fw, - FuProgress *progress, - GError **error) -{ - FuMmDevice *self = FU_MM_DEVICE(device); - MMModem *modem = mm_object_peek_modem(self->omodem); - g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(FuKernelSearchPathLocker) search_path_locker = NULL; - g_autoptr(FuArchive) archive = NULL; - g_autoptr(XbSilo) firehose_rawprogram_silo = NULL; - g_autoptr(GBytes) firehose_prog = NULL; - g_autoptr(GBytes) firehose_rawprogram = NULL; - g_autoptr(GPtrArray) firehose_rawprogram_actions = NULL; - - /* progress */ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 1, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 10, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); - - /* decompress entire archive ahead of time */ - archive = fu_archive_new(fw, FU_ARCHIVE_FLAG_IGNORE_PATH, error); - if (archive == NULL) - return FALSE; - - /* lookup and validate firehose-rawprogram actions */ - firehose_rawprogram = fu_archive_lookup_by_fn(archive, "firehose-rawprogram.xml", error); - if (firehose_rawprogram == NULL) - return FALSE; - if (!fu_firehose_updater_validate_rawprogram(firehose_rawprogram, - archive, - &firehose_rawprogram_silo, - &firehose_rawprogram_actions, - error)) { - g_prefix_error(error, "Invalid firehose rawprogram manifest: "); - return FALSE; - } - - /* lookup firehose-prog bootloader */ - firehose_prog = fu_archive_lookup_by_fn(archive, "firehose-prog.mbn", error); - if (firehose_prog == NULL) - return FALSE; - fu_progress_step_done(progress); - - /* Firehose program needs to be loaded to the modem before firehose update process can - * start. Generally, modems use Sahara protocol to load the firehose binary. - * - * In case of MHI PCI modems, the mhi-pci-generic driver reads the firehose binary from the - * firmware-loader and writes it to the modem. - **/ - if (g_strv_contains(mm_modem_get_drivers(modem), "mhi-pci-generic") && - self->port_qcdm != NULL) { - /* modify firmware search path */ - search_path_locker = fu_mm_device_search_path_locker_new(self, error); - if (search_path_locker == NULL) - return FALSE; - - /* firehose modems that use mhi_pci drivers require firehose binary - * to be present in the firmware-loader search path. */ - if (!fu_mm_device_copy_firehose_prog(self, firehose_prog, error)) - return FALSE; - - /* FIXME: this should have been done in attach */ - - /* trigger emergency download mode; this takes us to the EDL - * (embedded downloader) execution environment */ - if (!fu_mm_device_qcdm_switch_to_edl(self, error)) - return FALSE; - - g_debug("found edl port: %s", self->port_edl); - } -#if MM_CHECK_VERSION(1, 19, 1) - else if (FU_MM_DEVICE(self)->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA) { - locker = fu_device_locker_new_full(self, - (FuDeviceLockerFunc)fu_mm_device_sahara_open, - (FuDeviceLockerFunc)fu_mm_device_sahara_close, - error); - if (locker == NULL) - return FALSE; - - /* set zero-length packet support */ - fu_sahara_loader_set_supports_zlp(self->sahara_loader, - fu_mm_device_supports_zlp(self)); - - /* use sahara port to load firehose binary */ - if (!fu_sahara_loader_run(self->sahara_loader, firehose_prog, error)) - return FALSE; - } -#endif // MM_CHECK_VERSION(1, 19, 1) - else { - g_set_error(error, +#else + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "suitable port not found"); - return FALSE; - } - fu_progress_step_done(progress); - - /* download all files in the firehose-rawprogram manifest via Firehose */ - if (!fu_mm_device_firehose_write(self, - firehose_rawprogram_silo, - firehose_rawprogram_actions, - fu_progress_get_child(progress), - error)) - return FALSE; - fu_progress_step_done(progress); - - /* flag as restart again, the module is switching to modem mode */ - fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_RESTART); - return TRUE; -} - -#if MM_CHECK_VERSION(1, 24, 0) -static gboolean -fu_mm_device_write_firmware_fdl(FuDevice *device, GBytes *fw, FuProgress *progress, GError **error) -{ - FuMmDevice *self = FU_MM_DEVICE(device); - g_autoptr(FuDeviceLocker) locker = NULL; - - locker = fu_device_locker_new_full(device, - (FuDeviceLockerFunc)fu_mm_device_cinterion_fdl_open, - (FuDeviceLockerFunc)fu_mm_device_cinterion_fdl_close, - error); - if (locker == NULL) - return FALSE; - - fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); - - return fu_cinterion_fdl_updater_write(self->cinterion_fdl_updater, - progress, - device, - fw, - error); + "Not supported as not found"); + return FALSE; +#endif } static gboolean -fu_mm_device_write_firmware_dfota(FuDevice *device, - GBytes *fw, - FuProgress *progress, - GError **error) +fu_mm_device_open(FuDevice *device, GError **error) { FuMmDevice *self = FU_MM_DEVICE(device); - g_autoptr(FuDeviceLocker) locker = NULL; - g_autofree gchar *upload_cmd = NULL; - - locker = fu_device_locker_new_full(device, - (FuDeviceLockerFunc)fu_mm_device_io_open_dfota, - (FuDeviceLockerFunc)fu_mm_device_io_close_dfota, - error); - if (locker == NULL) + /* FuUdevDevice->open() */ + if (!FU_DEVICE_CLASS(fu_mm_device_parent_class)->open(device, error)) return FALSE; - /* put the device into upload mode */ - upload_cmd = g_strdup_printf("AT+QFUPL=\"%s\",%" G_GSIZE_FORMAT ",5,1", - FU_DFOTA_UPDATER_FILENAME, - g_bytes_get_size(fw)); - if (!fu_mm_device_at_cmd(self, upload_cmd, TRUE, error)) { - g_prefix_error(error, "failed to enable upload mode: "); - return FALSE; - } - - if (!fu_dfota_updater_upload_firmware(self->dfota_updater, fw, error)) - return FALSE; - - if (!fu_mm_device_at_cmd(self, - "AT+QFOTADL=\"/data/ufs/" FU_DFOTA_UPDATER_FILENAME "\"", - TRUE, - error)) { - g_prefix_error(error, "failed to start update: "); - return FALSE; + /* ignored ports have not previously been configured by ModemManager */ + if (fu_device_has_private_flag(device, FU_MM_DEVICE_FLAG_MAKE_SERIAL_RAW)) { + if (!fu_mm_device_make_serial_raw(self, error)) + return FALSE; } - /* wait and reopen port */ - if (!fu_device_locker_close(locker, error)) - return FALSE; - fu_device_sleep(device, FU_DFOTA_UPDATER_FOTA_RESTART_TIMEOUT_SECS * 1000); - locker = fu_device_locker_new_full(self, - (FuDeviceLockerFunc)fu_mm_device_io_open_dfota, - (FuDeviceLockerFunc)fu_mm_device_io_close_dfota, - error); - if (locker == NULL) - return FALSE; - return fu_dfota_updater_write(self->dfota_updater, progress, device, error); - /* uploaded firmware file is cleaned up automatically after device restart */ + /* success */ + return TRUE; } -#endif // MM_CHECK_VERSION(1, 24, 0) static gboolean -fu_mm_device_write_firmware(FuDevice *device, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) +fu_mm_device_setup(FuDevice *device, GError **error) { FuMmDevice *self = FU_MM_DEVICE(device); - g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(GBytes) fw = NULL; - - /* get default image */ - fw = fu_firmware_get_bytes(firmware, error); - if (fw == NULL) - return FALSE; - - /* lock device */ - locker = fu_device_locker_new(device, error); - if (locker == NULL) - return FALSE; + g_autoptr(GError) error_local = NULL; - /* qmi pdc write operation */ - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC) - return fu_mm_device_write_firmware_qmi_pdc(self, - fw, - &self->qmi_pdc_active_id, - error); - - /* mbim qdu write operation */ - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU) - return fu_mm_device_write_firmware_mbim_qdu(device, fw, progress, error); - - /* firehose operation */ - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_FIREHOSE) - return fu_mm_device_write_firmware_firehose(device, fw, progress, error); - -#if MM_CHECK_VERSION(1, 24, 0) - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_CINTERION_FDL) - return fu_mm_device_write_firmware_fdl(device, fw, progress, error); - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_DFOTA) - return fu_mm_device_write_firmware_dfota(device, fw, progress, error); -#endif // MM_CHECK_VERSION(1, 24, 0) + if (!fu_mm_device_ensure_branch(self, &error_local)) + g_debug("failed to set firmware branch: %s", error_local->message); + fu_mm_device_ensure_payload(self); - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "unsupported update method"); - return FALSE; + /* success */ + return TRUE; } static gboolean @@ -1834,14 +802,8 @@ { FuMmDevice *self = FU_MM_DEVICE(device); - /* load from quirks */ if (g_strcmp0(key, "ModemManagerBranchAtCommand") == 0) { - self->branch_at = g_strdup(value); - return TRUE; - } - - if (g_strcmp0(key, "ModemManagerFirehoseProgFile") == 0) { - self->firehose_prog_file = g_strdup(value); + fu_mm_device_set_branch_at(self, value); return TRUE; } @@ -1854,412 +816,192 @@ } static gboolean -fu_mm_device_attach_qmi_pdc(FuMmDevice *self, GError **error) +fu_mm_device_from_json(FuDevice *device, JsonObject *json_object, GError **error) { - g_autoptr(FuDeviceLocker) locker = NULL; - - /* ignore action if there is no active id specified */ - if (self->qmi_pdc_active_id == NULL) - return TRUE; - - /* errors closing may be expected if the device really reboots itself */ - locker = fu_device_locker_new_full(self, - (FuDeviceLockerFunc)fu_mm_device_qmi_open, - (FuDeviceLockerFunc)fu_mm_device_qmi_close_no_error, - error); - if (locker == NULL) - return FALSE; + FuMmDevice *self = FU_MM_DEVICE(device); + const gchar *tmp; - if (!fu_qmi_pdc_updater_activate(self->qmi_pdc_updater, self->qmi_pdc_active_id, error)) + /* FuUdevDevice->from_json */ + if (!FU_DEVICE_CLASS(fu_mm_device_parent_class)->from_json(device, json_object, error)) return FALSE; - return TRUE; -} - -static gboolean -fu_mm_device_attach_noop_idle(gpointer user_data) -{ - FuMmDevice *self = FU_MM_DEVICE(user_data); - self->attach_idle = 0; - g_signal_emit(self, signals[SIGNAL_ATTACH_FINISHED], 0); - return G_SOURCE_REMOVE; -} - -static gboolean -fu_mm_device_attach_qmi_pdc_idle(gpointer user_data) -{ - FuMmDevice *self = FU_MM_DEVICE(user_data); - g_autoptr(GError) error = NULL; - - if (!fu_mm_device_attach_qmi_pdc(self, &error)) - g_warning("qmi-pdc attach operation failed: %s", error->message); - else - g_debug("qmi-pdc attach operation successful"); - - self->attach_idle = 0; - g_signal_emit(self, signals[SIGNAL_ATTACH_FINISHED], 0); - return G_SOURCE_REMOVE; -} - -static gboolean -fu_mm_device_cleanup(FuDevice *device, - FuProgress *progress, - FwupdInstallFlags install_flags, - GError **error) -{ - FuMmDevice *self = FU_MM_DEVICE(device); + /* optional properties */ + tmp = json_object_get_string_member_with_default(json_object, "Version", NULL); + if (tmp != NULL) + fu_device_set_version(device, tmp); + tmp = json_object_get_string_member_with_default(json_object, "PhysicalId", NULL); + if (tmp != NULL) + fu_device_set_physical_id(device, tmp); + tmp = json_object_get_string_member_with_default(json_object, "BranchAt", NULL); + if (tmp != NULL) + fu_mm_device_set_branch_at(self, tmp); + + /* specified by ModemManager, unusually */ + if (json_object_has_member(json_object, "DeviceIds")) { + JsonArray *json_array = json_object_get_array_member(json_object, "DeviceIds"); + for (guint i = 0; i < json_array_get_length(json_array); i++) { + const gchar *instance_id = json_array_get_string_element(json_array, i); + if (!fu_mm_device_add_instance_id(self, instance_id, error)) + return FALSE; + } + } - /* restore default configuration */ - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU) { - if (!fu_mm_device_set_autosuspend_delay(self, 2000, error)) - return FALSE; + /* ports */ + if (json_object_has_member(json_object, "Ports")) { + JsonObject *json_ports = json_object_get_object_member(json_object, "Ports"); + g_autoptr(GList) keys = json_object_get_members(json_ports); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *port_type = l->data; + fu_mm_device_add_port(self, + fu_mm_device_port_type_from_string(port_type), + json_object_get_string_member(json_ports, port_type), + FU_MM_DEVICE_PORT_FLAG_NONE); + } } /* success */ return TRUE; } -static gboolean -fu_mm_device_attach(FuDevice *device, FuProgress *progress, GError **error) +static void +fu_mm_device_add_json(FuDevice *device, JsonBuilder *builder, FwupdCodecFlags flags) { FuMmDevice *self = FU_MM_DEVICE(device); - g_autoptr(FuDeviceLocker) locker = NULL; + FuMmDevicePrivate *priv = GET_PRIVATE(self); + GPtrArray *instance_ids = fu_device_get_instance_ids(device); + GPtrArray *vendor_ids = fu_device_get_vendor_ids(device); - /* lock device */ - locker = fu_device_locker_new(device, error); - if (locker == NULL) - return FALSE; + /* FuUdevDevice->add_json */ + FU_DEVICE_CLASS(fu_mm_device_parent_class)->add_json(device, builder, flags); - /* we want this attach operation to be triggered asynchronously, because the engine - * must learn that it has to wait for replug before we actually trigger the reset. */ - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_QMI_PDC) - self->attach_idle = g_idle_add((GSourceFunc)fu_mm_device_attach_qmi_pdc_idle, self); - else - self->attach_idle = g_idle_add((GSourceFunc)fu_mm_device_attach_noop_idle, self); - -#if MM_CHECK_VERSION(1, 24, 0) - /* devices with fdl-based update won't replug */ - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_CINTERION_FDL) - return TRUE; -#endif // MM_CHECK_VERSION(1, 24, 0) + /* optional properties */ + fwupd_codec_json_append(builder, "GType", G_OBJECT_TYPE_NAME(self)); + if (fu_device_get_version(device) != NULL) + fwupd_codec_json_append(builder, "Version", fu_device_get_version(device)); + if (fu_device_get_physical_id(device) != NULL) + fwupd_codec_json_append(builder, "PhysicalId", fu_device_get_physical_id(device)); + if (priv->branch_at != NULL) + fwupd_codec_json_append(builder, "BranchAt", priv->branch_at); - /* wait for re-probing after uninhibiting */ - fu_device_set_remove_delay(device, FU_MM_DEVICE_REMOVE_DELAY_REPROBE); - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - return TRUE; -} - -static gboolean -fu_mm_device_setup_branch_at(FuMmDevice *self, GError **error) -{ - g_autoptr(FuDeviceLocker) locker = NULL; - - /* nothing to do if there is no AT port available or - * ModemManagerBranchAtCommand quirk is not set */ - if (self->port_at == NULL || self->branch_at == NULL) - return TRUE; - - if (fu_device_has_flag(self, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Firmware branches are not supported if the devices is signed"); - return FALSE; + /* specified by ModemManager, unusually */ + json_builder_set_member_name(builder, "DeviceIds"); + json_builder_begin_array(builder); + for (guint i = 0; i < instance_ids->len; i++) { + const gchar *instance_id = g_ptr_array_index(instance_ids, i); + json_builder_add_string_value(builder, instance_id); } - - /* Create IO channel to send AT commands to the modem */ - locker = fu_device_locker_new_full(self, - (FuDeviceLockerFunc)fu_mm_device_io_open, - (FuDeviceLockerFunc)fu_mm_device_io_close, - error); - if (locker == NULL) - return FALSE; - - if (!fu_mm_device_at_cmd(self, self->branch_at, TRUE, error)) - return FALSE; - - if (fu_device_get_branch(self) != NULL) - g_info("using firmware branch: %s", fu_device_get_branch(self)); - else - g_info("using firmware branch: default"); - - return TRUE; -} - -static void -fu_mm_device_setup_secboot_status_quectel(FuMmDevice *self) -{ - const gchar *version = fu_device_get_version(FU_DEVICE(self)); - g_autofree gchar *name = NULL; - - const gchar *at_cmd[] = {"AT+QSECBOOT=\"status\"", "AT+QCFG=\"secbootstat\"", NULL}; - - struct { - const gchar *name; - const gchar *version; - } secboot[] = {{"EM05GF", "EM05GFAR07A07M1G_01.005.01.005"}, - {"EM05CE", "EM05CEFCR08A16M1G_LNV"}, - {NULL, NULL}}; - - if (self->port_at != NULL) { - g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(GError) error_local = NULL; - - /* Create IO channel to send AT commands to the modem */ - locker = fu_device_locker_new_full(self, - (FuDeviceLockerFunc)fu_mm_device_io_open, - (FuDeviceLockerFunc)fu_mm_device_io_close, - &error_local); - if (locker == NULL) { - g_debug("failed to open AT port: %s", error_local->message); - return; + for (guint i = 0; i < vendor_ids->len; i++) { + const gchar *vendor_id = g_ptr_array_index(vendor_ids, i); + if (g_str_has_prefix(vendor_id, "USB:0x")) { + g_autofree gchar *id = g_strdup_printf("USB\\VID_%s", vendor_id + 6); + json_builder_add_string_value(builder, id); } - - /* try to query sec boot status with AT commands */ - for (guint i = 0; at_cmd[i] != NULL; i++) { - g_autoptr(GError) error_loop = NULL; - if (!fu_mm_device_at_cmd(self, at_cmd[i], TRUE, &error_loop)) { - g_debug("AT command failed (%s): %s", - at_cmd[i], - error_loop->message); - } else { - return; - } + if (g_str_has_prefix(vendor_id, "PCI:0x")) { + g_autofree gchar *id = g_strdup_printf("PCI\\VEN_%s", vendor_id + 6); + json_builder_add_string_value(builder, id); } } + json_builder_end_array(builder); - /* find model name and compare with table from Quectel */ - if (version == NULL) - return; - name = g_strndup(version, 6); - for (guint i = 0; secboot[i].name != NULL; i++) { - if (g_strcmp0(name, secboot[i].name) == 0) { - if (fu_version_compare(version, - secboot[i].version, - FWUPD_VERSION_FORMAT_PLAIN) >= 0) { - fu_device_add_flag(FU_DEVICE(self), - FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); - } else { - fu_device_add_flag(FU_DEVICE(self), - FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - } - return; - } + /* ports always specified */ + json_builder_set_member_name(builder, "Ports"); + json_builder_begin_object(builder); + for (guint i = 0; i < priv->ports->len; i++) { + FuMmDevicePort *port = g_ptr_array_index(priv->ports, i); + fwupd_codec_json_append(builder, + fu_mm_device_port_type_to_string(port->type), + port->device_file); } + json_builder_end_object(builder); } static void -fu_mm_device_setup_secboot_status(FuDevice *device) +fu_mm_device_get_property(GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) { - FuMmDevice *self = FU_MM_DEVICE(device); - - if (fu_device_has_vendor_id(device, "USB:0x2C7C") || - fu_device_has_vendor_id(device, "PCI:0x1EAC")) - fu_mm_device_setup_secboot_status_quectel(self); - else if (fu_device_has_vendor_id(device, "USB:0x2CB7")) { - fu_device_add_private_flag(FU_DEVICE(self), - FU_DEVICE_PRIVATE_FLAG_SAVE_INTO_BACKUP_REMOTE); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + FuMmDevice *self = FU_MM_DEVICE(object); + FuMmDevicePrivate *priv = GET_PRIVATE(self); + switch (prop_id) { + case PROP_INHIBITED: + g_value_set_boolean(value, priv->inhibited); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; } } -static gboolean -fu_mm_device_setup(FuDevice *device, GError **error) -{ - FuMmDevice *self = FU_MM_DEVICE(device); - g_autoptr(GError) error_local = NULL; - - fu_mm_device_setup_secboot_status(device); - - if (!fu_mm_device_setup_branch_at(self, &error_local)) - g_warning("Failed to set firmware branch: %s", error_local->message); - -#if MM_CHECK_VERSION(1, 24, 0) - if (self->update_methods & MM_MODEM_FIRMWARE_UPDATE_METHOD_DFOTA) - if (!fu_mm_device_setup_firmware_file_dfota(device, error)) - return FALSE; -#endif // MM_CHECK_VERSION(1, 24, 0) - - return TRUE; -} - -static void -fu_mm_device_incorporate(FuDevice *device, FuDevice *donor_device) -{ - FuMmDevice *self = FU_MM_DEVICE(device); - FuMmDevice *donor = FU_MM_DEVICE(donor_device); - - g_return_if_fail(FU_IS_MM_DEVICE(self)); - g_return_if_fail(FU_IS_MM_DEVICE(donor)); - - self->update_methods = fu_mm_device_get_update_methods(donor); - self->detach_fastboot_at = g_strdup(donor->detach_fastboot_at); - self->inhibition_uid = g_strdup(fu_mm_device_get_inhibition_uid(donor)); - g_set_object(&self->manager, donor->manager); -} - static void -fu_mm_device_set_progress(FuDevice *self, FuProgress *progress) +fu_mm_device_set_property(GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) { - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, "write"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 2, "attach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); + FuMmDevice *self = FU_MM_DEVICE(object); + switch (prop_id) { + case PROP_INHIBITED: + fu_mm_device_set_inhibited(self, g_value_get_boolean(value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); + break; + } } static void fu_mm_device_init(FuMmDevice *self) { + FuMmDevicePrivate *priv = GET_PRIVATE(self); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_RUNTIME_VERSION); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_ARCHIVE_FIRMWARE); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_RUNTIME_VERSION); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_VERFMT); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV); + fu_device_add_private_flag(FU_DEVICE(self), FU_UDEV_DEVICE_FLAG_SYSFS_USE_PHYSICAL_ID); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); fu_device_set_summary(FU_DEVICE(self), "Mobile broadband device"); - fu_device_add_icon(FU_DEVICE(self), "modem"); - fu_device_register_private_flag(FU_DEVICE(self), - FU_MM_DEVICE_FLAG_DETACH_AT_FASTBOOT_HAS_NO_RESPONSE); - fu_device_register_private_flag(FU_DEVICE(self), - FU_MM_DEVICE_FLAG_UNINHIBIT_MM_AFTER_FASTBOOT_REBOOT); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_MODEM); fu_device_register_private_flag(FU_DEVICE(self), FU_MM_DEVICE_FLAG_USE_BRANCH); - fu_device_register_private_flag(FU_DEVICE(self), FU_MM_DEVICE_FLAG_DISABLE_ZLP); + fu_device_register_private_flag(FU_DEVICE(self), FU_MM_DEVICE_FLAG_MAKE_SERIAL_RAW); + fu_device_add_possible_plugin(FU_DEVICE(self), "modem_manager"); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); + priv->ports = g_ptr_array_new_with_free_func((GDestroyNotify)fu_mm_device_port_free); } static void fu_mm_device_finalize(GObject *object) { FuMmDevice *self = FU_MM_DEVICE(object); - if (self->udev_device != NULL) - g_object_unref(self->udev_device); - if (self->attach_idle) - g_source_remove(self->attach_idle); - if (self->qmi_pdc_active_id) - g_array_unref(self->qmi_pdc_active_id); - if (self->manager != NULL) - g_object_unref(self->manager); - if (self->omodem != NULL) - g_object_unref(self->omodem); - g_free(self->detach_fastboot_at); - g_free(self->branch_at); - g_free(self->port_at); - g_free(self->port_qmi); - g_free(self->port_mbim); - g_free(self->port_qcdm); - g_free(self->inhibition_uid); - g_free(self->firmware_path); - g_free(self->firehose_prog_file); + FuMmDevicePrivate *priv = GET_PRIVATE(self); + + g_free(priv->branch_at); + g_free(priv->inhibition_uid); + g_ptr_array_unref(priv->ports); + G_OBJECT_CLASS(fu_mm_device_parent_class)->finalize(object); } static void fu_mm_device_class_init(FuMmDeviceClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS(klass); FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + GParamSpec *pspec; + object_class->finalize = fu_mm_device_finalize; + object_class->get_property = fu_mm_device_get_property; + object_class->set_property = fu_mm_device_set_property; device_class->setup = fu_mm_device_setup; - device_class->reload = fu_mm_device_setup; device_class->to_string = fu_mm_device_to_string; device_class->set_quirk_kv = fu_mm_device_set_quirk_kv; - device_class->probe = fu_mm_device_probe; - device_class->detach = fu_mm_device_detach; - device_class->write_firmware = fu_mm_device_write_firmware; - device_class->attach = fu_mm_device_attach; - device_class->cleanup = fu_mm_device_cleanup; - device_class->set_progress = fu_mm_device_set_progress; - device_class->incorporate = fu_mm_device_incorporate; - - /** - * FuMmDevice::attach-finished: - * @self: the #FuMmDevice instance that emitted the signal - * - * The ::attach-finished signal is emitted when the device has attached. - **/ - signals[SIGNAL_ATTACH_FINISHED] = g_signal_new("attach-finished", - G_TYPE_FROM_CLASS(object_class), - G_SIGNAL_RUN_LAST, - 0, - NULL, - NULL, - g_cclosure_marshal_VOID__VOID, - G_TYPE_NONE, - 0); -} - -FuMmDevice * -fu_mm_device_new(FuContext *ctx, MMManager *manager, MMObject *omodem) -{ - FuMmDevice *self = g_object_new(FU_TYPE_MM_DEVICE, "context", ctx, NULL); - self->manager = g_object_ref(manager); - self->omodem = g_object_ref(omodem); - return self; -} - -FuMmDevice * -fu_mm_device_shadow_new(FuMmDevice *device) -{ - FuMmDevice *shadow_device = NULL; - shadow_device = g_object_new(FU_TYPE_MM_DEVICE, - "context", - fu_device_get_context(FU_DEVICE(device)), - NULL); - fu_device_incorporate(FU_DEVICE(shadow_device), - FU_DEVICE(device), - FU_DEVICE_INCORPORATE_FLAG_ALL); - return shadow_device; -} - -void -fu_mm_device_set_udev_device(FuMmDevice *self, FuUdevDevice *udev_device) -{ - g_return_if_fail(FU_IS_MM_DEVICE(self)); - g_return_if_fail(FU_IS_UDEV_DEVICE(udev_device)); - - g_set_object(&self->udev_device, udev_device); - - /* copy across any vendor IDs */ - if (udev_device != NULL) { - fu_device_incorporate(FU_DEVICE(self), - FU_DEVICE(udev_device), - FU_DEVICE_INCORPORATE_FLAG_VENDOR_IDS); - } -} - -FuMmDevice * -fu_mm_device_udev_new(FuContext *ctx, MMManager *manager, FuMmDevice *shadow_device) -{ - FuMmDevice *self = g_object_new(FU_TYPE_MM_DEVICE, "context", ctx, NULL); - g_debug("creating udev-based mm device at %s", - fu_device_get_physical_id(FU_DEVICE(shadow_device))); - fu_device_incorporate(FU_DEVICE(self), - FU_DEVICE(shadow_device), - FU_DEVICE_INCORPORATE_FLAG_ALL); - return self; -} - -void -fu_mm_device_udev_add_port(FuMmDevice *self, const gchar *subsystem, const gchar *path) -{ - g_return_if_fail(FU_IS_MM_DEVICE(self)); - - if (g_str_equal(subsystem, "usbmisc") && self->port_qmi == NULL) { - g_debug("added QMI port %s (%s)", path, subsystem); - self->port_qmi = g_strdup(path); - return; - } - - if (g_str_equal(subsystem, "tty") && self->port_at == NULL) { - g_debug("added AT port %s (%s)", path, subsystem); - self->port_at = g_strdup(path); - return; - } - - /* otherwise, ignore all other ports */ - g_debug("ignoring port %s (%s)", path, subsystem); + device_class->from_json = fu_mm_device_from_json; + device_class->add_json = fu_mm_device_add_json; + device_class->open = fu_mm_device_open; + + pspec = g_param_spec_boolean("inhibited", + NULL, + NULL, + FALSE, + G_PARAM_READWRITE | G_PARAM_STATIC_NAME); + g_object_class_install_property(object_class, PROP_INHIBITED, pspec); } diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-device.h fwupd-2.0.20/plugins/modem-manager/fu-mm-device.h --- fwupd-2.0.8/plugins/modem-manager/fu-mm-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,5 @@ /* * Copyright 2018 Richard Hughes - * Copyright 2019 Aleksander Morgado * * SPDX-License-Identifier: LGPL-2.1-or-later */ @@ -11,27 +10,42 @@ #include -#define FU_MM_DEVICE_FLAG_DETACH_AT_FASTBOOT_HAS_NO_RESPONSE "detach-at-fastboot-has-no-response" -#define FU_MM_DEVICE_FLAG_UNINHIBIT_MM_AFTER_FASTBOOT_REBOOT \ - "uninhibit-modemmanager-after-fastboot-reboot" -#define FU_MM_DEVICE_FLAG_USE_BRANCH "use-branch" -#define FU_MM_DEVICE_FLAG_DISABLE_ZLP "disable-zlp" - #define FU_TYPE_MM_DEVICE (fu_mm_device_get_type()) -G_DECLARE_FINAL_TYPE(FuMmDevice, fu_mm_device, FU, MM_DEVICE, FuDevice) +G_DECLARE_DERIVABLE_TYPE(FuMmDevice, fu_mm_device, FU, MM_DEVICE, FuUdevDevice) + +#define FU_MM_DEVICE_FLAG_USE_BRANCH "use-branch" + +#define FU_MM_DEVICE_FLAG_MAKE_SERIAL_RAW "make-serial-raw" + +/* less ifdefs */ +#if !MM_CHECK_VERSION(1, 24, 0) +#define MM_MODEM_FIRMWARE_UPDATE_METHOD_DFOTA (1 << 5) +#define MM_MODEM_FIRMWARE_UPDATE_METHOD_CINTERION_FDL (1 << 6) +#endif + +struct _FuMmDeviceClass { + FuUdevDeviceClass parent_class; +}; -FuMmDevice * -fu_mm_device_new(FuContext *ctx, MMManager *manager, MMObject *omodem); void -fu_mm_device_set_udev_device(FuMmDevice *self, FuUdevDevice *udev_device); +fu_mm_device_set_inhibited(FuMmDevice *self, gboolean inhibited) G_GNUC_NON_NULL(1); +gboolean +fu_mm_device_get_inhibited(FuMmDevice *self) G_GNUC_NON_NULL(1); const gchar * -fu_mm_device_get_inhibition_uid(FuMmDevice *device); -MMModemFirmwareUpdateMethod -fu_mm_device_get_update_methods(FuMmDevice *device); - -FuMmDevice * -fu_mm_device_shadow_new(FuMmDevice *device); -FuMmDevice * -fu_mm_device_udev_new(FuContext *ctx, MMManager *manager, FuMmDevice *shadow_device); -void -fu_mm_device_udev_add_port(FuMmDevice *self, const gchar *subsystem, const gchar *path); +fu_mm_device_get_inhibition_uid(FuMmDevice *self) G_GNUC_NON_NULL(1); +gboolean +fu_mm_device_set_device_file(FuMmDevice *self, MMModemPortType port_type, GError **error) + G_GNUC_NON_NULL(1); + +gboolean +fu_mm_device_probe_from_omodem(FuMmDevice *self, MMObject *omodem, GError **error) + G_GNUC_NON_NULL(1, 2); +gboolean +fu_mm_device_at_cmd(FuMmDevice *self, const gchar *cmd, gboolean has_response, GError **error) + G_GNUC_NON_NULL(1, 2); +gboolean +fu_mm_device_set_autosuspend_delay(FuMmDevice *self, guint timeout_ms, GError **error) + G_GNUC_NON_NULL(1); +gboolean +fu_mm_device_add_instance_id(FuMmDevice *self, const gchar *device_id, GError **error) + G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-dfota-device.c fwupd-2.0.20/plugins/modem-manager/fu-mm-dfota-device.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-dfota-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-dfota-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,488 @@ +/* + * Copyright 2024 TDT AG + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-mm-dfota-device.h" + +struct _FuMmDfotaDevice { + FuMmDevice parent_instance; +}; + +G_DEFINE_TYPE(FuMmDfotaDevice, fu_mm_dfota_device, FU_TYPE_MM_DEVICE) + +#define FU_MM_DFOTA_DEVICE_FILENAME "dfota_update.bin" + +#define FU_MM_DFOTA_DEVICE_FOTA_READ_TIMEOUT_SECS 90 +#define FU_MM_DFOTA_DEVICE_TIMEOUT_SECS 5 + +static gboolean +fu_mm_dfota_device_probe(FuDevice *device, GError **error) +{ + FuMmDfotaDevice *self = FU_MM_DFOTA_DEVICE(device); + return fu_mm_device_set_device_file(FU_MM_DEVICE(self), MM_MODEM_PORT_TYPE_AT, error); +} + +static gboolean +fu_mm_dfota_device_setup(FuDevice *device, GError **error) +{ + FuMmDfotaDevice *self = FU_MM_DFOTA_DEVICE(device); + g_autoptr(GError) error_local = NULL; + + if (!fu_mm_device_at_cmd(FU_MM_DEVICE(self), "AT+QFLST=?", TRUE, error)) { + g_prefix_error_literal(error, "listing files not supported: "); + return FALSE; + } + /* if listing firmware file does not fail, there is an old firmware file to remove */ + if (!fu_mm_device_at_cmd(FU_MM_DEVICE(self), + "AT+QFLST=\"UFS:" FU_MM_DFOTA_DEVICE_FILENAME "\"", + TRUE, + &error_local)) { + g_debug("no old firmware found in filesystem: %s", error_local->message); + return TRUE; + } + + g_debug("found orphaned firmware file; trying to delete it"); + if (!fu_mm_device_at_cmd(FU_MM_DEVICE(self), + "AT+QFDEL=\"" FU_MM_DFOTA_DEVICE_FILENAME "\"", + TRUE, + error)) { + g_prefix_error_literal(error, "failed to delete existing firmware file: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +/* compute 16 bit checksum based on bitwise XOR */ +static gboolean +fu_mm_dfota_device_compute_checksum_cb(const guint8 *buf, + gsize bufsz, + gpointer user_data, + GError **error) +{ + guint16 *checksum = (guint16 *)user_data; + for (gsize i = 0; i < bufsz; i += 2) { + guint16 word = buf[i] << 8; + if (i < bufsz - 1) + word |= buf[i + 1]; + *checksum ^= word; + } + return TRUE; +} + +static gboolean +fu_mm_dfota_device_upload_chunk(FuMmDfotaDevice *self, FuChunk *chk, GError **error) +{ + g_autoptr(GBytes) ack_bytes = NULL; + g_autoptr(GRegex) ack_regex = NULL; + g_autoptr(GBytes) chunk_bytes = NULL; + const gchar *ack_result = NULL; + gsize ack_size; + gsize chunk_size; + gsize acks_expected; + + ack_regex = g_regex_new("^A+$", 0, 0, NULL); + chunk_size = g_bytes_get_size(fu_chunk_get_bytes(chk)); + /* expect one byte as response for every 1024 bytes sent */ + acks_expected = chunk_size / 1024; + /* pad every chunk to 2048 bytes to received correct amount of ACKs */ + chunk_bytes = fu_bytes_pad(fu_chunk_get_bytes(chk), 0x800, 0xFF); + + if (!fu_udev_device_write_bytes(FU_UDEV_DEVICE(self), + chunk_bytes, + 1500, + FU_IO_CHANNEL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to upload firmware to the device: "); + return FALSE; + } + if (acks_expected == 0) + return TRUE; + + ack_bytes = fu_udev_device_read_bytes(FU_UDEV_DEVICE(self), + acks_expected, + FU_MM_DFOTA_DEVICE_TIMEOUT_SECS * 1000, + FU_IO_CHANNEL_FLAG_NONE, + error); + if (ack_bytes == NULL) { + g_prefix_error_literal(error, "failed to read response: "); + return FALSE; + } + + ack_result = g_bytes_get_data(ack_bytes, &ack_size); + if (ack_size != acks_expected) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "expected %" G_GSIZE_FORMAT " ACKs, got %" G_GSIZE_FORMAT, + acks_expected, + ack_size); + return FALSE; + } + if (!g_regex_match(ack_regex, ack_result, 0, NULL)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "expected ACKs (A), got %s", + ack_result); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_mm_dfota_device_parse_upload_result(FuMmDfotaDevice *self, + guint16 *checksum, + gsize *size, + GError **error) +{ + guint64 tmp; + const gchar *result = NULL; + g_autofree gchar *checksum_match = NULL; + g_autofree gchar *size_match = NULL; + g_autoptr(GBytes) result_bytes = NULL; + g_autoptr(GMatchInfo) match_info = NULL; + g_autoptr(GRegex) result_regex = NULL; + + /* +QFUPL: , */ + result_regex = g_regex_new("\\r\\n\\+QFUPL:\\s*(\\d+),([0-9a-f]+)\\r\\n", 0, 0, error); + if (result_regex == NULL) { + g_prefix_error_literal(error, "failed to build regex: "); + return FALSE; + } + + result_bytes = fu_udev_device_read_bytes(FU_UDEV_DEVICE(self), + 4096, + FU_MM_DFOTA_DEVICE_TIMEOUT_SECS * 1000, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error); + if (result_bytes == NULL) { + g_prefix_error_literal(error, "failed to read AT+QFUPL response: "); + return FALSE; + } + result = g_bytes_get_data(result_bytes, NULL); + + if (g_strrstr(result, "\r\nOK\r\n") == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "upload command exited with error"); + return FALSE; + } + if (!g_regex_match(result_regex, result, 0, &match_info)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "could not match QFUPL response"); + return FALSE; + } + if (!g_match_info_matches(match_info)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "could not match size and checksum"); + return FALSE; + } + + /* success, so convert to integers */ + size_match = g_match_info_fetch(match_info, 1); + checksum_match = g_match_info_fetch(match_info, 2); + g_debug("parsed checksum '%s' and size '%s'", checksum_match, size_match); + if (!fu_strtoull(size_match, &tmp, 0x0, G_MAXSIZE, FU_INTEGER_BASE_10, error)) + return FALSE; + if (size != NULL) + *size = tmp; + if (!fu_strtoull(checksum_match, &tmp, 0x0, G_MAXUINT16, FU_INTEGER_BASE_16, error)) + return FALSE; + if (checksum != NULL) + *checksum = tmp; + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_dfota_device_upload_stream(FuMmDfotaDevice *self, GInputStream *stream, GError **error) +{ + gsize size = 0; + gsize size_parsed = 0; + guint16 checksum = 0; + guint16 checksum_parsed = 0; + g_autoptr(FuChunkArray) chunks = NULL; + + chunks = fu_chunk_array_new_from_stream(stream, 0x0, FU_CHUNK_PAGESZ_NONE, 0x800, error); + if (chunks == NULL) + return FALSE; + for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { + g_autoptr(FuChunk) chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) + return FALSE; + if (!fu_mm_dfota_device_upload_chunk(self, chk, error)) { + g_prefix_error(error, "failed at chunk %u: ", i); + return FALSE; + } + if (i % 100 == 0) + g_debug("wrote chunk %u/%u", i, fu_chunk_array_length(chunks) - 1); + } + + /* check result */ + if (!fu_input_stream_size(stream, &size, error)) + return FALSE; + if (!fu_input_stream_chunkify(stream, + fu_mm_dfota_device_compute_checksum_cb, + &checksum, + error)) + return FALSE; + if (!fu_mm_dfota_device_parse_upload_result(self, &checksum_parsed, &size_parsed, error)) + return FALSE; + if (size != size_parsed) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware size mismatch - expected 0x%x, but was 0x%x", + (guint)size, + (guint)size_parsed); + return FALSE; + } + if (checksum != checksum_parsed) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "checksum mismatch - expected 0x%04x, but was 0x%04x", + checksum, + checksum_parsed); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_dfota_device_parse_fota_response(FuMmDfotaDevice *self, + const gchar *response, + FuProgress *progress, + gboolean *finished, + GError **error) +{ + g_autoptr(GRegex) fota_regex = NULL; + g_autoptr(GMatchInfo) match_info = NULL; + g_autofree gchar *status_match = NULL; + g_autofree gchar *status_number_match = NULL; + guint64 status_number = 0; + + /* +QIND: "FOTA",""(,)? */ + fota_regex = g_regex_new("\\+QIND:\\s*\"FOTA\",\"([A-Z]+)\"(,(\\d+))?", 0, 0, error); + if (fota_regex == NULL) { + g_prefix_error_literal(error, "failed to build regex: "); + return FALSE; + } + + if (!g_regex_match(fota_regex, response, 0, &match_info)) { + /* + * Log and continue on unexpected responses because devices + * may incorrectly return an incomplete status message 1-2 + * times. + */ + g_debug("got unexpected response '%s'", response); + return TRUE; + } + if (!g_match_info_matches(match_info)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "could not match fota status"); + return FALSE; + } + + status_match = g_match_info_fetch(match_info, 1); + + if (g_strcmp0(status_match, "START") == 0) { + g_debug("update started successfully"); + return TRUE; + } + + /* expect status and number, which means four matches in the above regex */ + if (g_match_info_get_match_count(match_info) != 4) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "badly formatted message '%s'", + response); + return FALSE; + } + + status_number_match = g_match_info_fetch(match_info, 3); + if (!g_ascii_string_to_unsigned(status_number_match, + 10, + 0, + G_MAXUINT, + &status_number, + error)) + return FALSE; + + if (g_strcmp0(status_match, "UPDATING") == 0) { + fu_progress_set_percentage(progress, (guint)status_number); + return TRUE; + } + if (g_strcmp0(status_match, "END") == 0) { + if (status_number != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "update exited with error code %" G_GUINT64_FORMAT, + status_number); + return FALSE; + } + + g_debug("updated finished successfully"); + *finished = TRUE; + return TRUE; + } + + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unhandled fota status '%s'", + status_match); + return FALSE; +} + +static gboolean +fu_mm_dfota_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuMmDfotaDevice *self = FU_MM_DFOTA_DEVICE(device); + gboolean finished = FALSE; + + while (!finished) { + g_autoptr(GBytes) bytes = NULL; + g_autofree gchar *result = NULL; + + bytes = fu_udev_device_read_bytes(FU_UDEV_DEVICE(self), + 4096, + FU_MM_DFOTA_DEVICE_FOTA_READ_TIMEOUT_SECS * 1000, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error); + if (bytes == NULL) + return FALSE; + result = fu_strsafe_bytes(bytes, G_MAXSIZE); + + /* ignore empty responses */ + if (result == NULL) + continue; + + g_strstrip(result); + if (strlen(result) == 0) + continue; + + if (!fu_mm_dfota_device_parse_fota_response(self, + result, + progress, + &finished, + error)) + return FALSE; + } + + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_mm_dfota_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmDfotaDevice *self = FU_MM_DFOTA_DEVICE(device); + g_autofree gchar *upload_cmd = NULL; + g_autoptr(GInputStream) stream = NULL; + + /* get default stream */ + stream = fu_firmware_get_stream(firmware, error); + if (stream == NULL) + return FALSE; + + /* put the device into upload mode */ + upload_cmd = g_strdup_printf("AT+QFUPL=\"%s\",%" G_GSIZE_FORMAT ",5,1", + FU_MM_DFOTA_DEVICE_FILENAME, + fu_firmware_get_size(firmware)); + if (!fu_mm_device_at_cmd(FU_MM_DEVICE(self), upload_cmd, TRUE, error)) { + g_prefix_error_literal(error, "failed to enable upload mode: "); + return FALSE; + } + if (!fu_mm_dfota_device_upload_stream(self, stream, error)) + return FALSE; + if (!fu_mm_device_at_cmd(FU_MM_DEVICE(self), + "AT+QFOTADL=\"/data/ufs/" FU_MM_DFOTA_DEVICE_FILENAME "\"", + TRUE, + error)) { + g_prefix_error_literal(error, "failed to start update: "); + return FALSE; + } + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_dfota_device_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmDfotaDevice *self = FU_MM_DFOTA_DEVICE(device); + fu_mm_device_set_inhibited(FU_MM_DEVICE(self), TRUE); + return TRUE; +} + +static gboolean +fu_mm_dfota_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmDfotaDevice *self = FU_MM_DFOTA_DEVICE(device); + fu_mm_device_set_inhibited(FU_MM_DEVICE(self), FALSE); + return TRUE; +} + +static void +fu_mm_dfota_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 13, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 85, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_mm_dfota_device_init(FuMmDfotaDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.quectel.dfota"); + fu_device_set_remove_delay(FU_DEVICE(self), 15000); +} + +static void +fu_mm_dfota_device_class_init(FuMmDfotaDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->probe = fu_mm_dfota_device_probe; + device_class->attach = fu_mm_dfota_device_attach; + device_class->prepare = fu_mm_dfota_device_prepare; + device_class->cleanup = fu_mm_dfota_device_cleanup; + device_class->setup = fu_mm_dfota_device_setup; + device_class->set_progress = fu_mm_dfota_device_set_progress; + device_class->write_firmware = fu_mm_dfota_device_write_firmware; +} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-dfota-device.h fwupd-2.0.20/plugins/modem-manager/fu-mm-dfota-device.h --- fwupd-2.0.8/plugins/modem-manager/fu-mm-dfota-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-dfota-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2024 TDT AG + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-mm-device.h" + +#define FU_TYPE_MM_DFOTA_DEVICE (fu_mm_dfota_device_get_type()) +G_DECLARE_FINAL_TYPE(FuMmDfotaDevice, fu_mm_dfota_device, FU, MM_DFOTA_DEVICE, FuMmDevice) diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-fastboot-device.c fwupd-2.0.20/plugins/modem-manager/fu-mm-fastboot-device.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-fastboot-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-fastboot-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,147 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-mm-fastboot-device.h" + +struct _FuMmFastbootDevice { + FuMmDevice parent_instance; + gchar *detach_at; +}; + +G_DEFINE_TYPE(FuMmFastbootDevice, fu_mm_fastboot_device, FU_TYPE_MM_DEVICE) + +#define FU_MM_FASTBOOT_DEVICE_FLAG_DETACH_AT_NO_RESPONSE "detach-at-fastboot-has-no-response" + +static void +fu_mm_fastboot_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuMmFastbootDevice *self = FU_MM_FASTBOOT_DEVICE(device); + fwupd_codec_string_append(str, idt, "DetachAt", self->detach_at); +} + +void +fu_mm_fastboot_device_set_detach_at(FuMmFastbootDevice *self, const gchar *detach_at) +{ + g_return_if_fail(FU_IS_MM_FASTBOOT_DEVICE(self)); + g_return_if_fail(detach_at != NULL); + g_free(self->detach_at); + self->detach_at = g_strdup(detach_at); +} + +static gboolean +fu_mm_fastboot_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuMmFastbootDevice *self = FU_MM_FASTBOOT_DEVICE(device); + gboolean has_response = TRUE; + + /* expect response for fastboot AT command */ + if (fu_device_has_private_flag(FU_DEVICE(self), + FU_MM_FASTBOOT_DEVICE_FLAG_DETACH_AT_NO_RESPONSE)) { + has_response = FALSE; + } + if (!fu_mm_device_at_cmd(FU_MM_DEVICE(self), "AT", TRUE, error)) + return FALSE; + if (!fu_mm_device_at_cmd(FU_MM_DEVICE(self), self->detach_at, has_response, error)) { + g_prefix_error_literal(error, "rebooting into fastboot not supported: "); + return FALSE; + } + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_mm_fastboot_device_probe(FuDevice *device, GError **error) +{ + FuMmFastbootDevice *self = FU_MM_FASTBOOT_DEVICE(device); + return fu_mm_device_set_device_file(FU_MM_DEVICE(self), MM_MODEM_PORT_TYPE_AT, error); +} + +static gboolean +fu_mm_fastboot_device_from_json(FuDevice *device, JsonObject *json_object, GError **error) +{ + FuMmFastbootDevice *self = FU_MM_FASTBOOT_DEVICE(device); + const gchar *tmp; + + /* FuMmDevice->from_json */ + if (!FU_DEVICE_CLASS(fu_mm_fastboot_device_parent_class) + ->from_json(device, json_object, error)) + return FALSE; + + /* optional properties */ + tmp = json_object_get_string_member_with_default(json_object, "DetachAt", NULL); + if (tmp != NULL) + fu_mm_fastboot_device_set_detach_at(self, tmp); + + /* success */ + return TRUE; +} + +static void +fu_mm_fastboot_device_add_json(FuDevice *device, JsonBuilder *builder, FwupdCodecFlags flags) +{ + FuMmFastbootDevice *self = FU_MM_FASTBOOT_DEVICE(device); + + /* FuMmDevice->add_json */ + FU_DEVICE_CLASS(fu_mm_fastboot_device_parent_class)->add_json(device, builder, flags); + + /* optional properties */ + if (self->detach_at != NULL) + fwupd_codec_json_append(builder, "DetachAt", self->detach_at); +} + +static void +fu_mm_fastboot_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_mm_fastboot_device_init(FuMmFastbootDevice *self) +{ + fu_device_set_remove_delay(FU_DEVICE(self), 20000); + fu_device_add_protocol(FU_DEVICE(self), "com.google.fastboot"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); + fu_device_register_private_flag(FU_DEVICE(self), + FU_MM_FASTBOOT_DEVICE_FLAG_DETACH_AT_NO_RESPONSE); + fu_device_add_instance_id_full(FU_DEVICE(self), + "USB\\VID_18D1&PID_D00D", + FU_DEVICE_INSTANCE_FLAG_COUNTERPART); + fu_device_add_instance_id_full(FU_DEVICE(self), + "USB\\VID_2CB7&PID_D00D", + FU_DEVICE_INSTANCE_FLAG_COUNTERPART); +} + +static void +fu_mm_fastboot_device_finalize(GObject *object) +{ + FuMmFastbootDevice *self = FU_MM_FASTBOOT_DEVICE(object); + g_free(self->detach_at); + G_OBJECT_CLASS(fu_mm_fastboot_device_parent_class)->finalize(object); +} + +static void +fu_mm_fastboot_device_class_init(FuMmFastbootDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_mm_fastboot_device_finalize; + device_class->set_progress = fu_mm_fastboot_device_set_progress; + device_class->detach = fu_mm_fastboot_device_detach; + device_class->probe = fu_mm_fastboot_device_probe; + device_class->to_string = fu_mm_fastboot_device_to_string; + device_class->from_json = fu_mm_fastboot_device_from_json; + device_class->add_json = fu_mm_fastboot_device_add_json; +} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-fastboot-device.h fwupd-2.0.20/plugins/modem-manager/fu-mm-fastboot-device.h --- fwupd-2.0.8/plugins/modem-manager/fu-mm-fastboot-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-fastboot-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-mm-device.h" + +#define FU_TYPE_MM_FASTBOOT_DEVICE (fu_mm_fastboot_device_get_type()) +G_DECLARE_FINAL_TYPE(FuMmFastbootDevice, fu_mm_fastboot_device, FU, MM_FASTBOOT_DEVICE, FuMmDevice) + +void +fu_mm_fastboot_device_set_detach_at(FuMmFastbootDevice *self, const gchar *detach_at) + G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-fdl-device.c fwupd-2.0.20/plugins/modem-manager/fu-mm-fdl-device.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-fdl-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-fdl-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,370 @@ +/* + * Copyright 2024 TDT AG + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-mm-fdl-device.h" +#include "fu-mm-fdl-struct.h" + +#ifdef HAVE_TERMIOS_H +#include +#endif + +#ifdef HAVE_TERMIOS_H +#define FU_CINTERION_FDL_DEFAULT_BAUDRATE B115200 +#endif +#define FU_CINTERION_FDL_MAX_READ_RETRIES 100 +#define FU_CINTERION_FDL_MAX_WRITE_RETRIES 10 +#define FU_CINTERION_FDL_SIZE_BYTES 2 + +struct _FuMmFdlDevice { + FuMmDevice parent_instance; +}; + +G_DEFINE_TYPE(FuMmFdlDevice, fu_mm_fdl_device, FU_TYPE_MM_DEVICE) + +static gboolean +fu_mm_fdl_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuMmFdlDevice *self = FU_MM_FDL_DEVICE(device); + + if (!fu_mm_device_at_cmd(FU_MM_DEVICE(self), "AT", TRUE, error)) + return FALSE; + if (!fu_mm_device_at_cmd(FU_MM_DEVICE(self), "AT^SFDL", TRUE, error)) { + g_prefix_error_literal(error, "enabling firmware download mode not supported: "); + return FALSE; + } + + /* wait 15 s before reopening port */ + fu_device_sleep(device, 15000); + return TRUE; +} + +static gboolean +fu_mm_fdl_device_wait_ready_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuMmFdlDevice *self = FU_MM_FDL_DEVICE(device); + gsize bytes_read = 0; + guint8 buf[1] = {0}; + + if (!fu_udev_device_read(FU_UDEV_DEVICE(self), + buf, + sizeof(buf), + &bytes_read, + 100, + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, + error)) { + return FALSE; + } + if (bytes_read != 1 || buf[0] != FU_MM_CINTERION_FDL_RESPONSE_OK) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, "invalid response"); + return FALSE; + } + + /* success */ + g_debug("start signal read"); + return TRUE; +} + +static gboolean +fu_mm_fdl_device_write_chunk(FuMmFdlDevice *self, + GBytes *size_bytes, + GBytes *chunk_bytes, + GError **error) +{ + if (!fu_udev_device_write_bytes(FU_UDEV_DEVICE(self), + size_bytes, + 1500, + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, + error)) { + return FALSE; + } + if (!fu_udev_device_write_bytes(FU_UDEV_DEVICE(self), + chunk_bytes, + 1500, + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, + error)) { + return FALSE; + } + return TRUE; +} + +static gboolean +fu_mm_fdl_device_read_response(FuMmFdlDevice *self, + FuMmCinterionFdlResponse *response, + GError **error) +{ + guint8 buf[1] = {0}; + gsize bytes_read = 0; + + if (!fu_udev_device_read(FU_UDEV_DEVICE(self), + buf, + sizeof(buf), + &bytes_read, + 100, + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, + error)) { + return FALSE; + } + if (bytes_read != 1) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, "invalid response"); + return FALSE; + } + + switch (buf[0]) { + case FU_MM_CINTERION_FDL_RESPONSE_OK: + case FU_MM_CINTERION_FDL_RESPONSE_RETRY: + case FU_MM_CINTERION_FDL_RESPONSE_BUSY: + *response = buf[0]; + break; + default: + *response = FU_MM_CINTERION_FDL_RESPONSE_UNKNOWN; + break; + } + + /* success */ + return TRUE; +} + +typedef struct { + GBytes *size_bytes; + GBytes *chunk_bytes; +} FuMmFdlDeviceWriteHelper; + +static void +fu_mm_fdl_device_write_helper_free(FuMmFdlDeviceWriteHelper *helper) +{ + if (helper->size_bytes) + g_object_unref(helper->size_bytes); + if (helper->chunk_bytes) + g_object_unref(helper->chunk_bytes); + g_free(helper); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuMmFdlDeviceWriteHelper, fu_mm_fdl_device_write_helper_free) + +static gboolean +fu_mm_fdl_device_read_chunk_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuMmFdlDevice *self = FU_MM_FDL_DEVICE(device); + FuMmCinterionFdlResponse *response = (FuMmCinterionFdlResponse *)user_data; + + if (!fu_mm_fdl_device_read_response(self, response, error)) + return FALSE; + + /* retry reading response */ + if (*response == FU_MM_CINTERION_FDL_RESPONSE_BUSY) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "response busy"); + return FALSE; + } + if (*response == FU_MM_CINTERION_FDL_RESPONSE_UNKNOWN) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "response unknown"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_fdl_device_write_chunk_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuMmFdlDevice *self = FU_MM_FDL_DEVICE(device); + FuMmFdlDeviceWriteHelper *helper = (FuMmFdlDeviceWriteHelper *)user_data; + FuMmCinterionFdlResponse response = FU_MM_CINTERION_FDL_RESPONSE_UNKNOWN; + + if (!fu_mm_fdl_device_write_chunk(self, helper->size_bytes, helper->chunk_bytes, error)) + return FALSE; + if (!fu_device_retry_full(FU_DEVICE(self), + fu_mm_fdl_device_read_chunk_cb, + FU_CINTERION_FDL_MAX_READ_RETRIES, + 10, /* ms */ + &response, + error)) + return FALSE; + + /* stop reading and retry write */ + if (response == FU_MM_CINTERION_FDL_RESPONSE_RETRY) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "response retry"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_fdl_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmFdlDevice *self = FU_MM_FDL_DEVICE(device); + guint chunk = 0; + gsize offset = 0; + gsize fw_len = fu_firmware_get_size(firmware); + g_autoptr(GBytes) fw = NULL; + + /* wait to be ready */ + if (!fu_device_retry_full(device, + fu_mm_fdl_device_wait_ready_cb, + FU_CINTERION_FDL_MAX_READ_RETRIES, + 100, /* ms */ + NULL, + error)) + return FALSE; + + /* send each [variable-sized] section */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + while (offset < fw_len) { + g_autoptr(FuMmFdlDeviceWriteHelper) helper = g_new0(FuMmFdlDeviceWriteHelper, 1); + guint16 chunk_size = 0; + + helper->size_bytes = + g_bytes_new_from_bytes(fw, offset, FU_CINTERION_FDL_SIZE_BYTES); + if (!fu_memread_uint16_safe(g_bytes_get_data(helper->size_bytes, NULL), + g_bytes_get_size(helper->size_bytes), + 0x0, + &chunk_size, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + offset += FU_CINTERION_FDL_SIZE_BYTES; + + helper->chunk_bytes = g_bytes_new_from_bytes(fw, offset, chunk_size); + offset += chunk_size; + + if (!fu_device_retry_full(FU_DEVICE(self), + fu_mm_fdl_device_write_chunk_cb, + FU_CINTERION_FDL_MAX_WRITE_RETRIES, + 10, /* ms */ + helper, + error)) { + g_prefix_error(error, "could not write chunk %u: ", chunk); + return FALSE; + } + if (chunk % 100 == 0) + g_debug("wrote chunk %u successfully", chunk); + + fu_progress_set_percentage_full(progress, offset, fw_len); + chunk++; + } + if (fw_len != offset) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "expected %" G_GSIZE_FORMAT " bytes, but wrote %" G_GSIZE_FORMAT, + fw_len, + offset); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_fdl_device_probe(FuDevice *device, GError **error) +{ + FuMmFdlDevice *self = FU_MM_FDL_DEVICE(device); + return fu_mm_device_set_device_file(FU_MM_DEVICE(self), MM_MODEM_PORT_TYPE_AT, error); +} + +static gboolean +fu_mm_fdl_device_ensure_io_flags(FuMmFdlDevice *self, GError **error) +{ +#ifdef HAVE_TERMIOS_H + gint fd = fu_io_channel_unix_get_fd(fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self))); + struct termios tio = { + .c_cflag = CS8 | CREAD | CLOCAL | HUPCL | FU_CINTERION_FDL_DEFAULT_BAUDRATE, + }; + if (tcsetattr(fd, TCSANOW, &tio) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "could not set termios attributes"); + return FALSE; + } + return TRUE; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as not found"); + return FALSE; +#endif +} + +static gboolean +fu_mm_fdl_device_open(FuDevice *device, GError **error) +{ + FuMmFdlDevice *self = FU_MM_FDL_DEVICE(device); + + /* FuUdevDevice->open */ + if (!FU_DEVICE_CLASS(fu_mm_fdl_device_parent_class)->open(device, error)) + return FALSE; + return fu_mm_fdl_device_ensure_io_flags(self, error); +} + +static gboolean +fu_mm_fdl_device_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmFdlDevice *self = FU_MM_FDL_DEVICE(device); + fu_mm_device_set_inhibited(FU_MM_DEVICE(self), TRUE); + return TRUE; +} + +static gboolean +fu_mm_fdl_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmFdlDevice *self = FU_MM_FDL_DEVICE(device); + fu_mm_device_set_inhibited(FU_MM_DEVICE(self), FALSE); + return TRUE; +} + +static void +fu_mm_fdl_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_mm_fdl_device_init(FuMmFdlDevice *self) +{ + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); + fu_device_add_protocol(FU_DEVICE(self), "com.cinterion.fdl"); +} + +static void +fu_mm_fdl_device_class_init(FuMmFdlDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->open = fu_mm_fdl_device_open; + device_class->probe = fu_mm_fdl_device_probe; + device_class->detach = fu_mm_fdl_device_detach; + device_class->prepare = fu_mm_fdl_device_prepare; + device_class->cleanup = fu_mm_fdl_device_cleanup; + device_class->set_progress = fu_mm_fdl_device_set_progress; + device_class->write_firmware = fu_mm_fdl_device_write_firmware; +} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-fdl-device.h fwupd-2.0.20/plugins/modem-manager/fu-mm-fdl-device.h --- fwupd-2.0.8/plugins/modem-manager/fu-mm-fdl-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-fdl-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2024 TDT AG + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-mm-device.h" + +#define FU_TYPE_MM_FDL_DEVICE (fu_mm_fdl_device_get_type()) +G_DECLARE_FINAL_TYPE(FuMmFdlDevice, fu_mm_fdl_device, FU, MM_FDL_DEVICE, FuMmDevice) diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-fdl.rs fwupd-2.0.20/plugins/modem-manager/fu-mm-fdl.rs --- fwupd-2.0.8/plugins/modem-manager/fu-mm-fdl.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-fdl.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2024 TDT AG + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#[repr(u8)] +enum FuMmCinterionFdlResponse { + Ok = 0x01, + Retry = 0x02, + Unknown = 0x03, + Busy = 0x04, +} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-firehose-device.c fwupd-2.0.20/plugins/modem-manager/fu-mm-firehose-device.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-firehose-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-firehose-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,94 @@ +/* + * Copyright 2020 Aleksander Morgado + * Copyright 2021 Ivan Mikhanchuk + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-mm-firehose-device.h" + +struct _FuMmFirehoseDevice { + FuMmDevice parent_instance; +}; + +G_DEFINE_TYPE(FuMmFirehoseDevice, fu_mm_firehose_device, FU_TYPE_MM_DEVICE) + +static gboolean +fu_mm_firehose_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuMmFirehoseDevice *self = FU_MM_FIREHOSE_DEVICE(device); + + if (!fu_mm_device_at_cmd(FU_MM_DEVICE(self), "AT", TRUE, error)) + return FALSE; + if (!fu_mm_device_at_cmd(FU_MM_DEVICE(self), "AT^SFIREHOSE", TRUE, error)) { + g_prefix_error_literal(error, "enabling firmware download mode not supported: "); + return FALSE; + } + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_mm_firehose_device_probe(FuDevice *device, GError **error) +{ + FuMmFirehoseDevice *self = FU_MM_FIREHOSE_DEVICE(device); + return fu_mm_device_set_device_file(FU_MM_DEVICE(self), MM_MODEM_PORT_TYPE_AT, error); +} + +static gboolean +fu_mm_firehose_device_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmFirehoseDevice *self = FU_MM_FIREHOSE_DEVICE(device); + fu_mm_device_set_inhibited(FU_MM_DEVICE(self), TRUE); + return TRUE; +} + +static gboolean +fu_mm_firehose_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmFirehoseDevice *self = FU_MM_FIREHOSE_DEVICE(device); + fu_mm_device_set_inhibited(FU_MM_DEVICE(self), FALSE); + return TRUE; +} + +static void +fu_mm_firehose_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_mm_firehose_device_init(FuMmFirehoseDevice *self) +{ + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); + fu_device_add_protocol(FU_UDEV_DEVICE(self), "com.qualcomm.firehose"); +} + +static void +fu_mm_firehose_device_class_init(FuMmFirehoseDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->probe = fu_mm_firehose_device_probe; + device_class->detach = fu_mm_firehose_device_detach; + device_class->prepare = fu_mm_firehose_device_prepare; + device_class->cleanup = fu_mm_firehose_device_cleanup; + device_class->set_progress = fu_mm_firehose_device_set_progress; +} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-firehose-device.h fwupd-2.0.20/plugins/modem-manager/fu-mm-firehose-device.h --- fwupd-2.0.8/plugins/modem-manager/fu-mm-firehose-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-firehose-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,14 @@ +/* + * Copyright 2020 Aleksander Morgado + * Copyright 2021 Ivan Mikhanchuk + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-mm-device.h" + +#define FU_TYPE_MM_FIREHOSE_DEVICE (fu_mm_firehose_device_get_type()) +G_DECLARE_FINAL_TYPE(FuMmFirehoseDevice, fu_mm_firehose_device, FU, MM_FIREHOSE_DEVICE, FuMmDevice) diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-mbim-device.c fwupd-2.0.20/plugins/modem-manager/fu-mm-mbim-device.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-mbim-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-mbim-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,584 @@ +/* + * Copyright 2021 Jarvis Jiang + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-mm-mbim-device.h" + +typedef struct { + FuMmDevice parent_instance; + MbimDevice *mbim_device; + GMainContext *main_ctx; +} FuMmMbimDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuMmMbimDevice, fu_mm_mbim_device, FU_TYPE_MM_DEVICE) + +#define GET_PRIVATE(o) (fu_mm_mbim_device_get_instance_private(o)) + +#define FU_MM_MBIM_DEVICE_MAX_OPEN_ATTEMPTS 8 + +#define FU_MM_MBIM_DEVICE_TIMEOUT_MS 1500 + +gboolean +fu_mm_mbim_device_error_convert(GError **error) +{ + const FuErrorConvertEntry entries[] = { + /* clang-format off */ + {MBIM_CORE_ERROR, MBIM_CORE_ERROR_FAILED, FWUPD_ERROR_INTERNAL}, + {MBIM_CORE_ERROR, MBIM_CORE_ERROR_WRONG_STATE, FWUPD_ERROR_INTERNAL}, + {MBIM_CORE_ERROR, MBIM_CORE_ERROR_TIMEOUT, FWUPD_ERROR_TIMED_OUT}, + {MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_ARGS, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_CORE_ERROR, MBIM_CORE_ERROR_INVALID_MESSAGE, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_CORE_ERROR, MBIM_CORE_ERROR_UNSUPPORTED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_CORE_ERROR, MBIM_CORE_ERROR_ABORTED, FWUPD_ERROR_INTERNAL}, + {MBIM_CORE_ERROR, MBIM_CORE_ERROR_UNKNOWN_STATE, FWUPD_ERROR_INTERNAL}, + {MBIM_CORE_ERROR, MBIM_CORE_ERROR_INCOMPLETE_MESSAGE, FWUPD_ERROR_INVALID_DATA}, + {MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_INVALID, FWUPD_ERROR_INTERNAL}, + {MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_TIMEOUT_FRAGMENT, FWUPD_ERROR_TIMED_OUT}, + {MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_FRAGMENT_OUT_OF_SEQUENCE, FWUPD_ERROR_INVALID_DATA}, + {MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_LENGTH_MISMATCH, FWUPD_ERROR_INVALID_DATA}, + {MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_DUPLICATED_TID, FWUPD_ERROR_INVALID_DATA}, + {MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_NOT_OPENED, FWUPD_ERROR_INTERNAL}, + {MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_UNKNOWN, FWUPD_ERROR_INTERNAL}, + {MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_CANCEL, FWUPD_ERROR_INTERNAL}, + {MBIM_PROTOCOL_ERROR, MBIM_PROTOCOL_ERROR_MAX_TRANSFER, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NONE, FWUPD_ERROR_INTERNAL}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_BUSY, FWUPD_ERROR_BUSY}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FAILURE, FWUPD_ERROR_INTERNAL}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SIM_NOT_INSERTED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_BAD_SIM, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PIN_REQUIRED, FWUPD_ERROR_AUTH_EXPIRED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PIN_DISABLED, FWUPD_ERROR_AUTH_FAILED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NOT_REGISTERED, FWUPD_ERROR_AUTH_FAILED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PROVIDERS_NOT_FOUND, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NO_DEVICE_SUPPORT, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PROVIDER_NOT_VISIBLE, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_DATA_CLASS_NOT_AVAILABLE, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PACKET_SERVICE_DETACHED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_MAX_ACTIVATED_CONTEXTS, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NOT_INITIALIZED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_VOICE_CALL_IN_PROGRESS, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_CONTEXT_NOT_ACTIVATED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SERVICE_NOT_ACTIVATED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_ACCESS_STRING, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_USER_NAME_PWD, FWUPD_ERROR_AUTH_FAILED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_RADIO_POWER_OFF, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_PARAMETERS, FWUPD_ERROR_INVALID_DATA}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_READ_FAILURE, FWUPD_ERROR_READ}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_WRITE_FAILURE, FWUPD_ERROR_WRITE}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NO_PHONEBOOK, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_PARAMETER_TOO_LONG, FWUPD_ERROR_INVALID_DATA}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_STK_BUSY, FWUPD_ERROR_BUSY}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_OPERATION_NOT_ALLOWED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_MEMORY_FAILURE, FWUPD_ERROR_INTERNAL}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_MEMORY_INDEX, FWUPD_ERROR_INVALID_DATA}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_MEMORY_FULL, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FILTER_NOT_SUPPORTED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_DSS_INSTANCE_LIMIT, FWUPD_ERROR_INTERNAL}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_DEVICE_SERVICE_OPERATION, FWUPD_ERROR_INTERNAL}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_AUTH_INCORRECT_AUTN, FWUPD_ERROR_AUTH_FAILED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_AUTH_SYNC_FAILURE, FWUPD_ERROR_INTERNAL}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_AUTH_AMF_NOT_SET, FWUPD_ERROR_INTERNAL}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_CONTEXT_NOT_SUPPORTED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SMS_UNKNOWN_SMSC_ADDRESS, FWUPD_ERROR_INVALID_DATA}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SMS_NETWORK_TIMEOUT, FWUPD_ERROR_TIMED_OUT}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SMS_LANG_NOT_SUPPORTED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SMS_ENCODING_NOT_SUPPORTED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SMS_FORMAT_NOT_SUPPORTED, FWUPD_ERROR_NOT_SUPPORTED}, +#if MBIM_CHECK_VERSION(1, 30, 0) + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_SIGNATURE, FWUPD_ERROR_INVALID_DATA}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_IMEI, FWUPD_ERROR_INVALID_DATA}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_INVALID_TIMESTAMP, FWUPD_ERROR_INVALID_DATA}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_NETWORK_LIST_TOO_LARGE, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_SIGNATURE_ALGORITHM_NOT_SUPPORTED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_FEATURE_NOT_SUPPORTED, FWUPD_ERROR_NOT_SUPPORTED}, + {MBIM_STATUS_ERROR, MBIM_STATUS_ERROR_DECODE_OR_PARSING_ERROR, FWUPD_ERROR_INVALID_DATA}, +#endif + /* clang-format on */ + }; + return fu_error_convert(entries, G_N_ELEMENTS(entries), error); +} + +typedef struct { + gboolean ret; + GMainContext *main_ctx; + GMainLoop *loop; + GSource *timeout_source; + GCancellable *cancellable; + MbimDevice *mbim_device; + MbimMessage *mbim_message; + GError *error; +} FuMmMbimDeviceHelper; + +static gboolean +fu_mm_mbim_device_helper_timeout_cb(gpointer user_data) +{ + FuMmMbimDeviceHelper *helper = (FuMmMbimDeviceHelper *)user_data; + g_cancellable_cancel(helper->cancellable); + return G_SOURCE_REMOVE; +} + +static FuMmMbimDeviceHelper * +fu_mm_mbim_device_helper_helper_new(GMainContext *main_ctx, guint timeout_ms) +{ + FuMmMbimDeviceHelper *helper = g_new0(FuMmMbimDeviceHelper, 1); + helper->timeout_source = g_timeout_source_new(timeout_ms); + helper->cancellable = g_cancellable_new(); + helper->main_ctx = g_main_context_ref(main_ctx); + helper->loop = g_main_loop_new(helper->main_ctx, FALSE); + g_source_set_callback(helper->timeout_source, + fu_mm_mbim_device_helper_timeout_cb, + helper, + NULL); + g_source_attach(helper->timeout_source, helper->main_ctx); + g_main_context_push_thread_default(helper->main_ctx); + return helper; +} + +static void +fu_mm_mbim_device_helper_free(FuMmMbimDeviceHelper *helper) +{ + g_main_context_pop_thread_default(helper->main_ctx); + g_source_destroy(helper->timeout_source); + if (helper->mbim_device != NULL) + g_object_unref(helper->mbim_device); + if (helper->mbim_message != NULL) + mbim_message_unref(helper->mbim_message); + g_source_unref(helper->timeout_source); + g_object_unref(helper->cancellable); + g_main_loop_unref(helper->loop); + g_main_context_unref(helper->main_ctx); + g_free(helper); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuMmMbimDeviceHelper, fu_mm_mbim_device_helper_free) + +static void +fu_mm_mbim_device_new_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FuMmMbimDeviceHelper *helper = (FuMmMbimDeviceHelper *)user_data; + helper->mbim_device = mbim_device_new_finish(res, &helper->error); + g_main_loop_quit(helper->loop); +} + +static MbimDevice * +fu_mm_mbim_device_new_sync(FuMmMbimDevice *self, GFile *file, guint timeout_ms, GError **error) +{ + FuMmMbimDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuMmMbimDeviceHelper) helper = + fu_mm_mbim_device_helper_helper_new(priv->main_ctx, timeout_ms); + FuDeviceEvent *event = NULL; + g_autofree gchar *event_id = NULL; + + g_return_val_if_fail(G_IS_FILE(file), NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* need event ID */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || + fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)), + FU_CONTEXT_FLAG_SAVE_EVENTS)) { + g_autofree gchar *path = g_file_get_path(file); + event_id = g_strdup_printf("MbimDeviceNew:Path=%s", path); + } + + /* emulated */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) { + event = fu_device_load_event(FU_DEVICE(self), event_id, error); + if (event == NULL) + return NULL; + if (!fu_device_event_check_error(event, error)) + return NULL; + return g_object_new(MBIM_TYPE_DEVICE, "device-file", file, NULL); + } + + /* save */ + if (event_id != NULL) + event = fu_device_save_event(FU_DEVICE(self), event_id); + + mbim_device_new(file, helper->cancellable, fu_mm_mbim_device_new_cb, helper); + g_main_loop_run(helper->loop); + + /* save response */ + if (helper->mbim_device == NULL) { + fu_mm_mbim_device_error_convert(&helper->error); + if (event != NULL) + fu_device_event_set_error(event, helper->error); + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + + /* success */ + return g_steal_pointer(&helper->mbim_device); +} + +static void +fu_mm_mbim_device_open_sync_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FuMmMbimDeviceHelper *helper = (FuMmMbimDeviceHelper *)user_data; + helper->ret = mbim_device_open_full_finish(helper->mbim_device, res, &helper->error); + g_main_loop_quit(helper->loop); +} + +static gboolean +fu_mm_mbim_device_open_sync(FuMmMbimDevice *self, guint timeout_ms, GError **error) +{ + FuMmMbimDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuMmMbimDeviceHelper) helper = + fu_mm_mbim_device_helper_helper_new(priv->main_ctx, timeout_ms); + FuDeviceEvent *event = NULL; + g_autofree gchar *event_id = NULL; + + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* need event ID */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || + fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)), + FU_CONTEXT_FLAG_SAVE_EVENTS)) { + event_id = g_strdup("MbimDeviceOpen"); + } + + /* emulated */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) { + event = fu_device_load_event(FU_DEVICE(self), event_id, error); + if (event == NULL) + return FALSE; + return fu_device_event_check_error(event, error); + } + + /* save */ + if (event_id != NULL) + event = fu_device_save_event(FU_DEVICE(self), event_id); + + mbim_device_open_full(priv->mbim_device, + MBIM_DEVICE_OPEN_FLAGS_PROXY, + 10, + helper->cancellable, + fu_mm_mbim_device_open_sync_cb, + helper); + g_main_loop_run(helper->loop); + + /* save response */ + if (!helper->ret) { + fu_mm_mbim_device_error_convert(&helper->error); + if (event != NULL) + fu_device_event_set_error(event, helper->error); + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_mm_mbim_device_close_sync_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FuMmMbimDeviceHelper *helper = (FuMmMbimDeviceHelper *)user_data; + helper->ret = mbim_device_close_finish(helper->mbim_device, res, &helper->error); + g_main_loop_quit(helper->loop); +} + +static gboolean +fu_mm_mbim_device_close_sync(FuMmMbimDevice *self, guint timeout_ms, GError **error) +{ + FuMmMbimDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuMmMbimDeviceHelper) helper = + fu_mm_mbim_device_helper_helper_new(priv->main_ctx, timeout_ms); + FuDeviceEvent *event = NULL; + g_autofree gchar *event_id = NULL; + + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* need event ID */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || + fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)), + FU_CONTEXT_FLAG_SAVE_EVENTS)) { + event_id = g_strdup("MbimDeviceClose"); + } + + /* emulated */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) { + event = fu_device_load_event(FU_DEVICE(self), event_id, error); + if (event == NULL) + return FALSE; + g_clear_object(&priv->mbim_device); + return fu_device_event_check_error(event, error); + } + + /* save */ + if (event_id != NULL) + event = fu_device_save_event(FU_DEVICE(self), event_id); + + mbim_device_close(priv->mbim_device, + 5, + helper->cancellable, + fu_mm_mbim_device_close_sync_cb, + helper); + g_main_loop_run(helper->loop); + g_clear_object(&priv->mbim_device); + + /* save response */ + if (!helper->ret) { + fu_mm_mbim_device_error_convert(&helper->error); + if (event != NULL) + fu_device_event_set_error(event, helper->error); + g_propagate_error(error, g_steal_pointer(&helper->error)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_mm_mbim_device_command_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FuMmMbimDeviceHelper *helper = (FuMmMbimDeviceHelper *)user_data; + g_autoptr(MbimMessage) response = NULL; + + response = mbim_device_command_finish(helper->mbim_device, res, &helper->error); + if (response != NULL) { + if (mbim_message_response_get_result(response, + MBIM_MESSAGE_TYPE_COMMAND_DONE, + &helper->error)) { + helper->mbim_message = g_steal_pointer(&response); + } + } + g_main_loop_quit(helper->loop); +} + +MbimMessage * +fu_mm_mbim_device_command_sync(FuMmMbimDevice *self, + MbimMessage *mbim_message, + guint timeout_ms, + GError **error) +{ + FuMmMbimDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuMmMbimDeviceHelper) helper = + fu_mm_mbim_device_helper_helper_new(priv->main_ctx, timeout_ms); + FuDeviceEvent *event = NULL; + g_autofree gchar *event_id = NULL; + + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* need event ID */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || + fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)), + FU_CONTEXT_FLAG_SAVE_EVENTS)) { + const guint8 *buf; + guint32 bufsz = 0; + g_autofree gchar *data_base64 = NULL; + + buf = mbim_message_get_raw(mbim_message, &bufsz, error); + if (buf == NULL) { + fu_mm_mbim_device_error_convert(error); + return NULL; + } + data_base64 = g_base64_encode(buf, bufsz); + event_id = g_strdup_printf("MbimDeviceCommand:Data=%s,Length=0x%x", + data_base64, + (guint)bufsz); + } + + /* emulated */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) { + g_autoptr(GBytes) blob = NULL; + event = fu_device_load_event(FU_DEVICE(self), event_id, error); + if (event == NULL) + return NULL; + if (!fu_device_event_check_error(event, error)) + return NULL; + blob = fu_device_event_get_bytes(event, "Data", error); + if (blob == NULL) + return NULL; + return mbim_message_new(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob)); + } + + /* save */ + if (event_id != NULL) + event = fu_device_save_event(FU_DEVICE(self), event_id); + + mbim_device_command(priv->mbim_device, + mbim_message, + 2 * timeout_ms / 1000, + helper->cancellable, + fu_mm_mbim_device_command_cb, + helper); + g_main_loop_run(helper->loop); + + /* save response */ + if (helper->mbim_message == NULL) { + fu_mm_mbim_device_error_convert(&helper->error); + if (event != NULL) + fu_device_event_set_error(event, helper->error); + g_propagate_error(error, g_steal_pointer(&helper->error)); + return NULL; + } + + /* save response */ + if (event != NULL) { + const guint8 *buf; + guint32 bufsz = 0; + + buf = mbim_message_get_raw(helper->mbim_message, &bufsz, error); + if (buf == NULL) { + fu_mm_mbim_device_error_convert(error); + return NULL; + } + fu_device_event_set_data(event, "Data", buf, bufsz); + } + + /* success */ + return g_steal_pointer(&helper->mbim_message); +} + +static gboolean +fu_mm_mbim_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuMmMbimDevice *self = FU_MM_MBIM_DEVICE(device); + FuMmMbimDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GError) error_local = NULL; + g_autoptr(MbimMessage) request = NULL; + g_autoptr(MbimMessage) response = NULL; + + request = mbim_message_qdu_quectel_reboot_set_new(MBIM_QDU_QUECTEL_REBOOT_TYPE_EDL, NULL); + response = fu_mm_mbim_device_command_sync(self, request, 5 * 1000, &error_local); + if (response == NULL) { + /* MBIM port goes away */ + if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND) && + !g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL)) { + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + g_clear_object(&priv->mbim_device); + g_debug("ignoring, and clearing MbimDevice: %s", error_local->message); + } + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_mm_mbim_device_probe(FuDevice *device, GError **error) +{ + FuMmMbimDevice *self = FU_MM_MBIM_DEVICE(device); + fu_device_add_protocol(device, "com.qualcomm.firehose"); + fu_device_add_instance_id_full(device, + "USB\\VID_05C6&PID_9008", + FU_DEVICE_INSTANCE_FLAG_COUNTERPART); + return fu_mm_device_set_device_file(FU_MM_DEVICE(self), MM_MODEM_PORT_TYPE_MBIM, error); +} + +static gboolean +fu_mm_mbim_device_open_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuMmMbimDevice *self = FU_MM_MBIM_DEVICE(device); + return fu_mm_mbim_device_open_sync(self, FU_MM_MBIM_DEVICE_TIMEOUT_MS, error); +} + +static gboolean +fu_mm_mbim_device_open(FuDevice *device, GError **error) +{ + FuMmMbimDevice *self = FU_MM_MBIM_DEVICE(device); + FuMmMbimDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(GFile) mbim_device_file = + g_file_new_for_path(fu_udev_device_get_device_file(FU_UDEV_DEVICE(self))); + + /* create and open */ + priv->mbim_device = + fu_mm_mbim_device_new_sync(self, mbim_device_file, FU_MM_MBIM_DEVICE_TIMEOUT_MS, error); + if (priv->mbim_device == NULL) + return FALSE; + return fu_device_retry(device, + fu_mm_mbim_device_open_cb, + FU_MM_MBIM_DEVICE_MAX_OPEN_ATTEMPTS, + NULL, + error); +} + +static gboolean +fu_mm_mbim_device_close(FuDevice *device, GError **error) +{ + FuMmMbimDevice *self = FU_MM_MBIM_DEVICE(device); + FuMmMbimDevicePrivate *priv = GET_PRIVATE(self); + + /* sanity check */ + if (priv->mbim_device == NULL) + return TRUE; + return fu_mm_mbim_device_close_sync(self, FU_MM_MBIM_DEVICE_TIMEOUT_MS, error); +} + +static gboolean +fu_mm_mbim_device_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmMbimDevice *self = FU_MM_MBIM_DEVICE(device); + return fu_mm_device_set_autosuspend_delay(FU_MM_DEVICE(self), 20000, error); +} + +static gboolean +fu_mm_mbim_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmMbimDevice *self = FU_MM_MBIM_DEVICE(device); + return fu_mm_device_set_autosuspend_delay(FU_MM_DEVICE(self), 2000, error); +} + +static void +fu_mm_mbim_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 3, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 58, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 38, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_mm_mbim_device_init(FuMmMbimDevice *self) +{ + FuMmMbimDevicePrivate *priv = GET_PRIVATE(self); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + /* we must only use one context per device because MBIM messages are only delivered to the + * main context which the device is opened with */ + priv->main_ctx = g_main_context_new(); +} + +static void +fu_mm_mbim_device_finalize(GObject *object) +{ + FuMmMbimDevice *self = FU_MM_MBIM_DEVICE(object); + FuMmMbimDevicePrivate *priv = GET_PRIVATE(self); + if (priv->mbim_device != NULL) + g_object_unref(priv->mbim_device); + g_main_context_unref(priv->main_ctx); + G_OBJECT_CLASS(fu_mm_mbim_device_parent_class)->finalize(object); +} + +static void +fu_mm_mbim_device_class_init(FuMmMbimDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_mm_mbim_device_finalize; + device_class->open = fu_mm_mbim_device_open; + device_class->close = fu_mm_mbim_device_close; + device_class->probe = fu_mm_mbim_device_probe; + device_class->detach = fu_mm_mbim_device_detach; + device_class->prepare = fu_mm_mbim_device_prepare; + device_class->cleanup = fu_mm_mbim_device_cleanup; + device_class->set_progress = fu_mm_mbim_device_set_progress; +} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-mbim-device.h fwupd-2.0.20/plugins/modem-manager/fu-mm-mbim-device.h --- fwupd-2.0.8/plugins/modem-manager/fu-mm-mbim-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-mbim-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,27 @@ +/* + * Copyright 2021 Jarvis Jiang + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#include "fu-mm-device.h" + +#define FU_TYPE_MM_MBIM_DEVICE (fu_mm_mbim_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuMmMbimDevice, fu_mm_mbim_device, FU, MM_MBIM_DEVICE, FuMmDevice) + +struct _FuMmMbimDeviceClass { + FuMmDeviceClass parent_class; +}; + +gboolean +fu_mm_mbim_device_error_convert(GError **error); +MbimMessage * +fu_mm_mbim_device_command_sync(FuMmMbimDevice *self, + MbimMessage *mbim_message, + guint timeout_ms, + GError **error); diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-mhi-qcdm-device.c fwupd-2.0.20/plugins/modem-manager/fu-mm-mhi-qcdm-device.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-mhi-qcdm-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-mhi-qcdm-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,209 @@ +/* + * Copyright 2020 Aleksander Morgado + * Copyright 2021 Ivan Mikhanchuk + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-mm-mhi-qcdm-device.h" + +struct _FuMmMhiQcdmDevice { + FuMmQcdmDevice parent_instance; + FuKernelSearchPathLocker *search_path_locker; + GBytes *firehose_prog; + gchar *firehose_prog_file; +}; + +G_DEFINE_TYPE(FuMmMhiQcdmDevice, fu_mm_mhi_qcdm_device, FU_TYPE_MM_QCDM_DEVICE) + +static gboolean +fu_mm_mhi_qcdm_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuMmMhiQcdmDevice *self = FU_MM_MHI_QCDM_DEVICE(device); + const gchar *path; + g_autofree gchar *fn = NULL; + + /* sanity check */ + if (self->search_path_locker == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "search path locker not set"); + return FALSE; + } + if (self->firehose_prog_file == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "Firehose prog filename is not set for the device"); + return FALSE; + } + + /* copy the firehose prog into the search path locker */ + path = fu_kernel_search_path_locker_get_path(self->search_path_locker); + fn = g_build_filename(path, "qcom", self->firehose_prog_file, NULL); + if (!fu_path_mkdir_parent(fn, error)) + return FALSE; + if (!fu_bytes_set_contents(fn, self->firehose_prog, error)) + return FALSE; + + /* trigger emergency download mode; this takes us to the EDL execution environment */ + return FU_DEVICE_CLASS(fu_mm_mhi_qcdm_device_parent_class)->detach(device, progress, error); +} + +static FuKernelSearchPathLocker * +fu_mm_mhi_qcdm_device_search_path_locker_new(FuMmMhiQcdmDevice *self, GError **error) +{ + g_autofree gchar *mm_fw_dir = NULL; + g_autoptr(FuKernelSearchPathLocker) locker = NULL; + + /* create a directory to store firmware files for modem-manager plugin */ + mm_fw_dir = fu_path_build(FU_PATH_KIND_CACHEDIR_PKG, "modem-manager", "firmware", NULL); + if (g_mkdir_with_parents(mm_fw_dir, 0700) == -1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to create '%s': %s", + mm_fw_dir, + fwupd_strerror(errno)); + return NULL; + } + locker = fu_kernel_search_path_locker_new(mm_fw_dir, error); + if (locker == NULL) + return NULL; + return g_steal_pointer(&locker); +} + +static gboolean +fu_mm_mhi_qcdm_device_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmMhiQcdmDevice *self = FU_MM_MHI_QCDM_DEVICE(device); + + /* in the case of MHI PCI modems, the mhi-pci-generic driver reads the firehose binary + * from the firmware-loader and writes it to the modem */ + self->search_path_locker = fu_mm_mhi_qcdm_device_search_path_locker_new(self, error); + if (self->search_path_locker == NULL) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_mhi_qcdm_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmMhiQcdmDevice *self = FU_MM_MHI_QCDM_DEVICE(device); + + /* restore the firmware search path */ + g_clear_object(&self->search_path_locker); + + /* no longer required */ + if (self->firehose_prog != NULL) { + g_bytes_unref(self->firehose_prog); + self->firehose_prog = NULL; + } + + /* success */ + return TRUE; +} + +static FuFirmware * +fu_mm_mhi_qcdm_device_prepare_firmware(FuDevice *device, + GInputStream *stream, + FuProgress *progress, + FuFirmwareParseFlags flags, + GError **error) +{ + FuMmMhiQcdmDevice *self = FU_MM_MHI_QCDM_DEVICE(device); + g_autoptr(FuFirmware) firmware = fu_archive_firmware_new(); + + /* parse as archive */ + if (!fu_firmware_parse_stream(firmware, stream, 0x0, flags, error)) + return NULL; + + /* firehose modems that use mhi_pci drivers require firehose binary + * to be present in the firmware-loader search path. */ + self->firehose_prog = + fu_firmware_get_image_by_id_bytes(firmware, + "firehose-prog.mbn|prog_nand*.mbn|prog_firehose*", + error); + if (self->firehose_prog == NULL) + return NULL; + + /* success */ + return g_steal_pointer(&firmware); +} + +static gboolean +fu_mm_mhi_qcdm_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuMmMhiQcdmDevice *self = FU_MM_MHI_QCDM_DEVICE(device); + + if (g_strcmp0(key, "ModemManagerFirehoseProgFile") == 0) { + self->firehose_prog_file = g_strdup(value); + return TRUE; + } + + /* failed */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_mm_mhi_qcdm_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_mm_mhi_qcdm_device_init(FuMmMhiQcdmDevice *self) +{ + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); + fu_device_add_protocol(FU_UDEV_DEVICE(self), "com.qualcomm.firehose"); +} + +static void +fu_mm_mhi_qcdm_device_finalize(GObject *object) +{ + FuMmMhiQcdmDevice *self = FU_MM_MHI_QCDM_DEVICE(object); + g_free(self->firehose_prog_file); + if (self->firehose_prog != NULL) + g_bytes_unref(self->firehose_prog); + G_OBJECT_CLASS(fu_mm_mhi_qcdm_device_parent_class)->finalize(object); +} + +static void +fu_mm_mhi_qcdm_device_class_init(FuMmMhiQcdmDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_mm_mhi_qcdm_device_finalize; + device_class->detach = fu_mm_mhi_qcdm_device_detach; + device_class->prepare = fu_mm_mhi_qcdm_device_prepare; + device_class->cleanup = fu_mm_mhi_qcdm_device_cleanup; + device_class->prepare_firmware = fu_mm_mhi_qcdm_device_prepare_firmware; + device_class->set_quirk_kv = fu_mm_mhi_qcdm_device_set_quirk_kv; + device_class->set_progress = fu_mm_mhi_qcdm_device_set_progress; +} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-mhi-qcdm-device.h fwupd-2.0.20/plugins/modem-manager/fu-mm-mhi-qcdm-device.h --- fwupd-2.0.8/plugins/modem-manager/fu-mm-mhi-qcdm-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-mhi-qcdm-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-mm-qcdm-device.h" + +#define FU_TYPE_MM_MHI_QCDM_DEVICE (fu_mm_mhi_qcdm_device_get_type()) +G_DECLARE_FINAL_TYPE(FuMmMhiQcdmDevice, + fu_mm_mhi_qcdm_device, + FU, + MM_MHI_QCDM_DEVICE, + FuMmQcdmDevice) diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-plugin.c fwupd-2.0.20/plugins/modem-manager/fu-mm-plugin.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,3 @@ - /* * Copyright 2018 Richard Hughes * @@ -7,503 +6,44 @@ #include "config.h" -#include -#include - +#include "fu-mm-backend.h" #include "fu-mm-device.h" +#include "fu-mm-dfota-device.h" +#include "fu-mm-fastboot-device.h" +#include "fu-mm-fdl-device.h" +#include "fu-mm-firehose-device.h" +#include "fu-mm-mbim-device.h" +#include "fu-mm-mhi-qcdm-device.h" +#include "fu-mm-qcdm-device.h" +#include "fu-mm-qdu-mbim-device.h" +#include "fu-mm-qmi-device.h" -/* amount of time to wait for ports of the same device being exposed by kernel */ -#define FU_MM_UDEV_DEVICE_PORTS_TIMEOUT 3 /* s */ - -/* out-of-tree modem-power driver is unsupported */ -#define MODEM_POWER_SYSFS_PATH "/sys/class/modem-power" - -struct FuPluginData { - MMManager *manager; - gboolean manager_ready; - GFileMonitor *modem_power_monitor; - guint udev_timeout_id; - - /* when a device is inhibited from MM, we store all relevant details - * ourselves to recreate a functional device object even without MM - */ - FuMmDevice *shadow_device; - - /* - * Used to mark whether FU_MM_DEVICE_FLAG_UNINHIBIT_MM_AFTER_FASTBOOT_REBOOT is being used - */ - gboolean device_ready_uninhibit_manager; -}; - -typedef FuPluginData FuModemManagerPlugin; #define FU_MODEM_MANAGER_PLUGIN(o) fu_plugin_get_data(FU_PLUGIN(o)) static void -fu_mm_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - fwupd_codec_string_append_bool(str, idt, "ManagerReady", self->manager_ready); - if (self->shadow_device != NULL) { - fwupd_codec_string_append(str, - idt, - "ShadowDevice", - fu_device_get_id(self->shadow_device)); - } -} - -static void fu_mm_plugin_load(FuContext *ctx) { fu_context_add_quirk_key(ctx, "ModemManagerBranchAtCommand"); } -static void -fu_mm_plugin_udev_device_removed(FuPlugin *plugin) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - FuMmDevice *dev; - - if (self->shadow_device == NULL) - return; - - dev = fu_plugin_cache_lookup(plugin, - fu_device_get_physical_id(FU_DEVICE(self->shadow_device))); - if (dev == NULL) - return; - - /* once the first port is gone, consider device is gone */ - fu_plugin_cache_remove(plugin, fu_device_get_physical_id(FU_DEVICE(self->shadow_device))); - fu_plugin_device_remove(plugin, FU_DEVICE(dev)); - - /* no need to wait for more ports, cancel that right away */ - if (self->udev_timeout_id != 0) { - g_source_remove(self->udev_timeout_id); - self->udev_timeout_id = 0; - } -} - -static void -fu_mm_plugin_uninhibit_device(FuPlugin *plugin) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - g_autoptr(FuMmDevice) shadow_device = NULL; - - /* get the device removed from the plugin cache before uninhibiting */ - fu_mm_plugin_udev_device_removed(plugin); - - shadow_device = g_steal_pointer(&self->shadow_device); - if (self->manager != NULL && shadow_device != NULL) { - const gchar *inhibition_uid = fu_mm_device_get_inhibition_uid(shadow_device); - g_debug("uninhibit modemmanager device with uid %s", inhibition_uid); - mm_manager_uninhibit_device_sync(self->manager, inhibition_uid, NULL, NULL); - } -} - -static gboolean -fu_mm_plugin_udev_device_ports_timeout(gpointer user_data) -{ - FuPlugin *plugin = user_data; - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - FuMmDevice *dev; - g_autoptr(GError) error = NULL; - - g_return_val_if_fail(self->shadow_device != NULL, G_SOURCE_REMOVE); - self->udev_timeout_id = 0; - - dev = fu_plugin_cache_lookup(plugin, - fu_device_get_physical_id(FU_DEVICE(self->shadow_device))); - if (dev != NULL) { - if (!fu_device_probe(FU_DEVICE(dev), &error)) { - g_debug("failed to probe MM device: %s", error->message); - } else { - fu_plugin_device_add(plugin, FU_DEVICE(dev)); - } - } - - return G_SOURCE_REMOVE; -} - -static void -fu_mm_plugin_udev_device_ports_timeout_reset(FuPlugin *plugin) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - - g_return_if_fail(self->shadow_device != NULL); - if (self->udev_timeout_id != 0) - g_source_remove(self->udev_timeout_id); - self->udev_timeout_id = g_timeout_add_seconds(FU_MM_UDEV_DEVICE_PORTS_TIMEOUT, - fu_mm_plugin_udev_device_ports_timeout, - plugin); -} - -static gboolean -fu_mm_plugin_inhibit_device(FuPlugin *plugin, FuDevice *device, GError **error) -{ - const gchar *inhibition_uid; - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - g_autoptr(FuMmDevice) shadow_device = NULL; - - fu_mm_plugin_uninhibit_device(plugin); - - shadow_device = fu_mm_device_shadow_new(FU_MM_DEVICE(device)); - inhibition_uid = fu_mm_device_get_inhibition_uid(shadow_device); - g_debug("inhibit modemmanager device with uid %s", inhibition_uid); - if (!mm_manager_inhibit_device_sync(self->manager, inhibition_uid, NULL, error)) - return FALSE; - - /* setup shadow_device device info */ - self->shadow_device = g_steal_pointer(&shadow_device); - - /* uninhibit when device re creation is detected */ - if (fu_device_has_private_flag(device, - FU_MM_DEVICE_FLAG_UNINHIBIT_MM_AFTER_FASTBOOT_REBOOT)) { - self->device_ready_uninhibit_manager = TRUE; - } else { - self->device_ready_uninhibit_manager = FALSE; - } - - return TRUE; -} - -static void -fu_mm_plugin_ensure_modem_power_inhibit(FuPlugin *plugin, FuDevice *device) -{ - if (g_file_test(MODEM_POWER_SYSFS_PATH, G_FILE_TEST_EXISTS)) { - fu_device_inhibit(device, - "modem-power", - "The modem-power kernel driver cannot be used"); - } else { - fu_device_uninhibit(device, "modem-power"); - } -} - -static void -fu_mm_plugin_device_add(FuPlugin *plugin, MMObject *modem) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - const gchar *object_path = mm_object_get_path(modem); - g_autoptr(FuMmDevice) dev = NULL; - g_autoptr(GError) error = NULL; - - g_debug("added modem: %s", object_path); - - if (fu_plugin_cache_lookup(plugin, object_path) != NULL) { - g_warning("MM device already added, ignoring"); - return; - } - dev = fu_mm_device_new(fu_plugin_get_context(plugin), self->manager, modem); - if (!fu_device_setup(FU_DEVICE(dev), &error)) { - g_debug("failed to probe MM device: %s", error->message); - return; - } - fu_mm_plugin_ensure_modem_power_inhibit(plugin, FU_DEVICE(dev)); - fu_plugin_device_add(plugin, FU_DEVICE(dev)); - fu_plugin_cache_add(plugin, object_path, dev); - fu_plugin_cache_add(plugin, fu_device_get_physical_id(FU_DEVICE(dev)), dev); -} - -static void -fu_mm_plugin_device_added_cb(MMManager *manager, MMObject *modem, FuPlugin *plugin) -{ - fu_mm_plugin_device_add(plugin, modem); -} - -static void -fu_mm_plugin_device_removed_cb(MMManager *manager, MMObject *modem, FuPlugin *plugin) -{ - const gchar *object_path = mm_object_get_path(modem); - FuMmDevice *dev = fu_plugin_cache_lookup(plugin, object_path); - MMModemFirmwareUpdateMethod update_methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_NONE; - - if (dev == NULL) - return; - g_debug("removed modem: %s", mm_object_get_path(modem)); - -#if MM_CHECK_VERSION(1, 19, 1) - /* No information will be displayed during the upgrade process if the - * device is removed, the main reason is that device is "removed" from - * ModemManager, but it still exists in the system */ - update_methods = - MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU | MM_MODEM_FIRMWARE_UPDATE_METHOD_SAHARA; -#else - update_methods = MM_MODEM_FIRMWARE_UPDATE_METHOD_MBIM_QDU; -#endif - - if (!(fu_mm_device_get_update_methods(FU_MM_DEVICE(dev)) & update_methods)) { - fu_plugin_cache_remove(plugin, object_path); - fu_plugin_device_remove(plugin, FU_DEVICE(dev)); - } -} - -static void -fu_mm_plugin_teardown_manager(FuPlugin *plugin) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - - if (self->manager_ready) { - g_debug("ModemManager no longer available"); - g_signal_handlers_disconnect_by_func(self->manager, - G_CALLBACK(fu_mm_plugin_device_added_cb), - plugin); - g_signal_handlers_disconnect_by_func(self->manager, - G_CALLBACK(fu_mm_plugin_device_removed_cb), - plugin); - self->manager_ready = FALSE; - } -} - -static void -fu_mm_plugin_setup_manager(FuPlugin *plugin) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - const gchar *version = mm_manager_get_version(self->manager); - GList *list; - - if (fu_version_compare(version, MM_REQUIRED_VERSION, FWUPD_VERSION_FORMAT_TRIPLET) < 0) { - g_warning("ModemManager %s is available, but need at least %s", - version, - MM_REQUIRED_VERSION); - return; - } - - g_info("ModemManager %s is available", version); - - g_signal_connect(G_DBUS_OBJECT_MANAGER(self->manager), - "object-added", - G_CALLBACK(fu_mm_plugin_device_added_cb), - plugin); - g_signal_connect(G_DBUS_OBJECT_MANAGER(self->manager), - "object-removed", - G_CALLBACK(fu_mm_plugin_device_removed_cb), - plugin); - - list = g_dbus_object_manager_get_objects(G_DBUS_OBJECT_MANAGER(self->manager)); - for (GList *l = list; l != NULL; l = g_list_next(l)) { - MMObject *modem = MM_OBJECT(l->data); - fu_mm_plugin_device_add(plugin, modem); - g_object_unref(modem); - } - g_list_free(list); - - self->manager_ready = TRUE; -} - -static void -fu_mm_plugin_name_owner_updated(FuPlugin *plugin) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - g_autofree gchar *name_owner = g_dbus_object_manager_client_get_name_owner( - G_DBUS_OBJECT_MANAGER_CLIENT(self->manager)); - if (name_owner != NULL) - fu_mm_plugin_setup_manager(plugin); - else - fu_mm_plugin_teardown_manager(plugin); -} - -static gboolean -fu_mm_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - g_signal_connect_swapped(MM_MANAGER(self->manager), - "notify::name-owner", - G_CALLBACK(fu_mm_plugin_name_owner_updated), - plugin); - fu_mm_plugin_name_owner_updated(plugin); - return TRUE; -} - -static void -fu_mm_plugin_modem_power_changed_cb(GFileMonitor *monitor, - GFile *file, - GFile *other_file, - GFileMonitorEvent event_type, - gpointer user_data) -{ - FuPlugin *plugin = FU_PLUGIN(user_data); - GPtrArray *devices = fu_plugin_get_devices(plugin); - for (guint i = 0; i < devices->len; i++) { - FuDevice *device = g_ptr_array_index(devices, i); - fu_mm_plugin_ensure_modem_power_inhibit(plugin, device); - } -} - -static gboolean -fu_mm_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - g_autoptr(GDBusConnection) connection = NULL; - g_autoptr(GFile) file = g_file_new_for_path(MODEM_POWER_SYSFS_PATH); - - connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); - if (connection == NULL) - return FALSE; - self->manager = mm_manager_new_sync(connection, - G_DBUS_OBJECT_MANAGER_CLIENT_FLAGS_DO_NOT_AUTO_START, - NULL, - error); - if (self->manager == NULL) - return FALSE; - - /* detect presence of unsupported modem-power driver */ - self->modem_power_monitor = g_file_monitor(file, G_FILE_MONITOR_NONE, NULL, error); - if (self->modem_power_monitor == NULL) - return FALSE; - g_signal_connect(self->modem_power_monitor, - "changed", - G_CALLBACK(fu_mm_plugin_modem_power_changed_cb), - plugin); - - return TRUE; -} - -static gboolean -fu_mm_plugin_detach(FuPlugin *plugin, FuDevice *device, FuProgress *progress, GError **error) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - g_autoptr(FuDeviceLocker) locker = NULL; - - /* open device */ - locker = fu_device_locker_new(device, error); - if (locker == NULL) - return FALSE; - - /* inhibit device and track it inside the plugin, not bound to the - * lifetime of the FuMmDevice, because that object will only exist for - * as long as the ModemManager device exists, and inhibiting will - * implicitly remove the device from ModemManager. */ - if (self->shadow_device == NULL) { - if (!fu_mm_plugin_inhibit_device(plugin, device, error)) - return FALSE; - } - - /* reset */ - if (!fu_device_detach_full(device, progress, error)) { - fu_mm_plugin_uninhibit_device(plugin); - return FALSE; - } - - /* note: wait for replug set by device if it really needs it */ - return TRUE; -} - -static void -fu_mm_plugin_device_attach_finished(gpointer user_data) -{ - FuPlugin *plugin = FU_PLUGIN(user_data); - fu_mm_plugin_uninhibit_device(plugin); -} - -static gboolean -fu_mm_plugin_attach(FuPlugin *plugin, FuDevice *device, FuProgress *progress, GError **error) -{ - g_autoptr(FuDeviceLocker) locker = NULL; - - /* open device */ - locker = fu_device_locker_new(device, error); - if (locker == NULL) - return FALSE; - - /* schedule device attach asynchronously, which is extremely important - * so that engine can setup the device "waiting" logic before the actual - * attach procedure happens (which will reset the module if it worked - * properly) */ - if (!fu_device_attach_full(device, progress, error)) - return FALSE; - - /* this signal will always be emitted asynchronously */ - g_signal_connect_swapped(FU_DEVICE(device), - "attach-finished", - G_CALLBACK(fu_mm_plugin_device_attach_finished), - plugin); - - return TRUE; -} - -static void -fu_mm_plugin_shadow_device_added(FuPlugin *plugin, FuDevice *device) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - FuMmDevice *existing; - const gchar *subsystem = fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)); - const gchar *device_file = fu_udev_device_get_device_file(FU_UDEV_DEVICE(device)); - g_autoptr(FuMmDevice) dev = NULL; - - /* device re creation, uninhibit manager */ - if (self->device_ready_uninhibit_manager) { - self->device_ready_uninhibit_manager = FALSE; - fu_mm_plugin_uninhibit_device(plugin); - } - - existing = - fu_plugin_cache_lookup(plugin, - fu_device_get_physical_id(FU_DEVICE(self->shadow_device))); - if (existing != NULL) { - /* add port to existing device */ - fu_mm_device_udev_add_port(existing, subsystem, device_file); - fu_mm_plugin_udev_device_ports_timeout_reset(plugin); - return; - } - - /* create device and add to cache */ - dev = fu_mm_device_udev_new(fu_plugin_get_context(plugin), - self->manager, - self->shadow_device); - fu_mm_device_udev_add_port(dev, subsystem, device_file); - fu_plugin_cache_add(plugin, - fu_device_get_physical_id(FU_DEVICE(self->shadow_device)), - device); - - /* wait a bit before probing, in case more ports get added */ - fu_mm_plugin_udev_device_ports_timeout_reset(plugin); -} - -static gboolean -fu_mm_plugin_backend_device_removed(FuPlugin *plugin, FuDevice *device, GError **error) -{ - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - - if (self->shadow_device != NULL && - g_strcmp0(fu_device_get_physical_id(device), - fu_device_get_physical_id(FU_DEVICE(self->shadow_device))) != 0) { - fu_mm_plugin_udev_device_removed(plugin); - } - - /* success */ - return TRUE; -} - static gboolean fu_mm_plugin_backend_device_added(FuPlugin *plugin, FuDevice *device, FuProgress *progress, GError **error) { - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - FuDevice *device_tmp; + g_autoptr(FuDeviceLocker) locker = NULL; - /* interesting device? */ - if (!FU_IS_UDEV_DEVICE(device)) - return TRUE; - - /* ignore all events for ports not owned by our device */ - if (self->shadow_device != NULL && - g_strcmp0(fu_device_get_physical_id(device), - fu_device_get_physical_id(FU_DEVICE(self->shadow_device))) != 0) { - fu_mm_plugin_shadow_device_added(plugin, device); + /* ignore anything from other backends, e.g. usb */ + if (!FU_IS_MM_DEVICE(device)) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported"); + return FALSE; } - /* set the latest udev device for the FuMmDevice that just appeared */ - device_tmp = - fu_plugin_cache_lookup(plugin, fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device))); - if (device_tmp == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "%s not added by ModemManager", - fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device))); + locker = fu_device_locker_new(device, error); + if (locker == NULL) return FALSE; - } - fu_mm_device_set_udev_device(FU_MM_DEVICE(device_tmp), FU_UDEV_DEVICE(device)); + fu_plugin_device_add(plugin, device); /* success */ return TRUE; @@ -513,29 +53,20 @@ fu_mm_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); - (void)fu_plugin_alloc_data(plugin, sizeof(FuModemManagerPlugin)); - fu_plugin_add_udev_subsystem(plugin, "tty"); - fu_plugin_add_udev_subsystem(plugin, "usbmisc"); - fu_plugin_add_udev_subsystem(plugin, "wwan"); - fu_plugin_add_device_gtype(plugin, FU_TYPE_MM_DEVICE); /* coverage */ -} - -static void -fu_mm_plugin_finalize(GObject *obj) -{ - FuPlugin *plugin = FU_PLUGIN(obj); - FuModemManagerPlugin *self = FU_MODEM_MANAGER_PLUGIN(plugin); - - fu_mm_plugin_uninhibit_device(plugin); - - if (self->udev_timeout_id) - g_source_remove(self->udev_timeout_id); - if (self->manager != NULL) - g_object_unref(self->manager); - if (self->modem_power_monitor != NULL) - g_object_unref(self->modem_power_monitor); + FuContext *ctx = fu_plugin_get_context(plugin); + g_autoptr(FuBackend) backend = fu_mm_backend_new(ctx); - /* G_OBJECT_CLASS(fu_mm_plugin_parent_class)->finalize() not required as modular */ + fu_context_add_backend(ctx, backend); + fu_plugin_add_device_gtype(plugin, FU_TYPE_MM_DEVICE); /* coverage */ + fu_plugin_add_device_gtype(plugin, FU_TYPE_MM_DFOTA_DEVICE); /* coverage */ + fu_plugin_add_device_gtype(plugin, FU_TYPE_MM_FASTBOOT_DEVICE); /* coverage */ + fu_plugin_add_device_gtype(plugin, FU_TYPE_MM_FDL_DEVICE); /* coverage */ + fu_plugin_add_device_gtype(plugin, FU_TYPE_MM_FIREHOSE_DEVICE); /* coverage */ + fu_plugin_add_device_gtype(plugin, FU_TYPE_MM_MBIM_DEVICE); /* coverage */ + fu_plugin_add_device_gtype(plugin, FU_TYPE_MM_MHI_QCDM_DEVICE); /* coverage */ + fu_plugin_add_device_gtype(plugin, FU_TYPE_MM_QCDM_DEVICE); /* coverage */ + fu_plugin_add_device_gtype(plugin, FU_TYPE_MM_QDU_MBIM_DEVICE); /* coverage */ + fu_plugin_add_device_gtype(plugin, FU_TYPE_MM_QMI_DEVICE); /* coverage */ } void @@ -543,12 +74,5 @@ { vfuncs->load = fu_mm_plugin_load; vfuncs->constructed = fu_mm_plugin_constructed; - vfuncs->finalize = fu_mm_plugin_finalize; - vfuncs->to_string = fu_mm_plugin_to_string; - vfuncs->startup = fu_mm_plugin_startup; - vfuncs->coldplug = fu_mm_plugin_coldplug; - vfuncs->attach = fu_mm_plugin_attach; - vfuncs->detach = fu_mm_plugin_detach; vfuncs->backend_device_added = fu_mm_plugin_backend_device_added; - vfuncs->backend_device_removed = fu_mm_plugin_backend_device_removed; } diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-qcdm-device.c fwupd-2.0.20/plugins/modem-manager/fu-mm-qcdm-device.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-qcdm-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-qcdm-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,134 @@ +/* + * Copyright 2020 Aleksander Morgado + * Copyright 2021 Ivan Mikhanchuk + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-mm-qcdm-device.h" + +G_DEFINE_TYPE(FuMmQcdmDevice, fu_mm_qcdm_device, FU_TYPE_MM_DEVICE) + +static gboolean +fu_mm_qcdm_device_cmd(FuMmQcdmDevice *self, const guint8 *buf, gsize bufsz, GError **error) +{ + g_autoptr(GBytes) qcdm_req = NULL; + g_autoptr(GBytes) qcdm_res = NULL; + + /* command */ + qcdm_req = g_bytes_new(buf, bufsz); + fu_dump_bytes(G_LOG_DOMAIN, "writing", qcdm_req); + if (!fu_udev_device_write_bytes(FU_UDEV_DEVICE(self), + qcdm_req, + 1500, + FU_IO_CHANNEL_FLAG_FLUSH_INPUT, + error)) { + g_prefix_error_literal(error, "failed to write qcdm command: "); + return FALSE; + } + + /* response */ + qcdm_res = fu_udev_device_read_bytes(FU_UDEV_DEVICE(self), + bufsz, + 1500, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error); + if (qcdm_res == NULL) { + g_prefix_error_literal(error, "failed to read qcdm response: "); + return FALSE; + } + fu_dump_bytes(G_LOG_DOMAIN, "read", qcdm_res); + + /* command == response */ + if (g_bytes_compare(qcdm_res, qcdm_req) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to read valid qcdm response"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_qcdm_device_switch_to_edl_cb(FuDevice *device, gpointer userdata, GError **error) +{ + FuMmQcdmDevice *self = FU_MM_QCDM_DEVICE(device); + const guint8 buf[] = {0x4B, 0x65, 0x01, 0x00, 0x54, 0x0F, 0x7E}; + + /* when the QCDM port does not exist anymore, we are detached */ + if (!g_file_test(fu_udev_device_get_device_file(FU_UDEV_DEVICE(self)), G_FILE_TEST_EXISTS)) + return TRUE; + return fu_mm_qcdm_device_cmd(self, buf, sizeof(buf), error); +} + +static gboolean +fu_mm_qcdm_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuMmQcdmDevice *self = FU_MM_QCDM_DEVICE(device); + + /* sanity check */ + if (fu_udev_device_get_device_file(FU_UDEV_DEVICE(self)) == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no device file"); + return FALSE; + } + + /* retry up to 30 times until the QCDM port goes away */ + if (!fu_device_retry_full(device, + fu_mm_qcdm_device_switch_to_edl_cb, + 30, + 1000, + NULL, + error)) + return FALSE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + return TRUE; +} + +static gboolean +fu_mm_qcdm_device_probe(FuDevice *device, GError **error) +{ + FuMmQcdmDevice *self = FU_MM_QCDM_DEVICE(device); + return fu_mm_device_set_device_file(FU_MM_DEVICE(self), MM_MODEM_PORT_TYPE_QCDM, error); +} + +static void +fu_mm_qcdm_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_mm_qcdm_device_init(FuMmQcdmDevice *self) +{ + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); + fu_device_add_instance_id_full(FU_DEVICE(self), + "USB\\VID_05C6&PID_9008", + FU_DEVICE_INSTANCE_FLAG_COUNTERPART); + fu_device_set_remove_delay(FU_DEVICE(self), 5000); + fu_device_add_protocol(FU_UDEV_DEVICE(self), "com.qualcomm.firehose"); +} + +static void +fu_mm_qcdm_device_class_init(FuMmQcdmDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->probe = fu_mm_qcdm_device_probe; + device_class->detach = fu_mm_qcdm_device_detach; + device_class->set_progress = fu_mm_qcdm_device_set_progress; +} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-qcdm-device.h fwupd-2.0.20/plugins/modem-manager/fu-mm-qcdm-device.h --- fwupd-2.0.8/plugins/modem-manager/fu-mm-qcdm-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-qcdm-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +/* + * Copyright 2020 Aleksander Morgado + * Copyright 2021 Ivan Mikhanchuk + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-mm-device.h" + +#define FU_TYPE_MM_QCDM_DEVICE (fu_mm_qcdm_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuMmQcdmDevice, fu_mm_qcdm_device, FU, MM_QCDM_DEVICE, FuMmDevice) + +struct _FuMmQcdmDeviceClass { + FuMmDeviceClass parent_class; +}; diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-qdu-mbim-device.c fwupd-2.0.20/plugins/modem-manager/fu-mm-qdu-mbim-device.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-qdu-mbim-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-qdu-mbim-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,322 @@ +/* + * Copyright 2021 Jarvis Jiang + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-mm-qdu-mbim-device.h" + +struct _FuMmQduMbimDevice { + FuMmMbimDevice parent_instance; +}; + +G_DEFINE_TYPE(FuMmQduMbimDevice, fu_mm_qdu_mbim_device, FU_TYPE_MM_MBIM_DEVICE) + +static gboolean +fu_mm_qdu_mbim_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + /* to override FuMmMbimDevice->detach */ + return TRUE; +} + +static gboolean +fu_mm_qdu_mbim_device_write_chunk(FuMmQduMbimDevice *self, FuChunk *chk, GError **error) +{ + g_autoptr(MbimMessage) request = NULL; + g_autoptr(MbimMessage) response = NULL; + + request = mbim_message_qdu_file_write_set_new(fu_chunk_get_data_sz(chk), + fu_chunk_get_data(chk), + NULL); + response = + fu_mm_mbim_device_command_sync(FU_MM_MBIM_DEVICE(self), request, 20 * 1000, error); + if (response == NULL) + return FALSE; + if (!mbim_message_qdu_file_write_response_parse(response, error)) { + fu_mm_mbim_device_error_convert(error); + g_prefix_error_literal(error, "failed to parse write-chunk response: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_qdu_mbim_device_write_chunks(FuMmQduMbimDevice *self, + FuChunkArray *chunks, + FuProgress *progress, + GError **error) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, fu_chunk_array_length(chunks)); + for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { + g_autoptr(FuChunk) chk = NULL; + chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) { + g_prefix_error_literal(error, "failed to get chunk: "); + return FALSE; + } + if (!fu_mm_qdu_mbim_device_write_chunk(self, chk, error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_qdu_mbim_device_write(FuMmQduMbimDevice *self, + const gchar *filename, + GBytes *blob, + FuProgress *progress, + GError **error) +{ + guint32 out_max_transfer_size = 0; + g_autoptr(FuChunkArray) chunks = NULL; + g_autoptr(MbimMessage) action_start_req = NULL; + g_autoptr(MbimMessage) action_start_res = NULL; + g_autoptr(MbimMessage) file_open_req = NULL; + g_autoptr(MbimMessage) file_open_res = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "start-update"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 2, "file-open"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, "send-chunks"); + + /* start update */ + action_start_req = mbim_message_qdu_update_session_set_new(MBIM_QDU_SESSION_ACTION_START, + MBIM_QDU_SESSION_TYPE_LE, + NULL); + action_start_res = fu_mm_mbim_device_command_sync(FU_MM_MBIM_DEVICE(self), + action_start_req, + 10 * 1000, + error); + if (action_start_res == NULL) + return FALSE; + if (!mbim_message_qdu_update_session_response_parse(action_start_res, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + error)) { + fu_mm_mbim_device_error_convert(error); + g_prefix_error_literal(error, "failed to parse action-start response: "); + return FALSE; + } + g_debug("successfully request modem to update session"); + fu_progress_step_done(progress); + + /* get the max transfer size */ + file_open_req = mbim_message_qdu_file_open_set_new(MBIM_QDU_FILE_TYPE_LITTLE_ENDIAN_PACKAGE, + g_bytes_get_size(blob), + NULL); + file_open_res = fu_mm_mbim_device_command_sync(FU_MM_MBIM_DEVICE(self), + file_open_req, + 10 * 1000, + error); + if (file_open_res == NULL) + return FALSE; + + if (!mbim_message_qdu_file_open_response_parse(file_open_res, + &out_max_transfer_size, + NULL, + error)) { + fu_mm_mbim_device_error_convert(error); + g_prefix_error_literal(error, "failed to parse file-open response: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* send chunks */ + chunks = fu_chunk_array_new_from_bytes(blob, + FU_CHUNK_ADDR_OFFSET_NONE, + FU_CHUNK_PAGESZ_NONE, + out_max_transfer_size); + if (!fu_mm_qdu_mbim_device_write_chunks(self, + chunks, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_qdu_mbim_device_ensure_firmware_version(FuMmQduMbimDevice *self, GError **error) +{ + g_autofree gchar *firmware_version = NULL; + g_autoptr(MbimMessage) request = NULL; + g_autoptr(MbimMessage) response = NULL; + + request = mbim_message_device_caps_query_new(NULL); + response = + fu_mm_mbim_device_command_sync(FU_MM_MBIM_DEVICE(self), request, 10 * 1000, error); + if (response == NULL) + return FALSE; + if (!mbim_message_device_caps_response_parse(response, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + &firmware_version, + NULL, + error)) { + g_debug("failed to parse caps-query response: "); + return FALSE; + } + g_debug("modem query caps firmware version: %s", firmware_version); + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_qdu_mbim_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmQduMbimDevice *self = FU_MM_QDU_MBIM_DEVICE(device); + XbNode *part = NULL; + const gchar *filename = NULL; + const gchar *csum; + g_autofree gchar *csum_actual = NULL; + g_autoptr(FuArchive) archive = NULL; + g_autoptr(GBytes) data_part = NULL; + g_autoptr(GBytes) data_xml = NULL; + g_autoptr(GInputStream) stream = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new(); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(XbSilo) silo = NULL; + + /* decompress entire archive ahead of time */ + stream = fu_firmware_get_stream(firmware, error); + if (stream == NULL) + return FALSE; + archive = fu_archive_new_stream(stream, FU_ARCHIVE_FLAG_IGNORE_PATH, error); + if (archive == NULL) + return FALSE; + + /* load the manifest of operations */ + data_xml = fu_archive_lookup_by_fn(archive, "flashfile.xml", error); + if (data_xml == NULL) + return FALSE; + if (!xb_builder_source_load_bytes(source, data_xml, XB_BUILDER_SOURCE_FLAG_NONE, error)) + return FALSE; + xb_builder_import_source(builder, source); + silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, error); + if (silo == NULL) + return FALSE; + + part = xb_silo_query_first(silo, "parts/part", error); + if (part == NULL) + return FALSE; + filename = xb_node_get_attr(part, "filename"); + csum = xb_node_get_attr(part, "MD5"); + data_part = fu_archive_lookup_by_fn(archive, filename, error); + if (data_part == NULL) + return FALSE; + csum_actual = g_compute_checksum_for_bytes(G_CHECKSUM_MD5, data_part); + if (g_strcmp0(csum, csum_actual) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "[%s] MD5 not matched", + filename); + return FALSE; + } + g_debug("[%s] MD5 matched", filename); + + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); + if (!fu_mm_qdu_mbim_device_write(self, filename, data_part, progress, error)) + return FALSE; + + /* read back new version */ + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + if (!fu_mm_qdu_mbim_device_ensure_firmware_version(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_qdu_mbim_device_probe(FuDevice *device, GError **error) +{ + FuMmQduMbimDevice *self = FU_MM_QDU_MBIM_DEVICE(device); + return fu_mm_device_set_device_file(FU_MM_DEVICE(self), MM_MODEM_PORT_TYPE_MBIM, error); +} + +static gboolean +fu_mm_qdu_mbim_device_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmQduMbimDevice *self = FU_MM_QDU_MBIM_DEVICE(device); + if (!fu_mm_device_set_autosuspend_delay(FU_MM_DEVICE(self), 20000, error)) + return FALSE; + fu_mm_device_set_inhibited(FU_MM_DEVICE(self), TRUE); + return TRUE; +} + +static gboolean +fu_mm_qdu_mbim_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmQduMbimDevice *self = FU_MM_QDU_MBIM_DEVICE(device); + if (!fu_mm_device_set_autosuspend_delay(FU_MM_DEVICE(self), 2000, error)) + return FALSE; + fu_mm_device_set_inhibited(FU_MM_DEVICE(self), FALSE); + return TRUE; +} + +static void +fu_mm_qdu_mbim_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_mm_qdu_mbim_device_init(FuMmQduMbimDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.qualcomm.mbim_qdu"); +} + +static void +fu_mm_qdu_mbim_device_class_init(FuMmQduMbimDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->detach = fu_mm_qdu_mbim_device_detach; + device_class->probe = fu_mm_qdu_mbim_device_probe; + device_class->prepare = fu_mm_qdu_mbim_device_prepare; + device_class->cleanup = fu_mm_qdu_mbim_device_cleanup; + device_class->set_progress = fu_mm_qdu_mbim_device_set_progress; + device_class->write_firmware = fu_mm_qdu_mbim_device_write_firmware; +} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-qdu-mbim-device.h fwupd-2.0.20/plugins/modem-manager/fu-mm-qdu-mbim-device.h --- fwupd-2.0.8/plugins/modem-manager/fu-mm-qdu-mbim-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-qdu-mbim-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +/* + * Copyright 2021 Jarvis Jiang + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-mm-mbim-device.h" + +#define FU_TYPE_MM_QDU_MBIM_DEVICE (fu_mm_qdu_mbim_device_get_type()) +G_DECLARE_FINAL_TYPE(FuMmQduMbimDevice, + fu_mm_qdu_mbim_device, + FU, + MM_QDU_MBIM_DEVICE, + FuMmMbimDevice) diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-qmi-device.c fwupd-2.0.20/plugins/modem-manager/fu-mm-qmi-device.c --- fwupd-2.0.8/plugins/modem-manager/fu-mm-qmi-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-qmi-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,964 @@ +/* + * Copyright 2025 Richard Hughes + * Copyright 2019 Aleksander Morgado + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include + +#include "fu-mm-qmi-device.h" + +struct _FuMmQmiDevice { + FuMmDevice parent_instance; + QmiDevice *qmi_device; + QmiClientPdc *qmi_client; + GArray *active_id; +}; + +G_DEFINE_TYPE(FuMmQmiDevice, fu_mm_qmi_device, FU_TYPE_MM_DEVICE) + +#define FU_QMI_PDC_MAX_OPEN_ATTEMPTS 8 + +typedef struct { + GMainLoop *mainloop; + QmiDevice *qmi_device; + QmiClientPdc *qmi_client; + GError *error; + guint open_attempts; +} FuMmQmiDeviceOpenContext; + +static void +fu_mm_qmi_device_qmi_device_open_attempt(FuMmQmiDeviceOpenContext *ctx); + +static void +fu_mm_qmi_device_qmi_device_open_abort_ready(GObject *qmi_device, + GAsyncResult *res, + gpointer user_data) +{ + FuMmQmiDeviceOpenContext *ctx = (FuMmQmiDeviceOpenContext *)user_data; + + g_warn_if_fail(ctx->error != NULL); + + /* ignore errors when aborting open */ + qmi_device_close_finish(QMI_DEVICE(qmi_device), res, NULL); + + ctx->open_attempts--; + if (ctx->open_attempts == 0) { + g_clear_object(&ctx->qmi_client); + g_clear_object(&ctx->qmi_device); + g_main_loop_quit(ctx->mainloop); + return; + } + + /* retry */ + g_clear_error(&ctx->error); + fu_mm_qmi_device_qmi_device_open_attempt(ctx); +} + +static void +fu_mm_qmi_device_open_abort(FuMmQmiDeviceOpenContext *ctx) +{ + qmi_device_close_async(ctx->qmi_device, + 15, + NULL, + fu_mm_qmi_device_qmi_device_open_abort_ready, + ctx); +} + +static void +fu_mm_qmi_device_qmi_device_allocate_client_ready(GObject *qmi_device, + GAsyncResult *res, + gpointer user_data) +{ + FuMmQmiDeviceOpenContext *ctx = (FuMmQmiDeviceOpenContext *)user_data; + + ctx->qmi_client = QMI_CLIENT_PDC( + qmi_device_allocate_client_finish(QMI_DEVICE(qmi_device), res, &ctx->error)); + if (ctx->qmi_client == NULL) { + fu_mm_qmi_device_open_abort(ctx); + return; + } + + g_main_loop_quit(ctx->mainloop); +} + +static void +fu_mm_qmi_device_qmi_device_open_cb(GObject *qmi_device, GAsyncResult *res, gpointer user_data) +{ + FuMmQmiDeviceOpenContext *ctx = (FuMmQmiDeviceOpenContext *)user_data; + + if (!qmi_device_open_finish(QMI_DEVICE(qmi_device), res, &ctx->error)) { + fu_mm_qmi_device_open_abort(ctx); + return; + } + + qmi_device_allocate_client(ctx->qmi_device, + QMI_SERVICE_PDC, + QMI_CID_NONE, + 5, + NULL, + fu_mm_qmi_device_qmi_device_allocate_client_ready, + ctx); +} + +static void +fu_mm_qmi_device_qmi_device_open_attempt(FuMmQmiDeviceOpenContext *ctx) +{ + g_debug("trying to open QMI device…"); + qmi_device_open( + ctx->qmi_device, + QMI_DEVICE_OPEN_FLAGS_AUTO | /* detect QMI and MBIM ports */ + QMI_DEVICE_OPEN_FLAGS_EXPECT_INDICATIONS | /* pdc requires indications */ + QMI_DEVICE_OPEN_FLAGS_PROXY, /* all comms through the proxy */ + 5, + NULL, + fu_mm_qmi_device_qmi_device_open_cb, + ctx); +} + +static void +fu_mm_qmi_device_qmi_device_new_ready(GObject *source, GAsyncResult *res, gpointer user_data) +{ + FuMmQmiDeviceOpenContext *ctx = (FuMmQmiDeviceOpenContext *)user_data; + + ctx->qmi_device = qmi_device_new_finish(res, &ctx->error); + if (ctx->qmi_device == NULL) { + g_main_loop_quit(ctx->mainloop); + return; + } + + fu_mm_qmi_device_qmi_device_open_attempt(ctx); +} + +static gboolean +fu_mm_qmi_device_open(FuDevice *device, GError **error) +{ + FuMmQmiDevice *self = FU_MM_QMI_DEVICE(device); + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + g_autoptr(GFile) qmi_device_file = NULL; + FuMmQmiDeviceOpenContext ctx = { + .mainloop = mainloop, + .qmi_device = NULL, + .qmi_client = NULL, + .error = NULL, + .open_attempts = FU_QMI_PDC_MAX_OPEN_ATTEMPTS, + }; + + qmi_device_file = g_file_new_for_path(fu_udev_device_get_device_file(FU_UDEV_DEVICE(self))); + qmi_device_new(qmi_device_file, NULL, fu_mm_qmi_device_qmi_device_new_ready, &ctx); + g_main_loop_run(mainloop); + + /* either we have all device, client and config list set, or otherwise error is set */ + + if (ctx.qmi_device != NULL && ctx.qmi_client != NULL) { + g_warn_if_fail(!ctx.error); + self->qmi_device = ctx.qmi_device; + self->qmi_client = ctx.qmi_client; + /* success */ + return TRUE; + } + + g_propagate_error(error, ctx.error); + return FALSE; +} + +typedef struct { + GMainLoop *mainloop; + QmiDevice *qmi_device; + QmiClientPdc *qmi_client; + GError *error; +} FuMmQmiDeviceCloseContext; + +static void +fu_mm_qmi_device_qmi_device_close_ready(GObject *qmi_device, GAsyncResult *res, gpointer user_data) +{ + FuMmQmiDeviceCloseContext *ctx = (FuMmQmiDeviceCloseContext *)user_data; + + /* ignore errors when closing if we had one already set when releasing client */ + qmi_device_close_finish(QMI_DEVICE(qmi_device), + res, + (ctx->error == NULL) ? &ctx->error : NULL); + g_clear_object(&ctx->qmi_device); + g_main_loop_quit(ctx->mainloop); +} + +static void +fu_mm_qmi_device_qmi_device_release_client_ready(GObject *qmi_device, + GAsyncResult *res, + gpointer user_data) +{ + FuMmQmiDeviceCloseContext *ctx = (FuMmQmiDeviceCloseContext *)user_data; + + qmi_device_release_client_finish(QMI_DEVICE(qmi_device), res, &ctx->error); + g_clear_object(&ctx->qmi_client); + + qmi_device_close_async(ctx->qmi_device, + 15, + NULL, + fu_mm_qmi_device_qmi_device_close_ready, + ctx); +} + +static gboolean +fu_mm_qmi_device_close(FuDevice *device, GError **error) +{ + FuMmQmiDevice *self = FU_MM_QMI_DEVICE(device); + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + FuMmQmiDeviceCloseContext ctx = { + .mainloop = mainloop, + .qmi_device = g_steal_pointer(&self->qmi_device), + .qmi_client = g_steal_pointer(&self->qmi_client), + }; + + /* sanity check */ + if (ctx.qmi_device == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no qmi_device"); + return FALSE; + } + + qmi_device_release_client(ctx.qmi_device, + QMI_CLIENT(ctx.qmi_client), + QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, + 5, + NULL, + fu_mm_qmi_device_qmi_device_release_client_ready, + &ctx); + g_main_loop_run(mainloop); + + /* we should always have both device and client cleared, and optionally error set */ + + if (ctx.error != NULL) { + g_propagate_error(error, ctx.error); + return FALSE; + } + + return TRUE; +} + +#define QMI_LOAD_CHUNK_SIZE 0x400 + +typedef struct { + GMainLoop *mainloop; + QmiClientPdc *qmi_client; + GError *error; + gulong indication_id; + guint timeout_id; + GBytes *blob; + GArray *digest; + gsize offset; + guint token; +} FuMmQmiDeviceWriteContext; + +static void +fu_mm_qmi_device_load_config(FuMmQmiDeviceWriteContext *ctx); + +static gboolean +fu_mm_qmi_device_load_config_timeout(gpointer user_data) +{ + FuMmQmiDeviceWriteContext *ctx = user_data; + + ctx->timeout_id = 0; + g_signal_handler_disconnect(ctx->qmi_client, ctx->indication_id); + ctx->indication_id = 0; + + g_set_error_literal(&ctx->error, + FWUPD_ERROR, + FWUPD_ERROR_TIMED_OUT, + "couldn't load mcfg: timed out"); + g_main_loop_quit(ctx->mainloop); + + return G_SOURCE_REMOVE; +} + +static void +fu_mm_qmi_device_load_config_indication(QmiClientPdc *client, + QmiIndicationPdcLoadConfigOutput *output, + FuMmQmiDeviceWriteContext *ctx) +{ + gboolean frame_reset; + guint32 remaining_size; + guint16 error_code = 0; + + g_source_remove(ctx->timeout_id); + ctx->timeout_id = 0; + g_signal_handler_disconnect(ctx->qmi_client, ctx->indication_id); + ctx->indication_id = 0; + + if (!qmi_indication_pdc_load_config_output_get_indication_result(output, + &error_code, + &ctx->error)) { + g_main_loop_quit(ctx->mainloop); + return; + } + + if (error_code != 0) { + /* when a given mcfg file already exists in the device, an "invalid id" error is + * returned; the error naming here is a bit off, as the same protocol error number + * is used both for 'invalid id' and 'invalid qos id' + */ + if (error_code == QMI_PROTOCOL_ERROR_INVALID_QOS_ID) { + g_debug("file already available in device"); + g_main_loop_quit(ctx->mainloop); + return; + } + + g_set_error(&ctx->error, /* nocheck:error-false-return */ + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "couldn't load mcfg: %s", + qmi_protocol_error_get_string((QmiProtocolError)error_code)); + g_main_loop_quit(ctx->mainloop); + return; + } + + if (qmi_indication_pdc_load_config_output_get_frame_reset(output, &frame_reset, NULL) && + frame_reset) { + g_set_error_literal(&ctx->error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "couldn't load mcfg: sent data discarded"); + g_main_loop_quit(ctx->mainloop); + return; + } + + if (!qmi_indication_pdc_load_config_output_get_remaining_size(output, + &remaining_size, + &ctx->error)) { + g_prefix_error_literal(&ctx->error, /* nocheck:error */ + "couldn't load remaining size: "); + g_main_loop_quit(ctx->mainloop); + return; + } + + if (remaining_size == 0) { + g_debug("finished loading mcfg"); + g_main_loop_quit(ctx->mainloop); + return; + } + + g_debug("loading next chunk (%u bytes remaining)", remaining_size); + fu_mm_qmi_device_load_config(ctx); +} + +static void +fu_mm_qmi_device_load_config_ready(GObject *qmi_client, GAsyncResult *res, gpointer user_data) +{ + FuMmQmiDeviceWriteContext *ctx = (FuMmQmiDeviceWriteContext *)user_data; + g_autoptr(QmiMessagePdcLoadConfigOutput) output = NULL; + + output = qmi_client_pdc_load_config_finish(QMI_CLIENT_PDC(qmi_client), res, &ctx->error); + if (output == NULL) { + g_main_loop_quit(ctx->mainloop); + return; + } + + if (!qmi_message_pdc_load_config_output_get_result(output, &ctx->error)) { + g_main_loop_quit(ctx->mainloop); + return; + } + + /* after receiving the response to our request, we now expect an indication + * with the actual result of the operation */ + g_warn_if_fail(ctx->indication_id == 0); + ctx->indication_id = g_signal_connect(QMI_CLIENT_PDC(ctx->qmi_client), + "load-config", + G_CALLBACK(fu_mm_qmi_device_load_config_indication), + ctx); + + /* don't wait forever */ + g_warn_if_fail(ctx->timeout_id == 0); + ctx->timeout_id = g_timeout_add_seconds(5, fu_mm_qmi_device_load_config_timeout, ctx); +} + +static void +fu_mm_qmi_device_load_config(FuMmQmiDeviceWriteContext *ctx) +{ + g_autoptr(QmiMessagePdcLoadConfigInput) input = NULL; + 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); + + full_size = g_bytes_get_size(ctx->blob); + if ((ctx->offset + QMI_LOAD_CHUNK_SIZE) > full_size) + chunk_size = full_size - ctx->offset; + else + chunk_size = QMI_LOAD_CHUNK_SIZE; + + chunk = g_array_sized_new(FALSE, FALSE, sizeof(guint8), chunk_size); + g_array_set_size(chunk, chunk_size); + 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)) { + g_critical("failed to copy chunk: %s", error->message); + } + + qmi_message_pdc_load_config_input_set_config_chunk(input, + QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, + ctx->digest, + full_size, + chunk, + NULL); + ctx->offset += chunk_size; + + qmi_client_pdc_load_config(ctx->qmi_client, + input, + 10, + NULL, + fu_mm_qmi_device_load_config_ready, + ctx); +} + +static GArray * +fu_mm_qmi_device_get_checksum(GBytes *blob) +{ + gsize file_size; + gsize hash_size; + GArray *digest; + g_autoptr(GChecksum) checksum = NULL; + + /* get checksum, to be used as unique id */ + file_size = g_bytes_get_size(blob); + hash_size = g_checksum_type_get_length(G_CHECKSUM_SHA1); + checksum = g_checksum_new(G_CHECKSUM_SHA1); + g_checksum_update(checksum, g_bytes_get_data(blob, NULL), file_size); + /* libqmi expects a GArray of bytes, not a GByteArray */ + digest = g_array_sized_new(FALSE, FALSE, sizeof(guint8), hash_size); + g_array_set_size(digest, hash_size); + g_checksum_get_digest(checksum, (guint8 *)digest->data, &hash_size); + + return digest; +} + +static GArray * +fu_mm_qmi_device_write(FuMmQmiDevice *self, const gchar *filename, GBytes *blob, GError **error) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + g_autoptr(GArray) digest = fu_mm_qmi_device_get_checksum(blob); + FuMmQmiDeviceWriteContext ctx = { + .mainloop = mainloop, + .qmi_client = self->qmi_client, + .error = NULL, + .indication_id = 0, + .timeout_id = 0, + .blob = blob, + .digest = digest, + .offset = 0, + .token = 0, + }; + + fu_mm_qmi_device_load_config(&ctx); + g_main_loop_run(mainloop); + + if (ctx.error != NULL) { + g_propagate_error(error, ctx.error); + return NULL; + } + + return g_steal_pointer(&digest); +} + +typedef struct { + GMainLoop *mainloop; + QmiClientPdc *qmi_client; + GError *error; + gulong indication_id; + guint timeout_id; + GArray *digest; + guint token; +} FuMmQmiDeviceActivateContext; + +static gboolean +fu_mm_qmi_device_activate_config_timeout(gpointer user_data) +{ + FuMmQmiDeviceActivateContext *ctx = user_data; + + ctx->timeout_id = 0; + g_signal_handler_disconnect(ctx->qmi_client, ctx->indication_id); + ctx->indication_id = 0; + + /* not an error, the device may go away without sending the indication */ + g_main_loop_quit(ctx->mainloop); + + return G_SOURCE_REMOVE; +} + +static void +fu_mm_qmi_device_activate_config_indication(QmiClientPdc *client, + QmiIndicationPdcActivateConfigOutput *output, + FuMmQmiDeviceActivateContext *ctx) +{ + guint16 error_code = 0; + + g_source_remove(ctx->timeout_id); + ctx->timeout_id = 0; + g_signal_handler_disconnect(ctx->qmi_client, ctx->indication_id); + ctx->indication_id = 0; + + if (!qmi_indication_pdc_activate_config_output_get_indication_result(output, + &error_code, + &ctx->error)) { + g_main_loop_quit(ctx->mainloop); + return; + } + + if (error_code != 0) { + g_set_error(&ctx->error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "couldn't activate config: %s", + qmi_protocol_error_get_string((QmiProtocolError)error_code)); + g_main_loop_quit(ctx->mainloop); + return; + } + + /* assume ok */ + g_debug("successful activate configuration indication: assuming device reset is ongoing"); + g_main_loop_quit(ctx->mainloop); +} + +static void +fu_mm_qmi_device_activate_config_ready(GObject *qmi_client, GAsyncResult *res, gpointer user_data) +{ + FuMmQmiDeviceActivateContext *ctx = (FuMmQmiDeviceActivateContext *)user_data; + g_autoptr(QmiMessagePdcActivateConfigOutput) output = NULL; + + output = + qmi_client_pdc_activate_config_finish(QMI_CLIENT_PDC(qmi_client), res, &ctx->error); + if (output == NULL) { + /* If we didn't receive a response, this is a good indication that the device + * reset itself, we can consider this a successful operation. + * Note: not using g_error_matches() to avoid matching the domain, because the + * error may be either QMI_CORE_ERROR_TIMEOUT or MBIM_CORE_ERROR_TIMEOUT (same + * numeric value), and we don't want to build-depend on libmbim just for this. + */ + if (ctx->error->code == QMI_CORE_ERROR_TIMEOUT) { + g_debug("request to activate configuration timed out: assuming device " + "reset is ongoing"); + g_clear_error(&ctx->error); + } + g_main_loop_quit(ctx->mainloop); + return; + } + + if (!qmi_message_pdc_activate_config_output_get_result(output, &ctx->error)) { + g_main_loop_quit(ctx->mainloop); + return; + } + + /* When we activate the config, if the operation is successful, we'll just + * see the modem going away completely. So, do not consider an error the timeout + * waiting for the Activate Config indication, as that is actually a good + * thing. + */ + g_warn_if_fail(ctx->indication_id == 0); + ctx->indication_id = + g_signal_connect(QMI_CLIENT_PDC(ctx->qmi_client), + "activate-config", + G_CALLBACK(fu_mm_qmi_device_activate_config_indication), + ctx); + + /* don't wait forever */ + g_warn_if_fail(ctx->timeout_id == 0); + ctx->timeout_id = g_timeout_add_seconds(5, fu_mm_qmi_device_activate_config_timeout, ctx); +} + +static void +fu_mm_qmi_device_activate_config(FuMmQmiDeviceActivateContext *ctx) +{ + g_autoptr(QmiMessagePdcActivateConfigInput) input = NULL; + + input = qmi_message_pdc_activate_config_input_new(); + qmi_message_pdc_activate_config_input_set_config_type(input, + QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, + NULL); + qmi_message_pdc_activate_config_input_set_token(input, ctx->token++, NULL); + + g_debug("activating selected configuration…"); + qmi_client_pdc_activate_config(ctx->qmi_client, + input, + 5, + NULL, + fu_mm_qmi_device_activate_config_ready, + ctx); +} + +static gboolean +fu_mm_qmi_device_set_selected_config_timeout(gpointer user_data) +{ + FuMmQmiDeviceActivateContext *ctx = user_data; + + ctx->timeout_id = 0; + g_signal_handler_disconnect(ctx->qmi_client, ctx->indication_id); + ctx->indication_id = 0; + + g_set_error_literal(&ctx->error, + FWUPD_ERROR, + FWUPD_ERROR_TIMED_OUT, + "couldn't set selected config: timed out"); + g_main_loop_quit(ctx->mainloop); + + return G_SOURCE_REMOVE; +} + +static void +fu_mm_qmi_device_set_selected_config_indication(QmiClientPdc *client, + QmiIndicationPdcSetSelectedConfigOutput *output, + FuMmQmiDeviceActivateContext *ctx) +{ + guint16 error_code = 0; + + g_source_remove(ctx->timeout_id); + ctx->timeout_id = 0; + g_signal_handler_disconnect(ctx->qmi_client, ctx->indication_id); + ctx->indication_id = 0; + + if (!qmi_indication_pdc_set_selected_config_output_get_indication_result(output, + &error_code, + &ctx->error)) { + g_main_loop_quit(ctx->mainloop); + return; + } + + if (error_code != 0) { + g_set_error(&ctx->error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "couldn't set selected config: %s", + qmi_protocol_error_get_string((QmiProtocolError)error_code)); + g_main_loop_quit(ctx->mainloop); + return; + } + + g_debug("current configuration successfully selected…"); + + /* now activate config */ + fu_mm_qmi_device_activate_config(ctx); +} + +static void +fu_mm_qmi_device_set_selected_config_ready(GObject *qmi_client, + GAsyncResult *res, + gpointer user_data) +{ + FuMmQmiDeviceActivateContext *ctx = (FuMmQmiDeviceActivateContext *)user_data; + g_autoptr(QmiMessagePdcSetSelectedConfigOutput) output = NULL; + + output = + qmi_client_pdc_set_selected_config_finish(QMI_CLIENT_PDC(qmi_client), res, &ctx->error); + if (output == NULL) { + g_main_loop_quit(ctx->mainloop); + return; + } + + if (!qmi_message_pdc_set_selected_config_output_get_result(output, &ctx->error)) { + g_main_loop_quit(ctx->mainloop); + return; + } + + /* after receiving the response to our request, we now expect an indication + * with the actual result of the operation */ + g_warn_if_fail(ctx->indication_id == 0); + ctx->indication_id = + g_signal_connect(QMI_CLIENT_PDC(ctx->qmi_client), + "set-selected-config", + G_CALLBACK(fu_mm_qmi_device_set_selected_config_indication), + ctx); + + /* don't wait forever */ + g_warn_if_fail(ctx->timeout_id == 0); + ctx->timeout_id = + g_timeout_add_seconds(5, fu_mm_qmi_device_set_selected_config_timeout, ctx); +} + +static void +fu_mm_qmi_device_set_selected_config(FuMmQmiDeviceActivateContext *ctx) +{ + g_autoptr(QmiMessagePdcSetSelectedConfigInput) input = NULL; + + input = qmi_message_pdc_set_selected_config_input_new(); + qmi_message_pdc_set_selected_config_input_set_type_with_id_v2( + input, + QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, + ctx->digest, + NULL); + qmi_message_pdc_set_selected_config_input_set_token(input, ctx->token++, NULL); + + g_debug("selecting current configuration…"); + qmi_client_pdc_set_selected_config(ctx->qmi_client, + input, + 10, + NULL, + fu_mm_qmi_device_set_selected_config_ready, + ctx); +} + +static gboolean +fu_mm_qmi_device_activate(FuMmQmiDevice *self, GError **error) +{ + g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); + FuMmQmiDeviceActivateContext ctx = { + .mainloop = mainloop, + .qmi_client = self->qmi_client, + .error = NULL, + .indication_id = 0, + .timeout_id = 0, + .digest = self->active_id, + .token = 0, + }; + + fu_mm_qmi_device_set_selected_config(&ctx); + g_main_loop_run(mainloop); + + if (ctx.error != NULL) { + g_propagate_error(error, ctx.error); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_mm_qmi_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuMmQmiDevice *self = FU_MM_QMI_DEVICE(device); + + /* ignore action if there is no active id specified */ + if (self->active_id == NULL) + return TRUE; + return fu_mm_qmi_device_activate(self, error); +} + +static gboolean +fu_mm_qmi_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + // FuMmQmiDevice *self = FU_MM_QMI_DEVICE(device); + return TRUE; +} + +static gboolean +fu_mm_qmi_device_probe(FuDevice *device, GError **error) +{ + FuMmQmiDevice *self = FU_MM_QMI_DEVICE(device); + return fu_mm_device_set_device_file(FU_MM_DEVICE(self), MM_MODEM_PORT_TYPE_QMI, error); +} + +typedef struct { + gchar *filename; + GBytes *bytes; + GArray *digest; + gboolean active; +} FuMmQmiDeviceFileInfo; + +static void +fu_mm_qmi_device_file_info_free(FuMmQmiDeviceFileInfo *file_info) +{ + g_clear_pointer(&file_info->digest, g_array_unref); + g_free(file_info->filename); + g_bytes_unref(file_info->bytes); + g_free(file_info); +} + +typedef struct { + FuMmQmiDevice *self; + GError *error; + GPtrArray *file_infos; +} FuMmQmiDeviceArchiveIterateCtx; + +static gboolean +fu_mm_qmi_device_should_be_active(const gchar *version, const gchar *filename) +{ + g_auto(GStrv) split = NULL; + g_autofree gchar *carrier_id = NULL; + + /* The filename of the mcfg file is composed of a "mcfg." prefix, then the + * carrier code, followed by the carrier version, and finally a ".mbn" + * prefix. Here we try to guess, based on the carrier code, whether the + * specific mcfg file should be activated after the firmware upgrade + * operation. + * + * This logic requires that the previous device version includes the carrier + * code also embedded in the version string. E.g. "xxxx.VF.xxxx". If we find + * this match, we assume this is the active config to use. + */ + + split = g_strsplit(filename, ".", -1); + if (g_strv_length(split) < 4) + return FALSE; + if (g_strcmp0(split[0], "mcfg") != 0) + return FALSE; + + carrier_id = g_strdup_printf(".%s.", split[1]); + return (g_strstr_len(version, -1, carrier_id) != NULL); +} + +static gboolean +fu_mm_qmi_device_archive_iterate_mcfg_cb(FuArchive *archive, + const gchar *filename, + GBytes *bytes, + gpointer user_data, + GError **error) +{ + FuMmQmiDeviceArchiveIterateCtx *ctx = user_data; + FuMmQmiDeviceFileInfo *file_info; + + /* filenames should be named as 'mcfg.*.mbn', e.g.: mcfg.A2.018.mbn */ + if (!g_str_has_prefix(filename, "mcfg.") || !g_str_has_suffix(filename, ".mbn")) + return TRUE; + + file_info = g_new0(FuMmQmiDeviceFileInfo, 1); + file_info->filename = g_strdup(filename); + file_info->bytes = g_bytes_ref(bytes); + file_info->active = + fu_mm_qmi_device_should_be_active(fu_device_get_version(FU_DEVICE(ctx->self)), + filename); + g_ptr_array_add(ctx->file_infos, file_info); + return TRUE; +} + +static gboolean +fu_mm_qmi_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmQmiDevice *self = FU_MM_QMI_DEVICE(device); + g_autoptr(FuArchive) archive = NULL; + g_autoptr(GInputStream) stream = NULL; + g_autoptr(GPtrArray) file_infos = + g_ptr_array_new_with_free_func((GDestroyNotify)fu_mm_qmi_device_file_info_free); + gint active_i = -1; + FuMmQmiDeviceArchiveIterateCtx archive_context = { + .self = self, + .error = NULL, + .file_infos = file_infos, + }; + + /* decompress entire archive ahead of time */ + stream = fu_firmware_get_stream(firmware, error); + if (stream == NULL) + return FALSE; + archive = fu_archive_new_stream(stream, FU_ARCHIVE_FLAG_IGNORE_PATH, error); + if (archive == NULL) + return FALSE; + + /* process the list of MCFG files to write */ + if (!fu_archive_iterate(archive, + fu_mm_qmi_device_archive_iterate_mcfg_cb, + &archive_context, + error)) + return FALSE; + + for (guint i = 0; i < file_infos->len; i++) { + FuMmQmiDeviceFileInfo *file_info = g_ptr_array_index(file_infos, i); + file_info->digest = fu_mm_qmi_device_write(self, + file_info->filename, + file_info->bytes, + &archive_context.error); + if (file_info->digest == NULL) { + g_prefix_error(&archive_context.error, /* nocheck:error */ + "Failed to write file %s: ", + file_info->filename); + break; + } + /* if we wrongly detect more than one, just assume the latest one; this + * is not critical, it may just take a bit more time to perform the + * automatic carrier config switching in ModemManager */ + if (file_info->active) + active_i = i; + } + + /* set expected active configuration */ + if (active_i >= 0 && self->active_id != NULL) { + FuMmQmiDeviceFileInfo *file_info = g_ptr_array_index(file_infos, active_i); + self->active_id = g_array_ref(file_info->digest); + } + + if (archive_context.error != NULL) { + g_propagate_error(error, archive_context.error); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_mm_qmi_device_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmQmiDevice *self = FU_MM_QMI_DEVICE(device); + fu_mm_device_set_inhibited(FU_MM_DEVICE(self), TRUE); + return TRUE; +} + +static gboolean +fu_mm_qmi_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuMmQmiDevice *self = FU_MM_QMI_DEVICE(device); + fu_mm_device_set_inhibited(FU_MM_DEVICE(self), FALSE); + return TRUE; +} + +static void +fu_mm_qmi_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 97, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 1, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reload"); +} + +static void +fu_mm_qmi_device_init(FuMmQmiDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.qualcomm.qmi_pdc"); +} + +static void +fu_mm_qmi_device_finalize(GObject *object) +{ + FuMmQmiDevice *self = FU_MM_QMI_DEVICE(object); + + if (self->active_id) + g_array_unref(self->active_id); + + G_OBJECT_CLASS(fu_mm_qmi_device_parent_class)->finalize(object); +} + +static void +fu_mm_qmi_device_class_init(FuMmQmiDeviceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + object_class->finalize = fu_mm_qmi_device_finalize; + device_class->attach = fu_mm_qmi_device_attach; + device_class->detach = fu_mm_qmi_device_detach; + device_class->open = fu_mm_qmi_device_open; + device_class->close = fu_mm_qmi_device_close; + device_class->prepare = fu_mm_qmi_device_prepare; + device_class->cleanup = fu_mm_qmi_device_cleanup; + device_class->probe = fu_mm_qmi_device_probe; + device_class->set_progress = fu_mm_qmi_device_set_progress; + device_class->write_firmware = fu_mm_qmi_device_write_firmware; +} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-mm-qmi-device.h fwupd-2.0.20/plugins/modem-manager/fu-mm-qmi-device.h --- fwupd-2.0.8/plugins/modem-manager/fu-mm-qmi-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-mm-qmi-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2019 Aleksander Morgado + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-mm-device.h" + +#define FU_TYPE_MM_QMI_DEVICE (fu_mm_qmi_device_get_type()) +G_DECLARE_FINAL_TYPE(FuMmQmiDevice, fu_mm_qmi_device, FU, MM_QMI_DEVICE, FuMmDevice) diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-qmi-pdc-updater.c fwupd-2.0.20/plugins/modem-manager/fu-qmi-pdc-updater.c --- fwupd-2.0.8/plugins/modem-manager/fu-qmi-pdc-updater.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-qmi-pdc-updater.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,773 +0,0 @@ -/* - * Copyright 2019 Aleksander Morgado - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include - -#include "fu-qmi-pdc-updater.h" - -#define FU_QMI_PDC_MAX_OPEN_ATTEMPTS 8 - -struct _FuQmiPdcUpdater { - GObject parent_instance; - gchar *qmi_port; - QmiDevice *qmi_device; - QmiClientPdc *qmi_client; -}; - -G_DEFINE_TYPE(FuQmiPdcUpdater, fu_qmi_pdc_updater, G_TYPE_OBJECT) - -typedef struct { - GMainLoop *mainloop; - QmiDevice *qmi_device; - QmiClientPdc *qmi_client; - GError *error; - guint open_attempts; -} OpenContext; - -static void -fu_qmi_pdc_updater_qmi_device_open_attempt(OpenContext *ctx); - -static void -fu_qmi_pdc_updater_qmi_device_open_abort_ready(GObject *qmi_device, - GAsyncResult *res, - gpointer user_data) -{ - OpenContext *ctx = (OpenContext *)user_data; - - g_warn_if_fail(ctx->error != NULL); - - /* ignore errors when aborting open */ - qmi_device_close_finish(QMI_DEVICE(qmi_device), res, NULL); - - ctx->open_attempts--; - if (ctx->open_attempts == 0) { - g_clear_object(&ctx->qmi_client); - g_clear_object(&ctx->qmi_device); - g_main_loop_quit(ctx->mainloop); - return; - } - - /* retry */ - g_clear_error(&ctx->error); - fu_qmi_pdc_updater_qmi_device_open_attempt(ctx); -} - -static void -fu_qmi_pdc_updater_open_abort(OpenContext *ctx) -{ - qmi_device_close_async(ctx->qmi_device, - 15, - NULL, - fu_qmi_pdc_updater_qmi_device_open_abort_ready, - ctx); -} - -static void -fu_qmi_pdc_updater_qmi_device_allocate_client_ready(GObject *qmi_device, - GAsyncResult *res, - gpointer user_data) -{ - OpenContext *ctx = (OpenContext *)user_data; - - ctx->qmi_client = QMI_CLIENT_PDC( - qmi_device_allocate_client_finish(QMI_DEVICE(qmi_device), res, &ctx->error)); - if (ctx->qmi_client == NULL) { - fu_qmi_pdc_updater_open_abort(ctx); - return; - } - - g_main_loop_quit(ctx->mainloop); -} - -static void -fu_qmi_pdc_updater_qmi_device_open_ready(GObject *qmi_device, GAsyncResult *res, gpointer user_data) -{ - OpenContext *ctx = (OpenContext *)user_data; - - if (!qmi_device_open_finish(QMI_DEVICE(qmi_device), res, &ctx->error)) { - fu_qmi_pdc_updater_open_abort(ctx); - return; - } - - qmi_device_allocate_client(ctx->qmi_device, - QMI_SERVICE_PDC, - QMI_CID_NONE, - 5, - NULL, - fu_qmi_pdc_updater_qmi_device_allocate_client_ready, - ctx); -} - -static void -fu_qmi_pdc_updater_qmi_device_open_attempt(OpenContext *ctx) -{ - QmiDeviceOpenFlags open_flags = QMI_DEVICE_OPEN_FLAGS_NONE; - - /* automatically detect QMI and MBIM ports */ - open_flags |= QMI_DEVICE_OPEN_FLAGS_AUTO; - /* qmi pdc requires indications, so enable them by default */ - open_flags |= QMI_DEVICE_OPEN_FLAGS_EXPECT_INDICATIONS; - /* all communication through the proxy */ - open_flags |= QMI_DEVICE_OPEN_FLAGS_PROXY; - - g_debug("trying to open QMI device..."); - qmi_device_open(ctx->qmi_device, - open_flags, - 5, - NULL, - fu_qmi_pdc_updater_qmi_device_open_ready, - ctx); -} - -static void -fu_qmi_pdc_updater_qmi_device_new_ready(GObject *source, GAsyncResult *res, gpointer user_data) -{ - OpenContext *ctx = (OpenContext *)user_data; - - ctx->qmi_device = qmi_device_new_finish(res, &ctx->error); - if (ctx->qmi_device == NULL) { - g_main_loop_quit(ctx->mainloop); - return; - } - - fu_qmi_pdc_updater_qmi_device_open_attempt(ctx); -} - -gboolean -fu_qmi_pdc_updater_open(FuQmiPdcUpdater *self, GError **error) -{ - g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); - g_autoptr(GFile) qmi_device_file = g_file_new_for_path(self->qmi_port); - OpenContext ctx = { - .mainloop = mainloop, - .qmi_device = NULL, - .qmi_client = NULL, - .error = NULL, - .open_attempts = FU_QMI_PDC_MAX_OPEN_ATTEMPTS, - }; - - qmi_device_new(qmi_device_file, NULL, fu_qmi_pdc_updater_qmi_device_new_ready, &ctx); - g_main_loop_run(mainloop); - - /* either we have all device, client and config list set, or otherwise error is set */ - - if ((ctx.qmi_device != NULL) && (ctx.qmi_client != NULL)) { - g_warn_if_fail(!ctx.error); - self->qmi_device = ctx.qmi_device; - self->qmi_client = ctx.qmi_client; - /* success */ - return TRUE; - } - - g_warn_if_fail(ctx.error != NULL); - g_warn_if_fail(ctx.qmi_device == NULL); - g_warn_if_fail(ctx.qmi_client == NULL); - g_propagate_error(error, ctx.error); - return FALSE; -} - -typedef struct { - GMainLoop *mainloop; - QmiDevice *qmi_device; - QmiClientPdc *qmi_client; - GError *error; -} CloseContext; - -static void -fu_qmi_pdc_updater_qmi_device_close_ready(GObject *qmi_device, - GAsyncResult *res, - gpointer user_data) -{ - CloseContext *ctx = (CloseContext *)user_data; - - /* ignore errors when closing if we had one already set when releasing client */ - qmi_device_close_finish(QMI_DEVICE(qmi_device), - res, - (ctx->error == NULL) ? &ctx->error : NULL); - g_clear_object(&ctx->qmi_device); - g_main_loop_quit(ctx->mainloop); -} - -static void -fu_qmi_pdc_updater_qmi_device_release_client_ready(GObject *qmi_device, - GAsyncResult *res, - gpointer user_data) -{ - CloseContext *ctx = (CloseContext *)user_data; - - qmi_device_release_client_finish(QMI_DEVICE(qmi_device), res, &ctx->error); - g_clear_object(&ctx->qmi_client); - - qmi_device_close_async(ctx->qmi_device, - 15, - NULL, - fu_qmi_pdc_updater_qmi_device_close_ready, - ctx); -} - -gboolean -fu_qmi_pdc_updater_close(FuQmiPdcUpdater *self, GError **error) -{ - g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); - CloseContext ctx = { - .mainloop = mainloop, - .qmi_device = g_steal_pointer(&self->qmi_device), - .qmi_client = g_steal_pointer(&self->qmi_client), - }; - - qmi_device_release_client(ctx.qmi_device, - QMI_CLIENT(ctx.qmi_client), - QMI_DEVICE_RELEASE_CLIENT_FLAGS_RELEASE_CID, - 5, - NULL, - fu_qmi_pdc_updater_qmi_device_release_client_ready, - &ctx); - g_main_loop_run(mainloop); - - /* we should always have both device and client cleared, and optionally error set */ - - g_warn_if_fail(ctx.qmi_device == NULL); - g_warn_if_fail(ctx.qmi_client == NULL); - - if (ctx.error != NULL) { - g_propagate_error(error, ctx.error); - return FALSE; - } - - return TRUE; -} - -#define QMI_LOAD_CHUNK_SIZE 0x400 - -typedef struct { - GMainLoop *mainloop; - QmiClientPdc *qmi_client; - GError *error; - gulong indication_id; - guint timeout_id; - GBytes *blob; - GArray *digest; - gsize offset; - guint token; -} WriteContext; - -static void -fu_qmi_pdc_updater_load_config(WriteContext *ctx); - -static gboolean -fu_qmi_pdc_updater_load_config_timeout(gpointer user_data) -{ - WriteContext *ctx = user_data; - - ctx->timeout_id = 0; - g_signal_handler_disconnect(ctx->qmi_client, ctx->indication_id); - ctx->indication_id = 0; - - g_set_error_literal(&ctx->error, - FWUPD_ERROR, - FWUPD_ERROR_TIMED_OUT, - "couldn't load mcfg: timed out"); - g_main_loop_quit(ctx->mainloop); - - return G_SOURCE_REMOVE; -} - -static void -fu_qmi_pdc_updater_load_config_indication(QmiClientPdc *client, - QmiIndicationPdcLoadConfigOutput *output, - WriteContext *ctx) -{ - gboolean frame_reset; - guint32 remaining_size; - guint16 error_code = 0; - - g_source_remove(ctx->timeout_id); - ctx->timeout_id = 0; - g_signal_handler_disconnect(ctx->qmi_client, ctx->indication_id); - ctx->indication_id = 0; - - if (!qmi_indication_pdc_load_config_output_get_indication_result(output, - &error_code, - &ctx->error)) { - g_main_loop_quit(ctx->mainloop); - return; - } - - if (error_code != 0) { - /* when a given mcfg file already exists in the device, an "invalid id" error is - * returned; the error naming here is a bit off, as the same protocol error number - * is used both for 'invalid id' and 'invalid qos id' - */ - if (error_code == QMI_PROTOCOL_ERROR_INVALID_QOS_ID) { - g_debug("file already available in device"); - g_main_loop_quit(ctx->mainloop); - return; - } - - g_set_error(&ctx->error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "couldn't load mcfg: %s", - qmi_protocol_error_get_string((QmiProtocolError)error_code)); - g_main_loop_quit(ctx->mainloop); - return; - } - - if (qmi_indication_pdc_load_config_output_get_frame_reset(output, &frame_reset, NULL) && - frame_reset) { - g_set_error(&ctx->error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "couldn't load mcfg: sent data discarded"); - g_main_loop_quit(ctx->mainloop); - return; - } - - if (!qmi_indication_pdc_load_config_output_get_remaining_size(output, - &remaining_size, - &ctx->error)) { - g_prefix_error(&ctx->error, "couldn't load remaining size: "); - g_main_loop_quit(ctx->mainloop); - return; - } - - if (remaining_size == 0) { - g_debug("finished loading mcfg"); - g_main_loop_quit(ctx->mainloop); - return; - } - - g_debug("loading next chunk (%u bytes remaining)", remaining_size); - fu_qmi_pdc_updater_load_config(ctx); -} - -static void -fu_qmi_pdc_updater_load_config_ready(GObject *qmi_client, GAsyncResult *res, gpointer user_data) -{ - WriteContext *ctx = (WriteContext *)user_data; - g_autoptr(QmiMessagePdcLoadConfigOutput) output = NULL; - - output = qmi_client_pdc_load_config_finish(QMI_CLIENT_PDC(qmi_client), res, &ctx->error); - if (output == NULL) { - g_main_loop_quit(ctx->mainloop); - return; - } - - if (!qmi_message_pdc_load_config_output_get_result(output, &ctx->error)) { - g_main_loop_quit(ctx->mainloop); - return; - } - - /* after receiving the response to our request, we now expect an indication - * with the actual result of the operation */ - g_warn_if_fail(ctx->indication_id == 0); - ctx->indication_id = g_signal_connect(QMI_CLIENT_PDC(ctx->qmi_client), - "load-config", - G_CALLBACK(fu_qmi_pdc_updater_load_config_indication), - ctx); - - /* don't wait forever */ - g_warn_if_fail(ctx->timeout_id == 0); - ctx->timeout_id = g_timeout_add_seconds(5, fu_qmi_pdc_updater_load_config_timeout, ctx); -} - -static void -fu_qmi_pdc_updater_load_config(WriteContext *ctx) -{ - g_autoptr(QmiMessagePdcLoadConfigInput) input = NULL; - 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); - - full_size = g_bytes_get_size(ctx->blob); - if ((ctx->offset + QMI_LOAD_CHUNK_SIZE) > full_size) - chunk_size = full_size - ctx->offset; - else - chunk_size = QMI_LOAD_CHUNK_SIZE; - - chunk = g_array_sized_new(FALSE, FALSE, sizeof(guint8), chunk_size); - g_array_set_size(chunk, chunk_size); - 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)) { - g_critical("failed to copy chunk: %s", error->message); - } - - qmi_message_pdc_load_config_input_set_config_chunk(input, - QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, - ctx->digest, - full_size, - chunk, - NULL); - ctx->offset += chunk_size; - - qmi_client_pdc_load_config(ctx->qmi_client, - input, - 10, - NULL, - fu_qmi_pdc_updater_load_config_ready, - ctx); -} - -static GArray * -fu_qmi_pdc_updater_get_checksum(GBytes *blob) -{ - gsize file_size; - gsize hash_size; - GArray *digest; - g_autoptr(GChecksum) checksum = NULL; - - /* get checksum, to be used as unique id */ - file_size = g_bytes_get_size(blob); - hash_size = g_checksum_type_get_length(G_CHECKSUM_SHA1); - checksum = g_checksum_new(G_CHECKSUM_SHA1); - g_checksum_update(checksum, g_bytes_get_data(blob, NULL), file_size); - /* libqmi expects a GArray of bytes, not a GByteArray */ - digest = g_array_sized_new(FALSE, FALSE, sizeof(guint8), hash_size); - g_array_set_size(digest, hash_size); - g_checksum_get_digest(checksum, (guint8 *)digest->data, &hash_size); - - return digest; -} - -GArray * -fu_qmi_pdc_updater_write(FuQmiPdcUpdater *self, const gchar *filename, GBytes *blob, GError **error) -{ - g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); - g_autoptr(GArray) digest = fu_qmi_pdc_updater_get_checksum(blob); - WriteContext ctx = { - .mainloop = mainloop, - .qmi_client = self->qmi_client, - .error = NULL, - .indication_id = 0, - .timeout_id = 0, - .blob = blob, - .digest = digest, - .offset = 0, - .token = 0, - }; - - fu_qmi_pdc_updater_load_config(&ctx); - g_main_loop_run(mainloop); - - if (ctx.error != NULL) { - g_propagate_error(error, ctx.error); - return NULL; - } - - return g_steal_pointer(&digest); -} - -typedef struct { - GMainLoop *mainloop; - QmiClientPdc *qmi_client; - GError *error; - gulong indication_id; - guint timeout_id; - GArray *digest; - guint token; -} ActivateContext; - -static gboolean -fu_qmi_pdc_updater_activate_config_timeout(gpointer user_data) -{ - ActivateContext *ctx = user_data; - - ctx->timeout_id = 0; - g_signal_handler_disconnect(ctx->qmi_client, ctx->indication_id); - ctx->indication_id = 0; - - /* not an error, the device may go away without sending the indication */ - g_main_loop_quit(ctx->mainloop); - - return G_SOURCE_REMOVE; -} - -static void -fu_qmi_pdc_updater_activate_config_indication(QmiClientPdc *client, - QmiIndicationPdcActivateConfigOutput *output, - ActivateContext *ctx) -{ - guint16 error_code = 0; - - g_source_remove(ctx->timeout_id); - ctx->timeout_id = 0; - g_signal_handler_disconnect(ctx->qmi_client, ctx->indication_id); - ctx->indication_id = 0; - - if (!qmi_indication_pdc_activate_config_output_get_indication_result(output, - &error_code, - &ctx->error)) { - g_main_loop_quit(ctx->mainloop); - return; - } - - if (error_code != 0) { - g_set_error(&ctx->error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "couldn't activate config: %s", - qmi_protocol_error_get_string((QmiProtocolError)error_code)); - g_main_loop_quit(ctx->mainloop); - return; - } - - /* assume ok */ - g_debug("successful activate configuration indication: assuming device reset is ongoing"); - g_main_loop_quit(ctx->mainloop); -} - -static void -fu_qmi_pdc_updater_activate_config_ready(GObject *qmi_client, GAsyncResult *res, gpointer user_data) -{ - ActivateContext *ctx = (ActivateContext *)user_data; - g_autoptr(QmiMessagePdcActivateConfigOutput) output = NULL; - - output = - qmi_client_pdc_activate_config_finish(QMI_CLIENT_PDC(qmi_client), res, &ctx->error); - if (output == NULL) { - /* If we didn't receive a response, this is a good indication that the device - * reset itself, we can consider this a successful operation. - * Note: not using g_error_matches() to avoid matching the domain, because the - * error may be either QMI_CORE_ERROR_TIMEOUT or MBIM_CORE_ERROR_TIMEOUT (same - * numeric value), and we don't want to build-depend on libmbim just for this. - */ - if (ctx->error->code == QMI_CORE_ERROR_TIMEOUT) { - g_debug("request to activate configuration timed out: assuming device " - "reset is ongoing"); - g_clear_error(&ctx->error); - } - g_main_loop_quit(ctx->mainloop); - return; - } - - if (!qmi_message_pdc_activate_config_output_get_result(output, &ctx->error)) { - g_main_loop_quit(ctx->mainloop); - return; - } - - /* When we activate the config, if the operation is successful, we'll just - * see the modem going away completely. So, do not consider an error the timeout - * waiting for the Activate Config indication, as that is actually a good - * thing. - */ - g_warn_if_fail(ctx->indication_id == 0); - ctx->indication_id = - g_signal_connect(QMI_CLIENT_PDC(ctx->qmi_client), - "activate-config", - G_CALLBACK(fu_qmi_pdc_updater_activate_config_indication), - ctx); - - /* don't wait forever */ - g_warn_if_fail(ctx->timeout_id == 0); - ctx->timeout_id = g_timeout_add_seconds(5, fu_qmi_pdc_updater_activate_config_timeout, ctx); -} - -static void -fu_qmi_pdc_updater_activate_config(ActivateContext *ctx) -{ - g_autoptr(QmiMessagePdcActivateConfigInput) input = NULL; - - input = qmi_message_pdc_activate_config_input_new(); - qmi_message_pdc_activate_config_input_set_config_type(input, - QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, - NULL); - qmi_message_pdc_activate_config_input_set_token(input, ctx->token++, NULL); - - g_debug("activating selected configuration..."); - qmi_client_pdc_activate_config(ctx->qmi_client, - input, - 5, - NULL, - fu_qmi_pdc_updater_activate_config_ready, - ctx); -} - -static gboolean -fu_qmi_pdc_updater_set_selected_config_timeout(gpointer user_data) -{ - ActivateContext *ctx = user_data; - - ctx->timeout_id = 0; - g_signal_handler_disconnect(ctx->qmi_client, ctx->indication_id); - ctx->indication_id = 0; - - g_set_error_literal(&ctx->error, - FWUPD_ERROR, - FWUPD_ERROR_TIMED_OUT, - "couldn't set selected config: timed out"); - g_main_loop_quit(ctx->mainloop); - - return G_SOURCE_REMOVE; -} - -static void -fu_qmi_pdc_updater_set_selected_config_indication(QmiClientPdc *client, - QmiIndicationPdcSetSelectedConfigOutput *output, - ActivateContext *ctx) -{ - guint16 error_code = 0; - - g_source_remove(ctx->timeout_id); - ctx->timeout_id = 0; - g_signal_handler_disconnect(ctx->qmi_client, ctx->indication_id); - ctx->indication_id = 0; - - if (!qmi_indication_pdc_set_selected_config_output_get_indication_result(output, - &error_code, - &ctx->error)) { - g_main_loop_quit(ctx->mainloop); - return; - } - - if (error_code != 0) { - g_set_error(&ctx->error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "couldn't set selected config: %s", - qmi_protocol_error_get_string((QmiProtocolError)error_code)); - g_main_loop_quit(ctx->mainloop); - return; - } - - g_debug("current configuration successfully selected..."); - - /* now activate config */ - fu_qmi_pdc_updater_activate_config(ctx); -} - -static void -fu_qmi_pdc_updater_set_selected_config_ready(GObject *qmi_client, - GAsyncResult *res, - gpointer user_data) -{ - ActivateContext *ctx = (ActivateContext *)user_data; - g_autoptr(QmiMessagePdcSetSelectedConfigOutput) output = NULL; - - output = - qmi_client_pdc_set_selected_config_finish(QMI_CLIENT_PDC(qmi_client), res, &ctx->error); - if (output == NULL) { - g_main_loop_quit(ctx->mainloop); - return; - } - - if (!qmi_message_pdc_set_selected_config_output_get_result(output, &ctx->error)) { - g_main_loop_quit(ctx->mainloop); - return; - } - - /* after receiving the response to our request, we now expect an indication - * with the actual result of the operation */ - g_warn_if_fail(ctx->indication_id == 0); - ctx->indication_id = - g_signal_connect(QMI_CLIENT_PDC(ctx->qmi_client), - "set-selected-config", - G_CALLBACK(fu_qmi_pdc_updater_set_selected_config_indication), - ctx); - - /* don't wait forever */ - g_warn_if_fail(ctx->timeout_id == 0); - ctx->timeout_id = - g_timeout_add_seconds(5, fu_qmi_pdc_updater_set_selected_config_timeout, ctx); -} - -static void -fu_qmi_pdc_updater_set_selected_config(ActivateContext *ctx) -{ - g_autoptr(QmiMessagePdcSetSelectedConfigInput) input = NULL; -#if !QMI_CHECK_VERSION(1, 32, 0) - QmiConfigTypeAndId type_and_id = { - .config_type = QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, - .id = ctx->digest, - }; -#endif - - input = qmi_message_pdc_set_selected_config_input_new(); -#if QMI_CHECK_VERSION(1, 32, 0) - qmi_message_pdc_set_selected_config_input_set_type_with_id_v2( - input, - QMI_PDC_CONFIGURATION_TYPE_SOFTWARE, - ctx->digest, - NULL); -#else - qmi_message_pdc_set_selected_config_input_set_type_with_id(input, &type_and_id, NULL); -#endif - qmi_message_pdc_set_selected_config_input_set_token(input, ctx->token++, NULL); - - g_debug("selecting current configuration..."); - qmi_client_pdc_set_selected_config(ctx->qmi_client, - input, - 10, - NULL, - fu_qmi_pdc_updater_set_selected_config_ready, - ctx); -} - -gboolean -fu_qmi_pdc_updater_activate(FuQmiPdcUpdater *self, GArray *digest, GError **error) -{ - g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); - ActivateContext ctx = { - .mainloop = mainloop, - .qmi_client = self->qmi_client, - .error = NULL, - .indication_id = 0, - .timeout_id = 0, - .digest = digest, - .token = 0, - }; - - fu_qmi_pdc_updater_set_selected_config(&ctx); - g_main_loop_run(mainloop); - - if (ctx.error != NULL) { - g_propagate_error(error, ctx.error); - return FALSE; - } - - return TRUE; -} - -static void -fu_qmi_pdc_updater_init(FuQmiPdcUpdater *self) -{ -} - -static void -fu_qmi_pdc_updater_finalize(GObject *object) -{ - FuQmiPdcUpdater *self = FU_QMI_PDC_UPDATER(object); - g_warn_if_fail(self->qmi_client == NULL); - g_warn_if_fail(self->qmi_device == NULL); - g_free(self->qmi_port); - G_OBJECT_CLASS(fu_qmi_pdc_updater_parent_class)->finalize(object); -} - -static void -fu_qmi_pdc_updater_class_init(FuQmiPdcUpdaterClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - object_class->finalize = fu_qmi_pdc_updater_finalize; -} - -FuQmiPdcUpdater * -fu_qmi_pdc_updater_new(const gchar *qmi_port) -{ - FuQmiPdcUpdater *self = g_object_new(FU_TYPE_QMI_PDC_UPDATER, NULL); - self->qmi_port = g_strdup(qmi_port); - return self; -} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-qmi-pdc-updater.h fwupd-2.0.20/plugins/modem-manager/fu-qmi-pdc-updater.h --- fwupd-2.0.8/plugins/modem-manager/fu-qmi-pdc-updater.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-qmi-pdc-updater.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,29 +0,0 @@ -/* - * Copyright 2019 Aleksander Morgado - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#ifndef __FU_QMI_PDC_UPDATER_H -#define __FU_QMI_PDC_UPDATER_H - -#include - -#define FU_TYPE_QMI_PDC_UPDATER (fu_qmi_pdc_updater_get_type()) -G_DECLARE_FINAL_TYPE(FuQmiPdcUpdater, fu_qmi_pdc_updater, FU, QMI_PDC_UPDATER, GObject) - -FuQmiPdcUpdater * -fu_qmi_pdc_updater_new(const gchar *qmi_port); -gboolean -fu_qmi_pdc_updater_open(FuQmiPdcUpdater *self, GError **error); -GArray * -fu_qmi_pdc_updater_write(FuQmiPdcUpdater *self, - const gchar *filename, - GBytes *blob, - GError **error); -gboolean -fu_qmi_pdc_updater_activate(FuQmiPdcUpdater *self, GArray *digest, GError **error); -gboolean -fu_qmi_pdc_updater_close(FuQmiPdcUpdater *self, GError **error); - -#endif /* __FU_QMI_PDC_UPDATER_H */ diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-sahara-loader.c fwupd-2.0.20/plugins/modem-manager/fu-sahara-loader.c --- fwupd-2.0.8/plugins/modem-manager/fu-sahara-loader.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-sahara-loader.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,422 +0,0 @@ -/* - * Copyright 2021 Quectel Wireless Solutions Co., Ltd. - * Ivan Mikhanchuk - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include -#include - -#include "fu-sahara-loader.h" -#include "fu-sahara-struct.h" - -#define FU_SAHARA_RAW_BUFFER_SIZE (4 * 1024) - -#define IO_TIMEOUT_MS 15000 - -struct _FuSaharaLoader { - GObject parent_instance; - - FuUsbDevice *usb_device; - - int ep_in; - int ep_out; - gsize maxpktsize_in; - gsize maxpktsize_out; - gboolean supports_zlp; -}; - -G_DEFINE_TYPE(FuSaharaLoader, fu_sahara_loader, G_TYPE_OBJECT) - -/* IO functions */ -static gboolean -fu_sahara_loader_find_interface(FuSaharaLoader *self, FuUsbDevice *usb_device, GError **error) -{ - g_autoptr(GPtrArray) intfs = NULL; - - /* all sahara devices use the same vid:pid pair */ - if (fu_device_get_vid(FU_DEVICE(usb_device)) != 0x05c6 || - fu_device_get_pid(FU_DEVICE(usb_device)) != 0x9008) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "wrong device and/or vendor id: 0x%04x 0x%04x", - fu_device_get_vid(FU_DEVICE(usb_device)), - fu_device_get_pid(FU_DEVICE(usb_device))); - return FALSE; - } - - /* parse usb interfaces and find suitable endpoints */ - intfs = fu_usb_device_get_interfaces(usb_device, error); - if (intfs == NULL) - return FALSE; - for (guint i = 0; i < intfs->len; i++) { - FuUsbInterface *intf = g_ptr_array_index(intfs, i); - if (fu_usb_interface_get_class(intf) == 0xFF && - fu_usb_interface_get_subclass(intf) == 0xFF && - (fu_usb_interface_get_protocol(intf) == 0xFF || - fu_usb_interface_get_protocol(intf) == 0x11)) { - FuUsbEndpoint *ep; - g_autoptr(GPtrArray) endpoints = NULL; - - endpoints = fu_usb_interface_get_endpoints(intf); - if (endpoints == NULL || endpoints->len == 0) - continue; - - for (guint j = 0; j < endpoints->len; j++) { - ep = g_ptr_array_index(endpoints, j); - if (fu_usb_endpoint_get_direction(ep) == - FU_USB_DIRECTION_DEVICE_TO_HOST) { - self->ep_in = fu_usb_endpoint_get_address(ep); - self->maxpktsize_in = - fu_usb_endpoint_get_maximum_packet_size(ep); - } else { - self->ep_out = fu_usb_endpoint_get_address(ep); - self->maxpktsize_out = - fu_usb_endpoint_get_maximum_packet_size(ep); - } - } - - fu_usb_device_add_interface(usb_device, fu_usb_interface_get_number(intf)); - - return TRUE; - } - } - - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "no valid usb interface found"); - return FALSE; -} - -gboolean -fu_sahara_loader_open(FuSaharaLoader *self, FuUsbDevice *usb_device, GError **error) -{ - if (!fu_sahara_loader_find_interface(self, usb_device, error)) - return FALSE; - if (!fu_device_open(FU_DEVICE(usb_device), error)) - return FALSE; - - self->usb_device = g_object_ref(usb_device); - - return TRUE; -} - -gboolean -fu_sahara_loader_close(FuSaharaLoader *self, GError **error) -{ - if (self->usb_device == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "usb device interface was not found"); - return FALSE; - } - if (!fu_device_close(FU_DEVICE(self->usb_device), error)) - return FALSE; - g_clear_object(&self->usb_device); - return TRUE; -} - -gboolean -fu_sahara_loader_qdl_is_open(FuSaharaLoader *self) -{ - if (self == NULL) - return FALSE; - return fu_device_has_private_flag(FU_DEVICE(self->usb_device), - FU_DEVICE_PRIVATE_FLAG_IS_OPEN); -} - -GByteArray * -fu_sahara_loader_qdl_read(FuSaharaLoader *self, GError **error) -{ - gsize actual_len = 0; - g_autoptr(GByteArray) buf = g_byte_array_sized_new(FU_SAHARA_RAW_BUFFER_SIZE); - fu_byte_array_set_size(buf, FU_SAHARA_RAW_BUFFER_SIZE, 0x00); - - if (!fu_usb_device_bulk_transfer(self->usb_device, - self->ep_in, - buf->data, - buf->len, - &actual_len, - IO_TIMEOUT_MS, - NULL, - error)) { - g_prefix_error(error, "failed to do bulk transfer (read): "); - return NULL; - } - - g_byte_array_set_size(buf, actual_len); - fu_dump_raw(G_LOG_DOMAIN, "rx packet", buf->data, buf->len); - - return g_steal_pointer(&buf); -} - -static gboolean -fu_sahara_loader_qdl_write(FuSaharaLoader *self, const guint8 *data, gsize sz, GError **error) -{ - gsize actual_len = 0; - g_autoptr(GPtrArray) chunks = NULL; - g_autoptr(GByteArray) bytes = NULL; - - /* copy const data to mutable GByteArray */ - bytes = g_byte_array_sized_new(sz); - bytes = g_byte_array_append(bytes, data, sz); - chunks = fu_chunk_array_mutable_new(bytes->data, bytes->len, 0, 0, self->maxpktsize_out); - for (guint i = 0; i < chunks->len; i++) { - FuChunk *chk = g_ptr_array_index(chunks, i); - - if (!fu_usb_device_bulk_transfer(self->usb_device, - self->ep_out, - fu_chunk_get_data_out(chk), - fu_chunk_get_data_sz(chk), - &actual_len, - IO_TIMEOUT_MS, - NULL, - error)) { - g_prefix_error(error, "failed to do bulk transfer (write data): "); - return FALSE; - } - if (actual_len != fu_chunk_get_data_sz(chk)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "only wrote %" G_GSIZE_FORMAT "bytes", - actual_len); - return FALSE; - } - } - if (self->supports_zlp && sz % self->maxpktsize_out == 0) { - /* sent zlp packet if needed */ - if (!fu_usb_device_bulk_transfer(self->usb_device, - self->ep_out, - NULL, - 0, - NULL, - IO_TIMEOUT_MS, - NULL, - error)) { - g_prefix_error(error, "failed to do bulk transfer (write zlp): "); - return FALSE; - } - } - - return TRUE; -} - -gboolean -fu_sahara_loader_qdl_write_bytes(FuSaharaLoader *self, GBytes *bytes, GError **error) -{ - gsize sz; - const guint8 *data = g_bytes_get_data(bytes, &sz); - return fu_sahara_loader_qdl_write(self, data, sz, error); -} - -void -fu_sahara_loader_set_supports_zlp(FuSaharaLoader *self, gboolean supports_zlp) -{ - self->supports_zlp = supports_zlp; -} - -static gboolean -fu_sahara_loader_write_prog(FuSaharaLoader *self, - guint32 offset, - guint32 length, - GBytes *prog, - GError **error) -{ - gsize sz; - const guint8 *data = g_bytes_get_data(prog, &sz); - - g_return_val_if_fail(offset + length <= sz, FALSE); - - g_debug("SENDING --> RAW_DATA: %u bytes (offset = %u, total = %" G_GSIZE_FORMAT ")", - length, - offset, - sz); - return fu_sahara_loader_qdl_write(self, &data[offset], length, error); -} - -static gboolean -fu_sahara_loader_send_packet(FuSaharaLoader *self, FuStructSaharaPkt *pkt, GError **error) -{ - fu_dump_raw(G_LOG_DOMAIN, "tx packet", pkt->data, pkt->len); - return fu_sahara_loader_qdl_write(self, pkt->data, pkt->len, error); -} - -static gboolean -fu_sahara_loader_send_reset_packet(FuSaharaLoader *self, GError **error) -{ - g_autoptr(GByteArray) buf = NULL; - g_autoptr(FuStructSaharaPktResetReq) st_res = NULL; - g_autoptr(FuStructSaharaPktResetRes) st_req = fu_struct_sahara_pkt_reset_req_new(); - - if (!fu_sahara_loader_send_packet(self, st_req, error)) { - g_prefix_error(error, "Failed to send reset packet: "); - return FALSE; - } - - buf = fu_sahara_loader_qdl_read(self, error); - if (buf == NULL) - return FALSE; - st_res = fu_struct_sahara_pkt_reset_res_parse(buf->data, buf->len, 0x0, error); - if (st_res == NULL) - return FALSE; - - g_debug("reset succeeded"); - return TRUE; -} - -static gboolean -fu_sahara_loader_wait_hello_rsp(FuSaharaLoader *self, GError **error) -{ - g_autoptr(FuStructSaharaPktHelloResponseReq) st_req = NULL; - g_autoptr(FuStructSaharaPktHelloRes) st_res = NULL; - g_autoptr(GByteArray) buf = NULL; - g_autoptr(GError) error_local = NULL; - - buf = fu_sahara_loader_qdl_read(self, &error_local); - if (buf == NULL) { - g_autoptr(FuStructSaharaPkt) ping = g_byte_array_sized_new(1); - g_debug("got %s, ignoring with ping", error_local->message); - g_byte_array_set_size(ping, 1); - fu_sahara_loader_send_packet(self, ping, NULL); - } - if (buf == NULL) { - buf = fu_sahara_loader_qdl_read(self, error); - if (buf == NULL) - return FALSE; - } - st_res = fu_struct_sahara_pkt_hello_res_parse(buf->data, buf->len, 0x0, error); - if (st_res == NULL) - return FALSE; - - st_req = fu_struct_sahara_pkt_hello_response_req_new(); - return fu_sahara_loader_send_packet(self, st_req, error); -} - -/* main routine */ -gboolean -fu_sahara_loader_run(FuSaharaLoader *self, GBytes *prog, GError **error) -{ - g_return_val_if_fail(prog != NULL, FALSE); - - g_debug("STATE -- SAHARA_WAIT_HELLO"); - if (!fu_sahara_loader_wait_hello_rsp(self, error)) - return FALSE; - - while (TRUE) { - guint32 command_id; - g_autoptr(GByteArray) buf = NULL; - g_autoptr(FuStructSaharaPkt) st_res = NULL; - g_autoptr(FuStructSaharaPkt) st_req = NULL; - g_autoptr(GError) error_local = NULL; - - g_debug("STATE -- SAHARA_WAIT_COMMAND"); - buf = fu_sahara_loader_qdl_read(self, error); - if (buf == NULL) - break; - st_res = fu_struct_sahara_pkt_parse(buf->data, buf->len, 0x0, error); - if (st_res == NULL) - return FALSE; - if (buf->len != fu_struct_sahara_pkt_get_hdr_length(st_res)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "received packet length is not matching"); - break; - } - - command_id = fu_struct_sahara_pkt_get_hdr_command_id(st_res); - if (command_id == FU_SAHARA_COMMAND_ID_HELLO) { - st_req = fu_struct_sahara_pkt_hello_response_req_new(); - fu_sahara_loader_send_packet(self, st_req, &error_local); - } else if (command_id == FU_SAHARA_COMMAND_ID_READ_DATA) { - g_autoptr(FuStructSaharaPktReadDataRes) st_res2 = - fu_struct_sahara_pkt_read_data_res_parse(buf->data, - buf->len, - 0x0, - error); - if (st_res2 == NULL) - return FALSE; - fu_sahara_loader_write_prog( - self, - fu_struct_sahara_pkt_read_data_res_get_offset(st_res2), - fu_struct_sahara_pkt_read_data_res_get_length(st_res2), - prog, - &error_local); - } else if (command_id == FU_SAHARA_COMMAND_ID_READ_DATA64) { - g_autoptr(FuStructSaharaPktReadData64Res) st_res2 = - fu_struct_sahara_pkt_read_data64_res_parse(buf->data, - buf->len, - 0x0, - error); - if (st_res2 == NULL) - return FALSE; - fu_sahara_loader_write_prog( - self, - fu_struct_sahara_pkt_read_data64_res_get_offset(st_res2), - fu_struct_sahara_pkt_read_data64_res_get_length(st_res2), - prog, - &error_local); - } else if (command_id == FU_SAHARA_COMMAND_ID_END_OF_IMAGE_TX) { - g_autoptr(FuStructSaharaPktEndOfImageTxRes) st_res2 = - fu_struct_sahara_pkt_end_of_image_tx_res_parse(buf->data, - buf->len, - 0x0, - error); - if (st_res2 == NULL) - return FALSE; - if (fu_struct_sahara_pkt_end_of_image_tx_res_get_status(st_res2) == - FU_SAHARA_STATUS_SUCCESS) { - st_req = fu_struct_sahara_pkt_done_req_new(); - fu_sahara_loader_send_packet(self, st_req, &error_local); - } - } else if (command_id == FU_SAHARA_COMMAND_ID_DONE_RESP) { - return TRUE; - } else { - g_warning("Unexpected packet received: cmd_id = %u, len = %u", - command_id, - fu_struct_sahara_pkt_get_hdr_length(st_res)); - } - - if (error_local != NULL) - g_warning("%s", error_local->message); - } - - fu_sahara_loader_send_reset_packet(self, NULL); - return FALSE; -} - -static void -fu_sahara_loader_init(FuSaharaLoader *self) -{ - /* supported by most devices - enable by default */ - self->supports_zlp = TRUE; -} - -static void -fu_sahara_loader_finalize(GObject *object) -{ - FuSaharaLoader *self = FU_SAHARA_LOADER(object); - if (self->usb_device != NULL) - g_object_unref(self->usb_device); - G_OBJECT_CLASS(fu_sahara_loader_parent_class)->finalize(object); -} - -static void -fu_sahara_loader_class_init(FuSaharaLoaderClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - object_class->finalize = fu_sahara_loader_finalize; -} - -FuSaharaLoader * -fu_sahara_loader_new(void) -{ - return g_object_new(FU_TYPE_SAHARA_LOADER, NULL); -} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-sahara-loader.h fwupd-2.0.20/plugins/modem-manager/fu-sahara-loader.h --- fwupd-2.0.8/plugins/modem-manager/fu-sahara-loader.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-sahara-loader.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,32 +0,0 @@ -/* - * Copyright 2021 Quectel Wireless Solutions Co., Ltd. - * Ivan Mikhanchuk - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_SAHARA_LOADER (fu_sahara_loader_get_type()) -G_DECLARE_FINAL_TYPE(FuSaharaLoader, fu_sahara_loader, FU, SAHARA_LOADER, GObject) - -FuSaharaLoader * -fu_sahara_loader_new(void); - -gboolean -fu_sahara_loader_qdl_is_open(FuSaharaLoader *self); -GByteArray * -fu_sahara_loader_qdl_read(FuSaharaLoader *self, GError **error); -gboolean -fu_sahara_loader_qdl_write_bytes(FuSaharaLoader *self, GBytes *bytes, GError **error); -void -fu_sahara_loader_set_supports_zlp(FuSaharaLoader *self, gboolean supports_zlp); - -gboolean -fu_sahara_loader_open(FuSaharaLoader *self, FuUsbDevice *usb_device, GError **error); -gboolean -fu_sahara_loader_run(FuSaharaLoader *self, GBytes *prog, GError **error); -gboolean -fu_sahara_loader_close(FuSaharaLoader *self, GError **error); diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-sahara.rs fwupd-2.0.20/plugins/modem-manager/fu-sahara.rs --- fwupd-2.0.8/plugins/modem-manager/fu-sahara.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-sahara.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,112 +0,0 @@ -// Copyright 2024 Richard Hughes -// SPDX-License-Identifier: LGPL-2.1-or-later - -#[repr(u32le)] -enum FuSaharaCommandId { - NoCmd, - Hello, - HelloResponse, - ReadData, - EndOfImageTx, - Done, - DoneResp, - Reset, - ResetResponse, - ReadData64 = 0x12, -} - -#[repr(u32le)] -enum FuSaharaStatus { - Success, - Failed, -} - -#[repr(u32le)] -enum FuSaharaMode { - ImageTxPending, - ImageTxComplete, -} - -#[derive(Parse)] -struct FuStructSaharaPkt { - hdr_command_id: FuSaharaCommandId, - hdr_length: u32le, -} - -#[derive(Default)] -struct FuStructSaharaPktHelloReq { - hdr_command_id: FuSaharaCommandId == Hello, - hdr_length: u32le, - version: u32le, - version_compatible: u32le, - max_packet_length: u32le, - mode: u32le, -} - -#[derive(Default, Parse)] -struct FuStructSaharaPktHelloRes { - hdr_command_id: FuSaharaCommandId == Hello, - hdr_length: u32le, -} - -#[derive(Default, New)] -struct FuStructSaharaPktHelloResponseReq { - hdr_command_id: FuSaharaCommandId == HelloResponse, - hdr_length: u32le == 0x30, - version: u32le == 2, - version_compatible: u32le == 1, - status: FuSaharaStatus == Success, - mode: FuSaharaMode == ImageTxPending, - reserved: [u32; 6], -} - -#[derive(Default, Parse)] -struct FuStructSaharaPktReadDataRes { - hdr_command_id: FuSaharaCommandId == ReadData, - hdr_length: u32le, - image_id: u32le, - offset: u32le, - length: u32le, -} - -#[derive(Default, Parse)] -struct FuStructSaharaPktEndOfImageTxRes { - hdr_command_id: FuSaharaCommandId == EndOfImageTx, - hdr_length: u32le, - image_id: u32le, - status: FuSaharaStatus, -} - -#[derive(Default, New)] -struct FuStructSaharaPktDoneReq { - hdr_command_id: FuSaharaCommandId == Done, - hdr_length: u32le == 0x08, -} - -#[derive(Default)] -struct FuStructSaharaPktDoneRespReq { - hdr_command_id: FuSaharaCommandId == DoneResp, - hdr_length: u32le, - image_transfer_status: u32le, -} - -#[derive(Default, New)] -struct FuStructSaharaPktResetReq { - hdr_command_id: FuSaharaCommandId == Reset, - hdr_length: u32le == 0x08, -} - -#[derive(Default, Parse)] -struct FuStructSaharaPktResetRes { - hdr_command_id: FuSaharaCommandId == ResetResponse, - hdr_length: u32le, -} - -#[derive(Default, Parse)] -struct FuStructSaharaPktReadData64Res { - hdr_command_id: FuSaharaCommandId == ReadData64, - hdr_length: u32le, - image_id: u64le, - offset: u64le, - length: u64le, -} diff -Nru fwupd-2.0.8/plugins/modem-manager/fu-self-test.c fwupd-2.0.20/plugins/modem-manager/fu-self-test.c --- fwupd-2.0.8/plugins/modem-manager/fu-self-test.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,109 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-context-private.h" +#include "fu-mm-device.h" + +static void +fu_mm_device_func(void) +{ + gboolean ret; + g_autofree gchar *str = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuMmDevice) mm_device = g_object_new(FU_TYPE_MM_DEVICE, "context", ctx, NULL); + g_autoptr(GError) error = NULL; + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + fu_device_set_physical_id(FU_DEVICE(mm_device), "/tmp"); + ret = fu_mm_device_set_autosuspend_delay(mm_device, 1500, &error); + g_assert_no_error(error); + g_assert_true(ret); + + fu_mm_device_set_inhibited(mm_device, TRUE); + g_assert_true(fu_mm_device_get_inhibited(mm_device)); + fu_mm_device_set_inhibited(mm_device, FALSE); + g_assert_false(fu_mm_device_get_inhibited(mm_device)); + + /* convert the instance IDs */ + ret = fu_mm_device_add_instance_id(mm_device, + "PCI\\SSVID_105B&SSPID_E142&REV_0000&CARRIER_CMCC", + &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_mm_device_add_instance_id(mm_device, + "PCI\\VID_17CB&PID_0308&REV_0000&CARRIER_CMCC", + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* show what we've got */ + str = fu_device_to_string(FU_DEVICE(mm_device)); + g_debug("%s", str); + + /* check it all makes sense */ + g_assert_cmpint(fu_device_get_vid(FU_DEVICE(mm_device)), ==, 0x17CB); + g_assert_cmpint(fu_device_get_pid(FU_DEVICE(mm_device)), ==, 0x0308); + g_assert_true(fu_device_has_instance_id(FU_DEVICE(mm_device), + "PCI\\VID_17CB", + FU_DEVICE_INSTANCE_FLAG_QUIRKS)); + g_assert_true(fu_device_has_instance_id(FU_DEVICE(mm_device), + "PCI\\VID_17CB&PID_0308", + FU_DEVICE_INSTANCE_FLAG_VISIBLE | + FU_DEVICE_INSTANCE_FLAG_QUIRKS)); + g_assert_true(fu_device_has_instance_id(FU_DEVICE(mm_device), + "PCI\\VID_17CB&PID_0308&SUBSYS_105BE142", + FU_DEVICE_INSTANCE_FLAG_VISIBLE | + FU_DEVICE_INSTANCE_FLAG_QUIRKS)); + + /* add rev */ + fu_device_add_private_flag(FU_DEVICE(mm_device), + FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV); + fu_mm_device_add_instance_id(mm_device, + "PCI\\VID_17CB&PID_0308&REV_0000&CARRIER_CMCC", + NULL); + fu_mm_device_add_instance_id(mm_device, + "PCI\\SSVID_105B&SSPID_E142&REV_0000&CARRIER_CMCC", + NULL); + g_assert_true(fu_device_has_instance_id(FU_DEVICE(mm_device), + "PCI\\VID_17CB&PID_0308&REV_0000", + FU_DEVICE_INSTANCE_FLAG_VISIBLE | + FU_DEVICE_INSTANCE_FLAG_QUIRKS)); + g_assert_true(fu_device_has_instance_id(FU_DEVICE(mm_device), + "PCI\\VID_17CB&PID_0308&SUBSYS_105BE142&REV_0000", + FU_DEVICE_INSTANCE_FLAG_VISIBLE | + FU_DEVICE_INSTANCE_FLAG_QUIRKS)); + + /* add branch */ + fu_device_add_private_flag(FU_DEVICE(mm_device), FU_MM_DEVICE_FLAG_USE_BRANCH); + fu_mm_device_add_instance_id(mm_device, + "PCI\\VID_17CB&PID_0308&REV_0000&CARRIER_CMCC", + NULL); + fu_mm_device_add_instance_id(mm_device, + "PCI\\SSVID_105B&SSPID_E142&REV_0000&CARRIER_CMCC", + NULL); + g_assert_true(fu_device_has_instance_id(FU_DEVICE(mm_device), + "PCI\\VID_17CB&PID_0308&REV_0000&CARRIER_CMCC", + FU_DEVICE_INSTANCE_FLAG_VISIBLE | + FU_DEVICE_INSTANCE_FLAG_QUIRKS)); + g_assert_true(fu_device_has_instance_id( + FU_DEVICE(mm_device), + "PCI\\VID_17CB&PID_0308&SUBSYS_105BE142&REV_0000&CARRIER_CMCC", + FU_DEVICE_INSTANCE_FLAG_VISIBLE | FU_DEVICE_INSTANCE_FLAG_QUIRKS)); +} + +int +main(int argc, char **argv) +{ + g_test_init(&argc, &argv, NULL); + g_test_add_func("/mm/device", fu_mm_device_func); + return g_test_run(); +} diff -Nru fwupd-2.0.8/plugins/modem-manager/meson.build fwupd-2.0.20/plugins/modem-manager/meson.build --- fwupd-2.0.8/plugins/modem-manager/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,11 +1,11 @@ -libmm_glib = dependency('mm-glib', version: '>= 1.18.0', required: get_option('plugin_modem_manager')) -libqmi_glib = dependency('qmi-glib', version: '>= 1.30.0', required: get_option('plugin_modem_manager')) -libmbim_glib = dependency('mbim-glib', version: '>= 1.26.0', required: get_option('plugin_modem_manager')) - -if libmm_glib.found() and \ - libqmi_glib.found() and \ - libmbim_glib.found() and \ - get_option('plugin_modem_manager').allowed() +libmm_glib = dependency('mm-glib', version: '>= 1.22.0', required: get_option('plugin_modem_manager')) +libqmi_glib = dependency('qmi-glib', version: '>= 1.32.0', required: get_option('plugin_modem_manager')) +libmbim_glib = dependency('mbim-glib', version: '>= 1.28.0', required: get_option('plugin_modem_manager')) + +libmm_glib.found() or subdir_done() +libqmi_glib.found() or subdir_done() +libmbim_glib.found() or subdir_done() +get_option('plugin_modem_manager').allowed() or subdir_done() cargs = ['-DG_LOG_DOMAIN="FuPluginMm"'] cargs +=['-DMM_REQUIRED_VERSION="1.10.0"'] @@ -15,18 +15,22 @@ shared_module('fu_plugin_modem_manager', rustgen.process( - 'fu-cinterion-fdl-updater.rs', - 'fu-sahara.rs', + 'fu-mm-fdl.rs', ), sources: [ - 'fu-mm-plugin.c', + 'fu-mm-backend.c', + 'fu-mm-common.c', 'fu-mm-device.c', - 'fu-qmi-pdc-updater.c', - 'fu-mbim-qdu-updater.c', - 'fu-firehose-updater.c', - 'fu-sahara-loader.c', - 'fu-cinterion-fdl-updater.c', - 'fu-dfota-updater.c', + 'fu-mm-dfota-device.c', + 'fu-mm-fastboot-device.c', + 'fu-mm-fdl-device.c', + 'fu-mm-firehose-device.c', + 'fu-mm-mbim-device.c', + 'fu-mm-mhi-qcdm-device.c', + 'fu-mm-plugin.c', + 'fu-mm-qcdm-device.c', + 'fu-mm-qdu-mbim-device.c', + 'fu-mm-qmi-device.c', ], include_directories: plugin_incdirs, install: true, @@ -41,4 +45,38 @@ libmbim_glib, ], ) + +device_tests += files( + 'tests/lenovo-em061kgl-mbim.json', + 'tests/foxconn-t99w373-mbim.json', + 'tests/qc-eg25ggc-fastboot.json', +) +enumeration_data += files( + 'tests/foxconn-t99w373-mbim-setup.json', + 'tests/qc-eg25ggc-fastboot-setup.json', +) + +if get_option('tests') + e = executable( + 'modem-manager-self-test', + sources: [ + 'fu-self-test.c', + 'fu-mm-common.c', + 'fu-mm-device.c', + ], + include_directories: plugin_incdirs, + dependencies: [ + plugin_deps, + libmm_glib, + libqmi_glib, + libmbim_glib, + ], + link_with: [ + plugin_libs, + ], + c_args: [ + cargs, + ], + ) + test('modem-manager-self-test', e) endif diff -Nru fwupd-2.0.8/plugins/modem-manager/modem-manager.quirk fwupd-2.0.20/plugins/modem-manager/modem-manager.quirk --- fwupd-2.0.8/plugins/modem-manager/modem-manager.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/modem-manager.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -1,141 +1,214 @@ # DW5821e [USB\VID_413C&PID_81D7] +GType = FuMmFastbootDevice Summary = Dell DW5821e LTE modem CounterpartGuid = USB\VID_413C&PID_81D6 # DW5821e/eSIM [USB\VID_413C&PID_81E0] +GType = FuMmFastbootDevice Summary = Dell DW5821e/eSIM LTE modem CounterpartGuid = USB\VID_413C&PID_81E1 # T77W968 [USB\VID_0489&PID_E0B4] +GType = FuMmFastbootDevice Summary = Foxconn T77w968 LTE modem CounterpartGuid = USB\VID_0489&PID_E0B7 # T77W968/eSIM [USB\VID_0489&PID_E0B5] +GType = FuMmFastbootDevice Summary = Foxconn T77w968/eSIM LTE modem CounterpartGuid = USB\VID_0489&PID_E0B8 # T99W265 with MBIM only [USB\VID_0489&PID_E0DA] +GType = FuMmQduMbimDevice Summary = Foxconn T99W265 LTE modem with MBIM only -CounterpartGuid = USB\VID_0489&PID_E0DA # T99W265 with MBIM and Serial port [USB\VID_0489&PID_E0DB] +GType = FuMmQduMbimDevice Summary = Foxconn T99W265 LTE modem with MBIM and Serial -CounterpartGuid = USB\VID_0489&PID_E0DB # T99W175 based on QC LE1.2 [PCI\VID_105B&PID_E0AB] +GType = FuMmQduMbimDevice Summary = Foxconn T99W175 5G modem -CounterpartGuid = PCI\VID_105B&PID_E0AB # T99W175(DW5930e with eSIM) [PCI\VID_105B&PID_E0B0] +GType = FuMmQduMbimDevice Summary = Foxconn T99W175 5G modem with eSIM for DW5930e -CounterpartGuid = PCI\VID_105B&PID_E0B0 # T99W175(DW5930e without eSIM) [PCI\VID_105B&PID_E0B1] +GType = FuMmQduMbimDevice Summary = Foxconn T99W175 5G modem without eSIM for DW5930e -CounterpartGuid = PCI\VID_105B&PID_E0B1 # T99W175 based on QC LE1.4 [PCI\VID_105B&PID_E0BF] +GType = FuMmQduMbimDevice Summary = Foxconn T99W175 5G modem -CounterpartGuid = PCI\VID_105B&PID_E0BF # T99W175 variant [PCI\VID_105B&PID_E0C3] +GType = FuMmQduMbimDevice Summary = Foxconn T99W175 5G modem -CounterpartGuid = PCI\VID_105B&PID_E0C3 # T99W368 Foxconn variant based on SDX65 [PCI\VID_105B&PID_E0D8] +GType = FuMmQduMbimDevice Summary = Foxconn T99W368 5G modem -CounterpartGuid = PCI\VID_105B&PID_E0D8 # T99W373 Foxconn variant based on SDX62 [PCI\VID_105B&PID_E0D9] +GType = FuMmQduMbimDevice Summary = Foxconn T99W373 5G modem -CounterpartGuid = PCI\VID_105B&PID_E0D9 # T99W373(DW5932e with eSIM) based on SDX62 [PCI\VID_105B&PID_E0F5] +GType = FuMmQduMbimDevice Summary = Foxconn T99W373 5G modem with eSIM for DW5932e -CounterpartGuid = PCI\VID_105B&PID_E0F5 # T99W373 (DW5932e without eSIM) based on SDX62 [PCI\VID_105B&PID_E0F9] +GType = FuMmQduMbimDevice Summary = Foxconn T99W373 5G modem without eSIM for DW5932e -CounterpartGuid = PCI\VID_105B&PID_E0F9 + +# T99W640 Foxconn variant based on SDX72 +[PCI\VID_105B&PID_E118] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W640 5G modem + +# T99W640(DW5934e with eSIM) based on SDX72 +[PCI\VID_105B&PID_E11D] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W640 5G modem with eSIM for DW5934e + +# T99W640 (DW5934e without eSIM) based on SDX72 +[PCI\VID_105B&PID_E11E] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W640 5G modem without eSIM for DW5934e + +# T99W696.01 based on SDX61 +[PCI\VID_17CB&PID_0308&SUBSYS_105BE142] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W696.01 5G modem for Lenovo + +# T99W696.02 based on SDX61 +[PCI\VID_17CB&PID_0308&SUBSYS_105BE143] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W696.02 5G modem for Lenovo + +# T99W696.03 based on SDX61 +[PCI\VID_17CB&PID_0308&SUBSYS_105BE144] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W696.03 5G modem for Lenovo + +# T99W696.04 based on SDX61 +[PCI\VID_17CB&PID_0308&SUBSYS_105BE145] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W696.04 5G modem for Lenovo + +# T99W696.00 based on SDX61 +[PCI\VID_17CB&PID_0308&SUBSYS_105BE146] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W696.00 5G modem + +# T99W696.05 based on SDX61 +[PCI\VID_17CB&PID_0308&SUBSYS_105BE150] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W696.05 5G modem for Lenovo + +# T99W696.06 based on SDX61 +[PCI\VID_17CB&PID_0308&SUBSYS_105BE151] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W696.06 5G modem for Lenovo + +# T99W696.07 based on SDX61 +[PCI\VID_17CB&PID_0308&SUBSYS_105BE152] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W696.07 5G modem for Lenovo + +# T99W696.08 based on SDX61 +[PCI\VID_17CB&PID_0308&SUBSYS_105BE153] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W696.08 5G modem for Lenovo + +# T99W696.09 based on SDX61 +[PCI\VID_17CB&PID_0308&SUBSYS_105BE154] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W696.09 5G modem for Lenovo + +# T99W696.10 based on SDX61 +[PCI\VID_17CB&PID_0308&SUBSYS_105BE155] +GType = FuMmQduMbimDevice +Summary = Foxconn T99W696.10 5G modem for Lenovo # Quectel EG25-G/EC25 [USB\VID_2C7C&PID_0125] Summary = Quectel EG25-G/EC25 modem -CounterpartGuid = USB\VID_18D1&PID_D00D Flags = detach-at-fastboot-has-no-response Plugin = modem_manager [USB\VID_2C7C&PID_0125&REV_0318&NAME_EG25GGB] ModemManagerBranchAtCommand = AT+GETFWBRANCH -# Qualcomm Sahara device -[USB\VID_05C6&PID_9008] -Summary = Qualcomm based modems in EDL (sahara) -Plugin = modem_manager - # Quectel EM120 firehose prog file [PCI\VID_1EAC&PID_1001] -Summary = Quectel EM120 firehose prog file +GType = FuMmMhiQcdmDevice ModemManagerFirehoseProgFile = prog_firehose_sdx24.mbn # Quectel EM160 firehose prog file [PCI\VID_1EAC&PID_1002] -Summary = Quectel EM160 firehose prog file +GType = FuMmMhiQcdmDevice +CounterpartGuid = PCI\VEN_1EAC&DEV_1002 ModemManagerFirehoseProgFile = prog_firehose_sdx24.mbn # Quectel EM160 firehose prog file [PCI\VID_1EAC&PID_100D] -Summary = Quectel EM160 firehose prog file +GType = FuMmMhiQcdmDevice +CounterpartGuid = PCI\VEN_1EAC&DEV_100D ModemManagerFirehoseProgFile = prog_firehose_sdx24.mbn # Quectel RM520 firehose prog file [PCI\VID_1EAC&PID_1004] -Summary = Quectel RM520 firehose prog file +GType = FuMmMhiQcdmDevice +CounterpartGuid = PCI\VEN_1EAC&DEV_1004 ModemManagerFirehoseProgFile = prog_firehose_sdx6x.elf # Quectel RM520 firehose prog file [PCI\VID_1EAC&PID_1007] -Summary = Quectel RM520 firehose prog file +GType = FuMmMhiQcdmDevice +CounterpartGuid = PCI\VEN_1EAC&DEV_1007 ModemManagerFirehoseProgFile = prog_firehose_sdx6x.elf # Fibocom FM101 [USB\VID_2CB7&PID_01A2] +GType = FuMmFastbootDevice Summary = Fibocom FM101-GL Module -Flags = uninhibit-modemmanager-after-fastboot-reboot,detach-at-fastboot-has-no-response -CounterpartGuid = USB\VID_2CB7&PID_D00D +Flags = detach-at-fastboot-has-no-response # Cinterion PLS8 -[USB\VID_1e2d&PID_0061] +[USB\VID_1E2D&PID_0061] +GType = FuMmFdlDevice Summary = Cinterion PLS8 AT^SFDL update Plugin = modem_manager # Netprisma LCUR57 firehose prog file [PCI\VID_203E&PID_1000] -Summary = Netprisma LCUR57 firehose prog file +GType = FuMmMhiQcdmDevice ModemManagerFirehoseProgFile = prog_firehose_sdx24.mbn # Netprisma FCUN69 firehose prog file [PCI\VID_203E&PID_1001] -Summary = Netprisma FCUN69 firehose prog file +GType = FuMmMhiQcdmDevice ModemManagerFirehoseProgFile = prog_firehose_sdx6x.elf # Fibocom NL668-EAU [USB\VID_1508&PID_1001] -Summary = Fibocom NL668-EAU LTE Module +GType = FuMmMhiQcdmDevice ModemManagerFirehoseProgFile = prog_nand_firehose_9x07.mbn Flags = disable-zlp diff -Nru fwupd-2.0.8/plugins/modem-manager/tests/foxconn-t99w373-mbim-setup.json fwupd-2.0.20/plugins/modem-manager/tests/foxconn-t99w373-mbim-setup.json --- fwupd-2.0.8/plugins/modem-manager/tests/foxconn-t99w373-mbim-setup.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/tests/foxconn-t99w373-mbim-setup.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,33 @@ +{ + "FwupdVersion": "2.0.13", + "UsbDevices": [ + { + "Created": "2025-06-20T10:35:23.098731Z", + "GType": "FuMmQduMbimDevice", + "BackendId": "/org/freedesktop/ModemManager1/Modem/0", + "DeviceFile": "/dev/wwan0mbim0", + "Events": [ + { + "Id": "MbimDeviceNew:Path=/dev/wwan0mbim0" + }, + { + "Id": "MbimDeviceOpen" + } + ], + "Version": "FDE.F0.3.0.1.1.CC.001.003", + "PhysicalId": "/sys/devices/pci0000:00/0000:00:1c.0/0000:55:00.0", + "DeviceIds": [ + "PCI\\VID_105B&PID_E0D9", + "PCI\\VID_105B&PID_E0D9&REV_0000", + "PCI\\VID_105B&PID_E0D9&CARRIER_CMCC", + "PCI\\VID_105B&PID_E0D9&REV_0000&CARRIER_CMCC", + "PCI\\VEN_105B" + ], + "Ports": { + "net": "/dev/wwan0", + "qcdm": "/dev/wwan0qcdm0", + "mbim": "/dev/wwan0mbim0" + } + } + ] +} diff -Nru fwupd-2.0.8/plugins/modem-manager/tests/foxconn-t99w373-mbim.json fwupd-2.0.20/plugins/modem-manager/tests/foxconn-t99w373-mbim.json --- fwupd-2.0.8/plugins/modem-manager/tests/foxconn-t99w373-mbim.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/tests/foxconn-t99w373-mbim.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +{ + "name": "Foxconn T99W373 (MBIM)", + "interactive": false, + "steps": [ + { + "emulation-file": "@enumeration_datadir@/foxconn-t99w373-mbim-setup.json", + "components": [ + { + "version": "FDE.F0.3.0.1.1.CC.001.003", + "protocol": "com.qualcomm.mbim_qdu", + "guids": [ + "5d0a12ac-c7d9-5bd8-9c0a-9a2b63817dbb" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/modem-manager/tests/lenovo-em061kgl-mbim.json fwupd-2.0.20/plugins/modem-manager/tests/lenovo-em061kgl-mbim.json --- fwupd-2.0.8/plugins/modem-manager/tests/lenovo-em061kgl-mbim.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/tests/lenovo-em061kgl-mbim.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +{ + "name": "Lenovo EM061KGL (MBIM -> EDL)", + "interactive": false, + "steps": [ + { + "url": "b6d45ea6cc285ae4ae7c9cdae9607a3b7b579c78c7bf1f65825a3dc707ec20cb-EM061KGLAAR01A02M2G_01.013.01.013.cab", + "emulation-url": "d2627a4a2334f567d42e7b10b8e4c2c3ff5cf0baf132cc5e607da642d6dbcea7-EM061KGLAAR01A02M2G_01.013.01.013_fixed.zip", + "components": [ + { + "version": "EM061KGLAAR01A02M2G_01.013.01.013", + "protocol": "com.qualcomm.firehose", + "guids": [ + "2bdac371-5939-5239-9dfa-eb4d4ff12bc8" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/modem-manager/tests/qc-eg25ggc-fastboot-setup.json fwupd-2.0.20/plugins/modem-manager/tests/qc-eg25ggc-fastboot-setup.json --- fwupd-2.0.8/plugins/modem-manager/tests/qc-eg25ggc-fastboot-setup.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/tests/qc-eg25ggc-fastboot-setup.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,85 @@ +{ + "UsbDevices": [ + { + "Created": "2025-03-05T15:58:10.344062Z", + "GType": "FuMmFastbootDevice", + "BackendId": "/org/freedesktop/ModemManager1/Modem/1", + "DeviceFile": "/dev/ttyUSB2", + "Events": [ + { + "Id": "#f697c6f3" + }, + { + "Id": "#1f7c4061", + "Data": "DQpFUlJPUg0K" + }, + { + "Id": "#f697c6f3" + }, + { + "Id": "#1f7c4061", + "Data": "DQpFUlJPUg0K" + }, + { + "Id": "#f697c6f3" + }, + { + "Id": "#1f7c4061", + "Data": "DQpFUlJPUg0K" + }, + { + "Id": "#ea95ac24" + }, + { + "Id": "#1f7c4061", + "Data": "DQpFUlJPUg0K" + }, + { + "Id": "#ea95ac24" + }, + { + "Id": "#1f7c4061", + "Data": "DQpFUlJPUg0K" + }, + { + "Id": "#ea95ac24" + }, + { + "Id": "#1f7c4061", + "Data": "DQpFUlJPUg0K" + }, + { + "Id": "#685152b7" + }, + { + "Id": "#1f7c4061", + "Error": 19, + "ErrorMsg": "timeout" + }, + { + "Id": "#685152b7" + }, + { + "Id": "#1f7c4061", + "Data": "DQorUUNGRzogInNlY2Jvb3RzdGF0IiwwDQoNCk9LDQo=" + } + ], + "Version": "EG25GGCR07A02M1G_30.202.30.202", + "PhysicalId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c5:00.3/usb1/1-2", + "BranchAt": "AT+GETFWBRANCH", + "DeviceIds": [ + "USB\\VID_2C7C&PID_0125&REV_0318&NAME_EG25GGC", + "USB\\VID_2C7C&PID_0125&REV_0318", + "USB\\VID_2C7C&PID_0125", + "USB\\VID_2C7C" + ], + "Ports": { + "net": "/dev/wwp197s0f3u2i4", + "at": "/dev/ttyUSB2", + "gps": "/dev/ttyUSB1", + "qmi": "/dev/cdc-wdm0" + }, + "DetachAt": "AT+QFASTBOOT" + } + ] +} diff -Nru fwupd-2.0.8/plugins/modem-manager/tests/qc-eg25ggc-fastboot.json fwupd-2.0.20/plugins/modem-manager/tests/qc-eg25ggc-fastboot.json --- fwupd-2.0.8/plugins/modem-manager/tests/qc-eg25ggc-fastboot.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/modem-manager/tests/qc-eg25ggc-fastboot.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +{ + "name": "Quectel EG25GGC (Fastboot)", + "interactive": false, + "steps": [ + { + "emulation-file": "@enumeration_datadir@/qc-eg25ggc-fastboot-setup.json", + "components": [ + { + "version": "EG25GGCR07A02M1G_30.202.30.202", + "protocol": "com.google.fastboot", + "guids": [ + "7b52b338-326c-5061-8cc6-d95c247758ad" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/msr/fu-msr-plugin.c fwupd-2.0.20/plugins/msr/fu-msr-plugin.c --- fwupd-2.0.8/plugins/msr/fu-msr-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/msr/fu-msr-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -262,7 +262,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "could not read IA32_DEBUG_INTERFACE: "); + g_prefix_error_literal(error, "could not read IA32_DEBUG_INTERFACE: "); return FALSE; } if (!fu_memread_uint32_safe(buf, @@ -279,7 +279,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "could not read IA32_TME_ACTIVATION: "); + g_prefix_error_literal(error, "could not read IA32_TME_ACTIVATION: "); return FALSE; } if (!fu_memread_uint64_safe(buf, @@ -296,7 +296,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "could not read IA32_ARCH_CAPABILITIES: "); + g_prefix_error_literal(error, "could not read IA32_ARCH_CAPABILITIES: "); return FALSE; } if (!fu_memread_uint64_safe(buf, @@ -313,7 +313,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "could not read IA32_MCU_OPT_CTRL: "); + g_prefix_error_literal(error, "could not read IA32_MCU_OPT_CTRL: "); return FALSE; } if (!fu_memread_uint64_safe(buf, @@ -332,7 +332,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "could not read PCI_MSR_AMD64_SYSCFG: "); + g_prefix_error_literal(error, "could not read PCI_MSR_AMD64_SYSCFG: "); return FALSE; } if (!fu_memread_uint32_safe(buf, @@ -349,7 +349,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "could not read PCI_MSR_AMD64_SEV: "); + g_prefix_error_literal(error, "could not read PCI_MSR_AMD64_SEV: "); return FALSE; } if (!fu_memread_uint32_safe(buf, @@ -366,7 +366,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "could not read PCI_MSR_AMD66_HWCFG: "); + g_prefix_error_literal(error, "could not read PCI_MSR_AMD66_HWCFG: "); return FALSE; } if (!fu_memread_uint64_safe(buf, @@ -390,7 +390,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "could not read IA32_BIOS_SIGN_ID: "); + g_prefix_error_literal(error, "could not read IA32_BIOS_SIGN_ID: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, "IA32_BIOS_SIGN_ID", buf, sizeof(buf)); diff -Nru fwupd-2.0.8/plugins/msr/meson.build fwupd-2.0.20/plugins/msr/meson.build --- fwupd-2.0.8/plugins/msr/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/msr/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,11 +1,14 @@ -if hsi and has_cpuid +hsi or subdir_done() +has_cpuid or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginMsr"'] plugins += {meson.current_source_dir().split('/')[-1]: true} if libsystemd.found() -install_data(['fwupd-msr.conf'], - install_dir: systemd_modules_load_dir, -) + install_data(['fwupd-msr.conf'], + install_tag: 'runtime', + install_dir: systemd_modules_load_dir, + ) endif plugin_builtins += static_library('fu_plugin_msr', @@ -17,4 +20,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/mtd/README.md fwupd-2.0.20/plugins/mtd/README.md --- fwupd-2.0.8/plugins/mtd/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -21,6 +21,14 @@ * `MTD\VENDOR_foo&NAME_baz` * `MTD\VENDOR_foo&PRODUCT_bar&NAME_baz` +Since 2.0.13, if the MTD device has a PCI parent device, this information will +be used instead, e.g. + +* `MTD\NAME_Factory` +* `MTD\VEN_1234&DEV_5678` +* `MTD\VEN_1234&DEV_5678&NAME_Factory` +* `MTD\DRIVER_xe` (quirk-only) + If the `FirmwareGType` quirk is set for the device then the firmware is read back from the device at daemon startup and parsed for the version number. In the event the firmware has multiple child images then the device GUIDs are used as firmware IDs. @@ -57,6 +65,26 @@ Since: 1.9.1 +### MtdFmapRegions + +A comma-separated list of region names that should be written to the device as images in the +specified order. Each image should have the required offset set as a physical address. + +This can be used to selectively target coreboot FMAP regions. + +Since: 2.0.17 + +### MtdFmapOffset + +An offset where the `__FMAP__` header can be found, for example `0x1090000`. + +This can be used to avoid loading and searching the entire SPI rom for a FMAP image. +If this option is set then the device `FirmwareGType` is automatically set to `FuFmapFirmware`. + +To test actual firmware blobs with mtram, use `sudo modprobe mtdram total_size=32768` + +Since: 2.0.17 + ### MtdMetadataSize The size of data to read from the MTD partition when using `FirmwareGType`. This is provided to @@ -64,6 +92,14 @@ Since: 1.9.1 +### `Flags=smbios-version-fallback` + +Fall back to the SMBIOS reported version if no version metadata could be found. +This uses a version from the DMI BIOS version string, falling back to a `pair` version format +using the BIOS major and minor release. + +Since: 2.0.18 + ## Vendor ID Security The vendor ID is set from the system vendor, for example `DMI:LENOVO` diff -Nru fwupd-2.0.8/plugins/mtd/ci.quirk fwupd-2.0.20/plugins/mtd/ci.quirk --- fwupd-2.0.8/plugins/mtd/ci.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/ci.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,5 @@ +[MTD\NAME_mtdram-test-device] +FirmwareGType = FuUswidFirmware +Flags = ~needs-reboot +MtdMetadataOffset = 0x0 +MtdMetadataSize = 0x100 diff -Nru fwupd-2.0.8/plugins/mtd/fu-mtd-device.c fwupd-2.0.20/plugins/mtd/fu-mtd-device.c --- fwupd-2.0.8/plugins/mtd/fu-mtd-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/fu-mtd-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,17 +10,26 @@ #include #endif +#ifdef HAVE_IOCTL_H +#include +#endif + #include "fu-mtd-device.h" #include "fu-mtd-ifd-device.h" -struct _FuMtdDevice { - FuUdevDevice parent_instance; +typedef struct { guint64 erasesize; guint64 metadata_offset; guint64 metadata_size; -}; -G_DEFINE_TYPE(FuMtdDevice, fu_mtd_device, FU_TYPE_UDEV_DEVICE) + /* FMAP specific */ + GPtrArray *fmap_regions; + FuFirmware *fmap_firmware; + guint64 fmap_offset; +} FuMtdDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuMtdDevice, fu_mtd_device, FU_TYPE_UDEV_DEVICE) +#define GET_PRIVATE(o) (fu_mtd_device_get_instance_private(o)) #define FU_MTD_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ @@ -28,19 +37,58 @@ fu_mtd_device_to_string(FuDevice *device, guint idt, GString *str) { FuMtdDevice *self = FU_MTD_DEVICE(device); - fwupd_codec_string_append_hex(str, idt, "EraseSize", self->erasesize); - fwupd_codec_string_append_hex(str, idt, "MetadataOffset", self->metadata_offset); - fwupd_codec_string_append_hex(str, idt, "MetadataSize", self->metadata_size); + FuMtdDevicePrivate *priv = GET_PRIVATE(self); + fwupd_codec_string_append_hex(str, idt, "EraseSize", priv->erasesize); + fwupd_codec_string_append_hex(str, idt, "MetadataOffset", priv->metadata_offset); + fwupd_codec_string_append_hex(str, idt, "MetadataSize", priv->metadata_size); + fwupd_codec_string_append_hex(str, idt, "FmapOffset", priv->fmap_offset); + if (priv->fmap_regions->len > 0) { + g_autofree gchar *fmap_regions = fu_strjoin(",", priv->fmap_regions); + fwupd_codec_string_append(str, idt, "FmapRegions", fmap_regions); + } } -static FuFirmware * -fu_mtd_device_read_firmware(FuDevice *device, FuProgress *progress, GError **error) +static gchar * +fu_mtd_device_convert_version(FuDevice *device, guint64 version_raw) { FuMtdDevice *self = FU_MTD_DEVICE(device); + + if (fu_device_get_version_format(self) == FWUPD_VERSION_FORMAT_NUMBER) + return g_strdup_printf("%" G_GUINT64_FORMAT, version_raw); + + return NULL; +} + +static GInputStream * +fu_mtd_device_read_stream(FuMtdDevice *self, FuProgress *progress, GError **error) +{ + FuDeviceEvent *event = NULL; const gchar *fn; - g_autoptr(FuFirmware) firmware = NULL; + g_autofree gchar *event_id = NULL; g_autoptr(GInputStream) stream = NULL; - g_autoptr(GInputStream) stream_partial = NULL; + + /* need event ID */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) || + fu_context_has_flag(fu_device_get_context(FU_DEVICE(self)), + FU_CONTEXT_FLAG_SAVE_EVENTS)) { + event_id = g_strdup("MtdReadFirmware"); + } + + /* emulated */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED)) { + g_autoptr(GBytes) blob = NULL; + event = fu_device_load_event(FU_DEVICE(self), event_id, error); + if (event == NULL) + return NULL; + blob = fu_device_event_get_bytes(event, "Data", error); + if (blob == NULL) + return NULL; + return g_memory_input_stream_new_from_bytes(blob); + } + + /* save */ + if (event_id != NULL) + event = fu_device_save_event(FU_DEVICE(self), event_id); /* read contents at the search offset */ fn = fu_udev_device_get_device_file(FU_UDEV_DEVICE(self)); @@ -53,88 +101,352 @@ } stream = fu_input_stream_from_path(fn, error); if (stream == NULL) { - g_prefix_error(error, "failed to open device: "); + g_prefix_error_literal(error, "failed to open device: "); return NULL; } - if (self->metadata_size > 0) { - stream_partial = fu_partial_input_stream_new(stream, - self->metadata_offset, - self->metadata_size, - error); - if (stream_partial == NULL) + + /* save response */ + if (event != NULL) { + g_autoptr(GBytes) blob = NULL; + blob = fu_input_stream_read_bytes(stream, 0x0, G_MAXSIZE, progress, error); + if (blob == NULL) return NULL; + fu_device_event_set_bytes(event, "Data", blob); + } + + /* success */ + return g_steal_pointer(&stream); +} + +static FuFirmware * +fu_mtd_device_read_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuMtdDevice *self = FU_MTD_DEVICE(device); + GType firmware_gtype = fu_device_get_firmware_gtype(device); + g_autoptr(FuFirmware) firmware = g_object_new(firmware_gtype, NULL); + g_autoptr(GInputStream) stream = NULL; + + /* parse as firmware image */ + stream = fu_mtd_device_read_stream(self, progress, error); + if (stream == NULL) + return NULL; + if (!fu_firmware_parse_stream(firmware, + stream, + 0x0, + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM | + FU_FIRMWARE_PARSE_FLAG_ONLY_PARTITION_LAYOUT, + error)) { + g_prefix_error_literal(error, "failed to parse image: "); + return NULL; + } + + /* success */ + return g_steal_pointer(&firmware); +} + +static gboolean +fu_mtd_device_metadata_ensure_version_from_image(FuMtdDevice *self, + FuFirmware *firmware, + GError **error) +{ + if (fu_firmware_get_version(firmware) != NULL) { + fu_device_set_version(FU_DEVICE(self), /* nocheck:set-version */ + fu_firmware_get_version(firmware)); + } + if (fu_firmware_get_version_raw(firmware) != G_MAXUINT64) + fu_device_set_version_raw(FU_DEVICE(self), fu_firmware_get_version_raw(firmware)); + + /* success */ + return TRUE; +} + +static gboolean +fu_mtd_device_metadata_load_uswid(FuMtdDevice *self, GInputStream *stream, GError **error) +{ + FuMtdDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuFirmware) img0 = NULL; + g_autoptr(FuFirmware) firmware = fu_uswid_firmware_new(); + g_autoptr(GInputStream) stream_partial = NULL; + + /* cut it down to something reasonable, then parse */ + if (priv->metadata_offset > 0 || priv->metadata_size > 0) { + stream_partial = fu_partial_input_stream_new( + stream, + priv->metadata_offset, + priv->metadata_size > 0 ? priv->metadata_size + : FU_FIRMWARE_SEARCH_MAGIC_BUFSZ_MAX, + error); + if (stream_partial == NULL) + return FALSE; } else { stream_partial = g_object_ref(stream); } - firmware = g_object_new(fu_device_get_firmware_gtype(FU_DEVICE(self)), NULL); if (!fu_firmware_parse_stream(firmware, stream_partial, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to parse image: "); - return NULL; + g_prefix_error_literal(error, "failed to parse uSWID: "); + return FALSE; + } + + /* coSWID */ + img0 = fu_firmware_get_image_by_idx(firmware, 0, error); + if (img0 == NULL) { + g_prefix_error_literal(error, "no coSWID image: "); + return FALSE; } /* success */ - return g_steal_pointer(&firmware); + return fu_mtd_device_metadata_ensure_version_from_image(self, img0, error); } static gboolean -fu_mtd_device_metadata_load(FuMtdDevice *self, GError **error) +fu_mtd_device_metadata_load_fmap(FuMtdDevice *self, GInputStream *stream, GError **error) { - GPtrArray *instance_ids; - g_autoptr(FuFirmware) firmware_child = NULL; - g_autoptr(GFile) file = NULL; + FuMtdDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuFirmware) firmware = fu_fmap_firmware_new(); + g_autoptr(FuFirmware) firmware_sbom = fu_uswid_firmware_new(); + g_autoptr(FuFirmware) img0 = NULL; + g_autoptr(GInputStream) stream_sbom = NULL; g_autoptr(GPtrArray) imgs = NULL; - g_autoptr(FuFirmware) firmware = NULL; - /* read firmware from stream */ - firmware = fu_mtd_device_read_firmware(FU_DEVICE(self), NULL, error); - if (firmware == NULL) + /* parse as firmware image */ + if (!fu_firmware_parse_stream(firmware, + stream, + priv->fmap_offset, + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM | + FU_FIRMWARE_PARSE_FLAG_ONLY_PARTITION_LAYOUT, + error)) { + g_prefix_error_literal(error, "failed to parse image: "); return FALSE; + } + stream_sbom = fu_firmware_get_image_by_id_stream(firmware, "SBOM", error); + if (stream_sbom == NULL) { + g_prefix_error_literal(error, "no SBOM image: "); + return FALSE; + } - /* add each IFD image as a sub-device */ - imgs = fu_firmware_get_images(firmware); - if (FU_IS_IFD_FIRMWARE(firmware)) { - for (guint i = 0; i < imgs->len; i++) { - FuIfdImage *img = g_ptr_array_index(imgs, i); - g_autoptr(FuMtdIfdDevice) child = - fu_mtd_ifd_device_new(FU_DEVICE(self), img); - fu_device_add_child(FU_DEVICE(self), FU_DEVICE(child)); + if (!fu_firmware_parse_stream(firmware_sbom, + stream_sbom, + 0x0, + FU_FIRMWARE_PARSE_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to parse uSWID from FMAP SBOM image: "); + return FALSE; + } + + /* coSWID, so find correct image */ + imgs = fu_firmware_get_images(firmware_sbom); + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + if (g_strcmp0(fu_coswid_firmware_get_persistent_id(FU_COSWID_FIRMWARE(img)), + "org.coreboot.rocks") == 0 || + g_strcmp0(fu_coswid_firmware_get_device_id(FU_COSWID_FIRMWARE(img)), + "SI_BIOS") == 0) { + return fu_mtd_device_metadata_ensure_version_from_image(self, img, error); } - return TRUE; } - /* find the firmware child that matches any of the device GUID, then use the first - * child that have a version, and finally use the main firmware as a fallback */ - instance_ids = fu_device_get_instance_ids(FU_DEVICE(self)); - 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); - firmware_child = fu_firmware_get_image_by_id(firmware, guid, NULL); - if (firmware_child != NULL) - break; + /* fallback to the *first* image */ + img0 = fu_firmware_get_image_by_idx(firmware_sbom, 0, error); + if (img0 == NULL) { + g_prefix_error_literal(error, "no first image: "); + return FALSE; } + + /* success */ + return fu_mtd_device_metadata_ensure_version_from_image(self, img0, error); +} + +static gboolean +fu_mtd_device_metadata_load_ifd(FuMtdDevice *self, GInputStream *stream, GError **error) +{ + FuMtdDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuFirmware) firmware = fu_ifd_firmware_new(); + g_autoptr(GPtrArray) imgs = NULL; + + if (!fu_firmware_parse_stream(firmware, + stream, + 0x0, + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM | + FU_FIRMWARE_PARSE_FLAG_ONLY_PARTITION_LAYOUT, + error)) { + g_prefix_error_literal(error, "failed to parse image: "); + return FALSE; + } + imgs = fu_firmware_get_images(firmware); for (guint i = 0; i < imgs->len; i++) { - FuFirmware *firmare_tmp = g_ptr_array_index(imgs, i); - if (fu_firmware_get_version(firmare_tmp) != NULL || - fu_firmware_get_version_raw(firmare_tmp) != 0) { - firmware_child = g_object_ref(firmare_tmp); - break; + FuIfdImage *img = g_ptr_array_index(imgs, i); + g_autoptr(FuMtdIfdDevice) child = fu_mtd_ifd_device_new(FU_DEVICE(self), img); + + /* if any region is not readable by the BIOS master, fwupd cannot do + * verification on the parent MTD device as a whole */ + if (!fu_device_probe(FU_DEVICE(child), error)) + return FALSE; + if (!fu_device_has_flag(child, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE)) { + fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); } + fu_device_add_child(FU_DEVICE(self), FU_DEVICE(child)); } - if (firmware_child == NULL) - firmware_child = g_object_ref(firmware); - /* copy over the version */ - if (fu_firmware_get_version(firmware_child) != NULL) { - fu_device_set_version(FU_DEVICE(self), /* nocheck:set-version */ - fu_firmware_get_version(firmware_child)); + /* also allow uSWID SBOMs, but only if specified */ + if (priv->metadata_offset > 0 || priv->metadata_size > 0) { + if (!fu_mtd_device_metadata_load_uswid(self, stream, error)) + return FALSE; } - if (fu_firmware_get_version_raw(firmware_child) != G_MAXUINT64) { - fu_device_set_version_raw(FU_DEVICE(self), - fu_firmware_get_version_raw(firmware_child)); + + /* success */ + return TRUE; +} + +static gboolean +fu_mtd_device_metadata_load_versions(FuMtdDevice *self, GError **error) +{ + GType firmware_gtype = fu_device_get_firmware_gtype(FU_DEVICE(self)); + g_autoptr(GInputStream) stream = NULL; + + /* read firmware from stream */ + stream = fu_mtd_device_read_stream(self, NULL, error); + if (stream == NULL) + return FALSE; + + /* read FMAP, which may have an SBOM section */ + if (firmware_gtype == FU_TYPE_FMAP_FIRMWARE) + return fu_mtd_device_metadata_load_fmap(self, stream, error); + + /* add each IFD image as a sub-device */ + if (firmware_gtype == FU_TYPE_IFD_FIRMWARE) + return fu_mtd_device_metadata_load_ifd(self, stream, error); + + /* just search entire image */ + if (firmware_gtype == FU_TYPE_USWID_FIRMWARE) + return fu_mtd_device_metadata_load_uswid(self, stream, error); + + /* success */ + return TRUE; +} + +static gboolean +fu_mtd_device_ensure_version_smbios_fallback(FuMtdDevice *self, GError **error) +{ + FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); + const gchar *version; + const gchar *version_major; + const gchar *version_minor; + g_autoptr(FuDevice) device_target = NULL; + + /* use the BIOS child if present, otherwise set on the parent device */ + device_target = fu_device_get_child_by_logical_id(FU_DEVICE(self), "bios", NULL); + if (device_target == NULL) + device_target = g_object_ref(FU_DEVICE(self)); + + /* SMBIOS BIOS version */ + version = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BIOS_VERSION); + if (version != NULL) { + /* some Lenovo hardware requires a specific prefix for the EC, + * so strip it before we use ensure-semver */ + if (strlen(version) > 9 && g_str_has_prefix(version, "CBET")) + version += 9; + + /* weird Lenovo version in format 'N3VET59W (1.59 )' */ + if (g_pattern_match_simple("???????? (*.*)", version)) { + fu_device_add_private_flag(device_target, + FU_DEVICE_PRIVATE_FLAG_ENSURE_SEMVER); + fu_device_set_version_format(device_target, FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_version(device_target, version + 8); + return TRUE; + } + + /* generic string */ + if (fu_device_get_version_format(device_target) == FWUPD_VERSION_FORMAT_UNKNOWN) { + fu_device_set_version_format(device_target, + fu_version_guess_format(version)); + } + fu_device_set_version(device_target, version); + return TRUE; + } + + /* BIOS [hex] major/minor release */ + version_major = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BIOS_MAJOR_RELEASE); + version_minor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_BIOS_MINOR_RELEASE); + if (version_major != NULL && version_minor != NULL) { + guint64 major = 0; + guint64 minor = 0; + g_autofree gchar *tmp = NULL; + + if (!fu_strtoull(version_major, &major, 0x0, G_MAXUINT, FU_INTEGER_BASE_16, error)) + return FALSE; + if (!fu_strtoull(version_minor, &minor, 0x0, G_MAXUINT, FU_INTEGER_BASE_16, error)) + return FALSE; + tmp = g_strdup_printf("%u.%u", (guint)major, (guint)minor); + fu_device_set_version_format(device_target, FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_version(device_target, tmp); + return TRUE; + } + + /* no version for you */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no SMBIOS MTD fallback values"); + return FALSE; +} + +static gboolean +fu_mtd_device_ensure_lockout_inhibit(FuMtdDevice *self, GError **error) +{ + g_autoptr(FuDevice) parent_device = NULL; + struct { + const gchar *attr; + const gchar *logical_id; + } lockouts[] = { + {"intel_spi_protected", NULL}, + {"intel_spi_bios_locked", "bios"}, + }; + + /* preserve compat with older emulation files */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) && + !fu_device_check_fwupd_version(FU_DEVICE(self), "2.0.18")) + return TRUE; + + /* look for PCI parent */ + parent_device = fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), "pci", NULL); + if (parent_device == NULL) + return TRUE; + if (!fu_device_probe(parent_device, error)) + return FALSE; + for (guint i = 0; i < G_N_ELEMENTS(lockouts); i++) { + g_autoptr(GError) error_local = NULL; + g_autofree gchar *value = NULL; + + value = fu_udev_device_read_sysfs(FU_UDEV_DEVICE(parent_device), + lockouts[i].attr, + FU_UDEV_DEVICE_ATTR_READ_TIMEOUT_DEFAULT, + &error_local); + if (value == NULL) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + g_debug("ignoring: %s", error_local->message); + continue; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + if (g_strcmp0(value, "1") != 0) + continue; + if (lockouts[i].logical_id != NULL) { + g_autoptr(FuDevice) child_device = NULL; + child_device = fu_device_get_child_by_logical_id(FU_DEVICE(self), + lockouts[i].logical_id, + error); + if (child_device == NULL) + return FALSE; + fu_device_add_problem(child_device, FWUPD_DEVICE_PROBLEM_FIRMWARE_LOCKED); + } else { + fu_device_add_problem(FU_DEVICE(self), + FWUPD_DEVICE_PROBLEM_FIRMWARE_LOCKED); + break; + } } /* success */ @@ -145,17 +457,44 @@ fu_mtd_device_setup(FuDevice *device, GError **error) { FuMtdDevice *self = FU_MTD_DEVICE(device); - GType firmware_gtype = fu_device_get_firmware_gtype(device); + FuMtdDevicePrivate *priv = GET_PRIVATE(self); + gsize firmware_size_max = fu_device_get_firmware_size_max(device); g_autoptr(GError) error_local = NULL; + /* sanity check */ + if (priv->metadata_offset > firmware_size_max) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "offset of metadata (0x%x) greater than image size (0x%x)", + (guint)priv->metadata_offset, + (guint)firmware_size_max); + return FALSE; + } + if (priv->metadata_size > firmware_size_max - priv->metadata_offset) { + priv->metadata_size = firmware_size_max - priv->metadata_offset; + g_debug("truncating metadata size to 0x%x", (guint)priv->metadata_size); + } + /* nothing to do */ - if (firmware_gtype == G_TYPE_INVALID) + if (fu_device_get_firmware_gtype(device) == G_TYPE_INVALID) return TRUE; - if (!fu_mtd_device_metadata_load(self, &error_local)) { - g_warning("no version metadata found: %s", error_local->message); + if (!fu_mtd_device_metadata_load_versions(self, &error_local)) { + g_debug("no version metadata found: %s", error_local->message); return TRUE; } + /* no version was found */ + if (fu_device_get_version(device) == NULL && + fu_device_has_private_flag(device, FU_MTD_DEVICE_FLAG_SMBIOS_VERSION_FALLBACK)) { + if (!fu_mtd_device_ensure_version_smbios_fallback(self, error)) + return FALSE; + } + + /* prevented by policy */ + if (!fu_mtd_device_ensure_lockout_inhibit(self, error)) + return FALSE; + /* success */ return TRUE; } @@ -187,13 +526,14 @@ { FuContext *ctx = fu_device_get_context(device); FuMtdDevice *self = FU_MTD_DEVICE(device); - const gchar *vendor; + FuMtdDevicePrivate *priv = GET_PRIVATE(self); guint64 flags = 0; guint64 size = 0; g_autofree gchar *attr_flags = NULL; g_autofree gchar *attr_size = NULL; g_autofree gchar *attr_name = NULL; g_autoptr(GError) error_local = NULL; + g_autoptr(FuDevice) parent_device = NULL; /* FuUdevDevice->probe */ if (!FU_DEVICE_CLASS(fu_mtd_device_parent_class)->probe(device, error)) @@ -230,19 +570,62 @@ if (attr_name != NULL) fu_device_set_name(FU_DEVICE(self), attr_name); - /* set vendor ID as the BIOS vendor */ - vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_MANUFACTURER); - fu_device_build_vendor_id(device, "DMI", vendor); - - /* use vendor and product as an optional instance ID prefix */ - fu_device_add_instance_strsafe(device, "NAME", attr_name); - fu_device_add_instance_strsafe(device, "VENDOR", vendor); - fu_device_add_instance_strsafe(device, - "PRODUCT", - fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_PRODUCT_NAME)); - fu_device_build_instance_id(device, NULL, "MTD", "NAME", NULL); - fu_device_build_instance_id(device, NULL, "MTD", "VENDOR", "NAME", NULL); - fu_device_build_instance_id(device, NULL, "MTD", "VENDOR", "PRODUCT", "NAME", NULL); + /* MTD devices backed by PCI should use that for identification */ + parent_device = fu_device_get_backend_parent_with_subsystem(device, "pci", NULL); + if (parent_device != NULL) { + const gchar *driver; + + /* ensure the parent gets probed (needed for emulation) */ + if (!fu_device_probe(parent_device, error)) + return FALSE; + + fu_device_incorporate( + device, + parent_device, + FU_DEVICE_INCORPORATE_FLAG_VENDOR | FU_DEVICE_INCORPORATE_FLAG_VENDOR_IDS | + FU_DEVICE_INCORPORATE_FLAG_VID | FU_DEVICE_INCORPORATE_FLAG_PID | + FU_DEVICE_INCORPORATE_FLAG_PHYSICAL_ID); + + if (fu_device_get_version(device) == NULL) + fu_device_set_version_raw( + device, + fu_pci_device_get_revision(FU_PCI_DEVICE(parent_device))); + + /* sometimes we want to ignore whole-classes of devices, e.g. the xe GPUs */ + driver = fu_udev_device_get_driver(FU_UDEV_DEVICE(parent_device)); + if (driver != NULL) { + fu_device_add_instance_strsafe(device, "DRIVER", driver); + fu_device_build_instance_id_full(device, + FU_DEVICE_INSTANCE_FLAG_QUIRKS | + FU_DEVICE_INSTANCE_FLAG_GENERIC, + NULL, + "MTD", + "DRIVER", + NULL); + } + + fu_device_add_instance_strsafe(device, "NAME", attr_name); + fu_device_build_instance_id(device, NULL, "MTD", "NAME", NULL); + fu_device_build_instance_id(device, NULL, "MTD", "VEN", "DEV", NULL); + fu_device_build_instance_id(device, NULL, "MTD", "VEN", "DEV", "NAME", NULL); + } else { + const gchar *vendor; + + /* set vendor ID as the BIOS vendor */ + vendor = fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_MANUFACTURER); + fu_device_build_vendor_id(device, "DMI", vendor); + + /* use vendor and product as an optional instance ID prefix */ + fu_device_add_instance_strsafe(device, "NAME", attr_name); + fu_device_add_instance_strsafe(device, "VENDOR", vendor); + fu_device_add_instance_strsafe( + device, + "PRODUCT", + fu_context_get_hwid_value(ctx, FU_HWIDS_KEY_PRODUCT_NAME)); + fu_device_build_instance_id(device, NULL, "MTD", "NAME", NULL); + fu_device_build_instance_id(device, NULL, "MTD", "VENDOR", "NAME", NULL); + fu_device_build_instance_id(device, NULL, "MTD", "VENDOR", "PRODUCT", "NAME", NULL); + } /* get properties about the device */ attr_size = fu_udev_device_read_sysfs(FU_UDEV_DEVICE(self), @@ -264,7 +647,7 @@ if (attr_erasesize == NULL) return FALSE; if (!fu_strtoull(attr_erasesize, - &self->erasesize, + &priv->erasesize, 0, G_MAXUINT64, FU_INTEGER_BASE_AUTO, @@ -282,15 +665,20 @@ } static gboolean -fu_mtd_device_erase(FuMtdDevice *self, GInputStream *stream, FuProgress *progress, GError **error) +fu_mtd_device_erase(FuMtdDevice *self, + GInputStream *stream, + gsize offset, + FuProgress *progress, + GError **error) { #ifdef HAVE_MTD_USER_H + FuMtdDevicePrivate *priv = GET_PRIVATE(self); g_autoptr(FuChunkArray) chunks = NULL; chunks = fu_chunk_array_new_from_stream(stream, - FU_CHUNK_ADDR_OFFSET_NONE, + offset, FU_CHUNK_PAGESZ_NONE, - self->erasesize, + priv->erasesize, error); if (chunks == NULL) return FALSE; @@ -311,8 +699,19 @@ return FALSE; erase.start = fu_chunk_get_address(chk); erase.length = fu_chunk_get_data_sz(chk); + + /* the last chunk may be smaller than the erasesize. if it is, extend the last erase + * up to the erasesize */ + if (erase.length < priv->erasesize) { + g_debug("extending last erase from %" G_GUINT32_FORMAT + " bytes to %" G_GUINT64_FORMAT " bytes", + erase.length, + priv->erasesize); + erase.length = priv->erasesize; + } + if (!fu_ioctl_execute(ioctl, - 2, + MEMERASE, (guint8 *)&erase, sizeof(erase), NULL, @@ -345,7 +744,7 @@ /* rewind */ if (!fu_udev_device_seek(FU_UDEV_DEVICE(self), 0x0, error)) { - g_prefix_error(error, "failed to rewind: "); + g_prefix_error_literal(error, "failed to rewind: "); return FALSE; } @@ -421,16 +820,14 @@ static gboolean fu_mtd_device_write_verify(FuMtdDevice *self, GInputStream *stream, + gsize offset, FuProgress *progress, GError **error) { g_autoptr(FuChunkArray) chunks = NULL; - chunks = fu_chunk_array_new_from_stream(stream, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - 10 * 1024, - error); + chunks = + fu_chunk_array_new_from_stream(stream, offset, FU_CHUNK_PAGESZ_NONE, 10 * 1024, error); if (chunks == NULL) return FALSE; @@ -489,6 +886,135 @@ } static gboolean +fu_mtd_device_write_stream(FuMtdDevice *self, + GInputStream *stream, + gsize offset, + FuProgress *progress, + GError **error) +{ + FuMtdDevicePrivate *priv = GET_PRIVATE(self); + + /* just one step required */ + if (priv->erasesize == 0) + return fu_mtd_device_write_verify(self, stream, offset, progress, error); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 50, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); + + /* erase */ + if (!fu_mtd_device_erase(self, stream, offset, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* write */ + if (!fu_mtd_device_write_verify(self, + stream, + offset, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +gboolean +fu_mtd_device_write_image(FuMtdDevice *self, FuFirmware *img, FuProgress *progress, GError **error) +{ + g_autoptr(GInputStream) img_stream = NULL; + + img_stream = fu_firmware_get_stream(img, error); + if (img_stream == NULL) + return FALSE; + g_debug("writing image %s @0x%x", + fu_firmware_get_id(img), + (guint)fu_firmware_get_addr(img)); + return fu_mtd_device_write_stream(self, + img_stream, + fu_firmware_get_addr(img), + progress, + error); +} + +static FuFirmware * +fu_mtd_device_fmap_prepare_firmware(FuMtdDevice *self, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ + FuMtdDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuFirmware) firmware = fu_fmap_firmware_new(); + + /* parse FMAP header */ + if (!fu_firmware_parse_stream(firmware, stream, priv->fmap_offset, flags, error)) + return NULL; + + /* check each FMAP area in order */ + for (guint i = 0; i < priv->fmap_regions->len; i++) { + const gchar *fmap_region = g_ptr_array_index(priv->fmap_regions, i); + g_autoptr(FuFirmware) img_device = NULL; + g_autoptr(FuFirmware) img_firmware = NULL; + + img_device = fu_firmware_get_image_by_id(priv->fmap_firmware, fmap_region, error); + if (img_device == NULL) + return NULL; + img_firmware = fu_firmware_get_image_by_id(firmware, fmap_region, error); + if (img_firmware == NULL) + return NULL; + + /* check they're compatible */ + if (fu_firmware_get_offset(img_device) != fu_firmware_get_offset(img_firmware)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "FMAP region %s moved, device @0x%x and firmware @0x%x", + fmap_region, + (guint)fu_firmware_get_offset(img_device), + (guint)fu_firmware_get_offset(img_firmware)); + return NULL; + } + if (fu_firmware_get_size(img_device) != fu_firmware_get_size(img_firmware)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "FMAP region %s size mismatch, device 0x%x and firmware 0x%x", + fmap_region, + (guint)fu_firmware_get_size(img_device), + (guint)fu_firmware_get_size(img_firmware)); + return NULL; + } + } + + /* success */ + return g_steal_pointer(&firmware); +} + +static FuFirmware * +fu_mtd_device_prepare_firmware(FuDevice *device, + GInputStream *stream, + FuProgress *progress, + FuFirmwareParseFlags flags, + GError **error) +{ + FuMtdDevice *self = FU_MTD_DEVICE(device); + FuMtdDevicePrivate *priv = GET_PRIVATE(self); + g_autoptr(FuFirmware) firmware = fu_firmware_new(); + + /* FMAP */ + if (priv->fmap_firmware != NULL) + return fu_mtd_device_fmap_prepare_firmware(self, stream, flags, error); + + /* plain blob */ + if (!fu_firmware_parse_stream(firmware, stream, 0x0, flags, error)) + return NULL; + return g_steal_pointer(&firmware); +} + +static gboolean fu_mtd_device_write_firmware(FuDevice *device, FuFirmware *firmware, FuProgress *progress, @@ -496,6 +1022,7 @@ GError **error) { FuMtdDevice *self = FU_MTD_DEVICE(device); + FuMtdDevicePrivate *priv = GET_PRIVATE(self); gsize streamsz = 0; g_autoptr(GInputStream) stream = NULL; @@ -515,25 +1042,28 @@ return FALSE; } - /* just one step required */ - if (self->erasesize == 0) - return fu_mtd_device_write_verify(self, stream, progress, error); + /* just a random blob */ + if (priv->fmap_regions->len == 0) + return fu_mtd_device_write_stream(self, stream, 0, progress, error); - /* progress */ + /* write each area in order */ fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 50, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); - - /* erase */ - if (!fu_mtd_device_erase(self, stream, fu_progress_get_child(progress), error)) - return FALSE; - fu_progress_step_done(progress); - - /* write */ - if (!fu_mtd_device_write_verify(self, stream, fu_progress_get_child(progress), error)) - return FALSE; - fu_progress_step_done(progress); + fu_progress_set_steps(progress, priv->fmap_regions->len); + for (guint i = 0; i < priv->fmap_regions->len; i++) { + const gchar *fmap_region = g_ptr_array_index(priv->fmap_regions, i); + g_autoptr(FuFirmware) img = NULL; + + img = fu_firmware_get_image_by_id(firmware, fmap_region, error); + if (img == NULL) { + g_prefix_error(error, "no FMAP region %s: ", fmap_region); + return FALSE; + } + if (!fu_mtd_device_write_image(self, img, fu_progress_get_child(progress), error)) { + g_prefix_error(error, "failed to write %s: ", fmap_region); + return FALSE; + } + fu_progress_step_done(progress); + } /* success */ return TRUE; @@ -543,11 +1073,12 @@ fu_mtd_device_set_quirk_kv(FuDevice *device, const gchar *key, const gchar *value, GError **error) { FuMtdDevice *self = FU_MTD_DEVICE(device); + FuMtdDevicePrivate *priv = GET_PRIVATE(self); /* load from quirks */ if (g_strcmp0(key, "MtdMetadataOffset") == 0) { return fu_strtoull(value, - &self->metadata_offset, + &priv->metadata_offset, 0x0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, @@ -555,12 +1086,44 @@ } if (g_strcmp0(key, "MtdMetadataSize") == 0) { return fu_strtoull(value, - &self->metadata_size, - 0x100, + &priv->metadata_size, + 0x0, FU_FIRMWARE_SEARCH_MAGIC_BUFSZ_MAX, FU_INTEGER_BASE_AUTO, error); } + if (g_strcmp0(key, "MtdFmapOffset") == 0) { + if (!fu_strtoull(value, + &priv->fmap_offset, + 0x0, + G_MAXUINT32, + FU_INTEGER_BASE_AUTO, + error)) + return FALSE; + fu_device_set_firmware_gtype(device, FU_TYPE_FMAP_FIRMWARE); + return TRUE; + } + if (g_strcmp0(key, "MtdFmapRegions") == 0) { + g_auto(GStrv) split = g_strsplit(value, ",", 0); + for (guint i = 0; split != NULL && split[i] != NULL; i++) { + if (split[i][0] == '\0') { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "an empty region is not supported"); + return FALSE; + } + g_ptr_array_add(priv->fmap_regions, fu_strstrip(split[i])); + } + if (priv->fmap_regions->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "regions cannot be empty"); + return FALSE; + } + return TRUE; + } /* failed */ g_set_error_literal(error, @@ -573,28 +1136,53 @@ static void fu_mtd_device_init(FuMtdDevice *self) { - self->metadata_size = FU_FIRMWARE_SEARCH_MAGIC_BUFSZ_MAX; + FuMtdDevicePrivate *priv = GET_PRIVATE(self); + g_type_ensure(FU_TYPE_USWID_FIRMWARE); + priv->fmap_regions = g_ptr_array_new_with_free_func(g_free); fu_device_set_summary(FU_DEVICE(self), "Memory Technology Device"); fu_device_add_protocol(FU_DEVICE(self), "org.infradead.mtd"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_ICON); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_NAME); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_SIGNED); - fu_device_add_icon(FU_DEVICE(self), "drive-harddisk-solidstate"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_VENDOR); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_VERFMT); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_INHIBIT_CHILDREN); + fu_device_register_private_flag(FU_DEVICE(self), + FU_MTD_DEVICE_FLAG_SMBIOS_VERSION_FALLBACK); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_DRIVE_SSD); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_SYNC); } static void +fu_mtd_device_finalize(GObject *object) +{ + FuMtdDevice *self = FU_MTD_DEVICE(object); + FuMtdDevicePrivate *priv = GET_PRIVATE(self); + g_ptr_array_unref(priv->fmap_regions); + if (priv->fmap_firmware != NULL) + g_object_unref(priv->fmap_firmware); + G_OBJECT_CLASS(fu_mtd_device_parent_class)->finalize(object); +} + +static void fu_mtd_device_class_init(FuMtdDeviceClass *klass) { FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_mtd_device_finalize; device_class->open = fu_mtd_device_open; device_class->probe = fu_mtd_device_probe; device_class->setup = fu_mtd_device_setup; device_class->to_string = fu_mtd_device_to_string; + device_class->convert_version = fu_mtd_device_convert_version; device_class->dump_firmware = fu_mtd_device_dump_firmware; device_class->read_firmware = fu_mtd_device_read_firmware; + device_class->prepare_firmware = fu_mtd_device_prepare_firmware; device_class->write_firmware = fu_mtd_device_write_firmware; device_class->set_quirk_kv = fu_mtd_device_set_quirk_kv; } diff -Nru fwupd-2.0.8/plugins/mtd/fu-mtd-device.h fwupd-2.0.20/plugins/mtd/fu-mtd-device.h --- fwupd-2.0.8/plugins/mtd/fu-mtd-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/fu-mtd-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,4 +9,14 @@ #include #define FU_TYPE_MTD_DEVICE (fu_mtd_device_get_type()) -G_DECLARE_FINAL_TYPE(FuMtdDevice, fu_mtd_device, FU, MTD_DEVICE, FuUdevDevice) +G_DECLARE_DERIVABLE_TYPE(FuMtdDevice, fu_mtd_device, FU, MTD_DEVICE, FuUdevDevice) + +struct _FuMtdDeviceClass { + FuUdevDeviceClass parent_class; +}; + +#define FU_MTD_DEVICE_FLAG_SMBIOS_VERSION_FALLBACK "smbios-version-fallback" + +gboolean +fu_mtd_device_write_image(FuMtdDevice *self, FuFirmware *img, FuProgress *progress, GError **error) + G_GNUC_NON_NULL(1, 2, 3); diff -Nru fwupd-2.0.8/plugins/mtd/fu-mtd-ifd-device.c fwupd-2.0.20/plugins/mtd/fu-mtd-ifd-device.c --- fwupd-2.0.8/plugins/mtd/fu-mtd-ifd-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/fu-mtd-ifd-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,6 +17,22 @@ G_DEFINE_TYPE(FuMtdIfdDevice, fu_mtd_ifd_device, FU_TYPE_DEVICE) static void +fu_mtd_ifd_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuMtdIfdDevice *self = FU_MTD_IFD_DEVICE(device); + if (self->img != NULL) { + fwupd_codec_string_append_hex(str, + idt, + "ImgOffset", + fu_firmware_get_addr(FU_FIRMWARE(self->img))); + fwupd_codec_string_append_hex(str, + idt, + "ImgSize", + fu_firmware_get_size(FU_FIRMWARE(self->img))); + } +} + +static void fu_mtd_ifd_device_add_security_attr_desc(FuMtdIfdDevice *self, FuSecurityAttrs *attrs) { g_autoptr(FwupdSecurityAttr) attr = NULL; @@ -31,9 +47,10 @@ /* check each */ for (guint i = 0; i < G_N_ELEMENTS(regions); i++) { FuIfdAccess ifd_access = fu_ifd_image_get_access(self->img, regions[i]); + g_autofree gchar *ifd_accessstr = fu_ifd_access_to_string(ifd_access); fwupd_security_attr_add_metadata(attr, fu_ifd_region_to_string(regions[i]), - fu_ifd_access_to_string(ifd_access)); + ifd_accessstr); ifd_access_global |= ifd_access; } if (ifd_access_global & FU_IFD_ACCESS_WRITE) { @@ -57,6 +74,32 @@ fu_mtd_ifd_device_add_security_attr_desc(self, attrs); } +static const gchar * +fu_mtd_ifd_device_region_to_name(FuIfdRegion region) +{ + if (region == FU_IFD_REGION_DESC) + return "IFD descriptor region"; + if (region == FU_IFD_REGION_BIOS) + return "BIOS"; + if (region == FU_IFD_REGION_ME) + return "Intel Management Engine"; + if (region == FU_IFD_REGION_GBE) + return "Gigabit Ethernet"; + if (region == FU_IFD_REGION_PLATFORM) + return "Platform firmware"; + if (region == FU_IFD_REGION_DEVEXP) + return "Device Firmware"; + if (region == FU_IFD_REGION_BIOS2) + return "BIOS Backup"; + if (region == FU_IFD_REGION_EC) + return "Embedded Controller"; + if (region == FU_IFD_REGION_IE) + return "Innovation Engine"; + if (region == FU_IFD_REGION_10GBE) + return "10 Gigabit Ethernet"; + return NULL; +} + static gboolean fu_mtd_ifd_device_probe(FuDevice *device, GError **error) { @@ -64,9 +107,30 @@ if (self->img != NULL) { FuIfdRegion region = fu_firmware_get_idx(FU_FIRMWARE(self->img)); - fu_device_set_name(device, fu_ifd_region_to_name(region)); - fu_device_set_logical_id(device, fu_ifd_region_to_string(region)); - fu_device_add_instance_str(device, "REGION", fu_ifd_region_to_string(region)); + FuIfdAccess ifd_access = fu_ifd_image_get_access(self->img, FU_IFD_REGION_BIOS); + g_autofree gchar *name = g_strdup(fu_mtd_ifd_device_region_to_name(region)); + g_autofree gchar *region_str = g_strdup(fu_ifd_region_to_string(region)); + + /* fallback to including the index */ + if (name == NULL) + name = g_strdup_printf("Region %u", region); + if (region_str == NULL) + region_str = g_strdup_printf("%u", region); + + /* always valid */ + fu_device_set_name(device, name); + fu_device_set_logical_id(device, region_str); + fu_device_add_instance_str(device, "REGION", region_str); + + /* region is updatable via the parent MTD device if the BIOS master + * (host CPU) has write permission for this region */ + if (ifd_access & FU_IFD_ACCESS_READ) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + if (ifd_access & FU_IFD_ACCESS_WRITE) { + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_protocol(device, "org.infradead.mtd"); + } } if (!fu_device_build_instance_id(device, error, "IFD", "REGION", NULL)) return FALSE; @@ -75,18 +139,114 @@ return TRUE; } +static FuFirmware * +fu_mtd_ifd_device_prepare_firmware(FuDevice *device, + GInputStream *stream, + FuProgress *progress, + FuFirmwareParseFlags flags, + GError **error) +{ + FuMtdIfdDevice *self = FU_MTD_IFD_DEVICE(device); + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(FuFirmware) img = NULL; + + /* sanity check */ + if (self->img == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "no IFD image"); + return NULL; + } + + /* parse as Intel Flash Descriptor or IFD image */ + firmware = fu_firmware_new_from_gtypes(stream, + 0x0, + flags, + error, + FU_TYPE_IFD_FIRMWARE, + FU_TYPE_IFD_IMAGE, + G_TYPE_INVALID); + if (firmware == NULL) { + g_prefix_error_literal(error, "failed to parse as IFD image: "); + return NULL; + } + + /* get correct image */ + if (FU_IS_IFD_FIRMWARE(firmware)) { + FuIfdRegion region = fu_firmware_get_idx(FU_FIRMWARE(self->img)); + img = fu_firmware_get_image_by_idx(firmware, region, error); + if (img == NULL) + return NULL; + if (fu_firmware_get_addr(img) != fu_firmware_get_addr(FU_FIRMWARE(self->img))) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "offset specified as 0x%x and expected 0x%x", + (guint)fu_firmware_get_addr(img), + (guint)fu_firmware_get_addr(FU_FIRMWARE(self->img))); + return NULL; + } + } else { + img = g_object_ref(firmware); + fu_firmware_set_addr(img, fu_firmware_get_addr(FU_FIRMWARE(self->img))); + } + + /* sanity check */ + if (fu_firmware_get_size(img) > fu_firmware_get_size(FU_FIRMWARE(self->img))) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "size is 0x%x and expected <= 0x%x", + (guint)fu_firmware_get_size(img), + (guint)fu_firmware_get_size(FU_FIRMWARE(self->img))); + return NULL; + } + + /* success */ + return g_steal_pointer(&img); +} + +static gboolean +fu_mtd_ifd_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + return fu_mtd_device_write_image(FU_MTD_DEVICE(proxy), firmware, progress, error); +} + static void fu_mtd_ifd_device_init(FuMtdIfdDevice *self) { - fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_MTD_DEVICE); +} + +static void +fu_mtd_ifd_device_finalize(GObject *object) +{ + FuMtdIfdDevice *self = FU_MTD_IFD_DEVICE(object); + if (self->img != NULL) + g_object_unref(self->img); + G_OBJECT_CLASS(fu_mtd_ifd_device_parent_class)->finalize(object); } static void fu_mtd_ifd_device_class_init(FuMtdIfdDeviceClass *klass) { FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_mtd_ifd_device_finalize; + device_class->to_string = fu_mtd_ifd_device_to_string; device_class->probe = fu_mtd_ifd_device_probe; device_class->add_security_attrs = fu_mtd_ifd_device_add_security_attrs; + device_class->prepare_firmware = fu_mtd_ifd_device_prepare_firmware; + device_class->write_firmware = fu_mtd_ifd_device_write_firmware; } FuMtdIfdDevice * diff -Nru fwupd-2.0.8/plugins/mtd/fu-mtd-plugin.c fwupd-2.0.20/plugins/mtd/fu-mtd-plugin.c --- fwupd-2.0.8/plugins/mtd/fu-mtd-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/fu-mtd-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -41,6 +41,8 @@ FuContext *ctx = fu_plugin_get_context(plugin); fu_context_add_quirk_key(ctx, "MtdMetadataOffset"); fu_context_add_quirk_key(ctx, "MtdMetadataSize"); + fu_context_add_quirk_key(ctx, "MtdFmapRegions"); + fu_context_add_quirk_key(ctx, "MtdFmapOffset"); fu_plugin_add_device_udev_subsystem(plugin, "mtd"); fu_plugin_set_device_gtype_default(plugin, FU_TYPE_MTD_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_MTD_IFD_DEVICE); /* coverage */ diff -Nru fwupd-2.0.8/plugins/mtd/fu-self-test.c fwupd-2.0.20/plugins/mtd/fu-self-test.c --- fwupd-2.0.8/plugins/mtd/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,89 +6,306 @@ #include "config.h" +#include "fu-config-private.h" #include "fu-context-private.h" +#include "fu-device-private.h" #include "fu-mtd-device.h" #include "fu-udev-device-private.h" +typedef struct { + FuContext *ctx; +} FuTest; + static void -fu_test_mtd_device_func(void) +fu_test_free(FuTest *self) { - gsize bufsz; - gboolean ret; - g_autoptr(FuContext) ctx = fu_context_new(); + if (self->ctx != NULL) + g_object_unref(self->ctx); + g_free(self); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuTest, fu_test_free) + +static FuMtdDevice * +fu_test_mtd_find_mtdram(FuContext *ctx, GError **error) +{ + g_autoptr(GPtrArray) mtd_files = NULL; g_autoptr(FuDevice) device = NULL; - g_autoptr(FuDeviceLocker) locker = NULL; + const gchar *device_file; + + mtd_files = fu_path_glob("/sys/devices/virtual/mtd", "mtd?", error); + if (mtd_files == NULL) { + g_prefix_error_literal(error, "no mtdram device: "); + return NULL; + } + + /* create device */ + device_file = g_ptr_array_index(mtd_files, 0); + device = g_object_new(FU_TYPE_MTD_DEVICE, "context", ctx, "backend-id", device_file, NULL); + if (!fu_device_probe(device, error)) + return NULL; + if (g_strcmp0(fu_device_get_name(device), "mtdram test device") != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "device is not mtdram test device"); + return NULL; + } + fu_device_set_firmware_gtype(FU_DEVICE(device), G_TYPE_INVALID); + if (!fu_device_open(device, error)) + return NULL; + return FU_MTD_DEVICE(g_steal_pointer(&device)); +} + +static FuFirmware * +fu_test_mtd_prepare_mtdram_device(FuMtdDevice *device, + GType firmware_gtype, + const gchar *filename_xml) +{ + gboolean ret; + gsize bufsz = fu_device_get_firmware_size_max(FU_DEVICE(device)); + g_autoptr(FuFirmware) firmware = g_object_new(firmware_gtype, NULL); + g_autoptr(FuProgress) progress = fu_progress_new(NULL); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GError) error = NULL; + + /* build the image */ + if (filename_xml != NULL) { + g_autoptr(GBytes) blob_tmp = NULL; + g_autofree gchar *filename = NULL; + + filename = g_test_build_filename(G_TEST_DIST, "tests", filename_xml, NULL); + g_debug("loading from %s", filename); + ret = fu_firmware_build_from_filename(firmware, filename, &error); + g_assert_no_error(error); + g_assert_true(ret); + blob_tmp = fu_firmware_write(firmware, &error); + g_assert_no_error(error); + g_assert_nonnull(blob_tmp); + blob = fu_bytes_pad(blob_tmp, bufsz, 0xFF); + } else { + g_autoptr(GByteArray) buf = g_byte_array_new(); + fu_byte_array_set_size(buf, bufsz, 0xFF); + blob = g_bytes_new(buf->data, buf->len); + } + fu_firmware_set_bytes(firmware, blob); + ret = fu_device_write_firmware(FU_DEVICE(device), + firmware, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* success */ + fu_device_probe_invalidate(FU_DEVICE(device)); + fu_device_set_firmware_gtype(FU_DEVICE(device), firmware_gtype); + return g_steal_pointer(&firmware); +} + +static void +fu_test_mtd_device_raw_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuMtdDevice) device = NULL; g_autoptr(FuFirmware) firmware = NULL; g_autoptr(FuProgress) progress = fu_progress_new(NULL); - g_autoptr(GByteArray) buf = g_byte_array_new(); g_autoptr(GBytes) fw2 = NULL; g_autoptr(GBytes) fw = NULL; g_autoptr(GError) error = NULL; - g_autoptr(GRand) rand = g_rand_new_with_seed(0); - /* do not save silo */ - ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + /* find correct device */ + device = fu_test_mtd_find_mtdram(self->ctx, &error); + if (device == NULL) { + g_test_skip(error->message); + return; + } + + /* write the IFD image */ + firmware = fu_test_mtd_prepare_mtdram_device(device, FU_TYPE_FIRMWARE, NULL); + g_assert_nonnull(firmware); + fw = fu_firmware_get_bytes(firmware, &error); g_assert_no_error(error); - g_assert_true(ret); - ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_LOAD_SMBIOS, &error); + g_assert_nonnull(fw); + + /* dump back */ + fu_progress_reset(progress); + fw2 = fu_device_dump_firmware(FU_DEVICE(device), progress, &error); + g_assert_no_error(error); + g_assert_nonnull(fw2); + + /* verify */ + ret = fu_bytes_compare(fw, fw2, &error); g_assert_no_error(error); g_assert_true(ret); +} - /* create device */ - device = g_object_new(FU_TYPE_MTD_DEVICE, - "context", - ctx, - "backend-id", - "/sys/devices/virtual/mtd/mtd0", - NULL); - locker = fu_device_locker_new(device, &error); - if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND) || - g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { - g_test_skip("no permission to read mtdram device"); +static void +fu_test_mtd_device_ifd_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autofree gchar *str = NULL; + g_autoptr(FuMtdDevice) device = NULL; + g_autoptr(FuDevice) device_bios = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(FuFirmware) firmware_bios = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(NULL); + g_autoptr(GError) error = NULL; + + /* find correct device */ + device = fu_test_mtd_find_mtdram(self->ctx, &error); + if (device == NULL) { + g_test_skip(error->message); return; } + + /* write the IFD image */ + firmware = + fu_test_mtd_prepare_mtdram_device(device, FU_TYPE_IFD_FIRMWARE, "mtd-ifd.builder.xml"); + g_assert_nonnull(firmware); + + /* re-probe image */ + ret = fu_device_setup(FU_DEVICE(device), &error); g_assert_no_error(error); - g_assert_nonnull(locker); - if (!g_file_test(fu_udev_device_get_device_file(FU_UDEV_DEVICE(device)), - G_FILE_TEST_EXISTS)) { - g_test_skip("/dev/mtd0 doesn't exist"); + g_assert_true(ret); + device_bios = fu_device_get_child_by_logical_id(FU_DEVICE(device), "bios", &error); + g_assert_no_error(error); + g_assert_nonnull(device_bios); + + /* just for debugging */ + str = fu_device_to_string(FU_DEVICE(device)); + g_debug("%s", str); + + /* re-write just the BIOS */ + firmware_bios = fu_firmware_get_image_by_id(firmware, "bios", &error); + g_assert_no_error(error); + g_assert_nonnull(device_bios); + ret = fu_device_write_firmware(device_bios, + firmware_bios, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_test_mtd_device_fmap_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuMtdDevice) device = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(NULL); + g_autoptr(GError) error = NULL; + + /* find correct device */ + device = fu_test_mtd_find_mtdram(self->ctx, &error); + if (device == NULL) { + g_test_skip(error->message); return; } - if (g_strcmp0(fu_device_get_name(device), "mtdram test device") != 0) { - g_test_skip("device is not mtdram test device"); + + /* write the FMAP image */ + firmware = fu_test_mtd_prepare_mtdram_device(device, + FU_TYPE_FMAP_FIRMWARE, + "mtd-fmap.builder.xml"); + g_assert_nonnull(firmware); + + /* re-probe image */ + ret = fu_device_setup(FU_DEVICE(device), &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpstr(fu_device_get_version(device), ==, "456"); + + /* re-write firmware, this time in chunks */ + ret = fu_device_set_quirk_kv(FU_DEVICE(device), + "MtdFmapRegions", + "SBOM,FMAP", + FU_CONTEXT_QUIRK_SOURCE_DB, + &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_device_write_firmware(FU_DEVICE(device), + firmware, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_test_mtd_device_uswid_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuMtdDevice) device = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(GError) error = NULL; + + /* find correct device */ + device = fu_test_mtd_find_mtdram(self->ctx, &error); + if (device == NULL) { + g_test_skip(error->message); return; } - bufsz = fu_device_get_firmware_size_max(device); - g_assert_cmpint(bufsz, ==, 0x400000); + /* write the uSWID image */ + firmware = fu_test_mtd_prepare_mtdram_device(device, + FU_TYPE_USWID_FIRMWARE, + "mtd-uswid.builder.xml"); + g_assert_nonnull(firmware); - /* create a random payload exactly the same size */ - for (gsize i = 0; i < bufsz; i++) - fu_byte_array_append_uint8(buf, g_rand_int_range(rand, 0x00, 0xFF)); - fw = g_bytes_new(buf->data, buf->len); - - /* write with a verify */ - firmware = fu_firmware_new_from_bytes(fw); - ret = fu_device_write_firmware(device, firmware, progress, FWUPD_INSTALL_FLAG_NONE, &error); + /* re-probe image */ + ret = fu_device_setup(FU_DEVICE(device), &error); g_assert_no_error(error); g_assert_true(ret); + g_assert_cmpstr(fu_device_get_version(device), ==, "456"); +} - /* dump back */ - fu_progress_reset(progress); - fw2 = fu_device_dump_firmware(device, progress, &error); - g_assert_no_error(error); - g_assert_nonnull(fw2); +static void +fu_test_mtd_device_smbios_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(FuMtdDevice) device = NULL; + g_autoptr(GError) error = NULL; - /* verify */ - ret = fu_bytes_compare(fw, fw2, &error); + /* find correct device */ + device = fu_test_mtd_find_mtdram(self->ctx, &error); + if (device == NULL) { + g_test_skip(error->message); + return; + } + + /* write the empty image */ + firmware = fu_test_mtd_prepare_mtdram_device(device, FU_TYPE_FIRMWARE, NULL); + g_assert_nonnull(firmware); + + /* re-probe image */ + fu_device_add_private_flag(FU_DEVICE(device), FU_MTD_DEVICE_FLAG_SMBIOS_VERSION_FALLBACK); + ret = fu_device_setup(FU_DEVICE(device), &error); g_assert_no_error(error); g_assert_true(ret); + g_assert_cmpstr(fu_device_get_version(FU_DEVICE(device)), ==, "1.59"); + g_assert_cmpint(fu_device_get_version_format(FU_DEVICE(device)), + ==, + FWUPD_VERSION_FORMAT_PAIR); } int main(int argc, char **argv) { + gboolean ret; g_autofree gchar *testdatadir = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(NULL); + g_autoptr(GError) error = NULL; + g_autoptr(FuTest) self = g_new0(FuTest, 1); + (void)g_setenv("G_TEST_SRCDIR", SRCDIR, FALSE); g_test_init(&argc, &argv, NULL); (void)g_setenv("FWUPD_MTD_VERBOSE", "1", TRUE); @@ -97,7 +314,21 @@ (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); (void)g_setenv("CONFIGURATION_DIRECTORY", testdatadir, TRUE); + /* do not save silo */ + self->ctx = fu_context_new(); + fu_config_set_basename(fu_context_get_config(self->ctx), "mtd-fwupd.conf"); + ret = fu_context_load_quirks(self->ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_context_load_hwinfo(self->ctx, progress, FU_CONTEXT_HWID_FLAG_LOAD_CONFIG, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); - g_test_add_func("/mtd/device", fu_test_mtd_device_func); + g_test_add_data_func("/mtd/device{raw}", self, fu_test_mtd_device_raw_func); + g_test_add_data_func("/mtd/device{uswid}", self, fu_test_mtd_device_uswid_func); + g_test_add_data_func("/mtd/device{ifd}", self, fu_test_mtd_device_ifd_func); + g_test_add_data_func("/mtd/device{fmap}", self, fu_test_mtd_device_fmap_func); + g_test_add_data_func("/mtd/device{smbios}", self, fu_test_mtd_device_smbios_func); return g_test_run(); } diff -Nru fwupd-2.0.8/plugins/mtd/meson.build fwupd-2.0.20/plugins/mtd/meson.build --- fwupd-2.0.8/plugins/mtd/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginMtd"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -16,6 +17,11 @@ ) plugin_builtins += plugin_builtin_mtd +if not supported_build + plugin_quirks += files('ci.quirk') + device_tests += files('tests/bnr-mtd.json', 'tests/mtdram.json') +endif + if get_option('tests') env = environment() env.set('G_TEST_SRCDIR', meson.current_source_dir()) @@ -33,6 +39,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ cargs, @@ -40,5 +47,13 @@ ], ) test('mtd-self-test', e, env: env) # added to installed-tests -endif + install_data( + [ + 'tests/mtd-fmap.builder.xml', + 'tests/mtd-ifd.builder.xml', + 'tests/mtd-uswid.builder.xml', + 'tests/mtd-fwupd.conf', + ], + install_dir: join_paths(installed_test_datadir, 'tests'), + ) endif diff -Nru fwupd-2.0.8/plugins/mtd/mtd.quirk fwupd-2.0.20/plugins/mtd/mtd.quirk --- fwupd-2.0.8/plugins/mtd/mtd.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/mtd.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -15,4 +15,17 @@ # Intel SPI Controller [MTD\NAME_BIOS] Name = Internal SPI Controller -#FirmwareGType = FuIfdFirmware +FirmwareGType = FuIfdFirmware +Flags = smbios-version-fallback + +[MTD\NAME_0000:00:1f.5] +Name = PCH SPI Controller + +# B&R Industrial Automation GmbH 5ACCIFM0.FCAN-000 +[MTD\VEN_1677&DEV_28A4] +Flags = ~needs-reboot,needs-shutdown,usable-during-update,unsigned-payload +VersionFormat = number + +# Intel GPUs +[MTD\DRIVER_xe] +Flags = no-probe diff -Nru fwupd-2.0.8/plugins/mtd/tests/.gitignore fwupd-2.0.20/plugins/mtd/tests/.gitignore --- fwupd-2.0.8/plugins/mtd/tests/.gitignore 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/tests/.gitignore 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,2 @@ +*.bin +*.cab diff -Nru fwupd-2.0.8/plugins/mtd/tests/bnr-mtd.json fwupd-2.0.20/plugins/mtd/tests/bnr-mtd.json --- fwupd-2.0.8/plugins/mtd/tests/bnr-mtd.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/tests/bnr-mtd.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +{ + "name": "B&R Automation Expansion option", + "interactive": false, + "steps": [ + { + "url": "691300a8557b1f341fc8ca12b04fd9809f318a2d876c78297629e152e55884c3-5ACCIFM0.FCAN-000_7.cab", + "emulation-url": "c7d573a52589357b1f807cf5f6ee25955a2c1bb8ac204f54b9910b2ab2df5a75-5ACCIFM0.FCAN-000_7-emulation.zip", + "components": [ + { + "version": "5", + "guids": [ + "0f303377-5909-50d3-b0f0-4cadc99cebd1" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/mtd/tests/build.sh fwupd-2.0.20/plugins/mtd/tests/build.sh --- fwupd-2.0.8/plugins/mtd/tests/build.sh 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/tests/build.sh 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,4 @@ +#!/bin/sh +set -e +fwupdtool firmware-build uswid.builder.xml firmware.bin --force +fwupdtool build-cabinet mtd-test-1.2.3.cab firmware.bin org.fwupd.mtd-test.metainfo.xml diff -Nru fwupd-2.0.8/plugins/mtd/tests/mtd-fmap.builder.xml fwupd-2.0.20/plugins/mtd/tests/mtd-fmap.builder.xml --- fwupd-2.0.8/plugins/mtd/tests/mtd-fmap.builder.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/tests/mtd-fmap.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ + + + FMAP + aGVsbG8gd29ybGQ= + + + SBOM + + 456 + + + diff -Nru fwupd-2.0.8/plugins/mtd/tests/mtd-fwupd.conf fwupd-2.0.20/plugins/mtd/tests/mtd-fwupd.conf --- fwupd-2.0.8/plugins/mtd/tests/mtd-fwupd.conf 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/tests/mtd-fwupd.conf 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,4 @@ +[fwupd] +BiosVersion=N3VET59W (1.59 ) +BiosMajorRelease=01 +BiosMinorRelease=3b diff -Nru fwupd-2.0.8/plugins/mtd/tests/mtd-ifd.builder.xml fwupd-2.0.20/plugins/mtd/tests/mtd-ifd.builder.xml --- fwupd-2.0.8/plugins/mtd/tests/mtd-ifd.builder.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/tests/mtd-ifd.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ + + 0x40003 + 0x58100208 + 0x310330 + + bios + 0x1 + 0x1000 + V29ybGQh + + + me + 0x2 + 0x2000 + V29ybGQh + + diff -Nru fwupd-2.0.8/plugins/mtd/tests/mtd-uswid.builder.xml fwupd-2.0.20/plugins/mtd/tests/mtd-uswid.builder.xml --- fwupd-2.0.8/plugins/mtd/tests/mtd-uswid.builder.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/tests/mtd-uswid.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,6 @@ + + SBOM + + 456 + + diff -Nru fwupd-2.0.8/plugins/mtd/tests/mtdram.json fwupd-2.0.20/plugins/mtd/tests/mtdram.json --- fwupd-2.0.8/plugins/mtd/tests/mtdram.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/tests/mtdram.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +{ + "name": "MTD RAM", + "interactive": false, + "steps": [ + { + "url": "29804f5bfe107862dc5f090036d065ccf455284268e122a1b0246a71f25dec86-mtd-test-1.2.3.cab", + "emulation-url": "fc1b69d3ae89f1931655371e9336b3a0553f0a9d79c4b2b0fd5009e971c08dcd-emulation.zip", + "components": [ + { + "version": "1.2.3", + "guids": [ + "26ab306d-d6ce-58f0-94db-2215e24690b9" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/mtd/tests/org.fwupd.mtdram.metainfo.xml fwupd-2.0.20/plugins/mtd/tests/org.fwupd.mtdram.metainfo.xml --- fwupd-2.0.8/plugins/mtd/tests/org.fwupd.mtdram.metainfo.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/tests/org.fwupd.mtdram.metainfo.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,33 @@ + + + org.fwupd.mtdram.firmware + MTD RAM + System firmware for the MTD RAM device + +

    The RAM device can be updated using MTD.

    +
    + + 26ab306d-d6ce-58f0-94db-2215e24690b9 + + http://fwupd.org/ + CC0-1.0 + CC0-1.0 + + X-Device + + + unsigned + triplet + org.infradead.mtd + + + + +

    This release updates firmware to version 1.2.3 which includes support for a mythical new feature.

    +
    +
    +
    + + org.freedesktop.fwupd + +
    diff -Nru fwupd-2.0.8/plugins/mtd/tests/uswid.builder.xml fwupd-2.0.20/plugins/mtd/tests/uswid.builder.xml --- fwupd-2.0.8/plugins/mtd/tests/uswid.builder.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/mtd/tests/uswid.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,8 @@ + + 0x1 + + fwupd + 1.2.3 + semver + + diff -Nru fwupd-2.0.8/plugins/nordic-hid/README.md fwupd-2.0.20/plugins/nordic-hid/README.md --- fwupd-2.0.8/plugins/nordic-hid/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nordic-hid/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -89,9 +89,3 @@ The format version of the `dfu_application.zip` file generated by the nRF Connect SDK build system was updated from `0` to `1` since the [nRF Connect SDK v2.7.0 release](https://docs.nordicsemi.com/bundle/ncs-latest/page/nrf/releases_and_maturity/releases/release-notes-2.7.0.html). The format version `1` is supported by the fwupd since the `1.9.25` release. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be consulted before making major or functional changes: - -* Marek Pieta: @MarekPieta diff -Nru fwupd-2.0.8/plugins/nordic-hid/fu-nordic-hid-archive.c fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-archive.c --- fwupd-2.0.8/plugins/nordic-hid/fu-nordic-hid-archive.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-archive.c 2026-02-26 11:36:18.000000000 +0000 @@ -16,7 +16,7 @@ #define MAX_VERSION_FORMAT 1 struct _FuNordicHidArchive { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; }; G_DEFINE_TYPE(FuNordicHidArchive, fu_nordic_hid_archive, FU_TYPE_FIRMWARE) @@ -139,7 +139,7 @@ G_MAXINT64, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "fu_strtoll failed for image_index:"); + g_prefix_error_literal(error, "failed to parse image_index: "); return FALSE; } @@ -159,7 +159,7 @@ return FALSE; } if (!fu_strtoll(slot_str, &slot, G_MININT64, G_MAXINT64, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "fu_strtoll failed for slot:"); + g_prefix_error_literal(error, "failed to parse slot: "); return FALSE; } @@ -215,7 +215,7 @@ static gboolean fu_nordic_hid_archive_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { JsonNode *json_root_node; @@ -240,7 +240,7 @@ (const gchar *)g_bytes_get_data(manifest, NULL), (gssize)g_bytes_get_size(manifest), error)) { - g_prefix_error(error, "manifest not in JSON format: "); + g_prefix_error_literal(error, "manifest not in JSON format: "); return FALSE; } json_root_node = json_parser_get_root(parser); @@ -340,7 +340,7 @@ if (!fu_firmware_parse_bytes(image, blob, 0x0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return FALSE; @@ -352,7 +352,7 @@ fu_firmware_set_addr(image, image_addr); } - if (!fu_firmware_add_image_full(firmware, image, error)) + if (!fu_firmware_add_image(firmware, image, error)) return FALSE; } diff -Nru fwupd-2.0.8/plugins/nordic-hid/fu-nordic-hid-cfg-channel.c fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-cfg-channel.c --- fwupd-2.0.8/plugins/nordic-hid/fu-nordic-hid-cfg-channel.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-cfg-channel.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,6 +9,7 @@ #include "fu-nordic-hid-archive.h" #include "fu-nordic-hid-cfg-channel.h" +#include "fu-nordic-hid-struct.h" #define HID_REPORT_ID 6 #define REPORT_SIZE 30 @@ -326,7 +327,7 @@ } if (!fu_nordic_hid_cfg_channel_send(self, (guint8 *)msg, sizeof(*msg), error)) { - g_prefix_error(error, "failed to send: "); + g_prefix_error_literal(error, "failed to send: "); return FALSE; } @@ -361,7 +362,7 @@ data, data_len, error)) { - g_prefix_error(error, "failed to send: "); + g_prefix_error_literal(error, "failed to send: "); return FALSE; } @@ -386,7 +387,7 @@ FU_NORDIC_HID_CFG_CHANNEL_RETRIES, &helper, error)) { - g_prefix_error(error, "Failed on receive: "); + g_prefix_error_literal(error, "failed on receive: "); return FALSE; } @@ -504,7 +505,7 @@ NULL, 0, error)) { - g_prefix_error(error, "INDEX_PEERS cmd_send failed: "); + g_prefix_error_literal(error, "INDEX_PEERS cmd_send failed: "); return FALSE; } @@ -516,9 +517,9 @@ return TRUE; } - /* Peers available */ + /* peers available */ if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) { - g_prefix_error(error, "INDEX_PEERS cmd_receive failed: "); + g_prefix_error_literal(error, "INDEX_PEERS cmd_receive failed: "); return FALSE; } @@ -540,12 +541,12 @@ NULL, 0, error)) { - g_prefix_error(error, "GET_PEER cmd_send failed: "); + g_prefix_error_literal(error, "GET_PEER cmd_send failed: "); return FALSE; } if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) { - g_prefix_error(error, "GET_PEER cmd_receive failed: "); + g_prefix_error_literal(error, "GET_PEER cmd_receive failed: "); return FALSE; } @@ -572,7 +573,7 @@ NULL, 0, error)) { - g_prefix_error(error, "GET_PEERS_CACHE cmd_send failed: "); + g_prefix_error_literal(error, "GET_PEERS_CACHE cmd_send failed: "); return FALSE; } @@ -586,7 +587,7 @@ /* configuration channel peer caching available */ if (!fu_nordic_hid_cfg_channel_cmd_receive(self, CONFIG_STATUS_SUCCESS, res, error)) { - g_prefix_error(error, "GET_PEERS_CACHE cmd_receive failed: "); + g_prefix_error_literal(error, "GET_PEERS_CACHE cmd_receive failed: "); return FALSE; } @@ -786,7 +787,7 @@ if (self->bl_name != NULL) { g_autofree gchar *tmp = g_strndup((const gchar *)res->data, res->data_len); - g_debug("Bootloader readout '%s' overrides bootloader from quirk '%s'", + g_debug("bootloader readout '%s' overrides bootloader from quirk '%s'", tmp, self->bl_name); g_free(self->bl_name); @@ -1018,10 +1019,9 @@ static gboolean fu_nordic_hid_cfg_channel_dfu_fwinfo(FuNordicHidCfgChannel *self, GError **error) { - guint16 ver_rev; - guint32 ver_build_nr; g_autofree gchar *version = NULL; g_autoptr(FuNordicCfgChannelMsg) res = g_new0(FuNordicCfgChannelMsg, 1); + g_autoptr(FuStructNordicHidDfuFwinfo) st = NULL; if (!fu_nordic_hid_cfg_channel_cmd_send(self, "dfu", @@ -1035,34 +1035,29 @@ return FALSE; /* parsing fwinfo answer */ + st = fu_struct_nordic_hid_dfu_fwinfo_parse(res->data, REPORT_SIZE, 0x0, error); + if (st == NULL) + return FALSE; + self->flashed_image_len = fu_struct_nordic_hid_dfu_fwinfo_get_flashed_image_len(st); + /* TODO: add banks amount into quirk */ - if (res->data[0] > 1) { + if (fu_struct_nordic_hid_dfu_fwinfo_get_flash_area_id(st) > 1) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "invalid flash area returned by device"); return FALSE; } + /* set the target flash ID area */ - self->flash_area_id = res->data[0] ^ 1; + self->flash_area_id = fu_struct_nordic_hid_dfu_fwinfo_get_flash_area_id(st) ^ 1; - if (!fu_memread_uint32_safe(res->data, - REPORT_SIZE, - 0x01, - &self->flashed_image_len, - G_LITTLE_ENDIAN, - error)) - return FALSE; - if (!fu_memread_uint16_safe(res->data, REPORT_SIZE, 0x07, &ver_rev, G_LITTLE_ENDIAN, error)) - return FALSE; - if (!fu_memread_uint32_safe(res->data, - REPORT_SIZE, - 0x09, - &ver_build_nr, - G_LITTLE_ENDIAN, - error)) - return FALSE; - version = g_strdup_printf("%u.%u.%u.%u", res->data[4], res->data[5], ver_rev, ver_build_nr); + /* build the version */ + version = g_strdup_printf("%u.%u.%u.%u", + fu_struct_nordic_hid_dfu_fwinfo_get_ver_major(st), + fu_struct_nordic_hid_dfu_fwinfo_get_ver_minor(st), + fu_struct_nordic_hid_dfu_fwinfo_get_ver_rev(st), + fu_struct_nordic_hid_dfu_fwinfo_get_ver_build_nr(st)); fu_device_set_version(FU_DEVICE(self), version); /* success */ @@ -1175,7 +1170,7 @@ FU_NORDIC_HID_CFG_CHANNEL_DFU_RETRY_DELAY, &helper, error)) { - g_prefix_error(error, "failed on dfu sync: "); + g_prefix_error_literal(error, "failed on dfu sync: "); return FALSE; } dfu_info->dfu_state = res->data[0]; @@ -1294,7 +1289,7 @@ "BOARD", "BL", NULL)) { - g_prefix_error(error, "failed to add ID without generation: "); + g_prefix_error_literal(error, "failed to add ID without generation: "); return FALSE; } } @@ -1308,7 +1303,7 @@ "BL", "GEN", NULL)) { - g_prefix_error(error, "failed to add complete ID: "); + g_prefix_error_literal(error, "failed to add complete ID: "); return FALSE; } @@ -1402,7 +1397,7 @@ } static void -fu_nordic_hid_cfg_channel_set_progress(FuDevice *self, FuProgress *progress) +fu_nordic_hid_cfg_channel_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/nordic-hid/fu-nordic-hid-firmware-b0.c fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-firmware-b0.c --- fwupd-2.0.8/plugins/nordic-hid/fu-nordic-hid-firmware-b0.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-firmware-b0.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,7 @@ #define UPDATE_IMAGE_MAGIC_NRF53 0x00003502 struct _FuNordicHidFirmwareB0 { - FuNordicHidFirmwareClass parent_instance; + FuNordicHidFirmware parent_instance; }; G_DEFINE_TYPE(FuNordicHidFirmwareB0, fu_nordic_hid_firmware_b0, FU_TYPE_NORDIC_HID_FIRMWARE) @@ -39,7 +39,9 @@ } static gboolean -fu_nordic_hid_firmware_b0_read_fwinfo(FuFirmware *firmware, GInputStream *stream, GError **error) +fu_nordic_hid_firmware_b0_read_fwinfo(FuNordicHidFirmwareB0 *self, + GInputStream *stream, + GError **error) { guint32 magic_common; guint32 magic_fwinfo; @@ -93,7 +95,7 @@ ver_minor, ver_rev, ver_build_nr); - fu_firmware_set_version(firmware, version); + fu_firmware_set_version(FU_FIRMWARE(self), version); return TRUE; default: break; @@ -110,13 +112,14 @@ static gboolean fu_nordic_hid_firmware_b0_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { + FuNordicHidFirmwareB0 *self = FU_NORDIC_HID_FIRMWARE_B0(firmware); if (!FU_FIRMWARE_CLASS(fu_nordic_hid_firmware_b0_parent_class) ->parse(firmware, stream, flags, error)) return FALSE; - return fu_nordic_hid_firmware_b0_read_fwinfo(firmware, stream, error); + return fu_nordic_hid_firmware_b0_read_fwinfo(self, stream, error); } static void diff -Nru fwupd-2.0.8/plugins/nordic-hid/fu-nordic-hid-firmware-mcuboot.c fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-firmware-mcuboot.c --- fwupd-2.0.8/plugins/nordic-hid/fu-nordic-hid-firmware-mcuboot.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-firmware-mcuboot.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,7 +13,7 @@ #define IMAGE_TLV_PROT_INFO_MAGIC 0x6908 struct _FuNordicHidFirmwareMcuboot { - FuNordicHidFirmwareClass parent_instance; + FuNordicHidFirmware parent_instance; }; G_DEFINE_TYPE(FuNordicHidFirmwareMcuboot, @@ -60,7 +60,9 @@ /* simple validation of the image */ static gboolean -fu_nordic_hid_firmware_mcuboot_validate(FuFirmware *firmware, GInputStream *stream, GError **error) +fu_nordic_hid_firmware_mcuboot_validate(FuNordicHidFirmwareMcuboot *self, + GInputStream *stream, + GError **error) { guint32 magic; guint16 hdr_size; @@ -116,7 +118,7 @@ return FALSE; version = g_strdup_printf("%u.%u.%u.%u", ver_major, ver_minor, ver_rev, ver_build_nr); - fu_firmware_set_version(firmware, version); + fu_firmware_set_version(FU_FIRMWARE(self), version); return TRUE; } @@ -124,13 +126,14 @@ static gboolean fu_nordic_hid_firmware_mcuboot_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { + FuNordicHidFirmwareMcuboot *self = FU_NORDIC_HID_FIRMWARE_MCUBOOT(firmware); if (!FU_FIRMWARE_CLASS(fu_nordic_hid_firmware_mcuboot_parent_class) ->parse(firmware, stream, flags, error)) return FALSE; - return fu_nordic_hid_firmware_mcuboot_validate(firmware, stream, error); + return fu_nordic_hid_firmware_mcuboot_validate(self, stream, error); } static void diff -Nru fwupd-2.0.8/plugins/nordic-hid/fu-nordic-hid-firmware.c fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-firmware.c --- fwupd-2.0.8/plugins/nordic-hid/fu-nordic-hid-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -41,7 +41,7 @@ static gboolean fu_nordic_hid_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuNordicHidFirmware *self = FU_NORDIC_HID_FIRMWARE(firmware); diff -Nru fwupd-2.0.8/plugins/nordic-hid/fu-nordic-hid.rs fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid.rs --- fwupd-2.0.8/plugins/nordic-hid/fu-nordic-hid.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/nordic-hid/fu-nordic-hid.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[derive(Parse)] +#[repr(C, packed)] +struct FuStructNordicHidDfuFwinfo { + flash_area_id: u8, + flashed_image_len: u32le, + ver_major: u8, + ver_minor: u8, + ver_rev: u16le, + ver_build_nr: u32le, +} diff -Nru fwupd-2.0.8/plugins/nordic-hid/meson.build fwupd-2.0.20/plugins/nordic-hid/meson.build --- fwupd-2.0.8/plugins/nordic-hid/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nordic-hid/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,9 +1,11 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginNordicHid"'] plugins += {meson.current_source_dir().split('/')[-1]: true} plugin_quirks += files('nordic-hid.quirk') plugin_builtins += static_library('fu_plugin_nordic_hid', + rustgen.process('fu-nordic-hid.rs'), sources: [ 'fu-nordic-hid-plugin.c', 'fu-nordic-hid-cfg-channel.c', @@ -17,7 +19,6 @@ c_args: cargs, dependencies: plugin_deps, ) -endif device_tests += files('tests/nordic-hid-nrf52840-mcuboot.json', 'tests/nordic-hid-nrf52840-b0.json', diff -Nru fwupd-2.0.8/plugins/nordic-hid/nordic-hid.quirk fwupd-2.0.20/plugins/nordic-hid/nordic-hid.quirk --- fwupd-2.0.8/plugins/nordic-hid/nordic-hid.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nordic-hid/nordic-hid.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -5,3 +5,11 @@ GType = FuNordicHidCfgChannel NordicHidBootloader = B0 NordicHidGeneration = default + +# Lenovo Sapphire Folio Keyboard +[HIDRAW\VEN_17EF&DEV_62CC] +Plugin = nordic_hid +Flags = no-generic-guids +GType = FuNordicHidCfgChannel +NordicHidBootloader = B0 +NordicHidGeneration = default diff -Nru fwupd-2.0.8/plugins/nordic-hid/tests/nordic-hid-nrf52840-b0.json fwupd-2.0.20/plugins/nordic-hid/tests/nordic-hid-nrf52840-b0.json --- fwupd-2.0.8/plugins/nordic-hid/tests/nordic-hid-nrf52840-b0.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nordic-hid/tests/nordic-hid-nrf52840-b0.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/1f2931d9573f54266fffed606ea4fe8cc9fce5a9876be365ae74292f55dc3d34-nordic-nrf52840dk-0.0.0.2.cab", - "emulation-url": "https://fwupd.org/downloads/19ebd9d8f941f80dc6c582fcb66da5db99cb571fad642a0d27fa32d0f76b1387-nordic-nrf52840dk-0.0.0.2.zip", + "url": "1f2931d9573f54266fffed606ea4fe8cc9fce5a9876be365ae74292f55dc3d34-nordic-nrf52840dk-0.0.0.2.cab", + "emulation-url": "19ebd9d8f941f80dc6c582fcb66da5db99cb571fad642a0d27fa32d0f76b1387-nordic-nrf52840dk-0.0.0.2.zip", "components": [ { "version": "0.0.0.2", diff -Nru fwupd-2.0.8/plugins/nordic-hid/tests/nordic-hid-nrf52840-mcuboot.json fwupd-2.0.20/plugins/nordic-hid/tests/nordic-hid-nrf52840-mcuboot.json --- fwupd-2.0.8/plugins/nordic-hid/tests/nordic-hid-nrf52840-mcuboot.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nordic-hid/tests/nordic-hid-nrf52840-mcuboot.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/904ff137256218bc617661bb4fc2bfddad19522c7e54773d1fd0290b54adfcee-nordic-nrf52840dk-mcuboot-0.0.0_2.cab", + "url": "904ff137256218bc617661bb4fc2bfddad19522c7e54773d1fd0290b54adfcee-nordic-nrf52840dk-mcuboot-0.0.0_2.cab", "components": [ { "version": "0.0.0.2", @@ -14,7 +14,7 @@ ] }, { - "url": "https://fwupd.org/downloads/2eb21f9439f0c4928c14548ff5c5c73ad6590d23fa704c58c4afa88168bcea90-nordic-nrf52840dk-mcuboot-0.0.0_3.cab", + "url": "2eb21f9439f0c4928c14548ff5c5c73ad6590d23fa704c58c4afa88168bcea90-nordic-nrf52840dk-mcuboot-0.0.0_3.cab", "components": [ { "version": "0.0.0.3", diff -Nru fwupd-2.0.8/plugins/nvme/README.md fwupd-2.0.20/plugins/nvme/README.md --- fwupd-2.0.8/plugins/nvme/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nvme/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -34,6 +34,15 @@ * `NVME\VEN_1179&DEV_010F&VER_3B2QGXA7` +Additionally, an extra instance IDs containing the serial number suffix may be added. + +* `NVME\VEN_1179&DEV_010F&SNSUFFIX_12345` + +When the model name can be read from the controller data the instance ID also +includes it: + +* `NVME\VEN_1179&DEV_010F&SNSUFFIX_12345&NAME_Lexar-SSD-NM790-1TB` + Additionally, for NVMe drives with Dell vendor firmware two extra GUIDs are added: @@ -56,6 +65,12 @@ Since: 1.1.3 +### NvmeSerialSuffixChars + +The number of characters of the serial number to use for `SNSUFFIX` in the instance ID. Defaults to 0. + +Since: 2.0.17 + ### `Flags=force-align` Force alignment of the firmware file. diff -Nru fwupd-2.0.8/plugins/nvme/fu-nvme-common.c fwupd-2.0.20/plugins/nvme/fu-nvme-common.c --- fwupd-2.0.8/plugins/nvme/fu-nvme-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nvme/fu-nvme-common.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,140 +0,0 @@ -/* - * Copyright 2019 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-nvme-common.h" - -const gchar * -fu_nvme_status_to_string(guint32 status) -{ - switch (status) { - case NVME_SC_SUCCESS: - return "Command completed successfully"; - case NVME_SC_INVALID_OPCODE: - return "Associated command opcode field is not valid"; - case NVME_SC_INVALID_FIELD: - return "Unsupported value in a defined field"; - case NVME_SC_CMDID_CONFLICT: - return "Command identifier is already in use"; - case NVME_SC_DATA_XFER_ERROR: - return "Error while trying to transfer the data or metadata"; - case NVME_SC_POWER_LOSS: - return "Command aborted due to power loss notification"; - case NVME_SC_INTERNAL: - return "Internal error"; - case NVME_SC_ABORT_REQ: - return "Command Abort request"; - case NVME_SC_ABORT_QUEUE: - return "Delete I/O Submission Queue request"; - case NVME_SC_FUSED_FAIL: - return "Other command in a fused operation failing"; - case NVME_SC_FUSED_MISSING: - return "Missing Fused Command"; - case NVME_SC_INVALID_NS: - return "Namespace or the format of that namespace is invalid"; - case NVME_SC_CMD_SEQ_ERROR: - return "Protocol violation in a multicommand sequence"; - case NVME_SC_SANITIZE_FAILED: - return "No recovery actions has been successfully completed"; - case NVME_SC_SANITIZE_IN_PROGRESS: - return "A sanitize operation is in progress"; - case NVME_SC_LBA_RANGE: - return "LBA exceeds the size of the namespace"; - case NVME_SC_NS_WRITE_PROTECTED: - return "Namespace is write protected by the host"; - case NVME_SC_CAP_EXCEEDED: - return "Capacity of the namespace to be exceeded"; - case NVME_SC_NS_NOT_READY: - return "Namespace is not ready to be accessed"; - case NVME_SC_RESERVATION_CONFLICT: - return "Conflict with a reservation on the accessed namespace"; - case NVME_SC_CQ_INVALID: - return "Completion Queue does not exist"; - case NVME_SC_QID_INVALID: - return "Invalid queue identifier specified"; - case NVME_SC_QUEUE_SIZE: - return "Invalid queue size"; - case NVME_SC_ABORT_LIMIT: - return "Outstanding Abort commands has exceeded the limit"; - case NVME_SC_ABORT_MISSING: - return "Abort command is missing"; - case NVME_SC_ASYNC_LIMIT: - return "Outstanding Async commands has been exceeded"; - case NVME_SC_FIRMWARE_SLOT: - return "Slot is invalid or read only"; - case NVME_SC_FIRMWARE_IMAGE: - return "Image specified for activation is invalid"; - case NVME_SC_INVALID_VECTOR: - return "Creation failed due to an invalid interrupt vector"; - case NVME_SC_INVALID_LOG_PAGE: - return "Log page indicated is invalid"; - case NVME_SC_INVALID_FORMAT: - return "LBA Format specified is not supported"; - case NVME_SC_FW_NEEDS_CONV_RESET: - return "commit was successful, but activation requires reset"; - case NVME_SC_INVALID_QUEUE: - return "Failed to delete the I/O Completion Queue specified"; - case NVME_SC_FEATURE_NOT_SAVEABLE: - return "Feature Identifier does not support a saveable value"; - case NVME_SC_FEATURE_NOT_CHANGEABLE: - return "Feature Identifier is not able to be changed"; - case NVME_SC_FEATURE_NOT_PER_NS: - return "Feature Identifier specified is not namespace specific"; - case NVME_SC_FW_NEEDS_SUBSYS_RESET: - return "Commit was successful, activation requires NVM Subsystem"; - case NVME_SC_FW_NEEDS_RESET: - return "Commit was successful, activation requires a reset"; - case NVME_SC_FW_NEEDS_MAX_TIME: - return "Would exceed the Maximum Time for Firmware Activation"; - case NVME_SC_FW_ACTIVATE_PROHIBITED: - return "Image specified is being prohibited from activation"; - case NVME_SC_OVERLAPPING_RANGE: - return "Image has overlapping ranges"; - case NVME_SC_NS_INSUFFICENT_CAP: - return "Requires more free space than is currently available"; - case NVME_SC_NS_ID_UNAVAILABLE: - return "Number of namespaces supported has been exceeded"; - case NVME_SC_NS_ALREADY_ATTACHED: - return "Controller is already attached to the namespace"; - case NVME_SC_NS_IS_PRIVATE: - return "Namespace is private"; - case NVME_SC_NS_NOT_ATTACHED: - return "Controller is not attached to the namespace"; - case NVME_SC_THIN_PROV_NOT_SUPP: - return "Thin provisioning is not supported by the controller"; - case NVME_SC_CTRL_LIST_INVALID: - return "Controller list provided is invalid"; - case NVME_SC_BP_WRITE_PROHIBITED: - return "Trying to modify a Boot Partition while it is locked"; - case NVME_SC_BAD_ATTRIBUTES: - return "Bad attributes"; - case NVME_SC_WRITE_FAULT: - return "Write data could not be committed to the media"; - case NVME_SC_READ_ERROR: - return "Read data could not be recovered from the media"; - case NVME_SC_GUARD_CHECK: - return "End-to-end guard check failure"; - case NVME_SC_APPTAG_CHECK: - return "End-to-end application tag check failure"; - case NVME_SC_REFTAG_CHECK: - return "End-to-end reference tag check failure"; - case NVME_SC_COMPARE_FAILED: - return "Miscompare during a Compare command"; - case NVME_SC_ACCESS_DENIED: - return "Access denied"; - case NVME_SC_UNWRITTEN_BLOCK: - return "Read from an LBA range containing a unwritten block"; - case NVME_SC_ANA_PERSISTENT_LOSS: - return "Namespace is in the ANA Persistent Loss state"; - case NVME_SC_ANA_INACCESSIBLE: - return "Namespace being in the ANA Inaccessible state"; - case NVME_SC_ANA_TRANSITION: - return "Namespace transitioning between Async Access states"; - default: - return "Unknown"; - } -} diff -Nru fwupd-2.0.8/plugins/nvme/fu-nvme-common.h fwupd-2.0.20/plugins/nvme/fu-nvme-common.h --- fwupd-2.0.8/plugins/nvme/fu-nvme-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nvme/fu-nvme-common.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,123 +0,0 @@ -/* - * Copyright 2019 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -enum { - /* - * Generic Command Status: - */ - NVME_SC_SUCCESS = 0x0, - NVME_SC_INVALID_OPCODE = 0x1, - NVME_SC_INVALID_FIELD = 0x2, - NVME_SC_CMDID_CONFLICT = 0x3, - NVME_SC_DATA_XFER_ERROR = 0x4, - NVME_SC_POWER_LOSS = 0x5, - NVME_SC_INTERNAL = 0x6, - NVME_SC_ABORT_REQ = 0x7, - NVME_SC_ABORT_QUEUE = 0x8, - NVME_SC_FUSED_FAIL = 0x9, - NVME_SC_FUSED_MISSING = 0xa, - NVME_SC_INVALID_NS = 0xb, - NVME_SC_CMD_SEQ_ERROR = 0xc, - NVME_SC_SGL_INVALID_LAST = 0xd, - NVME_SC_SGL_INVALID_COUNT = 0xe, - NVME_SC_SGL_INVALID_DATA = 0xf, - NVME_SC_SGL_INVALID_METADATA = 0x10, - NVME_SC_SGL_INVALID_TYPE = 0x11, - - NVME_SC_SGL_INVALID_OFFSET = 0x16, - NVME_SC_SGL_INVALID_SUBTYPE = 0x17, - - NVME_SC_SANITIZE_FAILED = 0x1C, - NVME_SC_SANITIZE_IN_PROGRESS = 0x1D, - - NVME_SC_NS_WRITE_PROTECTED = 0x20, - - NVME_SC_LBA_RANGE = 0x80, - NVME_SC_CAP_EXCEEDED = 0x81, - NVME_SC_NS_NOT_READY = 0x82, - NVME_SC_RESERVATION_CONFLICT = 0x83, - - /* - * Command Specific Status: - */ - NVME_SC_CQ_INVALID = 0x100, - NVME_SC_QID_INVALID = 0x101, - NVME_SC_QUEUE_SIZE = 0x102, - NVME_SC_ABORT_LIMIT = 0x103, - NVME_SC_ABORT_MISSING = 0x104, - NVME_SC_ASYNC_LIMIT = 0x105, - NVME_SC_FIRMWARE_SLOT = 0x106, - NVME_SC_FIRMWARE_IMAGE = 0x107, - NVME_SC_INVALID_VECTOR = 0x108, - NVME_SC_INVALID_LOG_PAGE = 0x109, - NVME_SC_INVALID_FORMAT = 0x10a, - NVME_SC_FW_NEEDS_CONV_RESET = 0x10b, - NVME_SC_INVALID_QUEUE = 0x10c, - NVME_SC_FEATURE_NOT_SAVEABLE = 0x10d, - NVME_SC_FEATURE_NOT_CHANGEABLE = 0x10e, - NVME_SC_FEATURE_NOT_PER_NS = 0x10f, - NVME_SC_FW_NEEDS_SUBSYS_RESET = 0x110, - NVME_SC_FW_NEEDS_RESET = 0x111, - NVME_SC_FW_NEEDS_MAX_TIME = 0x112, - NVME_SC_FW_ACTIVATE_PROHIBITED = 0x113, - NVME_SC_OVERLAPPING_RANGE = 0x114, - NVME_SC_NS_INSUFFICENT_CAP = 0x115, - NVME_SC_NS_ID_UNAVAILABLE = 0x116, - NVME_SC_NS_ALREADY_ATTACHED = 0x118, - NVME_SC_NS_IS_PRIVATE = 0x119, - NVME_SC_NS_NOT_ATTACHED = 0x11a, - NVME_SC_THIN_PROV_NOT_SUPP = 0x11b, - NVME_SC_CTRL_LIST_INVALID = 0x11c, - NVME_SC_BP_WRITE_PROHIBITED = 0x11e, - - /* - * I/O Command Set Specific - NVM commands: - */ - NVME_SC_BAD_ATTRIBUTES = 0x180, - NVME_SC_INVALID_PI = 0x181, - NVME_SC_READ_ONLY = 0x182, - NVME_SC_ONCS_NOT_SUPPORTED = 0x183, - - /* - * I/O Command Set Specific - Fabrics commands: - */ - NVME_SC_CONNECT_FORMAT = 0x180, - NVME_SC_CONNECT_CTRL_BUSY = 0x181, - NVME_SC_CONNECT_INVALID_PARAM = 0x182, - NVME_SC_CONNECT_RESTART_DISC = 0x183, - NVME_SC_CONNECT_INVALID_HOST = 0x184, - - NVME_SC_DISCOVERY_RESTART = 0x190, - NVME_SC_AUTH_REQUIRED = 0x191, - - /* - * Media and Data Integrity Errors: - */ - NVME_SC_WRITE_FAULT = 0x280, - NVME_SC_READ_ERROR = 0x281, - NVME_SC_GUARD_CHECK = 0x282, - NVME_SC_APPTAG_CHECK = 0x283, - NVME_SC_REFTAG_CHECK = 0x284, - NVME_SC_COMPARE_FAILED = 0x285, - NVME_SC_ACCESS_DENIED = 0x286, - NVME_SC_UNWRITTEN_BLOCK = 0x287, - - /* - * Path-related Errors: - */ - NVME_SC_ANA_PERSISTENT_LOSS = 0x301, - NVME_SC_ANA_INACCESSIBLE = 0x302, - NVME_SC_ANA_TRANSITION = 0x303, - - NVME_SC_DNR = 0x4000, -}; - -const gchar * -fu_nvme_status_to_string(guint32 status); diff -Nru fwupd-2.0.8/plugins/nvme/fu-nvme-device.c fwupd-2.0.20/plugins/nvme/fu-nvme-device.c --- fwupd-2.0.8/plugins/nvme/fu-nvme-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nvme/fu-nvme-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,15 +9,14 @@ #include #include -#include "fu-nvme-common.h" #include "fu-nvme-device.h" - -#define FU_NVME_ID_CTRL_SIZE 0x1000 +#include "fu-nvme-struct.h" struct _FuNvmeDevice { FuPciDevice parent_instance; guint pci_depth; guint64 write_block_size; + guint serial_suffix; }; #define FU_NVME_COMMIT_ACTION_CA0 0b000 /* replace only */ @@ -28,6 +27,10 @@ #define FU_NVME_DEVICE_FLAG_FORCE_ALIGN "force-align" #define FU_NVME_DEVICE_FLAG_COMMIT_CA3 "commit-ca3" +#define FU_NVME_LOG_FW_SLOT 0x03 + +#define FU_NVME_FW_SLOT_INFO_AFI_FWUP (1u << 3) + G_DEFINE_TYPE(FuNvmeDevice, fu_nvme_device, FU_TYPE_PCI_DEVICE) #define FU_NVME_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ @@ -37,6 +40,7 @@ { FuNvmeDevice *self = FU_NVME_DEVICE(device); fwupd_codec_string_append_int(str, idt, "PciDepth", self->pci_depth); + fwupd_codec_string_append_int(str, idt, "SerialSuffix", self->serial_suffix); } /* @addr_start and @addr_end are *inclusive* to match the NMVe specification */ @@ -125,11 +129,11 @@ /* check the error code */ err = rc & 0x3ff; switch (err) { - case NVME_SC_SUCCESS: + case FU_NVME_STATUS_SUCCESS: /* devices are always added with _NEEDS_REBOOT, so ignore */ - case NVME_SC_FW_NEEDS_CONV_RESET: - case NVME_SC_FW_NEEDS_SUBSYS_RESET: - case NVME_SC_FW_NEEDS_RESET: + case FU_NVME_STATUS_FW_NEEDS_CONV_RESET: + case FU_NVME_STATUS_FW_NEEDS_SUBSYS_RESET: + case FU_NVME_STATUS_FW_NEEDS_RESET: return TRUE; default: break; @@ -190,6 +194,60 @@ return fu_nvme_device_submit_admin_passthru(self, &cmd, buf_mut, bufsz, error); } +static FuStructNvmeFwSlotInfoLog * +fu_nvme_device_get_fw_slot_info(FuNvmeDevice *self, GError **error) +{ + guint8 buf[FU_STRUCT_NVME_FW_SLOT_INFO_LOG_SIZE] = {0}; + guint32 numd = (sizeof(buf) >> 2) - 1; + struct nvme_admin_cmd cmd = { + .opcode = 0x02, + .nsid = 0xffffffff, + .addr = 0x0, /* memory address of data */ + .data_len = sizeof(buf), + .cdw10 = (numd << 16) | FU_NVME_LOG_FW_SLOT, + }; + + if (!fu_nvme_device_submit_admin_passthru(self, &cmd, buf, sizeof(buf), error)) + return NULL; + return fu_struct_nvme_fw_slot_info_log_parse(buf, sizeof(buf), 0x0, error); +} + +static gboolean +fu_nvme_device_wait_for_fw_download_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuNvmeDevice *self = FU_NVME_DEVICE(device); + g_autoptr(FuStructNvmeFwSlotInfoLog) st_log = NULL; + + st_log = fu_nvme_device_get_fw_slot_info(self, error); + if (st_log == NULL) + return FALSE; + if (fu_struct_nvme_fw_slot_info_log_get_afi(st_log) & FU_NVME_FW_SLOT_INFO_AFI_FWUP) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_TIMED_OUT, + "download still marked in-progress"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_nvme_device_wait_for_fw_download(FuNvmeDevice *self, GError **error) +{ + /* preserve compat with older emulation files */ + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_EMULATED) && + !fu_device_check_fwupd_version(FU_DEVICE(self), "2.0.17")) + return TRUE; + return fu_device_retry_full(FU_DEVICE(self), + fu_nvme_device_wait_for_fw_download_cb, + 600, + 100, /* ms */ + NULL, + error); +} + static void fu_nvme_device_parse_cns_maybe_dell(FuNvmeDevice *self, const guint8 *buf) { @@ -218,55 +276,107 @@ fu_device_add_instance_id(FU_DEVICE(self), guid_efi); } +gboolean +fu_nvme_device_set_serial(FuNvmeDevice *self, const gchar *serial, GError **error) +{ + gsize serialsz; + + g_return_val_if_fail(serial != NULL, FALSE); + + /* always */ + fu_device_set_serial(FU_DEVICE(self), serial); + + /* some vendors seem to think it's logical to use the last 5 (Lexar) or 6 (Phison) + * characters of the serial number for the firmware variant */ + serialsz = strlen(serial); + if (self->serial_suffix > 0 && serialsz > self->serial_suffix) { + /* keep SNSUFFIX as just the tail */ + fu_device_add_instance_str(FU_DEVICE(self), + "SNSUFFIX", + serial + (serialsz - self->serial_suffix)); + if (!fu_device_build_instance_id(FU_DEVICE(self), + error, + "NVME", + "VEN", + "DEV", + "SNSUFFIX", + NULL)) + return FALSE; + + fu_device_build_instance_id(FU_DEVICE(self), + NULL, + "NVME", + "VEN", + "DEV", + "SNSUFFIX", + "NAME", + NULL); + } + + /* success */ + return TRUE; +} + static gboolean -fu_nvme_device_parse_cns(FuNvmeDevice *self, const guint8 *buf, gsize sz, GError **error) +fu_nvme_device_parse_cns(FuNvmeDevice *self, const guint8 *buf, gsize bufsz, GError **error) { + guint8 frmw; guint8 fawr; guint8 fwug; guint8 nfws; guint8 s1ro; - g_autofree gchar *gu = NULL; + const fwupd_guid_t *gu; g_autofree gchar *mn = NULL; + g_autofree gchar *mn_stripped = NULL; g_autofree gchar *sn = NULL; - g_autofree gchar *sr = NULL; + g_autofree gchar *fr = NULL; + g_autoptr(FuStructNvmeIdCtrl) st = NULL; /* wrong size */ - if (sz != FU_NVME_ID_CTRL_SIZE) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "failed to parse blob, expected 0x%04x bytes", - (guint)FU_NVME_ID_CTRL_SIZE); + st = fu_struct_nvme_id_ctrl_parse(buf, bufsz, 0x0, error); + if (st == NULL) return FALSE; - } - /* get sanitized string from CNS -- see the following doc for offsets: - * NVM-Express-1_3c-2018.05.24-Ratified.pdf */ - sn = fu_nvme_device_get_string_safe(buf, 4, 23); - if (sn != NULL) - fu_device_set_serial(FU_DEVICE(self), sn); - mn = fu_nvme_device_get_string_safe(buf, 24, 63); - if (mn != NULL) - fu_device_set_name(FU_DEVICE(self), mn); - sr = fu_nvme_device_get_string_safe(buf, 64, 71); - if (sr != NULL) - fu_device_set_version(FU_DEVICE(self), sr); + /* get sanitized string from CNS -- see NVM-Express-1_3c-2018.05.24-Ratified.pdf */ + mn = fu_struct_nvme_id_ctrl_get_mn(st); + if (mn != NULL) { + mn_stripped = fu_strstrip(mn); + if (mn_stripped[0] != '\0') { + fu_device_add_instance_strsafe(FU_DEVICE(self), "NAME", mn_stripped); + fu_device_set_name(FU_DEVICE(self), mn_stripped); + } + } + sn = fu_struct_nvme_id_ctrl_get_sn(st); + if (sn != NULL) { + g_autofree gchar *str = fu_strstrip(sn); + if (!fu_nvme_device_set_serial(self, str, error)) + return FALSE; + } + fr = fu_struct_nvme_id_ctrl_get_fr(st); + if (fr != NULL) { + g_autofree gchar *str = fu_strstrip(fr); + if (str[0] != '\0') + fu_device_set_version(FU_DEVICE(self), fr); + } - /* firmware update granularity (FWUG) */ - fwug = buf[319]; + /* firmware update granularity */ + fwug = fu_struct_nvme_id_ctrl_get_fwug(st); if (fwug != 0x00 && fwug != 0xff) self->write_block_size = ((guint64)fwug) * 0x1000; /* firmware slot information */ - fawr = (buf[260] & 0x10) >> 4; - nfws = (buf[260] & 0x0e) >> 1; - s1ro = buf[260] & 0x01; + frmw = fu_struct_nvme_id_ctrl_get_frmw(st); + fawr = (frmw & 0x10) >> 4; + nfws = (frmw & 0x0e) >> 1; + s1ro = frmw & 0x01; g_debug("fawr: %u, nr fw slots: %u, slot1 r/o: %u", fawr, nfws, s1ro); - /* FRU globally unique identifier (FGUID) */ - gu = fu_nvme_device_get_guid_safe(buf, 127); - if (gu != NULL) - fu_device_add_instance_id(FU_DEVICE(self), gu); + /* FRU globally unique identifier */ + gu = fu_struct_nvme_id_ctrl_get_fguid(st); + if (fu_common_guid_is_plausible((const guint8 *)gu)) { + g_autofree gchar *guid = fwupd_guid_to_string(gu, FWUPD_GUID_FLAG_MIXED_ENDIAN); + fu_device_add_instance_id(FU_DEVICE(self), guid); + } /* Dell helpfully provide an EFI GUID we can use in the vendor offset, * but don't have a header or any magic we can use -- so check if the @@ -274,9 +384,9 @@ fu_nvme_device_parse_cns_maybe_dell(self, buf); /* fall back to the device description */ - if (mn != NULL && fu_device_get_guids(FU_DEVICE(self))->len == 0) { + if (mn_stripped != NULL && fu_device_get_guids(FU_DEVICE(self))->len == 0) { g_debug("no vendor GUID, falling back to mn"); - fu_device_add_instance_id(FU_DEVICE(self), mn); + fu_device_add_instance_id(FU_DEVICE(self), mn_stripped); } return TRUE; } @@ -327,10 +437,6 @@ if (!fu_nvme_device_pci_probe(self, error)) return FALSE; - /* fix up vendor name so we can remove it from the product name */ - if (g_strcmp0(fu_device_get_vendor(FU_DEVICE(device)), "Samsung Electronics Co Ltd") == 0) - fu_device_set_vendor(FU_DEVICE(device), "Samsung"); - /* look at the PCI depth to work out if in an external enclosure */ self->pci_depth = fu_udev_device_get_subsystem_depth(FU_UDEV_DEVICE(device), "pci"); if (self->pci_depth <= 2) { @@ -352,7 +458,7 @@ fu_nvme_device_setup(FuDevice *device, GError **error) { FuNvmeDevice *self = FU_NVME_DEVICE(device); - guint8 buf[FU_NVME_ID_CTRL_SIZE] = {0x0}; + guint8 buf[FU_STRUCT_NVME_ID_CTRL_SIZE] = {0x0}; /* get and parse CNS */ if (!fu_nvme_device_identify_ctrl(self, buf, sizeof(buf), error)) { @@ -438,6 +544,12 @@ } fu_progress_step_done(progress); + /* wait */ + if (!fu_nvme_device_wait_for_fw_download(self, error)) { + g_prefix_error_literal(error, "firmware download did not complete: "); + return FALSE; + } + /* commit */ if (fu_device_has_private_flag(device, FU_NVME_DEVICE_FLAG_COMMIT_CA3)) commit_action = FU_NVME_COMMIT_ACTION_CA3; @@ -446,7 +558,7 @@ commit_action, 0x00, /* boot partition identifier */ error)) { - g_prefix_error(error, "failed to commit to auto slot: "); + g_prefix_error_literal(error, "failed to commit to auto slot: "); return FALSE; } fu_progress_step_done(progress); @@ -466,6 +578,13 @@ self->write_block_size = tmp; return TRUE; } + if (g_strcmp0(key, "NvmeSerialSuffixChars") == 0) { + guint64 tmp = 0; + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->serial_suffix = tmp; + return TRUE; + } g_set_error_literal(error, FWUPD_ERROR, @@ -475,7 +594,7 @@ } static void -fu_nvme_device_set_progress(FuDevice *self, FuProgress *progress) +fu_nvme_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -495,7 +614,7 @@ fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_RETRY_OPEN); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); fu_device_set_summary(FU_DEVICE(self), "NVM Express solid state drive"); - fu_device_add_icon(FU_DEVICE(self), "drive-harddisk"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_DRIVE_HARDDISK); fu_device_add_protocol(FU_DEVICE(self), "org.nvmexpress"); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); fu_device_register_private_flag(FU_DEVICE(self), FU_NVME_DEVICE_FLAG_FORCE_ALIGN); diff -Nru fwupd-2.0.8/plugins/nvme/fu-nvme-device.h fwupd-2.0.20/plugins/nvme/fu-nvme-device.h --- fwupd-2.0.8/plugins/nvme/fu-nvme-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nvme/fu-nvme-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -13,3 +13,5 @@ FuNvmeDevice * fu_nvme_device_new_from_blob(FuContext *ctx, const guint8 *buf, gsize sz, GError **error); +gboolean +fu_nvme_device_set_serial(FuNvmeDevice *self, const gchar *serial, GError **error); diff -Nru fwupd-2.0.8/plugins/nvme/fu-nvme-plugin.c fwupd-2.0.20/plugins/nvme/fu-nvme-plugin.c --- fwupd-2.0.8/plugins/nvme/fu-nvme-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nvme/fu-nvme-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -26,6 +26,7 @@ FuPlugin *plugin = FU_PLUGIN(obj); FuContext *ctx = fu_plugin_get_context(plugin); fu_context_add_quirk_key(ctx, "NvmeBlockSize"); + fu_context_add_quirk_key(ctx, "NvmeSerialSuffixChars"); fu_plugin_add_device_udev_subsystem(plugin, "nvme"); fu_plugin_add_device_gtype(plugin, FU_TYPE_NVME_DEVICE); } diff -Nru fwupd-2.0.8/plugins/nvme/fu-nvme.rs fwupd-2.0.20/plugins/nvme/fu-nvme.rs --- fwupd-2.0.8/plugins/nvme/fu-nvme.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/nvme/fu-nvme.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,163 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[derive(ToString)] +enum FuNvmeStatus { + // Generic Command Status: + Success = 0x0, + InvalidOpcode = 0x1, + InvalidField = 0x2, + CmdidConflict = 0x3, + DataXferError = 0x4, + PowerLoss = 0x5, + Internal = 0x6, + AbortReq = 0x7, + AbortQueue = 0x8, + FusedFail = 0x9, + FusedMissing = 0xa, + InvalidNs = 0xb, + CmdSeqError = 0xc, + SglInvalidLast = 0xd, + SglInvalidCount = 0xe, + SglInvalidData = 0xf, + SglInvalidMetadata = 0x10, + SglInvalidType = 0x11, + + SglInvalidOffset = 0x16, + SglInvalidSubtype = 0x17, + + SanitizeFailed = 0x1c, + SanitizeInProgress = 0x1d, + + NsWriteProtected = 0x20, + + LbaRange = 0x80, + CapExceeded = 0x81, + NsNotReady = 0x82, + ReservationConflict = 0x83, + + // command specific status: + CqInvalid = 0x100, + QidInvalid = 0x101, + QueueSize = 0x102, + AbortLimit = 0x103, + AbortMissing = 0x104, + AsyncLimit = 0x105, + FirmwareSlot = 0x106, + FirmwareImage = 0x107, + InvalidVector = 0x108, + InvalidLogPage = 0x109, + InvalidFormat = 0x10a, + FwNeedsConvReset = 0x10b, + InvalidQueue = 0x10c, + FeatureNotSaveable = 0x10d, + FeatureNotChangeable = 0x10e, + FeatureNotPerNs = 0x10f, + FwNeedsSubsysReset = 0x110, + FwNeedsReset = 0x111, + FwNeedsMaxTime = 0x112, + FwActivateProhibited = 0x113, + OverlappingRange = 0x114, + NsInsufficentCap = 0x115, + NsIdUnavailable = 0x116, + NsAlreadyAttached = 0x118, + NsIsPrivate = 0x119, + NsNotAttached = 0x11a, + ThinProvNotSupp = 0x11b, + CtrlListInvalid = 0x11c, + BpWriteProhibited = 0x11e, + + // i/o command set specific - nvm commands: + BadAttributes = 0x180, + InvalidPi = 0x181, + ReadOnly = 0x182, + OncsNotSupported = 0x183, + + DiscoveryRestart = 0x190, + AuthRequired = 0x191, + + // media and data integrity errors: + WriteFault = 0x280, + ReadError = 0x281, + GuardCheck = 0x282, + ApptagCheck = 0x283, + ReftagCheck = 0x284, + CompareFailed = 0x285, + AccessDenied = 0x286, + UnwrittenBlock = 0x287, + + // path-related errors: + AnaPersistentLoss = 0x301, + AnaInaccessible = 0x302, + AnaTransition = 0x303, + + Dnr = 0x4000, +} + +#[derive(Parse)] +struct FuStructNvmeIdCtrl { + vid: u16le, + ssvid: u16le, + sn: [char; 20], + mn: [char; 40], + fr: [char; 8], + rab: u8, + ieee: [u8; 3], + cmic: u8, + mdts: u8, + cntlid: u16le, + ver: u32le, + rtd3r: u32le, + rtd3e: u32le, + oaes: u32le, + ctratt: u32le, + _rsvd100: [u8; 11], + cntrltype: u8, + fguid: Guid, + crdt1: u16le, + crdt2: u16le, + crdt3: u16le, + _rsvd134: [u8; 122], + oacs: u16le, + acl: u8, + aerl: u8, + frmw: u8, + lpa: u8, + elpe: u8, + npss: u8, + avscc: u8, + apsta: u8, + wctemp: u16le, + cctemp: u16le, + mtfa: u16le, + hmpre: u32le, + hmmin: u32le, + tnvmcap: [u8; 16], + unvmcap: [u8; 16], + rpmbs: u32le, + edstt: u16le, + dsto: u8, + fwug: u8, + kas: u16le, + hctma: u16le, + mntmt: u16le, + mxtmt: u16le, + sanicap: u32le, + hmminds: u32le, + hmmaxd: u16le, + nvmsetidmax: u16le, + endgidmax: u16le, + anatt: u8, + anacap: u8, + anagrpmax: u32le, + nanagrpid: u32le, + reserved: [u8; 3744], +} + +#[derive(Parse)] +struct FuStructNvmeFwSlotInfoLog { + afi: u8, + _reserved1[]: [u8; 7], + frs: [u64le; 7], + _reserved2[]: [u8; 448], +} diff -Nru fwupd-2.0.8/plugins/nvme/fu-self-test.c fwupd-2.0.20/plugins/nvme/fu-self-test.c --- fwupd-2.0.8/plugins/nvme/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nvme/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,6 +11,42 @@ #include "fu-nvme-device.h" static void +fu_nvme_serial_suffix_func(void) +{ + gboolean ret; + g_autofree gchar *str = NULL; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDevice) device = g_object_new(FU_TYPE_NVME_DEVICE, "context", ctx, NULL); + g_autoptr(GError) error = NULL; + + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + fu_device_add_instance_str(device, "VEN", "1234"); + fu_device_add_instance_str(device, "DEV", "5678"); + ret = fu_device_set_quirk_kv(device, + "NvmeSerialSuffixChars", + "8", + FU_CONTEXT_QUIRK_SOURCE_DB, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + ret = fu_nvme_device_set_serial(FU_NVME_DEVICE(device), "S6B0NL0ABCDEFGH", &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check has SNSUFFIX extra ID */ + str = fu_device_to_string(device); + g_debug("%s", str); + g_assert_cmpstr(fu_device_get_serial(device), ==, "S6B0NL0ABCDEFGH"); + g_assert_true(fu_device_has_instance_id(device, + "NVME\\VEN_1234&DEV_5678&SNSUFFIX_ABCDEFGH", + FU_DEVICE_INSTANCE_FLAG_VISIBLE)); +} + +static void fu_nvme_cns_func(void) { gboolean ret; @@ -89,6 +125,7 @@ { g_autofree gchar *testdatadir = NULL; (void)g_setenv("G_TEST_SRCDIR", SRCDIR, FALSE); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); g_test_init(&argc, &argv, NULL); /* only critical and error are fatal */ @@ -98,6 +135,7 @@ (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); /* tests go here */ + g_test_add_func("/fwupd/serial-suffix", fu_nvme_serial_suffix_func); g_test_add_func("/fwupd/cns", fu_nvme_cns_func); g_test_add_func("/fwupd/cns{all}", fu_nvme_cns_all_func); return g_test_run(); diff -Nru fwupd-2.0.8/plugins/nvme/meson.build fwupd-2.0.20/plugins/nvme/meson.build --- fwupd-2.0.8/plugins/nvme/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nvme/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,14 +1,14 @@ -nvme_header = cc.has_header('linux/nvme_ioctl.h', required: false) +host_machine.system() == 'linux' or subdir_done() +cc.has_header('linux/nvme_ioctl.h', required: false) or subdir_done() -if nvme_header and host_machine.system() == 'linux' cargs = ['-DG_LOG_DOMAIN="FuPluginNvme"'] plugins += {meson.current_source_dir().split('/')[-1]: true} plugin_quirks += files('nvme.quirk') plugin_builtin_nvme = static_library('fu_plugin_nvme', + rustgen.process('fu-nvme.rs'), sources: [ 'fu-nvme-plugin.c', - 'fu-nvme-common.c', 'fu-nvme-device.c', ], include_directories: plugin_incdirs, @@ -43,6 +43,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', @@ -50,4 +51,3 @@ ) test('nvme-self-test', e, env: env) # added to installed-tests endif -endif diff -Nru fwupd-2.0.8/plugins/nvme/nvme.quirk fwupd-2.0.20/plugins/nvme/nvme.quirk --- fwupd-2.0.8/plugins/nvme/nvme.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nvme/nvme.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -1,11 +1,16 @@ # Phison [NVME\VEN_1987] Flags = force-align,needs-shutdown +NvmeSerialSuffixChars = 6 # Kingston [NVME\VEN_2646] Flags = needs-shutdown +# Micron +[NVME\VEN_1344] +Flags = needs-shutdown + # KIOXIA [NVME\VEN_1179] Flags = signed-payload @@ -46,6 +51,16 @@ [NVME\VEN_025E&DEV_F1AB] Flags = needs-shutdown +# Lexar +[NVME\VEN_1D97] +Flags = ensure-semver,commit-ca3 +NvmeSerialSuffixChars = 5 + +# Maxio +[NVME\VEN_1E4B] +Flags = ensure-semver,commit-ca3 +NvmeSerialSuffixChars = 5 + [NVME\VEN_144D&DEV_A80A&VER_2B2QGXA7] Issue = https://www.pugetsystems.com/support/guides/critical-samsung-ssd-firmware-update/ [NVME\VEN_144D&DEV_A80A&VER_3B2QGXA7] diff -Nru fwupd-2.0.8/plugins/nvme/tests/lenovo-pcsn720.json fwupd-2.0.20/plugins/nvme/tests/lenovo-pcsn720.json --- fwupd-2.0.8/plugins/nvme/tests/lenovo-pcsn720.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/nvme/tests/lenovo-pcsn720.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/8e2a52b74fcdbcf2f82bf2e0f6b750d6f098be887bb1e4c6acba3b63454a4f73-Lenovo-WD-PCSN720-NVMe-10190101.cab", - "emulation-url": "https://fwupd.org/downloads/a97b2f699d80f30ac24c3dfda4ed51a7935bdf0289b899d5be1a3dffdd2c8843-Lenovo-WD-PCSN720-NVMe-10190101.zip", + "url": "8e2a52b74fcdbcf2f82bf2e0f6b750d6f098be887bb1e4c6acba3b63454a4f73-Lenovo-WD-PCSN720-NVMe-10190101.cab", + "emulation-url": "a97b2f699d80f30ac24c3dfda4ed51a7935bdf0289b899d5be1a3dffdd2c8843-Lenovo-WD-PCSN720-NVMe-10190101.zip", "components": [ { "version": "10190101", diff -Nru fwupd-2.0.8/plugins/optionrom/README.md fwupd-2.0.20/plugins/optionrom/README.md --- fwupd-2.0.8/plugins/optionrom/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/optionrom/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,26 +0,0 @@ ---- -title: Plugin: OptionROM ---- - -## Introduction - -This plugin is also able to read and parse the firmware of some PCI devices -which allows some host state verification to be done. - -## GUID Generation - -These devices use the standard USB DeviceInstanceId values, e.g. - -* `PCI\VEN_%04X&DEV_%04X` - -## Vendor ID Security - -The device is not upgradable and thus requires no vendor ID set. - -## External Interface Access - -This plugin requires read access to the rom file of PCI devices (`/sys/class/pci_bus/*/device/rom`) - -## Version Considerations - -This plugin has been available since fwupd version `1.3.3`. diff -Nru fwupd-2.0.8/plugins/optionrom/fu-optionrom-plugin.c fwupd-2.0.20/plugins/optionrom/fu-optionrom-plugin.c --- fwupd-2.0.8/plugins/optionrom/fu-optionrom-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/optionrom/fu-optionrom-plugin.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,68 +0,0 @@ -/* - * Copyright 2015-2016 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-optionrom-plugin.h" - -struct _FuOptionromPlugin { - FuPlugin parent_instance; -}; - -G_DEFINE_TYPE(FuOptionromPlugin, fu_optionrom_plugin, FU_TYPE_PLUGIN) - -static void -fu_optionrom_plugin_init(FuOptionromPlugin *self) -{ -} - -static void -fu_optionrom_plugin_constructed(GObject *obj) -{ - FuPlugin *plugin = FU_PLUGIN(obj); - fu_plugin_add_device_udev_subsystem(plugin, "pci"); - fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "udev"); - fu_plugin_add_device_gtype(plugin, FU_TYPE_OPROM_DEVICE); -} - -static gboolean -fu_optionrom_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) -{ - FuContext *ctx = fu_plugin_get_context(plugin); - if (fu_context_has_hwid_flag(ctx, "no-probe")) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "not supported on this platform"); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_optionrom_plugin_device_created(FuPlugin *self, FuDevice *device, GError **error) -{ - if (!fu_device_probe(device, error)) - return FALSE; - if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "unable to read firmware from device, 'rom' does not exist"); - return FALSE; - } - fu_device_set_logical_id(device, "rom"); - return TRUE; -} - -static void -fu_optionrom_plugin_class_init(FuOptionromPluginClass *klass) -{ - FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); - plugin_class->constructed = fu_optionrom_plugin_constructed; - plugin_class->device_created = fu_optionrom_plugin_device_created; - plugin_class->startup = fu_optionrom_plugin_startup; -} diff -Nru fwupd-2.0.8/plugins/optionrom/fu-optionrom-plugin.h fwupd-2.0.20/plugins/optionrom/fu-optionrom-plugin.h --- fwupd-2.0.8/plugins/optionrom/fu-optionrom-plugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/optionrom/fu-optionrom-plugin.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -G_DECLARE_FINAL_TYPE(FuOptionromPlugin, fu_optionrom_plugin, FU, OPTIONROM_PLUGIN, FuPlugin) Binary files /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/plugins/optionrom/fuzzing/header-data-payload.rom and /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/plugins/optionrom/fuzzing/header-data-payload.rom differ Binary files /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/plugins/optionrom/fuzzing/header-no-data.rom and /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/plugins/optionrom/fuzzing/header-no-data.rom differ Binary files /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/plugins/optionrom/fuzzing/ifr-header-data-payload.rom and /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/plugins/optionrom/fuzzing/ifr-header-data-payload.rom differ Binary files /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/plugins/optionrom/fuzzing/naked-ifr.rom and /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/plugins/optionrom/fuzzing/naked-ifr.rom differ diff -Nru fwupd-2.0.8/plugins/optionrom/meson.build fwupd-2.0.20/plugins/optionrom/meson.build --- fwupd-2.0.8/plugins/optionrom/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/optionrom/meson.build 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -if host_machine.system() == 'linux' -cargs = ['-DG_LOG_DOMAIN="FuPluginOptionrom"'] -plugins += {meson.current_source_dir().split('/')[-1]: true} - -plugin_quirks += files('optionrom.quirk') - -plugin_builtins += static_library('fu_plugin_optionrom', - sources: [ - 'fu-optionrom-plugin.c', - ], - include_directories: plugin_incdirs, - link_with: plugin_libs, - c_args: cargs, - dependencies: plugin_deps, -) -endif diff -Nru fwupd-2.0.8/plugins/optionrom/optionrom.quirk fwupd-2.0.20/plugins/optionrom/optionrom.quirk --- fwupd-2.0.8/plugins/optionrom/optionrom.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/optionrom/optionrom.quirk 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -# Apple Inc. HwID -[80f95c96-a739-5ef5-8482-3d65cb39ff55] -Flags = no-probe diff -Nru fwupd-2.0.8/plugins/parade-lspcon/README.md fwupd-2.0.20/plugins/parade-lspcon/README.md --- fwupd-2.0.8/plugins/parade-lspcon/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/parade-lspcon/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -46,10 +46,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.6.2`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* PingYao Chen: @Pingyao5115 diff -Nru fwupd-2.0.8/plugins/parade-lspcon/fu-parade-lspcon-device.c fwupd-2.0.20/plugins/parade-lspcon/fu-parade-lspcon-device.c --- fwupd-2.0.8/plugins/parade-lspcon/fu-parade-lspcon-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/parade-lspcon/fu-parade-lspcon-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -633,7 +633,7 @@ if (buf == NULL) return FALSE; if (!fu_memcmp_safe(buf->data, buf->len, 0x0, readback_buf, blocksz, 0x0, blocksz, error)) { - g_prefix_error(error, "flash contents do not match: "); + g_prefix_error_literal(error, "flash contents do not match: "); return FALSE; } fu_progress_step_done(progress); @@ -670,7 +670,7 @@ 0x0, MIN(sizeof(flag_data), blocksz), error)) { - g_prefix_error(error, "flag partition contents do not match: "); + g_prefix_error_literal(error, "flag partition contents do not match: "); return FALSE; } @@ -887,7 +887,7 @@ } static void -fu_parade_lspcon_device_set_progress(FuDevice *self, FuProgress *progress) +fu_parade_lspcon_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -910,7 +910,7 @@ fu_parade_lspcon_device_init(FuParadeLspconDevice *self) { fu_device_add_protocol(FU_DEVICE(self), "com.paradetech.ps176"); - fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_VIDEO_DISPLAY); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); diff -Nru fwupd-2.0.8/plugins/parade-lspcon/meson.build fwupd-2.0.20/plugins/parade-lspcon/meson.build --- fwupd-2.0.8/plugins/parade-lspcon/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/parade-lspcon/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginParadeLspcon"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -19,4 +20,3 @@ ], dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/parade-usbhub/README.md fwupd-2.0.20/plugins/parade-usbhub/README.md --- fwupd-2.0.8/plugins/parade-usbhub/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/parade-usbhub/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -27,7 +27,7 @@ * Load 384KB SPI ROM data buffer * Specify target SPI ROM bank, ex: HUB_FW#1 or HUB_FW#2 * Set UFP Disconnect Flag register to notify firmware that we are doing firmware update -* Acquire SPI Master +* Acquire SPI * Unprotect SPI ROM bank * Erase SPI ROM bank * Update SPI ROM bank @@ -59,12 +59,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Hub Lin: @hublin2024 -* Jimmy Tu: @jimmytu5167 -* Andy Chu: @andychu5168 diff -Nru fwupd-2.0.8/plugins/parade-usbhub/fu-parade-usbhub-device.c fwupd-2.0.20/plugins/parade-usbhub/fu-parade-usbhub-device.c --- fwupd-2.0.8/plugins/parade-usbhub/fu-parade-usbhub-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/parade-usbhub/fu-parade-usbhub-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -486,7 +486,7 @@ } static gboolean -fu_parade_usbhub_device_enable_spi_master(FuParadeUsbhubDevice *self, GError **error) +fu_parade_usbhub_device_enable_spi(FuParadeUsbhubDevice *self, GError **error) { return fu_parade_usbhub_device_mmio_set_bit(self, FU_PARADE_USBHUB_DEVICE_ADDR_SPI_MASTER, @@ -495,7 +495,7 @@ } static gboolean -fu_parade_usbhub_device_disable_spi_master(FuParadeUsbhubDevice *self, GError **error) +fu_parade_usbhub_device_disable_spi(FuParadeUsbhubDevice *self, GError **error) { return fu_parade_usbhub_device_mmio_clear_bit(self, FU_PARADE_USBHUB_DEVICE_ADDR_SPI_MASTER, @@ -564,7 +564,7 @@ { guint8 status = 0; guint8 status_new = 0; - guint8 buf_spi[2]; + guint8 buf_spi[2] = {0}; guint8 spi_cmd_write_en = 0; guint8 spi_cmd_read_status = 0; guint8 spi_cmd_write_status = 0; @@ -851,7 +851,7 @@ { guint8 status = 0; guint8 status_new = 0; - guint8 buf_spi[2]; + guint8 buf_spi[2] = {0}; guint8 spi_cmd_write_en = 0; guint8 spi_cmd_read_status = 0; guint8 spi_cmd_write_status = 0; @@ -957,10 +957,10 @@ guint8 buf_csum[4] = {0}; g_autoptr(GPtrArray) chunks = NULL; - /* acquire and enable SPI master after internal reset */ + /* acquire and enable SPI after internal reset */ if (!fu_parade_usbhub_device_acquire_spi_master(self, error)) return FALSE; - if (!fu_parade_usbhub_device_enable_spi_master(self, error)) + if (!fu_parade_usbhub_device_enable_spi(self, error)) return FALSE; /* calculate checksum internally */ @@ -992,7 +992,7 @@ } static gboolean -fu_parade_usbhub_device_set_ufp_disconnect_flag(FuParadeUsbhubDevice *self, GError **error) +fu_parade_usbhub_device_assert_ufp_disconnect_flag(FuParadeUsbhubDevice *self, GError **error) { guint8 val = 0; if (!fu_parade_usbhub_device_mmio_read_u8(self, @@ -1045,15 +1045,15 @@ FuParadeUsbhubDevice *self = FU_PARADE_USBHUB_DEVICE(device); if (!fu_parade_usbhub_device_acquire_spi_master(self, error)) { - g_prefix_error(error, "failed to acquire SPI master: "); + g_prefix_error_literal(error, "failed to acquire SPI: "); return FALSE; } - if (!fu_parade_usbhub_device_enable_spi_master(self, error)) { - g_prefix_error(error, "failed to enable SPI master: "); + if (!fu_parade_usbhub_device_enable_spi(self, error)) { + g_prefix_error_literal(error, "failed to enable SPI: "); return FALSE; } if (!fu_parade_usbhub_device_spi_rom_chip_unprotect(self, error)) { - g_prefix_error(error, "failed to unprotect SPI ROM: "); + g_prefix_error_literal(error, "failed to unprotect SPI ROM: "); return FALSE; } @@ -1067,11 +1067,11 @@ FuParadeUsbhubDevice *self = FU_PARADE_USBHUB_DEVICE(device); if (!fu_parade_usbhub_device_spi_rom_chip_protect(self, error)) { - g_prefix_error(error, "failed to protect SPI ROM: "); + g_prefix_error_literal(error, "failed to protect SPI ROM: "); return FALSE; } - if (!fu_parade_usbhub_device_disable_spi_master(self, error)) { - g_prefix_error(error, "failed to disable SPI master: "); + if (!fu_parade_usbhub_device_disable_spi(self, error)) { + g_prefix_error_literal(error, "failed to disable SPI: "); return FALSE; } @@ -1090,7 +1090,7 @@ /* get the version from the hardware */ if (!fu_parade_usbhub_device_ensure_version(self, error)) { - g_prefix_error(error, "failed to get version: "); + g_prefix_error_literal(error, "failed to get version: "); return FALSE; } @@ -1108,8 +1108,8 @@ if (self->chip == FU_PARADE_USBHUB_CHIP_PS188) { /* prevent staying in high-power charging mode if UFP is disconnected */ - if (!fu_parade_usbhub_device_set_ufp_disconnect_flag(self, error)) { - g_prefix_error(error, "failed to set UFP disconnect flag: "); + if (!fu_parade_usbhub_device_assert_ufp_disconnect_flag(self, error)) { + g_prefix_error_literal(error, "failed to set UFP disconnect flag: "); return FALSE; } } @@ -1138,7 +1138,7 @@ fu_parade_usbhub_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) firmware = fu_parade_usbhub_firmware_new(); @@ -1196,7 +1196,7 @@ /* compare checksum */ if (!fu_parade_usbhub_device_spi_rom_checksum(self, blob->len, &checksum_new, error)) { - g_prefix_error(error, "failed to get ROM checksum: "); + g_prefix_error_literal(error, "failed to get ROM checksum: "); return FALSE; } checksum = fu_crc32(FU_CRC_KIND_B32_MPEG2, blob->data, blob->len); @@ -1216,7 +1216,7 @@ } static void -fu_parade_usbhub_device_set_progress(FuDevice *self, FuProgress *progress) +fu_parade_usbhub_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -1262,7 +1262,7 @@ fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_USER_REPLUG); fu_device_set_firmware_size(FU_DEVICE(self), FU_PARADE_USBHUB_SPI_ROM_SIZE); fu_device_add_protocol(FU_DEVICE(self), "com.paradetech.usbhub"); - fu_device_add_icon(FU_DEVICE(self), "usb-hub"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_USB_HUB); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); diff -Nru fwupd-2.0.8/plugins/parade-usbhub/fu-parade-usbhub-firmware.c fwupd-2.0.20/plugins/parade-usbhub/fu-parade-usbhub-firmware.c --- fwupd-2.0.8/plugins/parade-usbhub/fu-parade-usbhub-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/parade-usbhub/fu-parade-usbhub-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -28,7 +28,7 @@ static gboolean fu_parade_usbhub_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { gsize streamsz = 0; diff -Nru fwupd-2.0.8/plugins/parade-usbhub/fu-parade-usbhub-plugin.c fwupd-2.0.20/plugins/parade-usbhub/fu-parade-usbhub-plugin.c --- fwupd-2.0.8/plugins/parade-usbhub/fu-parade-usbhub-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/parade-usbhub/fu-parade-usbhub-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -27,6 +27,7 @@ FuPlugin *plugin = FU_PLUGIN(obj); FuContext *ctx = fu_plugin_get_context(plugin); fu_context_add_quirk_key(ctx, "ParadeUsbhubChip"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_PARADE_USBHUB_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_PARADE_USBHUB_FIRMWARE); } diff -Nru fwupd-2.0.8/plugins/parade-usbhub/fu-parade-usbhub.rs fwupd-2.0.20/plugins/parade-usbhub/fu-parade-usbhub.rs --- fwupd-2.0.8/plugins/parade-usbhub/fu-parade-usbhub.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/parade-usbhub/fu-parade-usbhub.rs 2026-02-26 11:36:18.000000000 +0000 @@ -31,7 +31,7 @@ SpiMasterAcquire2 = 0xE2B3, // for PS188 } -enum FuParadeUsbhubDeviceStatusFlag { +enum FuParadeUsbhubDeviceStatusFlags { Write = 0b00000001, TriggerSpi = 0b00000010, TriggerDbi = 0b00000100, diff -Nru fwupd-2.0.8/plugins/parade-usbhub/meson.build fwupd-2.0.20/plugins/parade-usbhub/meson.build --- fwupd-2.0.8/plugins/parade-usbhub/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/parade-usbhub/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,3 @@ -if libusb.found() cargs = ['-DG_LOG_DOMAIN="FuPluginParadeUsbhub"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -19,4 +18,3 @@ device_tests += files( 'tests/parade-ps5512evb.json', ) -endif diff -Nru fwupd-2.0.8/plugins/parade-usbhub/parade-usbhub.quirk fwupd-2.0.20/plugins/parade-usbhub/parade-usbhub.quirk --- fwupd-2.0.8/plugins/parade-usbhub/parade-usbhub.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/parade-usbhub/parade-usbhub.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -1,21 +1,33 @@ # PS5511 devboard [USB\VID_1DA0&PID_5511] Plugin = parade_usbhub +Flags = enforce-requires [USB\VID_1DA0&PID_55A1] Plugin = parade_usbhub +Flags = enforce-requires # PS5512 devboard [USB\VID_1DA0&PID_5512] Plugin = parade_usbhub +Flags = enforce-requires [USB\VID_1DA0&PID_551D] Plugin = parade_usbhub +Flags = enforce-requires + +# PS5512 - Dell +[USB\VID_413C&PID_1020] +Plugin = parade_usbhub +[USB\VID_413C&PID_1021] +Plugin = parade_usbhub # PS188 [USB\VID_1DA0&PID_0188] Plugin = parade_usbhub +Flags = enforce-requires ParadeUsbhubChip = ps188 [USB\VID_1DA0&PID_2188] Plugin = parade_usbhub +Flags = enforce-requires ParadeUsbhubChip = ps188 # PS188 - Lenovo diff -Nru fwupd-2.0.8/plugins/parade-usbhub/tests/parade-ps5512evb.json fwupd-2.0.20/plugins/parade-usbhub/tests/parade-ps5512evb.json --- fwupd-2.0.8/plugins/parade-usbhub/tests/parade-ps5512evb.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/parade-usbhub/tests/parade-ps5512evb.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": true, "steps": [ { - "url": "https://fwupd.org/downloads/f8b4534e1cec74910797f9de77b8821a4989672b503a7d0362238c2266d80a40-Parade-PS5512_EVB-1.1.0.21.cab", - "emulation-url": "https://fwupd.org/downloads/dff3d1869ac203dc6531fd43eab0172c635ca24d3bf94619635267e68915addb-Parade-PS5512_EVB-1.1.0.21.zip", + "url": "f8b4534e1cec74910797f9de77b8821a4989672b503a7d0362238c2266d80a40-Parade-PS5512_EVB-1.1.0.21.cab", + "emulation-url": "dff3d1869ac203dc6531fd43eab0172c635ca24d3bf94619635267e68915addb-Parade-PS5512_EVB-1.1.0.21.zip", "components": [ { "version": "1.1.0.21", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/2de6371c452521a6913945ab45c0e0585982e0448762fd984a0e55570872fc2a-Parade-PS5512_EVB-1.1.0.22.cab", - "emulation-url": "https://fwupd.org/downloads/2b9b7ac4bb571687ccaa51287e2b67569b22517520222e6c70fa976df2a2b72d-Parade-PS5512_EVB-1.1.0.22.zip", + "url": "2de6371c452521a6913945ab45c0e0585982e0448762fd984a0e55570872fc2a-Parade-PS5512_EVB-1.1.0.22.cab", + "emulation-url": "2b9b7ac4bb571687ccaa51287e2b67569b22517520222e6c70fa976df2a2b72d-Parade-PS5512_EVB-1.1.0.22.zip", "components": [ { "version": "1.1.0.22", diff -Nru fwupd-2.0.8/plugins/pci-bcr/fu-pci-bcr-plugin.c fwupd-2.0.20/plugins/pci-bcr/fu-pci-bcr-plugin.c --- fwupd-2.0.8/plugins/pci-bcr/fu-pci-bcr-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pci-bcr/fu-pci-bcr-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -190,7 +190,7 @@ /* grab BIOS Control Register */ if (!fu_udev_device_pread(FU_UDEV_DEVICE(device), self->bcr_addr, &self->bcr, 1, error)) { - g_prefix_error(error, "could not read BCR: "); + g_prefix_error_literal(error, "could not read BCR: "); return FALSE; } diff -Nru fwupd-2.0.8/plugins/pci-bcr/meson.build fwupd-2.0.20/plugins/pci-bcr/meson.build --- fwupd-2.0.8/plugins/pci-bcr/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pci-bcr/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if hsi +hsi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginPciBcr"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -12,4 +13,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/pci-mei/fu-pci-mei-plugin.c fwupd-2.0.20/plugins/pci-mei/fu-pci-mei-plugin.c --- fwupd-2.0.8/plugins/pci-mei/fu-pci-mei-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pci-mei/fu-pci-mei-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -233,7 +233,7 @@ return; } - /* Manufacturing Mode */ + /* manufacturing mode */ fwupd_security_attr_add_metadata(attr, "kind", fu_mei_family_to_string(self->family)); if (fu_mei_csme11_hfsts1_get_mfg_mode(hfsts1)) { fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); @@ -264,7 +264,7 @@ return; } - /* Manufacturing Mode, BIOS has access to the SPI descriptor */ + /* manufacturing mode, BIOS has access to the SPI descriptor */ if (fu_mei_csme18_hfsts1_get_spi_protection_mode(hfsts1)) { fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); @@ -302,7 +302,7 @@ return; } - /* Flash Descriptor Security Override Strap */ + /* flash descriptor security override strap */ fwupd_security_attr_add_metadata(attr, "kind", fu_mei_family_to_string(self->family)); if (fu_mei_csme11_hfsts1_get_operation_mode(hfsts1) == FU_ME_HFS_MODE_OVERRIDE_JUMPER) { fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); @@ -333,7 +333,7 @@ return; } - /* Flash Descriptor Security Override Strap */ + /* flash descriptor security override strap */ fwupd_security_attr_add_metadata(attr, "kind", fu_mei_family_to_string(self->family)); if (fu_mei_csme18_hfsts1_get_operation_mode(hfsts1) == FU_ME_HFS_MODE_OVERRIDE_JUMPER) { fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_LOCKED); @@ -650,7 +650,7 @@ fwupd_security_attr_add_metadata(attr, "version", version); fwupd_security_attr_add_metadata(attr, "kind", fu_mei_family_to_string(self->family)); - /* Flash Descriptor Security Override Strap */ + /* flash descriptor security override strap */ if (self->issue == FU_MEI_ISSUE_VULNERABLE) { fwupd_security_attr_set_result(attr, FWUPD_SECURITY_ATTR_RESULT_NOT_VALID); fwupd_security_attr_add_flag(attr, FWUPD_SECURITY_ATTR_FLAG_ACTION_CONTACT_OEM); diff -Nru fwupd-2.0.8/plugins/pci-mei/meson.build fwupd-2.0.20/plugins/pci-mei/meson.build --- fwupd-2.0.8/plugins/pci-mei/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pci-mei/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if hsi +hsi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginPciMei"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -16,4 +17,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/pci-psp/README.md fwupd-2.0.20/plugins/pci-psp/README.md --- fwupd-2.0.8/plugins/pci-psp/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pci-psp/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -20,10 +20,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.8.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Mario Limonciello: @superm1 diff -Nru fwupd-2.0.8/plugins/pci-psp/fu-pci-psp-device.c fwupd-2.0.20/plugins/pci-psp/fu-pci-psp-device.c --- fwupd-2.0.8/plugins/pci-psp/fu-pci-psp-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pci-psp/fu-pci-psp-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,6 +13,10 @@ #include "fu-pci-psp-device.h" +#define FU_CPU_AGESA_SMBIOS_TYPE 40 +#define FU_CPU_AGESA_SMBIOS_LENGTH 0x0E +#define FU_CPU_AGESA_SMBIOS_OFFSET 4 + struct _FuPciPspDevice { FuUdevDevice parent_instance; gboolean supported; @@ -21,10 +25,44 @@ G_DEFINE_TYPE(FuPciPspDevice, fu_pci_psp_device, FU_TYPE_UDEV_DEVICE) static gboolean +fu_pci_psp_device_ensure_agesa_version(FuPciPspDevice *self, GError **error) +{ + const gchar *agesa_stream; + g_auto(GStrv) split = NULL; + g_autofree gchar *summary = NULL; + + /* get the AGESA stream e.g. `AGESA!V9 StrixKrackanPI-FP8 1.1.0.0a` */ + agesa_stream = fu_device_get_smbios_string(FU_DEVICE(self), + FU_CPU_AGESA_SMBIOS_TYPE, + FU_CPU_AGESA_SMBIOS_LENGTH, + FU_CPU_AGESA_SMBIOS_OFFSET, + error); + if (agesa_stream == NULL) { + g_prefix_error_literal(error, "no SMBIOS data: "); + return FALSE; + } + split = g_strsplit(agesa_stream, " ", 3); + if (g_strv_length(split) != 3) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "invalid format: %s", + agesa_stream); + return FALSE; + } + summary = g_strdup_printf("AGESA %s %s", split[1], split[2]); + + fu_device_set_summary(FU_DEVICE(self), summary); + return TRUE; +} + +static gboolean fu_pci_psp_device_probe(FuDevice *device, GError **error) { + FuPciPspDevice *self = FU_PCI_PSP_DEVICE(device); g_autofree gchar *attr_bootloader_version = NULL; g_autofree gchar *attr_tee_version = NULL; + g_autoptr(GError) error_agesa = NULL; g_autoptr(GError) error_boot = NULL; g_autoptr(GError) error_tee = NULL; @@ -47,6 +85,9 @@ else fu_device_set_version(device, attr_tee_version); + if (!fu_pci_psp_device_ensure_agesa_version(self, &error_agesa)) + g_info("failed to read AGESA stream: %s", error_agesa->message); + return TRUE; } @@ -74,9 +115,8 @@ } static void -fu_pci_psp_device_set_valid_data(FuDevice *device, FuSecurityAttrs *attrs) +fu_pci_psp_device_set_valid_data(FuPciPspDevice *self, FuSecurityAttrs *attrs) { - FuPciPspDevice *self = FU_PCI_PSP_DEVICE(device); g_autoptr(FwupdSecurityAttr) attr = NULL; if (self->supported) @@ -92,7 +132,7 @@ } static FwupdSecurityAttr * -fu_pci_psp_device_get_security_attr(FuDevice *device, +fu_pci_psp_device_get_security_attr(FuPciPspDevice *self, FuSecurityAttrs *attrs, const gchar *appstream_id) { @@ -100,7 +140,7 @@ attr = fu_security_attrs_get_by_appstream_id(attrs, appstream_id, NULL); if (attr == NULL) { - attr = fu_device_security_attr_new(device, appstream_id); + attr = fu_device_security_attr_new(FU_DEVICE(self), appstream_id); fu_security_attrs_append(attrs, attr); } else if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_MISSING_DATA)) { g_debug("found missing data on old attribute, repopulating"); @@ -110,7 +150,7 @@ } static void -fu_pci_psp_device_add_security_attrs_tsme(FuDevice *device, +fu_pci_psp_device_add_security_attrs_tsme(FuPciPspDevice *self, const gchar *path, FuSecurityAttrs *attrs) { @@ -118,9 +158,8 @@ g_autoptr(GError) error_local = NULL; gboolean val; - attr = fu_pci_psp_device_get_security_attr(device, - attrs, - FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM); + attr = + fu_pci_psp_device_get_security_attr(self, attrs, FWUPD_SECURITY_ATTR_ID_ENCRYPTED_RAM); if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { g_debug("ignoring already populated attribute"); return; @@ -133,7 +172,7 @@ return; } - fu_pci_psp_device_set_valid_data(device, attrs); + fu_pci_psp_device_set_valid_data(self, attrs); /* BIOS knob used on Lenovo systems */ fu_security_attr_add_bios_target_value(attr, "com.thinklmi.TSME", "enable"); @@ -149,7 +188,7 @@ } static void -fu_pci_psp_device_add_security_attrs_fused_part(FuDevice *device, +fu_pci_psp_device_add_security_attrs_fused_part(FuPciPspDevice *self, const gchar *path, FuSecurityAttrs *attrs) { @@ -157,9 +196,8 @@ g_autoptr(GError) error_local = NULL; gboolean val; - attr = fu_pci_psp_device_get_security_attr(device, - attrs, - FWUPD_SECURITY_ATTR_ID_PLATFORM_FUSED); + attr = + fu_pci_psp_device_get_security_attr(self, attrs, FWUPD_SECURITY_ATTR_ID_PLATFORM_FUSED); if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { g_debug("ignoring already populated attribute"); return; @@ -172,7 +210,7 @@ return; } - fu_pci_psp_device_set_valid_data(device, attrs); + fu_pci_psp_device_set_valid_data(self, attrs); if (!val) { g_debug("part is not fused"); @@ -186,7 +224,7 @@ } static void -fu_pci_psp_device_add_security_attrs_debug_locked_part(FuDevice *device, +fu_pci_psp_device_add_security_attrs_debug_locked_part(FuPciPspDevice *self, const gchar *path, FuSecurityAttrs *attrs) { @@ -194,7 +232,7 @@ g_autoptr(GError) error_local = NULL; gboolean val; - attr = fu_pci_psp_device_get_security_attr(device, + attr = fu_pci_psp_device_get_security_attr(self, attrs, FWUPD_SECURITY_ATTR_ID_PLATFORM_DEBUG_LOCKED); if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { @@ -209,7 +247,7 @@ return; } - fu_pci_psp_device_set_valid_data(device, attrs); + fu_pci_psp_device_set_valid_data(self, attrs); if (!val) { g_debug("debug lock disabled"); @@ -223,7 +261,7 @@ } static void -fu_pci_psp_device_add_security_attrs_rollback_protection(FuDevice *device, +fu_pci_psp_device_add_security_attrs_rollback_protection(FuPciPspDevice *self, const gchar *path, FuSecurityAttrs *attrs) { @@ -231,7 +269,7 @@ g_autoptr(GError) error_local = NULL; gboolean val; - attr = fu_pci_psp_device_get_security_attr(device, + attr = fu_pci_psp_device_get_security_attr(self, attrs, FWUPD_SECURITY_ATTR_ID_AMD_ROLLBACK_PROTECTION); if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { @@ -246,7 +284,7 @@ return; } - fu_pci_psp_device_set_valid_data(device, attrs); + fu_pci_psp_device_set_valid_data(self, attrs); if (!val) { g_debug("rollback protection not enforced"); @@ -260,7 +298,7 @@ } static void -fu_pci_psp_device_add_security_attrs_rom_armor(FuDevice *device, +fu_pci_psp_device_add_security_attrs_rom_armor(FuPciPspDevice *self, const gchar *path, FuSecurityAttrs *attrs) { @@ -269,7 +307,7 @@ gboolean val; /* create attr */ - attr = fu_pci_psp_device_get_security_attr(device, + attr = fu_pci_psp_device_get_security_attr(self, attrs, FWUPD_SECURITY_ATTR_ID_AMD_SPI_WRITE_PROTECTION); if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { @@ -284,7 +322,7 @@ return; } - fu_pci_psp_device_set_valid_data(device, attrs); + fu_pci_psp_device_set_valid_data(self, attrs); if (!val) { g_debug("ROM armor not enforced"); @@ -298,7 +336,7 @@ } static void -fu_pci_psp_device_add_security_attrs_rpmc(FuDevice *device, +fu_pci_psp_device_add_security_attrs_rpmc(FuPciPspDevice *self, const gchar *path, FuSecurityAttrs *attrs) { @@ -308,7 +346,7 @@ /* create attr */ attr = - fu_pci_psp_device_get_security_attr(device, + fu_pci_psp_device_get_security_attr(self, attrs, FWUPD_SECURITY_ATTR_ID_AMD_SPI_REPLAY_PROTECTION); if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { @@ -324,7 +362,7 @@ return; } - fu_pci_psp_device_set_valid_data(device, attrs); + fu_pci_psp_device_set_valid_data(self, attrs); if (!val) { g_debug("no RPMC compatible SPI rom present"); @@ -365,12 +403,12 @@ self->supported = FALSE; - fu_pci_psp_device_add_security_attrs_tsme(device, sysfs_path, attrs); - fu_pci_psp_device_add_security_attrs_fused_part(device, sysfs_path, attrs); - fu_pci_psp_device_add_security_attrs_debug_locked_part(device, sysfs_path, attrs); - fu_pci_psp_device_add_security_attrs_rollback_protection(device, sysfs_path, attrs); - fu_pci_psp_device_add_security_attrs_rpmc(device, sysfs_path, attrs); - fu_pci_psp_device_add_security_attrs_rom_armor(device, sysfs_path, attrs); + fu_pci_psp_device_add_security_attrs_tsme(self, sysfs_path, attrs); + fu_pci_psp_device_add_security_attrs_fused_part(self, sysfs_path, attrs); + fu_pci_psp_device_add_security_attrs_debug_locked_part(self, sysfs_path, attrs); + fu_pci_psp_device_add_security_attrs_rollback_protection(self, sysfs_path, attrs); + fu_pci_psp_device_add_security_attrs_rpmc(self, sysfs_path, attrs); + fu_pci_psp_device_add_security_attrs_rom_armor(self, sysfs_path, attrs); } static void @@ -378,9 +416,9 @@ { fu_device_set_name(FU_DEVICE(self), "Secure Processor"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_CPU_CHILD); - fu_device_set_vendor(FU_DEVICE(self), "Advanced Micro Devices, Inc."); + fu_device_set_vendor(FU_DEVICE(self), "AMD"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); fu_device_set_physical_id(FU_DEVICE(self), "pci-psp"); } diff -Nru fwupd-2.0.8/plugins/pci-psp/meson.build fwupd-2.0.20/plugins/pci-psp/meson.build --- fwupd-2.0.8/plugins/pci-psp/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pci-psp/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if hsi and (host_cpu == 'x86' or host_cpu == 'x86_64') +hsi or subdir_done() +host_cpu in ['x86', 'x86_64'] or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginPciPsp"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -14,4 +16,9 @@ dependencies: plugin_deps, ) umockdev_tests += files('pci_psp_test.py') -endif +device_tests += files( + 'tests/pci-psp-strix-enumerate.json', +) +enumeration_data += files( + 'tests/pci-psp-strix.json', +) diff -Nru fwupd-2.0.8/plugins/pci-psp/tests/pci-psp-strix-enumerate.json fwupd-2.0.20/plugins/pci-psp/tests/pci-psp-strix-enumerate.json --- fwupd-2.0.8/plugins/pci-psp/tests/pci-psp-strix-enumerate.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pci-psp/tests/pci-psp-strix-enumerate.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +{ + "name": "pci psp strix (enumerate only)", + "interactive": false, + "steps": [ + { + "emulation-file": "@enumeration_datadir@/pci-psp-strix.json", + "components": [ + { + "version": "00.3e.04.74", + "guids": [ + "92c96a95-c187-5183-ae27-eab2f47e0f63" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/pci-psp/tests/pci-psp-strix.json fwupd-2.0.20/plugins/pci-psp/tests/pci-psp-strix.json --- fwupd-2.0.8/plugins/pci-psp/tests/pci-psp-strix.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pci-psp/tests/pci-psp-strix.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,112 @@ +{ + "FwupdVersion": "2.0.9", + "UsbDevices": [ + { + "Created": "2025-05-19T11:44:58.764803Z", + "GType": "FuUdevDevice", + "BackendId": "/sys/devices/pci0000:00/0000:00:08.1/0000:c1:00.2", + "Subsystem": "pci", + "Driver": "ccp", + "BindId": "0000:c1:00.2", + "Vendor": 4130, + "Model": 6112, + "Events": [ + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "pci" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "ccp" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=ccp\nPCI_CLASS=108000\nPCI_ID=1022:17E0\nPCI_SUBSYS_ID=F111:000B\nPCI_SLOT_NAME=0000:c1:00.2\nMODALIAS=pci:v00001022d000017E0sv0000F111sd0000000Bbc10sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x1022" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0x17e0" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x108000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "DRIVER=ccp\nPCI_CLASS=108000\nPCI_ID=1022:17E0\nPCI_SUBSYS_ID=F111:000B\nPCI_SLOT_NAME=0000:c1:00.2\nMODALIAS=pci:v00001022d000017E0sv0000F111sd0000000Bbc10sc80i00" + }, + { + "Id": "ReadProp:Key=DEVNAME" + }, + { + "Id": "ReadAttr:Attr=vendor", + "Data": "0x1022" + }, + { + "Id": "ReadAttr:Attr=device", + "Data": "0x17e0" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x108000" + }, + { + "Id": "ReadProp:Key=DEVTYPE" + }, + { + "Id": "GetBackendParent:Subsystem=i2c", + "Error": 8, + "ErrorMsg": "no parent with subsystem i2c" + }, + { + "Id": "ReadAttr:Attr=class", + "Data": "0x108000" + }, + { + "Id": "ReadAttr:Attr=revision", + "Data": "0x00" + }, + { + "Id": "ReadAttr:Attr=subsystem_vendor", + "Data": "0xf111" + }, + { + "Id": "ReadAttr:Attr=subsystem_device", + "Data": "0x000b" + }, + { + "Id": "ReadProp:Key=PCI_SLOT_NAME", + "Data": "0000:c1:00.2" + }, + { + "Id": "ReadAttr:Attr=bootloader_version", + "Data": "74.04.3e.00" + }, + { + "Id": "ReadAttr:Attr=tee_version", + "Data": "00.3e.04.74" + }, + { + "Id": "GetSmbiosString:Type=0x28,Length=0x0e,Offset=0x04", + "Data": "AGESA!V9 StrixKrackanPI-FP8 1.1.0.0a" + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/pixart-rf/README.md fwupd-2.0.20/plugins/pixart-rf/README.md --- fwupd-2.0.8/plugins/pixart-rf/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -52,10 +52,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.5.5`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Sam Chen: @sam412081go diff -Nru fwupd-2.0.8/plugins/pixart-rf/fu-pxi-ble-device.c fwupd-2.0.20/plugins/pixart-rf/fu-pxi-ble-device.c --- fwupd-2.0.8/plugins/pixart-rf/fu-pxi-ble-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/fu-pxi-ble-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -7,11 +7,6 @@ #include "config.h" -#ifdef HAVE_HIDRAW_H -#include -#include -#endif - #include "fu-pxi-ble-device.h" #include "fu-pxi-common.h" #include "fu-pxi-firmware.h" @@ -38,7 +33,7 @@ struct _FuPxiBleDevice { FuHidrawDevice parent_instance; - struct ota_fw_state fwstate; + FuPixartRfOtaFwState fwstate; guint8 retransmit_id; guint8 feature_report_id; guint8 input_report_id; @@ -47,25 +42,6 @@ G_DEFINE_TYPE(FuPxiBleDevice, fu_pxi_ble_device, FU_TYPE_HIDRAW_DEVICE) -#ifdef HAVE_HIDRAW_H -static gboolean -fu_pxi_ble_device_get_raw_info(FuPxiBleDevice *self, struct hidraw_devinfo *info, GError **error) -{ - g_autoptr(FuIoctl) ioctl = fu_udev_device_ioctl_new(FU_UDEV_DEVICE(self)); - if (!fu_ioctl_execute(ioctl, - HIDIOCGRAWINFO, - (guint8 *)info, - sizeof(*info), - NULL, - FU_PXI_DEVICE_IOCTL_TIMEOUT, - FU_IOCTL_FLAG_NONE, - error)) { - return FALSE; - } - return TRUE; -} -#endif - static void fu_pxi_ble_device_to_string(FuDevice *device, guint idt, GString *str) { @@ -81,7 +57,7 @@ fu_pxi_ble_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuPxiBleDevice *self = FU_PXI_BLE_DEVICE(device); @@ -109,7 +85,7 @@ /* check is compatible with hardware */ model_name = fu_pxi_firmware_get_model_name(FU_PXI_FIRMWARE(firmware)); - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) == 0) { if (self->model_name == NULL || model_name == NULL) { g_set_error_literal(error, FWUPD_ERROR, @@ -129,17 +105,16 @@ } } } else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "The firmware is incompatible with the device"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware is incompatible with the device"); return NULL; } return g_steal_pointer(&firmware); } -#ifdef HAVE_HIDRAW_H static gboolean fu_pxi_ble_device_set_feature_cb(FuDevice *device, gpointer user_data, GError **error) { @@ -150,7 +125,6 @@ FU_IOCTL_FLAG_NONE, error); } -#endif static gboolean fu_pxi_ble_device_set_feature(FuPxiBleDevice *self, GByteArray *req, GError **error) @@ -186,7 +160,7 @@ } static gboolean -fu_pxi_ble_device_search_hid_feature_report_id(FuFirmware *descriptor, +fu_pxi_ble_device_search_hid_feature_report_id(FuHidDescriptor *descriptor, guint16 usage_page, guint8 *report_id, GError **error) @@ -195,7 +169,7 @@ g_autoptr(FuHidReport) report = NULL; /* check ota retransmit feature report usage page exists */ - report = fu_hid_descriptor_find_report(FU_HID_DESCRIPTOR(descriptor), + report = fu_hid_descriptor_find_report(descriptor, error, "usage-page", usage_page, @@ -218,7 +192,7 @@ } static gboolean -fu_pxi_ble_device_search_hid_input_report_id(FuFirmware *descriptor, +fu_pxi_ble_device_search_hid_input_report_id(FuHidDescriptor *descriptor, guint16 usage_page, guint8 *report_id, GError **error) @@ -227,7 +201,7 @@ g_autoptr(FuFirmware) item_id = NULL; /* check ota retransmit feature report usage page exist or not */ - report = fu_hid_descriptor_find_report(FU_HID_DESCRIPTOR(descriptor), + report = fu_hid_descriptor_find_report(descriptor, error, "usage-page", usage_page, @@ -252,47 +226,14 @@ static gboolean fu_pxi_ble_device_check_support_report_id(FuPxiBleDevice *self, GError **error) { -#ifdef HAVE_HIDRAW_H - gint desc_size = 0; - g_autoptr(FuFirmware) descriptor = fu_hid_descriptor_new(); - g_autoptr(FuIoctl) ioctl = fu_udev_device_ioctl_new(FU_UDEV_DEVICE(self)); - g_autoptr(GBytes) fw = NULL; + g_autoptr(FuHidDescriptor) descriptor = NULL; g_autoptr(GError) error_local1 = NULL; g_autoptr(GError) error_local2 = NULL; g_autoptr(GError) error_local3 = NULL; - g_autoptr(GError) error_local = NULL; - - struct hidraw_report_descriptor rpt_desc = {0x0}; - /* Get Report Descriptor Size */ - if (!fu_ioctl_execute(ioctl, - HIDIOCGRDESCSIZE, - (guint8 *)&desc_size, - sizeof(desc_size), - NULL, - FU_PXI_DEVICE_IOCTL_TIMEOUT, - FU_IOCTL_FLAG_NONE, - error)) - return FALSE; - - rpt_desc.size = desc_size; - if (!fu_ioctl_execute(ioctl, - HIDIOCGRDESC, - (guint8 *)&rpt_desc, - sizeof(rpt_desc), - NULL, - FU_PXI_DEVICE_IOCTL_TIMEOUT, - FU_IOCTL_FLAG_NONE, - error)) - return FALSE; - fu_dump_raw(G_LOG_DOMAIN, "HID descriptor", rpt_desc.value, rpt_desc.size); - - /* parse the descriptor, but use the defaults if it fails */ - fw = g_bytes_new(rpt_desc.value, rpt_desc.size); - if (!fu_firmware_parse_bytes(descriptor, fw, 0x0, FWUPD_INSTALL_FLAG_NONE, &error_local)) { - g_debug("failed to parse descriptor: %s", error_local->message); - return TRUE; - } + descriptor = fu_hidraw_device_parse_descriptor(FU_HIDRAW_DEVICE(self), error); + if (descriptor == NULL) + return FALSE; /* check ota retransmit feature report usage page exists */ if (!fu_pxi_ble_device_search_hid_feature_report_id(descriptor, @@ -329,13 +270,6 @@ /* success */ return TRUE; -#else - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - " not available"); - return FALSE -#endif } static gboolean @@ -590,7 +524,7 @@ fu_byte_array_append_uint8(req, FU_PXI_OTA_DISCONNECT_REASON_RESET); if (!fu_pxi_ble_device_set_feature(self, req, error)) { - g_prefix_error(error, "failed to reset: "); + g_prefix_error_literal(error, "failed to reset: "); return FALSE; } @@ -741,7 +675,7 @@ /* send fw ota retransmit command to reset status */ if (!fu_pxi_ble_device_fw_ota_check_retransmit(self, error)) { - g_prefix_error(error, "failed to OTA check retransmit: "); + g_prefix_error_literal(error, "failed to OTA check retransmit: "); return FALSE; } /* send fw ota init command */ @@ -902,15 +836,11 @@ static gboolean fu_pxi_ble_device_setup_guid(FuPxiBleDevice *self, GError **error) { -#ifdef HAVE_HIDRAW_H FuDevice *device = FU_DEVICE(self); - struct hidraw_devinfo hid_raw_info = {0x0}; g_autoptr(GString) dev_name = NULL; g_autoptr(GString) model_name = NULL; /* extra GUID with device name */ - if (!fu_pxi_ble_device_get_raw_info(self, &hid_raw_info, error)) - return FALSE; dev_name = g_string_new(fu_device_get_name(device)); g_string_ascii_up(dev_name); g_string_replace(dev_name, " ", "_", 0); @@ -921,15 +851,14 @@ g_string_replace(model_name, " ", "_", 0); /* generate IDs */ - fu_device_add_instance_u16(device, "VEN", hid_raw_info.vendor); - fu_device_add_instance_u16(device, "DEV", hid_raw_info.product); + fu_device_add_instance_u16(device, "VEN", fu_device_get_vid(FU_DEVICE(self))); + fu_device_add_instance_u16(device, "DEV", fu_device_get_pid(FU_DEVICE(self))); fu_device_add_instance_str(device, "NAME", dev_name->str); fu_device_add_instance_str(device, "MODEL", model_name->str); if (!fu_device_build_instance_id(device, error, "HIDRAW", "VEN", "DEV", "NAME", NULL)) return FALSE; if (!fu_device_build_instance_id(device, error, "HIDRAW", "VEN", "DEV", "MODEL", NULL)) return FALSE; -#endif return TRUE; } @@ -939,27 +868,32 @@ FuPxiBleDevice *self = FU_PXI_BLE_DEVICE(device); if (!fu_pxi_ble_device_check_support_report_id(self, error)) { - g_prefix_error(error, "failed to check report id: "); + g_prefix_error_literal(error, "failed to check report id: "); return FALSE; } if (!fu_pxi_ble_device_fw_ota_check_retransmit(self, error)) { - g_prefix_error(error, "failed to OTA check retransmit: "); + g_prefix_error_literal(error, "failed to OTA check retransmit: "); return FALSE; } if (!fu_pxi_ble_device_fw_ota_init(self, error)) { - g_prefix_error(error, "failed to OTA init: "); + g_prefix_error_literal(error, "failed to OTA init: "); return FALSE; } if (!fu_pxi_ble_device_fw_get_info(self, error)) { - g_prefix_error(error, "failed to get info: "); + g_prefix_error_literal(error, "failed to get info: "); return FALSE; } if (!fu_pxi_ble_device_get_model_info(self, error)) { - g_prefix_error(error, "failed to get model: "); + g_prefix_error_literal(error, "failed to get model: "); return FALSE; } + + /* FuHidrawDevice->setup */ + if (!FU_DEVICE_CLASS(fu_pxi_ble_device_parent_class)->setup(device, error)) + return FALSE; + if (!fu_pxi_ble_device_setup_guid(self, error)) { - g_prefix_error(error, "failed to setup GUID: "); + g_prefix_error_literal(error, "failed to setup GUID: "); return FALSE; } @@ -967,7 +901,7 @@ } static void -fu_pxi_ble_device_set_progress(FuDevice *self, FuProgress *progress) +fu_pxi_ble_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/pixart-rf/fu-pxi-common.c fwupd-2.0.20/plugins/pixart-rf/fu-pxi-common.c --- fwupd-2.0.8/plugins/pixart-rf/fu-pxi-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/fu-pxi-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,7 @@ #include "fu-pxi-struct.h" void -fu_pxi_ota_fw_state_to_string(struct ota_fw_state *fwstate, guint idt, GString *str) +fu_pxi_ota_fw_state_to_string(FuPixartRfOtaFwState *fwstate, guint idt, GString *str) { fwupd_codec_string_append_hex(str, idt, "Status", fwstate->status); fwupd_codec_string_append_hex(str, idt, "NewFlow", fwstate->new_flow); @@ -31,53 +31,25 @@ } gboolean -fu_pxi_ota_fw_state_parse(struct ota_fw_state *fwstate, +fu_pxi_ota_fw_state_parse(FuPixartRfOtaFwState *fwstate, const guint8 *buf, gsize bufsz, gsize offset, GError **error) { - if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x00, &fwstate->status, error)) - return FALSE; - if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x01, &fwstate->new_flow, error)) - return FALSE; - if (!fu_memread_uint16_safe(buf, - bufsz, - offset + 0x2, - &fwstate->offset, - G_LITTLE_ENDIAN, - error)) - return FALSE; - if (!fu_memread_uint16_safe(buf, - bufsz, - offset + 0x4, - &fwstate->checksum, - G_LITTLE_ENDIAN, - error)) - return FALSE; - if (!fu_memread_uint32_safe(buf, - bufsz, - offset + 0x06, - &fwstate->max_object_size, - G_LITTLE_ENDIAN, - error)) - return FALSE; - if (!fu_memread_uint16_safe(buf, - bufsz, - offset + 0x0A, - &fwstate->mtu_size, - G_LITTLE_ENDIAN, - error)) - return FALSE; - if (!fu_memread_uint16_safe(buf, - bufsz, - offset + 0x0C, - &fwstate->prn_threshold, - G_LITTLE_ENDIAN, - error)) - return FALSE; - if (!fu_memread_uint8_safe(buf, bufsz, offset + 0x0E, &fwstate->spec_check_result, error)) + g_autoptr(FuStructPxiOtaFwState) st = NULL; + + st = fu_struct_pxi_ota_fw_state_parse(buf, bufsz, offset, error); + if (st == NULL) return FALSE; + fwstate->status = fu_struct_pxi_ota_fw_state_get_status(st); + fwstate->new_flow = fu_struct_pxi_ota_fw_state_get_new_flow(st); + fwstate->offset = fu_struct_pxi_ota_fw_state_get_offset(st); + fwstate->checksum = fu_struct_pxi_ota_fw_state_get_checksum(st); + fwstate->max_object_size = fu_struct_pxi_ota_fw_state_get_max_object_size(st); + fwstate->mtu_size = fu_struct_pxi_ota_fw_state_get_mtu_size(st); + fwstate->prn_threshold = fu_struct_pxi_ota_fw_state_get_prn_threshold(st); + fwstate->spec_check_result = fu_struct_pxi_ota_fw_state_get_spec_check_result(st); /* success */ return TRUE; diff -Nru fwupd-2.0.8/plugins/pixart-rf/fu-pxi-common.h fwupd-2.0.20/plugins/pixart-rf/fu-pxi-common.h --- fwupd-2.0.8/plugins/pixart-rf/fu-pxi-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/fu-pxi-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -28,23 +28,23 @@ #define FU_PXI_DEVICE_IOCTL_TIMEOUT 5000 /* ms */ /* pixart device model structure */ -struct ota_fw_dev_model { +typedef struct { guint8 status; guint8 name[FU_PXI_DEVICE_MODEL_NAME_LEN]; guint8 type; guint8 target; guint8 version[5]; guint16 checksum; -}; +} FuPixartRfOtaFwDevModel; /* pixart fw info structure */ -struct ota_fw_info { +typedef struct { guint8 status; guint8 version[5]; guint16 checksum; -}; +} FuPixartRfOtaFwInfo; -struct ota_fw_state { +typedef struct { guint8 status; guint8 new_flow; guint16 offset; @@ -53,7 +53,7 @@ guint16 mtu_size; guint16 prn_threshold; guint8 spec_check_result; -}; +} FuPixartRfOtaFwState; gboolean fu_pxi_composite_receiver_cmd(guint8 opcode, @@ -64,9 +64,9 @@ GError **error); void -fu_pxi_ota_fw_state_to_string(struct ota_fw_state *fwstate, guint idt, GString *str); +fu_pxi_ota_fw_state_to_string(FuPixartRfOtaFwState *fwstate, guint idt, GString *str); gboolean -fu_pxi_ota_fw_state_parse(struct ota_fw_state *fwstate, +fu_pxi_ota_fw_state_parse(FuPixartRfOtaFwState *fwstate, const guint8 *buf, gsize bufsz, gsize offset, diff -Nru fwupd-2.0.8/plugins/pixart-rf/fu-pxi-firmware.c fwupd-2.0.20/plugins/pixart-rf/fu-pxi-firmware.c --- fwupd-2.0.8/plugins/pixart-rf/fu-pxi-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/fu-pxi-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -62,7 +62,7 @@ &magic, G_BIG_ENDIAN, error)) { - g_prefix_error(error, "failed to read magic: "); + g_prefix_error_literal(error, "failed to read magic: "); return FALSE; } if (magic != PIXART_RF_FW_HEADER_MAGIC) { @@ -74,7 +74,7 @@ &magic, G_BIG_ENDIAN, error)) { - g_prefix_error(error, "failed to read magic: "); + g_prefix_error_literal(error, "failed to read magic: "); return FALSE; } if (magic != PIXART_RF_FW_HEADER_MAGIC) { @@ -96,13 +96,13 @@ static gboolean fu_pxi_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuPxiFirmware *self = FU_PXI_FIRMWARE(firmware); gsize streamsz = 0; guint32 version_raw = 0; - guint8 fw_header[PIXART_RF_FW_HEADER_SIZE]; + guint8 fw_header[PIXART_RF_FW_HEADER_SIZE] = {0}; guint8 model_name[FU_PXI_DEVICE_MODEL_NAME_LEN] = {0x0}; guint16 hpac_ver = 0; g_autofree gchar *version = NULL; @@ -125,7 +125,7 @@ streamsz - PIXART_RF_FW_HEADER_HPAC_POS_FROM_END, sizeof(fw_header), error)) { - g_prefix_error(error, "failed to read fw header: "); + g_prefix_error_literal(error, "failed to read fw header: "); return FALSE; } } else { @@ -143,7 +143,7 @@ streamsz - sizeof(fw_header), sizeof(fw_header), error)) { - g_prefix_error(error, "failed to read fw header: "); + g_prefix_error_literal(error, "failed to read fw header: "); return FALSE; } } @@ -180,7 +180,7 @@ 0x05, sizeof(model_name), error)) { - g_prefix_error(error, "failed to get fw model name: "); + g_prefix_error_literal(error, "failed to get fw model name: "); return FALSE; } self->model_name = g_strndup((gchar *)model_name, sizeof(model_name)); @@ -271,7 +271,7 @@ 0x0, /* src */ model_namesz, error)) { - g_prefix_error(error, "failed to get fw model name: "); + g_prefix_error_literal(error, "failed to get fw model name: "); return NULL; } } diff -Nru fwupd-2.0.8/plugins/pixart-rf/fu-pxi-receiver-device.c fwupd-2.0.20/plugins/pixart-rf/fu-pxi-receiver-device.c --- fwupd-2.0.8/plugins/pixart-rf/fu-pxi-receiver-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/fu-pxi-receiver-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,7 +13,7 @@ struct _FuPxiReceiverDevice { FuHidrawDevice parent_instance; - struct ota_fw_state fwstate; + FuPixartRfOtaFwState fwstate; guint8 sn; }; @@ -30,7 +30,7 @@ fu_pxi_receiver_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) firmware = fu_pxi_firmware_new(); @@ -52,10 +52,10 @@ return NULL; } else if (fu_device_has_private_flag(device, FU_PXI_DEVICE_FLAG_IS_HPAC) != fu_pxi_firmware_is_hpac(FU_PXI_FIRMWARE(firmware))) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "The firmware is incompatible with the device"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware is incompatible with the device"); return NULL; } @@ -181,9 +181,8 @@ } static gboolean -fu_pxi_receiver_device_check_crc(FuDevice *device, guint16 checksum, GError **error) +fu_pxi_receiver_device_check_crc(FuPxiReceiverDevice *self, guint16 checksum, GError **error) { - FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; guint8 status = 0x0; g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); @@ -192,7 +191,7 @@ /* ota check crc command */ fu_byte_array_append_uint8(ota_cmd, 0x3); /* ota command length */ fu_byte_array_append_uint8(ota_cmd, FU_PXI_DEVICE_CMD_FW_OTA_CHECK_CRC); /* ota command */ - fu_byte_array_append_uint16(ota_cmd, checksum, G_LITTLE_ENDIAN); /* checkesum */ + fu_byte_array_append_uint16(ota_cmd, checksum, G_LITTLE_ENDIAN); /* checksum */ /* increase the serial number */ self->sn++; @@ -232,9 +231,8 @@ } static gboolean -fu_pxi_receiver_device_fw_object_create(FuDevice *device, FuChunk *chk, GError **error) +fu_pxi_receiver_device_fw_object_create(FuPxiReceiverDevice *self, FuChunk *chk, GError **error) { - FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; guint8 status = 0x0; g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); @@ -286,9 +284,8 @@ } static gboolean -fu_pxi_receiver_device_write_payload(FuDevice *device, FuChunk *chk, GError **error) +fu_pxi_receiver_device_write_payload(FuPxiReceiverDevice *self, FuChunk *chk, GError **error) { - FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); @@ -318,15 +315,14 @@ } static gboolean -fu_pxi_receiver_device_write_chunk(FuDevice *device, FuChunk *chk, GError **error) +fu_pxi_receiver_device_write_chunk(FuPxiReceiverDevice *self, FuChunk *chk, GError **error) { - FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); guint32 prn = 0; g_autoptr(FuChunkArray) chunks = NULL; g_autoptr(GBytes) chk_bytes = fu_chunk_get_bytes(chk); /* send create fw object command */ - if (!fu_pxi_receiver_device_fw_object_create(device, chk, error)) + if (!fu_pxi_receiver_device_fw_object_create(self, chk, error)) return FALSE; /* write payload */ @@ -346,15 +342,13 @@ /* calculate checksum of each payload packet */ self->fwstate.checksum += fu_sum16(fu_chunk_get_data(chk2), fu_chunk_get_data_sz(chk2)); - if (!fu_pxi_receiver_device_write_payload(device, chk2, error)) + if (!fu_pxi_receiver_device_write_payload(self, chk2, error)) return FALSE; prn++; /* check crc at fw when PRN over threshold write or * offset reach max object sz or write offset reach fw length */ if (prn >= self->fwstate.prn_threshold || i == fu_chunk_array_length(chunks) - 1) { - if (!fu_pxi_receiver_device_check_crc(device, - self->fwstate.checksum, - error)) + if (!fu_pxi_receiver_device_check_crc(self, self->fwstate.checksum, error)) return FALSE; prn = 0; } @@ -364,12 +358,11 @@ } static gboolean -fu_pxi_receiver_device_fw_upgrade(FuDevice *device, +fu_pxi_receiver_device_fw_upgrade(FuPxiReceiverDevice *self, FuFirmware *firmware, FuProgress *progress, GError **error) { - FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); const gchar *version; guint8 fw_version[5] = {0x0}; guint8 res[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; @@ -435,7 +428,7 @@ return FALSE; /* delay for wireless module device read command */ - fu_device_sleep(device, 5); /* ms */ + fu_device_sleep(FU_DEVICE(self), 5); /* ms */ if (!fu_pxi_receiver_device_get_cmd_response(self, res, sizeof(res), error)) return FALSE; @@ -458,9 +451,8 @@ } static gboolean -fu_pxi_receiver_device_reset(FuDevice *device, GError **error) +fu_pxi_receiver_device_reset(FuPxiReceiverDevice *self, GError **error) { - FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); g_autoptr(GByteArray) receiver_device_cmd = g_byte_array_new(); g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); @@ -534,7 +526,7 @@ chk = fu_chunk_array_index(chunks, i, error); if (chk == NULL) return FALSE; - if (!fu_pxi_receiver_device_write_chunk(device, chk, error)) + if (!fu_pxi_receiver_device_write_chunk(self, chk, error)) return FALSE; fu_progress_set_percentage_full(fu_progress_get_child(progress), (gsize)i + self->fwstate.offset + 1, @@ -543,7 +535,7 @@ fu_progress_step_done(progress); /* fw upgrade command */ - if (!fu_pxi_receiver_device_fw_upgrade(device, + if (!fu_pxi_receiver_device_fw_upgrade(self, firmware, fu_progress_get_child(progress), error)) @@ -554,7 +546,7 @@ fu_device_sleep(device, 5); /* ms */ /* send device reset command */ - if (!fu_pxi_receiver_device_reset(device, error)) + if (!fu_pxi_receiver_device_reset(self, error)) return FALSE; fu_progress_step_done(progress); fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); @@ -565,7 +557,7 @@ static gboolean fu_pxi_receiver_device_get_peripheral_info(FuPxiReceiverDevice *self, - struct ota_fw_dev_model *model, + FuPixartRfOtaFwDevModel *model, guint idx, GError **error) { @@ -703,7 +695,7 @@ static gboolean fu_pxi_receiver_device_add_peripherals(FuPxiReceiverDevice *device, guint idx, GError **error) { - struct ota_fw_dev_model model = {0x0}; + FuPixartRfOtaFwDevModel model = {0x0}; guint16 hpac_ver = 0; g_autofree gchar *model_name = NULL; g_autofree gchar *model_version = NULL; @@ -802,19 +794,19 @@ FuPxiReceiverDevice *self = FU_PXI_RECEIVER_DEVICE(device); if (!fu_pxi_receiver_device_setup_guid(self, error)) { - g_prefix_error(error, "failed to setup GUID: "); + g_prefix_error_literal(error, "failed to setup GUID: "); return FALSE; } if (!fu_pxi_receiver_device_fw_ota_init_new(self, 0x0000, error)) { - g_prefix_error(error, "failed to OTA init new: "); + g_prefix_error_literal(error, "failed to OTA init new: "); return FALSE; } if (!fu_pxi_receiver_device_fw_ota_ini_new_check(self, error)) { - g_prefix_error(error, "failed to OTA init new check: "); + g_prefix_error_literal(error, "failed to OTA init new check: "); return FALSE; } if (!fu_pxi_receiver_device_check_peripherals(self, error)) { - g_prefix_error(error, "failed to add wireless module: "); + g_prefix_error_literal(error, "failed to add wireless module: "); return FALSE; } @@ -838,10 +830,10 @@ if (iface_nr == NULL) return FALSE; if (g_strcmp0(iface_nr, "01") != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "only USB interface 1 supported"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "only USB interface 1 supported"); return FALSE; } @@ -850,7 +842,7 @@ } static void -fu_pxi_receiver_device_set_progress(FuDevice *self, FuProgress *progress) +fu_pxi_receiver_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -865,7 +857,7 @@ { fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - fu_device_add_icon(FU_DEVICE(self), "usb-receiver"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_USB_RECEIVER); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); fu_device_build_vendor_id_u16(FU_DEVICE(self), "USB", 0x093A); fu_device_add_protocol(FU_DEVICE(self), "com.pixart.rf"); diff -Nru fwupd-2.0.8/plugins/pixart-rf/fu-pxi-wireless-device.c fwupd-2.0.20/plugins/pixart-rf/fu-pxi-wireless-device.c --- fwupd-2.0.8/plugins/pixart-rf/fu-pxi-wireless-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/fu-pxi-wireless-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -4,12 +4,8 @@ * * SPDX-License-Identifier: LGPL-2.1-or-later */ -#include "config.h" -#ifdef HAVE_HIDRAW_H -#include -#include -#endif +#include "config.h" #include "fu-pxi-common.h" #include "fu-pxi-firmware.h" @@ -22,9 +18,9 @@ struct _FuPxiWirelessDevice { FuDevice parent_instance; - struct ota_fw_state fwstate; + FuPixartRfOtaFwState fwstate; guint8 sn; - struct ota_fw_dev_model model; + FuPixartRfOtaFwDevModel model; }; G_DEFINE_TYPE(FuPxiWirelessDevice, fu_pxi_wireless_device, FU_TYPE_DEVICE) @@ -40,27 +36,26 @@ } static FuPxiReceiverDevice * -fu_pxi_wireless_device_get_parent(FuDevice *self, GError **error) +fu_pxi_wireless_device_get_parent(FuPxiWirelessDevice *self, GError **error) { - FuDevice *parent = fu_device_get_parent(FU_DEVICE(self)); - if (parent == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no parent set"); + FuDevice *parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) return NULL; - } - return FU_PXI_RECEIVER_DEVICE(FU_UDEV_DEVICE(parent)); + return FU_PXI_RECEIVER_DEVICE(parent); } static FuFirmware * fu_pxi_wireless_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { + FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); FuPxiReceiverDevice *parent; g_autoptr(FuFirmware) firmware = fu_pxi_firmware_new(); - parent = fu_pxi_wireless_device_get_parent(device, error); + parent = fu_pxi_wireless_device_get_parent(self, error); if (parent == NULL) return NULL; @@ -81,10 +76,10 @@ return NULL; } else if (fu_device_has_private_flag(FU_DEVICE(parent), FU_PXI_DEVICE_FLAG_IS_HPAC) != fu_pxi_firmware_is_hpac(FU_PXI_FIRMWARE(firmware))) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "The firmware is incompatible with the device"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware is incompatible with the device"); return NULL; } @@ -92,7 +87,7 @@ } static gboolean -fu_pxi_wireless_device_get_cmd_response(FuPxiWirelessDevice *device, +fu_pxi_wireless_device_get_cmd_response(FuPxiWirelessDevice *self, guint8 *buf, guint bufsz, GError **error) @@ -101,7 +96,7 @@ guint16 retry = 0; guint8 status = 0x0; - parent = fu_pxi_wireless_device_get_parent(FU_DEVICE(device), error); + parent = fu_pxi_wireless_device_get_parent(self, error); if (parent == NULL) return FALSE; @@ -110,7 +105,7 @@ memset(buf, 0, bufsz); buf[0] = PXI_HID_WIRELESS_DEV_OTA_REPORT_ID; - fu_device_sleep(FU_DEVICE(device), FU_PXI_WIRELESS_DEV_DELAY_MS); /* ms */ + fu_device_sleep(FU_DEVICE(self), FU_PXI_WIRELESS_DEV_DELAY_MS); /* ms */ if (!fu_hidraw_device_get_feature(FU_HIDRAW_DEVICE(parent), buf, @@ -121,7 +116,7 @@ if (!fu_memread_uint8_safe(buf, bufsz, 0x4, &sn, error)) return FALSE; - if (device->sn != sn) + if (self->sn != sn) retry++; else break; @@ -132,7 +127,7 @@ FWUPD_ERROR_READ, "reach retry maximum hid sn fail, got 0x%04x, expected 0x%04x", sn, - device->sn); + self->sn); return FALSE; } @@ -148,10 +143,9 @@ } static gboolean -fu_pxi_wireless_device_check_crc(FuDevice *device, guint16 checksum, GError **error) +fu_pxi_wireless_device_check_crc(FuPxiWirelessDevice *self, guint16 checksum, GError **error) { FuPxiReceiverDevice *parent; - FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; guint16 checksum_device = 0x0; guint8 status = 0x0; @@ -159,14 +153,14 @@ g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); /* proxy */ - parent = fu_pxi_wireless_device_get_parent(device, error); + parent = fu_pxi_wireless_device_get_parent(self, error); if (parent == NULL) return FALSE; /* ota check crc command */ fu_byte_array_append_uint8(ota_cmd, 0x3); /* ota command length */ fu_byte_array_append_uint8(ota_cmd, FU_PXI_DEVICE_CMD_FW_OTA_CHECK_CRC); /* ota command */ - fu_byte_array_append_uint16(ota_cmd, checksum, G_LITTLE_ENDIAN); /* checkesum */ + fu_byte_array_append_uint16(ota_cmd, checksum, G_LITTLE_ENDIAN); /* checksum */ /* increase the serial number */ self->sn++; @@ -221,17 +215,16 @@ } static gboolean -fu_pxi_wireless_device_fw_object_create(FuDevice *device, FuChunk *chk, GError **error) +fu_pxi_wireless_device_fw_object_create(FuPxiWirelessDevice *self, FuChunk *chk, GError **error) { FuPxiReceiverDevice *parent; - FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; guint8 status = 0x0; g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); /* proxy */ - parent = fu_pxi_wireless_device_get_parent(device, error); + parent = fu_pxi_wireless_device_get_parent(self, error); if (parent == NULL) return FALSE; @@ -261,7 +254,7 @@ return FALSE; /* delay for wireless module device get command response*/ - fu_device_sleep(device, FU_PXI_WIRELESS_DEV_DELAY_MS); + fu_device_sleep(FU_DEVICE(self), FU_PXI_WIRELESS_DEV_DELAY_MS); if (!fu_pxi_wireless_device_get_cmd_response(self, buf, sizeof(buf), error)) return FALSE; @@ -282,15 +275,14 @@ } static gboolean -fu_pxi_wireless_device_write_payload(FuDevice *device, FuChunk *chk, GError **error) +fu_pxi_wireless_device_write_payload(FuPxiWirelessDevice *self, FuChunk *chk, GError **error) { FuPxiReceiverDevice *parent; - FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); /* proxy */ - parent = fu_pxi_wireless_device_get_parent(device, error); + parent = fu_pxi_wireless_device_get_parent(self, error); if (parent == NULL) return FALSE; @@ -319,22 +311,21 @@ return FALSE; /* delay for each payload packet */ - fu_device_sleep(device, FU_PXI_WIRELESS_DEV_PAYLOAD_DELAY_MS); + fu_device_sleep(FU_DEVICE(self), FU_PXI_WIRELESS_DEV_PAYLOAD_DELAY_MS); /* success */ return TRUE; } static gboolean -fu_pxi_wireless_device_write_chunk(FuDevice *device, FuChunk *chk, GError **error) +fu_pxi_wireless_device_write_chunk(FuPxiWirelessDevice *self, FuChunk *chk, GError **error) { - FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); guint32 prn = 0; g_autoptr(FuChunkArray) chunks = NULL; g_autoptr(GBytes) chk_bytes = fu_chunk_get_bytes(chk); /* send create fw object command */ - if (!fu_pxi_wireless_device_fw_object_create(device, chk, error)) + if (!fu_pxi_wireless_device_fw_object_create(self, chk, error)) return FALSE; /* write payload */ @@ -354,16 +345,14 @@ /* calculate checksum of each payload packet */ self->fwstate.checksum += fu_sum16(fu_chunk_get_data(chk2), fu_chunk_get_data_sz(chk2)); - if (!fu_pxi_wireless_device_write_payload(device, chk2, error)) + if (!fu_pxi_wireless_device_write_payload(self, chk2, error)) return FALSE; prn++; /* check crc at fw when PRN over threshold write or * offset reach max object sz or write offset reach fw length */ if (prn >= self->fwstate.prn_threshold || i == (fu_chunk_array_length(chunks) - 1)) { - if (!fu_pxi_wireless_device_check_crc(device, - self->fwstate.checksum, - error)) + if (!fu_pxi_wireless_device_check_crc(self, self->fwstate.checksum, error)) return FALSE; prn = 0; } @@ -374,15 +363,14 @@ } static gboolean -fu_pxi_wireless_device_fw_ota_preceding(FuDevice *device, GError **error) +fu_pxi_wireless_device_fw_ota_preceding(FuPxiWirelessDevice *self, GError **error) { FuPxiReceiverDevice *parent; - FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); /* proxy */ - parent = fu_pxi_wireless_device_get_parent(device, error); + parent = fu_pxi_wireless_device_get_parent(self, error); if (parent == NULL) return FALSE; @@ -408,10 +396,9 @@ } static gboolean -fu_pxi_wireless_device_fw_ota_init_new(FuDevice *device, gsize bufsz, GError **error) +fu_pxi_wireless_device_fw_ota_init_new(FuPxiWirelessDevice *self, gsize bufsz, GError **error) { FuPxiReceiverDevice *parent; - FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; guint8 status = 0x0; guint8 fw_version[10] = {0x0}; @@ -419,7 +406,7 @@ g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); /* proxy */ - parent = fu_pxi_wireless_device_get_parent(device, error); + parent = fu_pxi_wireless_device_get_parent(self, error); if (parent == NULL) return FALSE; @@ -448,7 +435,7 @@ return FALSE; /* delay for wireless module device get command response*/ - fu_device_sleep(device, FU_PXI_WIRELESS_DEV_DELAY_MS); + fu_device_sleep(FU_DEVICE(self), FU_PXI_WIRELESS_DEV_DELAY_MS); if (!fu_pxi_wireless_device_get_cmd_response(self, buf, sizeof(buf), error)) return FALSE; @@ -469,17 +456,16 @@ } static gboolean -fu_pxi_wireless_device_fw_ota_ini_new_check(FuDevice *device, GError **error) +fu_pxi_wireless_device_fw_ota_ini_new_check(FuPxiWirelessDevice *self, GError **error) { FuPxiReceiverDevice *parent; - FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; guint8 status = 0x0; g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); /* proxy */ - parent = fu_pxi_wireless_device_get_parent(device, error); + parent = fu_pxi_wireless_device_get_parent(self, error); if (parent == NULL) return FALSE; @@ -507,7 +493,7 @@ return FALSE; /* delay for wireless module device get command response*/ - fu_device_sleep(device, FU_PXI_WIRELESS_DEV_DELAY_MS); + fu_device_sleep(FU_DEVICE(self), FU_PXI_WIRELESS_DEV_DELAY_MS); if (!fu_pxi_wireless_device_get_cmd_response(self, buf, sizeof(buf), error)) return FALSE; @@ -528,13 +514,12 @@ } static gboolean -fu_pxi_wireless_device_fw_upgrade(FuDevice *device, +fu_pxi_wireless_device_fw_upgrade(FuPxiWirelessDevice *self, FuFirmware *firmware, FuProgress *progress, GError **error) { FuPxiReceiverDevice *parent; - FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); guint8 buf[FU_PXI_RECEIVER_DEVICE_OTA_BUF_SZ] = {0x0}; guint8 status = 0x0; const gchar *version; @@ -549,7 +534,7 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 95, NULL); /* proxy */ - parent = fu_pxi_wireless_device_get_parent(device, error); + parent = fu_pxi_wireless_device_get_parent(self, error); if (parent == NULL) return FALSE; @@ -604,7 +589,7 @@ return FALSE; /* delay for wireless module device get command response*/ - fu_device_sleep(device, FU_PXI_WIRELESS_DEV_DELAY_MS); + fu_device_sleep(FU_DEVICE(self), FU_PXI_WIRELESS_DEV_DELAY_MS); if (!fu_pxi_wireless_device_get_cmd_response(self, buf, sizeof(buf), error)) return FALSE; @@ -625,15 +610,14 @@ } static gboolean -fu_pxi_wireless_device_reset(FuDevice *device, GError **error) +fu_pxi_wireless_device_reset(FuPxiWirelessDevice *self, GError **error) { FuPxiReceiverDevice *parent; - FuPxiWirelessDevice *self = FU_PXI_WIRELESS_DEVICE(device); g_autoptr(GByteArray) ota_cmd = g_byte_array_new(); g_autoptr(GByteArray) receiver_cmd = g_byte_array_new(); /* proxy */ - parent = fu_pxi_wireless_device_get_parent(device, error); + parent = fu_pxi_wireless_device_get_parent(self, error); if (parent == NULL) return FALSE; @@ -707,12 +691,12 @@ return FALSE; /* send preceding command */ - if (!fu_pxi_wireless_device_fw_ota_preceding(device, error)) + if (!fu_pxi_wireless_device_fw_ota_preceding(self, error)) return FALSE; /* send fw ota init command */ - if (!fu_pxi_wireless_device_fw_ota_init_new(device, g_bytes_get_size(fw), error)) + if (!fu_pxi_wireless_device_fw_ota_init_new(self, g_bytes_get_size(fw), error)) return FALSE; - if (!fu_pxi_wireless_device_fw_ota_ini_new_check(device, error)) + if (!fu_pxi_wireless_device_fw_ota_ini_new_check(self, error)) return FALSE; fu_progress_step_done(progress); @@ -732,7 +716,7 @@ chk = fu_chunk_array_index(chunks, i, error); if (chk == NULL) return FALSE; - if (!fu_pxi_wireless_device_write_chunk(device, chk, error)) + if (!fu_pxi_wireless_device_write_chunk(self, chk, error)) return FALSE; fu_progress_set_percentage_full(fu_progress_get_child(progress), (gsize)i + self->fwstate.offset + 1, @@ -741,7 +725,7 @@ fu_progress_step_done(progress); /* fw upgrade command */ - if (!fu_pxi_wireless_device_fw_upgrade(device, + if (!fu_pxi_wireless_device_fw_upgrade(self, firmware, fu_progress_get_child(progress), error)) @@ -749,11 +733,11 @@ fu_progress_step_done(progress); /* send device reset command */ - fu_device_sleep(device, FU_PXI_WIRELESS_DEV_DELAY_MS); - if (!fu_pxi_wireless_device_reset(device, error)) + fu_device_sleep(FU_DEVICE(self), FU_PXI_WIRELESS_DEV_DELAY_MS); + if (!fu_pxi_wireless_device_reset(self, error)) return FALSE; - parent = fu_pxi_wireless_device_get_parent(device, error); + parent = fu_pxi_wireless_device_get_parent(self, error); if (parent == NULL) return FALSE; @@ -765,7 +749,7 @@ } static void -fu_pxi_wireless_device_set_progress(FuDevice *self, FuProgress *progress) +fu_pxi_wireless_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -799,7 +783,7 @@ } FuPxiWirelessDevice * -fu_pxi_wireless_device_new(FuContext *ctx, struct ota_fw_dev_model *model) +fu_pxi_wireless_device_new(FuContext *ctx, FuPixartRfOtaFwDevModel *model) { FuPxiWirelessDevice *self = NULL; self = g_object_new(FU_TYPE_PXI_WIRELESS_DEVICE, "context", ctx, NULL); diff -Nru fwupd-2.0.8/plugins/pixart-rf/fu-pxi-wireless-device.h fwupd-2.0.20/plugins/pixart-rf/fu-pxi-wireless-device.h --- fwupd-2.0.8/plugins/pixart-rf/fu-pxi-wireless-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/fu-pxi-wireless-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -14,4 +14,4 @@ G_DECLARE_FINAL_TYPE(FuPxiWirelessDevice, fu_pxi_wireless_device, FU, PXI_WIRELESS_DEVICE, FuDevice) FuPxiWirelessDevice * -fu_pxi_wireless_device_new(FuContext *ctx, struct ota_fw_dev_model *model); +fu_pxi_wireless_device_new(FuContext *ctx, FuPixartRfOtaFwDevModel *model); diff -Nru fwupd-2.0.8/plugins/pixart-rf/fu-pxi.rs fwupd-2.0.20/plugins/pixart-rf/fu-pxi.rs --- fwupd-2.0.8/plugins/pixart-rf/fu-pxi.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/fu-pxi.rs 2026-02-26 11:36:18.000000000 +0000 @@ -53,3 +53,16 @@ FwOtaInitNewCheck = 0x42, FwOtaPreceding = 0x44, } + +#[derive(Parse)] +#[repr(C, packed)] +struct FuStructPxiOtaFwState { + status: u8, + new_flow: u8, + offset: u16le, + checksum: u16le, + max_object_size: u32le, + mtu_size: u16le, + prn_threshold: u16le, + spec_check_result: u8, +} diff -Nru fwupd-2.0.8/plugins/pixart-rf/fu-self-test.c fwupd-2.0.20/plugins/pixart-rf/fu-self-test.c --- fwupd-2.0.8/plugins/pixart-rf/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -36,10 +36,10 @@ g_assert_nonnull(fw); csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); g_assert_no_error(error); - g_assert_cmpstr(csum1, ==, "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"); + g_assert_cmpstr(csum1, ==, "61efee24b3fad7358ce64144300e9e004b825759"); /* ensure we can parse */ - ret = fu_firmware_parse_bytes(firmware3, fw, 0x0, FWUPD_INSTALL_FLAG_NONE, &error); + ret = fu_firmware_parse_bytes(firmware3, fw, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); diff -Nru fwupd-2.0.8/plugins/pixart-rf/meson.build fwupd-2.0.20/plugins/pixart-rf/meson.build --- fwupd-2.0.8/plugins/pixart-rf/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if cc.has_header('linux/hidraw.h', required: false) +cc.has_header('linux/hidraw.h', required: false) or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginPixartRf"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -48,6 +49,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', @@ -55,4 +57,3 @@ ) test('pxi-self-test', e, env: env) endif -endif diff -Nru fwupd-2.0.8/plugins/pixart-rf/pixart-rf.quirk fwupd-2.0.20/plugins/pixart-rf/pixart-rf.quirk --- fwupd-2.0.8/plugins/pixart-rf/pixart-rf.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/pixart-rf.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -38,6 +38,11 @@ Plugin = pixart_rf GType = FuPxiBleDevice +# Primax Ryder Mouse 2 +[HIDRAW\VEN_0461&DEV_4F31] +Plugin = pixart_rf +GType = FuPxiBleDevice + # Primax Mouse [HIDRAW\VEN_0461&DEV_4EEF] Plugin = pixart_rf diff -Nru fwupd-2.0.8/plugins/pixart-rf/tests/hp-910-bt-keyboard.json fwupd-2.0.20/plugins/pixart-rf/tests/hp-910-bt-keyboard.json --- fwupd-2.0.8/plugins/pixart-rf/tests/hp-910-bt-keyboard.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/tests/hp-910-bt-keyboard.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/57a202956b7dedef59e5a396d3593237f0789730426571d2f53df2747ccef860-HP-910-white-bt-keyboard-us-2.2.3.cab", + "url": "57a202956b7dedef59e5a396d3593237f0789730426571d2f53df2747ccef860-HP-910-white-bt-keyboard-us-2.2.3.cab", "components": [ { "version": "2.2.3", diff -Nru fwupd-2.0.8/plugins/pixart-rf/tests/hp-910-bt-mouse.json fwupd-2.0.20/plugins/pixart-rf/tests/hp-910-bt-mouse.json --- fwupd-2.0.8/plugins/pixart-rf/tests/hp-910-bt-mouse.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/tests/hp-910-bt-mouse.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/85bf9dca11caefcdc58112e2f0487a8596971e3a078d01ead7547ab792d51378-hp-910-white-bt-mouse-2.2.0.cab", + "url": "85bf9dca11caefcdc58112e2f0487a8596971e3a078d01ead7547ab792d51378-hp-910-white-bt-mouse-2.2.0.cab", "components": [ { "version": "2.2.0", diff -Nru fwupd-2.0.8/plugins/pixart-rf/tests/pixart-rf-2862-dongle.json fwupd-2.0.20/plugins/pixart-rf/tests/pixart-rf-2862-dongle.json --- fwupd-2.0.8/plugins/pixart-rf/tests/pixart-rf-2862-dongle.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-rf/tests/pixart-rf-2862-dongle.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/b2b4e0bbcd4a1f5ec007f5176ff7727d73d7fdb44d0d2836a18b7c2aaaa0aba1-dongle_v0100.cab", - "emulation-url": "https://fwupd.org/downloads/82b8f88a655960f14f365a857829fb1947560d919a7d5169b046dcf684d43765-dongle_v0100.zip", + "url": "b2b4e0bbcd4a1f5ec007f5176ff7727d73d7fdb44d0d2836a18b7c2aaaa0aba1-dongle_v0100.cab", + "emulation-url": "82b8f88a655960f14f365a857829fb1947560d919a7d5169b046dcf684d43765-dongle_v0100.zip", "components": [ { "version": "1.0.0", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/66ca80d83ac1e1ccc8da9d4d24e1b73bfc1eeb0421cad54d7c98e3038fe25c92-dongle_v0101.cab", - "emulation-url": "https://fwupd.org/downloads/7c7add0b4825221c67d823b200b1523c8b5bfa445789864bac0eabd2b11b6c93-dongle_v0101.zip", + "url": "66ca80d83ac1e1ccc8da9d4d24e1b73bfc1eeb0421cad54d7c98e3038fe25c92-dongle_v0101.cab", + "emulation-url": "7c7add0b4825221c67d823b200b1523c8b5bfa445789864bac0eabd2b11b6c93-dongle_v0101.zip", "components": [ { "version": "1.0.1", diff -Nru fwupd-2.0.8/plugins/pixart-tp/README.md fwupd-2.0.20/plugins/pixart-tp/README.md --- fwupd-2.0.8/plugins/pixart-tp/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,88 @@ +--- +title: Plugin: PixArt Touchpad +--- + +## Introduction + +The PixArt Touchpad plugin updates PixArt touchpad firmware that enumerates as HID devices, +either USB-HID or I²C-HID exposed via `hidraw`. + +The device can enter a lightweight bootloader (“engineer mode”) and is then flashed over the same +HID interface. + +This plugin sets the fwupd device protocol to `com.pixart.tp` and reports version numbers in hex format. + +## Firmware Format + +The firmware is parsed by `FuPixartTpFirmware` and validated using: + +- **Magic** `FWHD` (header v1.0) +- **Header CRC32** (over the header minus CRC field) +- **Payload CRC32** (over the bytes after the header) + +Each updateable **internal** section defines a flash start address and a file offset/length. +The plugin programs flash in **4 KiB sectors** with **256-byte pages** via a small SRAM window. + +## GUID Generation + +These devices use the standard HID DeviceInstanceId values, e.g. + +- `HIDRAW\VEN_093A&DEV_0343` + +> Note: If the same silicon enumerates as a USB interface on some systems, +> an additional USB GUID like `USB\VID_093A&PID_0343` may also be provided +> in the firmware metadata. GUIDs derived from hardware IDs are **stable +> across machines**. + +## Update Behavior + +High-level flow: + +1. **Detach (enter bootloader)** + Writes device registers to switch to engineer mode: + - `bank 0x01, reg 0x2c = 0xaa` + - `bank 0x01, reg 0x2d = 0xcc` + +2. **Erase/Program** + - Erase flash by **sector (4 KiB)** + - Program by **page (256 B)** using an SRAM selected by `SramSelect` + - Busy/write-enable checks are performed between operations + +3. **Attach (exit bootloader)** + - `bank 0x01, reg 0x2c = 0xaa` + - `bank 0x01, reg 0x2d = 0xbb` + +OS reboot is required. + +## Quirk Use + +This plugin supports the following plugin-specific quirks: + +### `PixartTpHidVersionBank` + +Defines which bank to read the device firmware version from. +**Default**: `0x00`. + +### `PixartTpHidVersionAddr` + +Defines which address to read the device firmware version from; the plugin reads `addr+0` (lo) and `addr+1` (hi). +**Default**: `0xb2`. + +### `PixartTpSramSelect` + +Selects the SRAM type used for 256-byte page programming. +**Default**: `0x0f`. + +### `PixartTpHasHaptic` + +Whether this TP has a TF/haptic child IC. + +**Default**: `false`. + +## Vendor ID Security + +The vendor ID is set from the HID vendor, in this instance set to `HIDRAW:0x093A` + +## Version Considerations + +This plugin has been available since fwupd version `2.0.19`. diff -Nru fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-device.c fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-device.c --- fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,1499 @@ +/* + * Copyright 2025 Harris Tai + * Copyright 2025 Micky Hsieh + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-pixart-tp-device.h" +#include "fu-pixart-tp-firmware.h" +#include "fu-pixart-tp-haptic-device.h" +#include "fu-pixart-tp-section.h" + +struct _FuPixartTpDevice { + FuHidrawDevice parent_instance; + guint8 sram_select; + guint8 ver_bank; + guint16 ver_addr; + gboolean has_tf_child; +}; + +G_DEFINE_TYPE(FuPixartTpDevice, fu_pixart_tp_device, FU_TYPE_HIDRAW_DEVICE) + +#define FU_PIXART_TP_DEVICE_SECTOR_SIZE 4096 +#define FU_PIXART_TP_DEVICE_PAGE_SIZE 256 + +static void +fu_pixart_tp_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + fwupd_codec_string_append_hex(str, idt, "SramSelect", self->sram_select); + fwupd_codec_string_append_hex(str, idt, "VerBank", self->ver_bank); + fwupd_codec_string_append_hex(str, idt, "VerAddr", self->ver_addr); + fwupd_codec_string_append_bool(str, idt, "HasTfChild", self->has_tf_child); +} + +#define REPORT_ID_SINGLE 0x42 +#define REPORT_ID_BURST 0x41 +#define REPORT_ID_USER 0x43 + +#define OP_READ 0x10 + +static gboolean +fu_pixart_tp_device_register_write(FuPixartTpDevice *self, + FuPixartTpSystemBank bank, + guint8 addr, + guint8 val, + GError **error) +{ + guint8 buf[4] = {REPORT_ID_SINGLE, addr, bank, val}; + + if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), + buf, + sizeof(buf), + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error(error, + "register write failed: bank=0x%02x addr=0x%02x val=0x%02x: ", + bank, + addr, + val); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_register_read(FuPixartTpDevice *self, + FuPixartTpSystemBank bank, + guint8 addr, + guint8 *out_val, + GError **error) +{ + guint8 cmd[4] = {REPORT_ID_SINGLE, addr, (guint8)(bank | OP_READ), 0x00}; + guint8 resp[4] = {REPORT_ID_SINGLE}; + + if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), + cmd, + sizeof(cmd), + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error(error, + "register read command failed: bank=0x%02x addr=0x%02x: ", + bank, + addr); + return FALSE; + } + + if (!fu_hidraw_device_get_feature(FU_HIDRAW_DEVICE(self), + resp, + sizeof(resp), + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error(error, + "register read response failed: bank=0x%02x addr=0x%02x: ", + bank, + addr); + return FALSE; + } + + /* success */ + *out_val = resp[3]; + return TRUE; +} + +gboolean +fu_pixart_tp_device_register_user_write(FuPixartTpDevice *self, + FuPixartTpUserBank bank, + guint8 addr, + guint8 val, + GError **error) +{ + guint8 buf[4] = {REPORT_ID_USER, addr, bank, val}; + + if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), + buf, + sizeof(buf), + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error(error, + "user register write failed: bank=0x%02x addr=0x%02x val=0x%02x: ", + bank, + addr, + val); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_register_user_read(FuPixartTpDevice *self, + FuPixartTpUserBank bank, + guint8 addr, + guint8 *out_val, + GError **error) +{ + guint8 cmd[4] = {REPORT_ID_USER, addr, (guint8)(bank | OP_READ), 0x00}; + guint8 resp[4] = {REPORT_ID_USER}; + + if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), + cmd, + sizeof(cmd), + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error(error, + "user register read command failed: bank=0x%02x addr=0x%02x: ", + bank, + addr); + return FALSE; + } + if (!fu_hidraw_device_get_feature(FU_HIDRAW_DEVICE(self), + resp, + sizeof(resp), + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error(error, + "user register read response failed: bank=0x%02x addr=0x%02x: ", + bank, + addr); + return FALSE; + } + + /* success */ + *out_val = resp[3]; + return TRUE; +} + +static gboolean +fu_pixart_tp_device_register_burst_write(FuPixartTpDevice *self, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + guint8 payload[257] = {REPORT_ID_BURST}; + + if (!fu_memcpy_safe(payload, + sizeof(payload), + 1, /* dst offset: payload[1..] */ + buf, + bufsz, + 0, /* src offset: buf[0..] */ + bufsz, + error)) { + g_prefix_error_literal(error, "burst write memcpy failed: "); + return FALSE; + } + if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), + payload, + sizeof(payload), + FU_IOCTL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "burst write feature report failed: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_reset(FuPixartTpDevice *self, FuPixartTpResetMode mode, GError **error) +{ + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK1, + FU_PIXART_TP_REG_SYS1_RESET_KEY1, + FU_PIXART_TP_RESET_KEY1_SUSPEND, + error)) + return FALSE; + fu_device_sleep(FU_DEVICE(self), 30); + + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK1, + FU_PIXART_TP_REG_SYS1_RESET_KEY2, + mode == FU_PIXART_TP_RESET_MODE_APPLICATION + ? FU_PIXART_TP_RESET_KEY2_REGULAR + : FU_PIXART_TP_RESET_KEY2_BOOTLOADER, + error)) + return FALSE; + fu_device_sleep(FU_DEVICE(self), mode == FU_PIXART_TP_RESET_MODE_APPLICATION ? 500 : 10); + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_flash_execute_wait_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + guint8 *out_val = user_data; + + if (!fu_pixart_tp_device_register_read(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_EXECUTE, + out_val, + error)) + return FALSE; + + /* not ready yet, ask fu_device_retry_full() to try again */ + if (*out_val != FU_PIXART_TP_FLASH_EXEC_STATE_SUCCESS) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "flash execute still in progress"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_flash_execute(FuPixartTpDevice *self, + guint8 inst_cmd, + guint32 ccr_cmd, + guint16 data_cnt, + GError **error) +{ + guint8 out_val = 0; + + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_INST_CMD, + inst_cmd, + error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_CCR0, + (guint8)((ccr_cmd >> 0) & 0xff), + error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_CCR1, + (guint8)((ccr_cmd >> 8) & 0xff), + error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_CCR2, + (guint8)((ccr_cmd >> 16) & 0xff), + error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_CCR3, + (guint8)((ccr_cmd >> 24) & 0xff), + error)) + return FALSE; + + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_DATA_CNT0, + (guint8)((data_cnt >> 0) & 0xff), + error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_DATA_CNT1, + (guint8)((data_cnt >> 8) & 0xff), + error)) + return FALSE; + + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_EXECUTE, + 0x01, /* flash_execute_start */ + error)) + return FALSE; + + if (!fu_device_retry_full(FU_DEVICE(self), + fu_pixart_tp_device_flash_execute_wait_cb, + 10, + 1, /* ms */ + &out_val, + error)) { + g_prefix_error_literal(error, "flash executes failure: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_flash_write_enable_wait_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + guint8 *out_val = user_data; + + /* send READ_STATUS command */ + if (!fu_pixart_tp_device_flash_execute(self, + FU_PIXART_TP_FLASH_INST_RD2_REG_BANK, + FU_PIXART_TP_FLASH_CCR_READ_STATUS, + 1, + error)) + return FALSE; + + /* small delay between command and status read */ + fu_device_sleep(device, 1); + + /* read FLASH_STATUS register */ + if (!fu_pixart_tp_device_register_read(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_STATUS, + out_val, + error)) + return FALSE; + + /* check WEL bit */ + if ((*out_val & FU_PIXART_TP_FLASH_WRITE_ENABLE_SUCCESS) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "flash write enable still not set"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_flash_write_enable(FuPixartTpDevice *self, GError **error) +{ + guint8 out_val = 0; + + /* send WRITE_ENABLE once */ + if (!fu_pixart_tp_device_flash_execute(self, + FU_PIXART_TP_FLASH_INST_NONE, + FU_PIXART_TP_FLASH_CCR_WRITE_ENABLE, + 0, + error)) + return FALSE; + + /* poll WEL bit using fu_device_retry_full() */ + if (!fu_device_retry_full(FU_DEVICE(self), + fu_pixart_tp_device_flash_write_enable_wait_cb, + 10, + 0, /* ms */ + &out_val, + error)) { + g_prefix_error_literal(error, "flash write enable failure: "); + g_debug("flash write enable failure"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_flash_wait_busy_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + guint8 *out_val = user_data; + + /* send READ_STATUS command */ + if (!fu_pixart_tp_device_flash_execute(self, + FU_PIXART_TP_FLASH_INST_RD2_REG_BANK, + FU_PIXART_TP_FLASH_CCR_READ_STATUS, + 1, + error)) + return FALSE; + + /* small delay before reading status */ + fu_device_sleep(device, 1); + + /* read FLASH_STATUS register */ + if (!fu_pixart_tp_device_register_read(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_STATUS, + out_val, + error)) + return FALSE; + + /* busy bit cleared? */ + if ((*out_val & FU_PIXART_TP_FLASH_STATUS_BUSY) != 0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "flash still busy"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_flash_wait_busy(FuPixartTpDevice *self, GError **error) +{ + guint8 out_val = 0; + + if (!fu_device_retry_full(FU_DEVICE(self), + fu_pixart_tp_device_flash_wait_busy_cb, + 1000, + 0, /* ms */ + &out_val, + error)) { + g_prefix_error_literal(error, "flash wait busy failure: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_flash_erase_sector(FuPixartTpDevice *self, guint8 sector, GError **error) +{ + guint32 flash_address = (guint32)sector * FU_PIXART_TP_DEVICE_SECTOR_SIZE; + + if (!fu_pixart_tp_device_flash_wait_busy(self, error)) + return FALSE; + if (!fu_pixart_tp_device_flash_write_enable(self, error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_ADDR0, + (guint8)((flash_address >> 0) & 0xff), + error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_ADDR1, + (guint8)((flash_address >> 8) & 0xff), + error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_ADDR2, + (guint8)((flash_address >> 16) & 0xff), + error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_ADDR3, + (guint8)((flash_address >> 24) & 0xff), + error)) + return FALSE; + + g_debug("erase sector %u (addr=0x%08x)", (guint)sector, flash_address); + + if (!fu_pixart_tp_device_flash_execute(self, + FU_PIXART_TP_FLASH_INST_NONE, + FU_PIXART_TP_FLASH_CCR_ERASE_SECTOR, + 0, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_flash_program_256b_to_flash(FuPixartTpDevice *self, + guint8 sector, + guint8 page, + GError **error) +{ + guint32 flash_address = (guint32)sector * FU_PIXART_TP_DEVICE_SECTOR_SIZE + + (guint32)page * FU_PIXART_TP_DEVICE_PAGE_SIZE; + + if (!fu_pixart_tp_device_flash_wait_busy(self, error)) + return FALSE; + if (!fu_pixart_tp_device_flash_write_enable(self, error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_BUF_ADDR0, + 0x00, + error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_BUF_ADDR1, + 0x00, + error)) + return FALSE; + + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_ADDR0, + (guint8)((flash_address >> 0) & 0xff), + error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_ADDR1, + (guint8)((flash_address >> 8) & 0xff), + error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_ADDR2, + (guint8)((flash_address >> 16) & 0xff), + error)) + return FALSE; + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_FLASH_ADDR3, + (guint8)((flash_address >> 24) & 0xff), + error)) + return FALSE; + + if (!fu_pixart_tp_device_flash_execute(self, + FU_PIXART_TP_FLASH_INST_PROGRAM | + FU_PIXART_TP_FLASH_INST_INTERNAL_SRAM_ACCESS, + FU_PIXART_TP_FLASH_CCR_PROGRAM_PAGE, + FU_PIXART_TP_DEVICE_PAGE_SIZE, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +/* SRAM write (256 bytes) */ + +static gboolean +fu_pixart_tp_device_write_sram_256b(FuPixartTpDevice *self, const guint8 *data, GError **error) +{ + enum { + /* + * SRAM_TRIGGER (bank6) + * 0x00: enable NCS move, start transferring data to target SRAM address + * 0x01: disable NCS move + */ + PIXART_TP_SRAM_TRIGGER_NCS_ENABLE = 0x00, + PIXART_TP_SRAM_TRIGGER_NCS_DISABLE = 0x01, + }; + + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK6, + FU_PIXART_TP_REG_SYS6_SRAM_ADDR0, + 0x00, + error)) + return FALSE; + + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK6, + FU_PIXART_TP_REG_SYS6_SRAM_ADDR1, + 0x00, + error)) + return FALSE; + + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK6, + FU_PIXART_TP_REG_SYS6_SRAM_SELECT, + self->sram_select, + error)) + return FALSE; + + /* enable NCS so that the following burst goes to SRAM buffer */ + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK6, + FU_PIXART_TP_REG_SYS6_SRAM_TRIGGER, + PIXART_TP_SRAM_TRIGGER_NCS_ENABLE, + error)) + return FALSE; + + if (!fu_pixart_tp_device_register_burst_write(self, + data, + FU_PIXART_TP_DEVICE_PAGE_SIZE, + error)) { + g_prefix_error_literal(error, "burst write buffer failure: "); + return FALSE; + } + + /* disable NCS and commit SRAM buffer to target address */ + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK6, + FU_PIXART_TP_REG_SYS6_SRAM_TRIGGER, + PIXART_TP_SRAM_TRIGGER_NCS_DISABLE, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_firmware_clear(FuPixartTpDevice *self, + FuPixartTpFirmware *firmware, + GError **error) +{ + FuPixartTpSection *section; + guint32 start_address = 0; + + section = fu_pixart_tp_firmware_find_section_by_type(firmware, + FU_PIXART_TP_UPDATE_TYPE_FW_SECTION, + error); + if (section == NULL) + return FALSE; + start_address = fu_pixart_tp_section_get_target_flash_start(section); + g_debug("clear firmware at start address 0x%08x", start_address); + if (!fu_pixart_tp_device_flash_erase_sector( + self, + (guint8)(start_address / FU_PIXART_TP_DEVICE_SECTOR_SIZE), + error)) { + g_prefix_error_literal(error, "clear firmware failure: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_crc_firmware_wait_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + guint8 *out_val = user_data; + + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_CTRL, + out_val, + error)) + return FALSE; + + /* busy bit cleared? */ + if ((*out_val & FU_PIXART_TP_CRC_CTRL_BUSY) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "firmware CRC still busy"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_crc_firmware(FuPixartTpDevice *self, guint32 *crc, GError **error) +{ + guint8 out_val = 0; + guint8 swap_flag = 0; + guint16 part_id = 0; + guint32 return_value = 0; + + g_return_val_if_fail(crc != NULL, FALSE); + *crc = 0; + + /* read swap_flag from system bank4 */ + if (!fu_pixart_tp_device_register_read(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_SWAP_FLAG, + &out_val, + error)) + return FALSE; + swap_flag = out_val; + + /* read part_id from user bank0 (little-endian) */ + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_PART_ID0, + &out_val, + error)) + return FALSE; + part_id = out_val; + + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_PART_ID1, + &out_val, + error)) + return FALSE; + part_id |= (guint16)out_val << 8; + + switch (part_id) { + case FU_PIXART_TP_PART_ID_PJP274: + if (swap_flag != 0) { + /* PJP274 + swap enabled → firmware on bank1 */ + if (!fu_pixart_tp_device_register_user_write( + self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_CTRL, + FU_PIXART_TP_CRC_CTRL_FW_BANK1, + error)) + return FALSE; + } else { + /* PJP274 normal boot → firmware on bank0 */ + if (!fu_pixart_tp_device_register_user_write( + self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_CTRL, + FU_PIXART_TP_CRC_CTRL_FW_BANK0, + error)) + return FALSE; + } + break; + + default: + /* other part_id: always use bank0 firmware CRC */ + if (!fu_pixart_tp_device_register_user_write(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_CTRL, + FU_PIXART_TP_CRC_CTRL_FW_BANK0, + error)) + return FALSE; + break; + } + + /* wait CRC calculation completed */ + if (!fu_device_retry_full(FU_DEVICE(self), + fu_pixart_tp_device_crc_firmware_wait_cb, + 1000, + 10, /* ms */ + &out_val, + error)) { + g_prefix_error_literal(error, "firmware CRC wait busy failure: "); + return FALSE; + } + + /* read CRC result (32-bit, little-endian) */ + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_RESULT0, + &out_val, + error)) + return FALSE; + return_value |= (guint32)out_val; + + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_RESULT1, + &out_val, + error)) + return FALSE; + return_value |= (guint32)out_val << 8; + + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_RESULT2, + &out_val, + error)) + return FALSE; + return_value |= (guint32)out_val << 16; + + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_RESULT3, + &out_val, + error)) + return FALSE; + return_value |= (guint32)out_val << 24; + + *crc = return_value; + g_debug("firmware CRC: 0x%08x", (guint)*crc); + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_crc_parameter_wait_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + guint8 *out_val = user_data; + + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_CTRL, + out_val, + error)) + return FALSE; + + /* busy bit cleared? */ + if ((*out_val & FU_PIXART_TP_CRC_CTRL_BUSY) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "parameter CRC still busy"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_crc_parameter(FuPixartTpDevice *self, guint32 *crc, GError **error) +{ + guint16 part_id = 0; + guint32 result = 0; + guint8 out_val = 0; + guint8 swap_flag; + + g_return_val_if_fail(crc != NULL, FALSE); + *crc = 0; + + /* read swap_flag from system bank4 */ + if (!fu_pixart_tp_device_register_read(self, + FU_PIXART_TP_SYSTEM_BANK_BANK4, + FU_PIXART_TP_REG_SYS4_SWAP_FLAG, + &out_val, + error)) + return FALSE; + swap_flag = out_val; + + /* read part_id from user bank0 (little-endian) */ + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_PART_ID0, + &out_val, + error)) + return FALSE; + part_id = out_val; + + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_PART_ID1, + &out_val, + error)) + return FALSE; + part_id |= (guint16)out_val << 8; + + /* select CRC source */ + switch (part_id) { + case FU_PIXART_TP_PART_ID_PJP274: + if (swap_flag != 0) { + if (!fu_pixart_tp_device_register_user_write( + self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_CTRL, + FU_PIXART_TP_CRC_CTRL_PARAM_BANK1, + error)) + return FALSE; + } else { + if (!fu_pixart_tp_device_register_user_write( + self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_CTRL, + FU_PIXART_TP_CRC_CTRL_PARAM_BANK0, + error)) + return FALSE; + } + break; + + default: + if (!fu_pixart_tp_device_register_user_write(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_CTRL, + FU_PIXART_TP_CRC_CTRL_PARAM_BANK0, + error)) + return FALSE; + break; + } + + /* wait CRC calculation completed */ + if (!fu_device_retry_full(FU_DEVICE(self), + fu_pixart_tp_device_crc_parameter_wait_cb, + 1000, + 10, /* ms */ + &out_val, + error)) { + g_prefix_error_literal(error, "parameter CRC wait busy failure: "); + return FALSE; + } + + /* read CRC result (32-bit LE) */ + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_RESULT0, + &out_val, + error)) + return FALSE; + result |= (guint32)out_val; + + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_RESULT1, + &out_val, + error)) + return FALSE; + result |= (guint32)out_val << 8; + + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_RESULT2, + &out_val, + error)) + return FALSE; + result |= (guint32)out_val << 16; + + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_CRC_RESULT3, + &out_val, + error)) + return FALSE; + result |= (guint32)out_val << 24; + + *crc = result; + g_debug("parameter CRC: 0x%08x", result); + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_write_page(FuPixartTpDevice *self, + guint8 sector, + guint8 page, + FuChunk *chk, + GError **error) +{ + g_autoptr(GByteArray) page_buf = g_byte_array_new(); + g_autoptr(GBytes) blob = fu_chunk_get_bytes(chk); + + /* initialize all extra bytes to 0xFF */ + fu_byte_array_append_bytes(page_buf, blob); + fu_byte_array_set_size(page_buf, FU_PIXART_TP_DEVICE_PAGE_SIZE, 0xFF); + + /* write to SRAM using the 256-byte buffer */ + if (!fu_pixart_tp_device_write_sram_256b(self, page_buf->data, error)) + return FALSE; + if (!fu_pixart_tp_device_flash_program_256b_to_flash(self, sector, page, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_write_sector(FuPixartTpDevice *self, + guint8 start_sector, + FuChunk *chk_sector, + GError **error) +{ + g_autoptr(FuChunk) chk0 = NULL; + g_autoptr(FuChunkArray) chunks = NULL; + g_autoptr(GBytes) blob = fu_chunk_get_bytes(chk_sector); + + /* pages 1..15 */ + chunks = fu_chunk_array_new_from_bytes(blob, 0x0, 0x0, FU_PIXART_TP_DEVICE_PAGE_SIZE); + for (guint8 i = 1; i < fu_chunk_array_length(chunks); i++) { + g_autoptr(FuChunk) chk = NULL; + + chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) + return FALSE; + if (!fu_pixart_tp_device_write_page( + self, + (guint8)(start_sector + fu_chunk_get_idx(chk_sector)), + i, + chk, + error)) + return FALSE; + } + + /* page 0 last */ + chk0 = fu_chunk_array_index(chunks, 0, error); + if (chk0 == NULL) + return FALSE; + if (!fu_pixart_tp_device_write_page(self, + start_sector + fu_chunk_get_idx(chk_sector), + 0, + chk0, + error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_write_section(FuPixartTpDevice *self, + FuPixartTpSection *section, + FuProgress *progress, + GError **error) +{ + FuPixartTpUpdateType update_type = FU_PIXART_TP_UPDATE_TYPE_GENERAL; + guint32 target_flash_start = fu_pixart_tp_section_get_target_flash_start(section); + guint8 start_sector; + g_autoptr(FuChunkArray) chunks = NULL; + g_autoptr(GInputStream) stream = NULL; + + /* nothing to do */ + if (fu_firmware_get_size(FU_FIRMWARE(section)) == 0) + return TRUE; + + /* TF_FORCE is now handled by the haptic child-device */ + update_type = fu_pixart_tp_section_get_update_type(section); + if (update_type != FU_PIXART_TP_UPDATE_TYPE_GENERAL && + update_type != FU_PIXART_TP_UPDATE_TYPE_FW_SECTION && + update_type != FU_PIXART_TP_UPDATE_TYPE_PARAM) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "unsupported update type %u for TP section", + (guint)update_type); + return FALSE; + } + + /* chunk section data into sectors */ + stream = fu_firmware_get_stream(FU_FIRMWARE(section), error); + if (stream == NULL) + return FALSE; + chunks = fu_chunk_array_new_from_stream(stream, + 0x0, + 0x0, + FU_PIXART_TP_DEVICE_SECTOR_SIZE, + error); + if (chunks == NULL) + return FALSE; + + /* nothing to do */ + if (fu_chunk_array_length(chunks) == 0) + return TRUE; + + /* progress: 2 steps per sector (erase + program) */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, fu_chunk_array_length(chunks) * 2); + + /* cpu clocks power up */ + if (!fu_pixart_tp_device_register_write(self, + FU_PIXART_TP_SYSTEM_BANK_BANK1, + FU_PIXART_TP_REG_SYS1_CLOCKS_POWER_UP, + FU_PIXART_TP_CLOCKS_POWER_UP_CPU, + error)) + return FALSE; + + /* erase phase */ + start_sector = (guint8)(target_flash_start / FU_PIXART_TP_DEVICE_SECTOR_SIZE); + for (guint8 i = 0; i < fu_chunk_array_length(chunks); i++) { + if (!fu_pixart_tp_device_flash_erase_sector(self, + (guint8)(start_sector + i), + error)) { + g_prefix_error(error, "failed to erase sector 0x%x: ", i); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* Keep the original order: write pages 1..15 first, then page 0. + * Each write is 256 bytes; last chunk in blob is padded with 0xFF. */ + for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { + g_autoptr(FuChunk) chk = NULL; + chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) + return FALSE; + if (!fu_pixart_tp_device_write_sector(self, start_sector, chk, error)) { + g_prefix_error(error, "failed to write sector 0x%x: ", i); + return FALSE; + } + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_write_sections(FuPixartTpDevice *self, + GPtrArray *sections, + FuProgress *progress, + GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, sections->len); + + for (guint i = 0; i < sections->len; i++) { + FuPixartTpSection *section = g_ptr_array_index(sections, i); + FuPixartTpUpdateType update_type = fu_pixart_tp_section_get_update_type(section); + + /* skip non-updatable sections */ + if (!fu_pixart_tp_section_has_flag(section, FU_PIXART_TP_FIRMWARE_FLAG_VALID) || + fu_pixart_tp_section_has_flag(section, + FU_PIXART_TP_FIRMWARE_FLAG_IS_EXTERNAL)) { + fu_progress_step_done(progress); + continue; + } + + /* skip TF_FORCE sections: + * - handled by TF/haptic child device using its own image + * - parent TP only handles TP firmware/parameter sections */ + if (update_type == FU_PIXART_TP_UPDATE_TYPE_TF_FORCE) { + g_debug("skip TF_FORCE section %u for TP parent device", i); + fu_progress_step_done(progress); + continue; + } + + if (fu_firmware_get_size(FU_FIRMWARE(section)) == 0) { + fu_progress_step_done(progress); + continue; + } + if (!fu_pixart_tp_device_write_section(self, + section, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_verify_crc(FuPixartTpDevice *self, + FuPixartTpFirmware *firmware, + FuProgress *progress, + GError **error) +{ + guint32 crc_value; + FuPixartTpSection *section; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 92, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 8, NULL); + + /* reset to bootloader before CRC check */ + if (!fu_pixart_tp_device_reset(self, FU_PIXART_TP_RESET_MODE_BOOTLOADER, error)) + return FALSE; + + /* firmware CRC */ + if (!fu_pixart_tp_device_crc_firmware(self, &crc_value, error)) + return FALSE; + section = fu_pixart_tp_firmware_find_section_by_type(firmware, + FU_PIXART_TP_UPDATE_TYPE_FW_SECTION, + error); + if (section == NULL) + return FALSE; + if (crc_value != fu_pixart_tp_section_get_crc(section)) { + g_autoptr(GError) error_local = NULL; + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware CRC compare failed"); + if (!fu_pixart_tp_device_firmware_clear(self, firmware, &error_local)) { + g_warning("failed to clear firmware after CRC error: %s", + error_local->message); + } + return FALSE; + } + fu_progress_step_done(progress); + + /* parameter CRC */ + if (!fu_pixart_tp_device_crc_parameter(self, &crc_value, error)) + return FALSE; + section = fu_pixart_tp_firmware_find_section_by_type(firmware, + FU_PIXART_TP_UPDATE_TYPE_PARAM, + error); + if (section == NULL) + return FALSE; + if (crc_value != fu_pixart_tp_section_get_crc(section)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "parameter CRC compare failed"); + (void)fu_pixart_tp_device_firmware_clear(self, firmware, error); + return FALSE; + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_setup(FuDevice *device, GError **error) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + guint8 buf[2] = {0}; + + /* read TP boot status*/ + if (!fu_pixart_tp_device_register_user_read(self, + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_BOOT_STAUS, + &buf[0], + error)) + return FALSE; + + /* avoid tp stuck in bootloader */ + if (buf[0] == FU_PIXART_TP_BOOT_STATUS_ROM) { + if (!fu_pixart_tp_device_reset(self, FU_PIXART_TP_RESET_MODE_APPLICATION, error)) + return FALSE; + } + + /* read low byte */ + if (!fu_pixart_tp_device_register_user_read(self, + self->ver_bank, + (guint8)(self->ver_addr + 0), + &buf[0], + error)) + return FALSE; + + /* read high byte */ + if (!fu_pixart_tp_device_register_user_read(self, + self->ver_bank, + (guint8)(self->ver_addr + 1), + &buf[1], + error)) + return FALSE; + + /* success */ + fu_device_set_version_raw(device, fu_memread_uint16(buf, G_LITTLE_ENDIAN)); + return TRUE; +} + +static gboolean +fu_pixart_tp_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + guint64 total_update_bytes = 0; + g_autoptr(GPtrArray) sections = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 2, NULL); + + /* sanity check */ + sections = fu_firmware_get_images(firmware); + if (sections->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no sections to write"); + return FALSE; + } + + /* calculate total bytes for valid internal TP sections + * NOTE: + * - TF_FORCE sections are skipped here; they are handled by the + * TF/haptic child-device using its own firmware image. + */ + for (guint i = 0; i < sections->len; i++) { + FuPixartTpSection *section = g_ptr_array_index(sections, i); + guint32 section_length = 0; + FuPixartTpUpdateType update_type; + + if (!fu_pixart_tp_section_has_flag(section, FU_PIXART_TP_FIRMWARE_FLAG_VALID) || + fu_pixart_tp_section_has_flag(section, FU_PIXART_TP_FIRMWARE_FLAG_IS_EXTERNAL)) + continue; + + update_type = fu_pixart_tp_section_get_update_type(section); + if (update_type == FU_PIXART_TP_UPDATE_TYPE_TF_FORCE) + continue; + + section_length = fu_firmware_get_size(FU_FIRMWARE(section)); + if (section_length > 0) + total_update_bytes += (guint64)section_length; + } + + /* sanity check */ + if (total_update_bytes == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no internal/valid TP sections to write"); + return FALSE; + } + g_debug("total TP update bytes=%" G_GUINT64_FORMAT, total_update_bytes); + + /* erase old firmware */ + if (!fu_pixart_tp_device_firmware_clear(self, FU_PIXART_TP_FIRMWARE(firmware), error)) + return FALSE; + + /* program all TP sections (TF_FORCE handled by child device) */ + if (!fu_pixart_tp_device_write_sections(self, + sections, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify CRC (firmware + parameter) */ + if (!fu_pixart_tp_device_verify_crc(self, + FU_PIXART_TP_FIRMWARE(firmware), + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + guint64 tmp = 0; + + if (g_strcmp0(key, "PixartTpHidVersionBank") == 0) { + if (!fu_strtoull(value, &tmp, 0, 0xff, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->ver_bank = (guint8)tmp; + return TRUE; + } + if (g_strcmp0(key, "PixartTpHidVersionAddr") == 0) { + if (!fu_strtoull(value, &tmp, 0, 0xffff, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->ver_addr = (guint16)tmp; + return TRUE; + } + if (g_strcmp0(key, "PixartTpSramSelect") == 0) { + if (!fu_strtoull(value, &tmp, 0, 0xff, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->sram_select = (guint8)tmp; + return TRUE; + } + if (g_strcmp0(key, "PixartTpHasHaptic") == 0) + return fu_strtobool(value, &self->has_tf_child, error); + + /* unknown quirk */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported: %s", + key); + return FALSE; +} + +static gboolean +fu_pixart_tp_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + + /* nothing to do if already in application mode */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + if (!fu_pixart_tp_device_reset(self, FU_PIXART_TP_RESET_MODE_APPLICATION, error)) + return FALSE; + + /* success */ + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_pixart_tp_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + + /* already in bootloader, nothing to do */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) + return TRUE; + + if (!fu_pixart_tp_device_reset(self, FU_PIXART_TP_RESET_MODE_BOOTLOADER, error)) + return FALSE; + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + return TRUE; +} + +static gboolean +fu_pixart_tp_device_reload(FuDevice *device, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* best-effort: do not fail the whole update just because reload failed */ + if (!fu_pixart_tp_device_setup(device, &error_local)) + g_debug("failed to refresh firmware version: %s", error_local->message); + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + + /* ensure we are not stuck in bootloader */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (!fu_pixart_tp_device_reset(self, FU_PIXART_TP_RESET_MODE_APPLICATION, error)) + return FALSE; + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } + + /* success */ + return TRUE; +} + +static void +fu_pixart_tp_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 93, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 6, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static gboolean +fu_pixart_tp_device_probe(FuDevice *device, GError **error) +{ + FuPixartTpDevice *self = FU_PIXART_TP_DEVICE(device); + + if (self->has_tf_child) { + g_autoptr(FuPixartTpHapticDevice) child = fu_pixart_tp_haptic_device_new(device); + fu_device_add_child(device, FU_DEVICE(child)); + } + + /* success */ + return TRUE; +} + +static gchar * +fu_pixart_tp_device_convert_version(FuDevice *device, guint64 version_raw) +{ + return g_strdup_printf("0x%04x", (guint16)version_raw); +} + +static void +fu_pixart_tp_device_init(FuPixartTpDevice *self) +{ + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_HEX); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_add_protocol(FU_DEVICE(self), "com.pixart.tp"); + fu_device_set_summary(FU_DEVICE(self), "Touchpad"); + fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_PIXART_TP_FIRMWARE); + self->sram_select = 0x0F; + self->ver_bank = 0x00; + self->ver_addr = 0xB2; +} + +static void +fu_pixart_tp_device_class_init(FuPixartTpDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->to_string = fu_pixart_tp_device_to_string; + device_class->probe = fu_pixart_tp_device_probe; + device_class->setup = fu_pixart_tp_device_setup; + device_class->write_firmware = fu_pixart_tp_device_write_firmware; + device_class->attach = fu_pixart_tp_device_attach; + device_class->detach = fu_pixart_tp_device_detach; + device_class->cleanup = fu_pixart_tp_device_cleanup; + device_class->set_progress = fu_pixart_tp_device_set_progress; + device_class->set_quirk_kv = fu_pixart_tp_device_set_quirk_kv; + device_class->convert_version = fu_pixart_tp_device_convert_version; + device_class->reload = fu_pixart_tp_device_reload; +} diff -Nru fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-device.h fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-device.h --- fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,22 @@ +/* + * Copyright 2025 Harris Tai + * Copyright 2025 Micky Hsieh + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#include "fu-pixart-tp-struct.h" + +#define FU_TYPE_PIXART_TP_DEVICE (fu_pixart_tp_device_get_type()) +G_DECLARE_FINAL_TYPE(FuPixartTpDevice, fu_pixart_tp_device, FU, PIXART_TP_DEVICE, FuHidrawDevice) + +gboolean +fu_pixart_tp_device_register_user_write(FuPixartTpDevice *self, + FuPixartTpUserBank bank, + guint8 addr, + guint8 val, + GError **error) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-firmware.c fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-firmware.c --- fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-firmware.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,300 @@ +/* + * Copyright 2025 Harris Tai + * Copyright 2025 Micky Hsieh + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-pixart-tp-firmware.h" +#include "fu-pixart-tp-section.h" + +struct _FuPixartTpFirmware { + FuFirmware parent_instance; + guint16 header_ver; + guint16 ic_part_id; + guint16 flash_sectors; +}; + +G_DEFINE_TYPE(FuPixartTpFirmware, fu_pixart_tp_firmware, FU_TYPE_FIRMWARE) + +static gboolean +fu_pixart_tp_firmware_validate(FuFirmware *firmware, + GInputStream *stream, + gsize offset, + GError **error) +{ + return fu_struct_pixart_tp_firmware_hdr_validate_stream(stream, offset, error); +} + +static gboolean +fu_pixart_tp_firmware_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ + FuPixartTpFirmware *self = FU_PIXART_TP_FIRMWARE(firmware); + gboolean saw_fw = FALSE; + gboolean saw_param = FALSE; + guint16 num_sections; + gsize offset = 0; + g_autoptr(FuStructPixartTpFirmwareHdr) st_hdr = NULL; + + /* parse FWHD header via rustgen struct */ + st_hdr = fu_struct_pixart_tp_firmware_hdr_parse_stream(stream, 0, error); + if (st_hdr == NULL) + return FALSE; + + self->header_ver = fu_struct_pixart_tp_firmware_hdr_get_header_ver(st_hdr); + fu_firmware_set_version_raw(firmware, + fu_struct_pixart_tp_firmware_hdr_get_file_ver(st_hdr)); + self->ic_part_id = fu_struct_pixart_tp_firmware_hdr_get_ic_part_id(st_hdr); + num_sections = fu_struct_pixart_tp_firmware_hdr_get_num_sections(st_hdr); + self->flash_sectors = fu_struct_pixart_tp_firmware_hdr_get_flash_sectors(st_hdr); + + /* header CRC check */ + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { + guint32 stored = 0; + guint32 calc = G_MAXUINT32; + g_autoptr(GInputStream) partial_stream = NULL; + + if (!fu_input_stream_read_u32(stream, + FU_STRUCT_PIXART_TP_FIRMWARE_HDR_DEFAULT_HEADER_LEN - + 4, + &stored, + G_LITTLE_ENDIAN, + error)) + return FALSE; + + partial_stream = fu_partial_input_stream_new( + stream, + 0, + FU_STRUCT_PIXART_TP_FIRMWARE_HDR_DEFAULT_HEADER_LEN - 4, + error); + if (partial_stream == NULL) + return FALSE; + if (!fu_input_stream_compute_crc32(partial_stream, + FU_CRC_KIND_B32_STANDARD, + &calc, + error)) + return FALSE; + if (stored != calc) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "header CRC mismatch, got 0x%08x and expected 0x%08x", + calc, + stored); + return FALSE; + } + } + + offset += FU_STRUCT_PIXART_TP_FIRMWARE_HDR_SIZE; + for (guint i = 0; i < num_sections; i++) { + FuPixartTpUpdateType update_type; + g_autoptr(FuPixartTpSection) section = fu_pixart_tp_section_new(); + + /* load section */ + if (!fu_firmware_parse_stream(FU_FIRMWARE(section), stream, offset, flags, error)) + return FALSE; + update_type = fu_pixart_tp_section_get_update_type(section); + if (update_type == FU_PIXART_TP_UPDATE_TYPE_FW_SECTION) { + if (!fu_pixart_tp_section_has_flag(section, + FU_PIXART_TP_FIRMWARE_FLAG_VALID)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "firmware section marked invalid"); + return FALSE; + } + saw_fw = TRUE; + fu_firmware_set_id(FU_FIRMWARE(section), FU_FIRMWARE_ID_PAYLOAD); + + } else if (update_type == FU_PIXART_TP_UPDATE_TYPE_PARAM) { + if (!fu_pixart_tp_section_has_flag(section, + FU_PIXART_TP_FIRMWARE_FLAG_VALID)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "parameter section marked invalid"); + return FALSE; + } + saw_param = TRUE; + fu_firmware_set_id(FU_FIRMWARE(section), "parameter"); + + } else if (update_type == FU_PIXART_TP_UPDATE_TYPE_TF_FORCE) { + fu_firmware_set_id(FU_FIRMWARE(section), "tf-force"); + } + + if (!fu_firmware_add_image(firmware, FU_FIRMWARE(section), error)) + return FALSE; + offset += FU_STRUCT_PIXART_TP_FIRMWARE_SECTION_HDR_SIZE; + } + + /* required section checks */ + if (!saw_fw) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "missing firmware section"); + return FALSE; + } + if (!saw_param) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "missing parameter section"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gchar * +fu_pixart_tp_firmware_convert_version(FuFirmware *firmware, guint64 version_raw) +{ + return fu_version_from_uint16((guint16)version_raw, + fu_firmware_get_version_format(firmware)); +} + +static void +fu_pixart_tp_firmware_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuPixartTpFirmware *self = FU_PIXART_TP_FIRMWARE(firmware); + fu_xmlb_builder_insert_kx(bn, "header_ver", self->header_ver); + fu_xmlb_builder_insert_kx(bn, "ic_part_id", self->ic_part_id); + fu_xmlb_builder_insert_kx(bn, "flash_sectors", self->flash_sectors); +} + +static GByteArray * +fu_pixart_tp_firmware_write(FuFirmware *firmware, GError **error) +{ + FuPixartTpFirmware *self = FU_PIXART_TP_FIRMWARE(firmware); + gsize offset = FU_STRUCT_PIXART_TP_FIRMWARE_HDR_DEFAULT_HEADER_LEN; + g_autoptr(FuStructPixartTpFirmwareHdr) st = fu_struct_pixart_tp_firmware_hdr_new(); + g_autoptr(GPtrArray) imgs = fu_firmware_get_images(firmware); + + fu_struct_pixart_tp_firmware_hdr_set_header_ver(st, self->header_ver); + fu_struct_pixart_tp_firmware_hdr_set_file_ver(st, fu_firmware_get_version_raw(firmware)); + fu_struct_pixart_tp_firmware_hdr_set_ic_part_id(st, self->ic_part_id); + fu_struct_pixart_tp_firmware_hdr_set_flash_sectors(st, self->flash_sectors); + + /* add section headers */ + fu_struct_pixart_tp_firmware_hdr_set_num_sections(st, imgs->len); + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + g_autoptr(GBytes) blob = NULL; + + fu_firmware_set_offset(img, offset); + blob = fu_firmware_write(img, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(st->buf, blob); + offset += fu_firmware_get_size(img); + } + fu_byte_array_set_size(st->buf, FU_STRUCT_PIXART_TP_FIRMWARE_HDR_DEFAULT_HEADER_LEN, 0x0); + + /* set header CRC */ + if (!fu_memwrite_uint32_safe( + st->buf->data, + st->buf->len, + st->buf->len - 4, + fu_crc32(FU_CRC_KIND_B32_STANDARD, st->buf->data, st->buf->len - 4), + G_LITTLE_ENDIAN, + error)) + return NULL; + + /* add section data */ + for (guint i = 0; i < imgs->len; i++) { + FuFirmware *img = g_ptr_array_index(imgs, i); + g_autoptr(GBytes) blob = NULL; + + blob = fu_firmware_get_bytes(img, error); + if (blob == NULL) + return NULL; + fu_byte_array_append_bytes(st->buf, blob); + } + + /* success */ + return g_steal_pointer(&st->buf); +} + +static gboolean +fu_pixart_tp_firmware_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuPixartTpFirmware *self = FU_PIXART_TP_FIRMWARE(firmware); + const gchar *tmp; + guint64 tmp64 = 0; + + /* simple properties */ + tmp = xb_node_query_text(n, "header_ver", NULL); + if (tmp != NULL) { + if (!fu_strtoull(tmp, &tmp64, 0, G_MAXUINT16, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->header_ver = (guint16)tmp64; + } + tmp = xb_node_query_text(n, "ic_part_id", NULL); + if (tmp != NULL) { + if (!fu_strtoull(tmp, &tmp64, 0, G_MAXUINT16, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->ic_part_id = (guint16)tmp64; + } + tmp = xb_node_query_text(n, "flash_sectors", NULL); + if (tmp != NULL) { + if (!fu_strtoull(tmp, &tmp64, 0, G_MAXUINT16, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->flash_sectors = (guint16)tmp64; + } + + /* success */ + return TRUE; +} + +FuPixartTpSection * +fu_pixart_tp_firmware_find_section_by_type(FuPixartTpFirmware *self, + FuPixartTpUpdateType update_type, + GError **error) +{ + g_autoptr(GPtrArray) secs = fu_firmware_get_images(FU_FIRMWARE(self)); + for (guint i = 0; i < secs->len; i++) { + FuPixartTpSection *section = FU_PIXART_TP_SECTION(g_ptr_array_index(secs, i)); + if (fu_pixart_tp_section_get_update_type(section) == update_type && + fu_pixart_tp_section_has_flag(section, FU_PIXART_TP_FIRMWARE_FLAG_VALID)) + return section; + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "cannot find section of type %u", + (guint)update_type); + return NULL; +} + +static void +fu_pixart_tp_firmware_init(FuPixartTpFirmware *self) +{ + fu_firmware_add_flag(FU_FIRMWARE(self), FU_FIRMWARE_FLAG_HAS_CHECKSUM); + fu_firmware_set_version_format(FU_FIRMWARE(self), FWUPD_VERSION_FORMAT_HEX); + fu_firmware_set_images_max(FU_FIRMWARE(self), 8); + g_type_ensure(FU_TYPE_PIXART_TP_SECTION); +} + +static void +fu_pixart_tp_firmware_class_init(FuPixartTpFirmwareClass *klass) +{ + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + firmware_class->validate = fu_pixart_tp_firmware_validate; + firmware_class->parse = fu_pixart_tp_firmware_parse; + firmware_class->export = fu_pixart_tp_firmware_export; + firmware_class->write = fu_pixart_tp_firmware_write; + firmware_class->build = fu_pixart_tp_firmware_build; + firmware_class->convert_version = fu_pixart_tp_firmware_convert_version; +} + +FuFirmware * +fu_pixart_tp_firmware_new(void) +{ + return FU_FIRMWARE(g_object_new(FU_TYPE_PIXART_TP_FIRMWARE, NULL)); +} diff -Nru fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-firmware.h fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-firmware.h --- fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-firmware.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-firmware.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,21 @@ +/* + * Copyright 2025 Harris Tai + * Copyright 2025 Micky Hsieh + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-pixart-tp-section.h" +#include "fu-pixart-tp-struct.h" + +#define FU_TYPE_PIXART_TP_FIRMWARE (fu_pixart_tp_firmware_get_type()) +G_DECLARE_FINAL_TYPE(FuPixartTpFirmware, fu_pixart_tp_firmware, FU, PIXART_TP_FIRMWARE, FuFirmware) + +FuFirmware * +fu_pixart_tp_firmware_new(void); +FuPixartTpSection * +fu_pixart_tp_firmware_find_section_by_type(FuPixartTpFirmware *self, + FuPixartTpUpdateType update_type, + GError **error) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-haptic-device.c fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-haptic-device.c --- fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-haptic-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-haptic-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,843 @@ +/* Copyright 2025 Harris Tai + * Copyright 2025 Micky Hsieh + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-pixart-tp-device.h" +#include "fu-pixart-tp-firmware.h" +#include "fu-pixart-tp-haptic-device.h" +#include "fu-pixart-tp-section.h" +#include "fu-pixart-tp-struct.h" + +struct _FuPixartTpHapticDevice { + FuDevice parent_instance; + guint8 status; + guint16 packet_number; +}; + +G_DEFINE_TYPE(FuPixartTpHapticDevice, fu_pixart_tp_haptic_device, FU_TYPE_DEVICE) + +#define FU_PIXART_TP_TF_RETRY_COUNT 3 + +#define FU_PIXART_TP_TF_RETRY_INTERVAL_MS 10 + +#define FU_PIXART_TP_TF_FRAME_SIZE_FEATURE_REPORT_LEN 64 + +static void +fu_pixart_tp_haptic_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuPixartTpHapticDevice *self = FU_PIXART_TP_HAPTIC_DEVICE(device); + fwupd_codec_string_append_hex(str, idt, "Status", self->status); + fwupd_codec_string_append_hex(str, idt, "PacketNumber", self->packet_number); +} + +typedef struct { + guint8 mode; + guint8 major; + guint8 minor; + guint8 patch; +} FuPixartTpTfReadFwVersionCtx; + +typedef struct { + guint32 packet_total; + guint32 packet_index; + const guint8 *chunk_data; + gsize chunk_len; +} FuPixartTpTfWritePacketCtx; + +static gboolean +fu_pixart_tp_haptic_device_tf_write_rmi_cmd(FuPixartTpHapticDevice *self, + guint16 addr, + guint8 cmd, + GError **error) +{ + FuDevice *proxy; + gsize crc_off = 2; + guint8 crc; + g_autoptr(FuStructPixartTpTfWriteSimpleCmd) st = + fu_struct_pixart_tp_tf_write_simple_cmd_new(); + + fu_struct_pixart_tp_tf_write_simple_cmd_set_addr(st, addr); + fu_struct_pixart_tp_tf_write_simple_cmd_set_len(st, sizeof(cmd)); + fu_byte_array_append_uint8(st->buf, cmd); + + crc = fu_crc8(FU_CRC_KIND_B8_STANDARD, st->buf->data + crc_off, st->buf->len - crc_off); + g_byte_array_append(st->buf, &crc, 1); + fu_byte_array_append_uint8(st->buf, FU_PIXART_TP_TF_FRAME_CONST_TAIL); + fu_byte_array_set_size(st->buf, FU_PIXART_TP_TF_FRAME_SIZE_FEATURE_REPORT_LEN, 0x00); + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + return fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(proxy), + st->buf->data, + st->buf->len, + FU_IOCTL_FLAG_NONE, + error); +} + +static gboolean +fu_pixart_tp_haptic_device_tf_write_rmi_with_packet(FuPixartTpHapticDevice *self, + guint16 addr, + gsize packet_total, + gsize packet_index, + const guint8 *in_buf, + gsize in_bufsz, + GError **error) +{ + FuDevice *proxy; + gsize crc_off = 2; + guint8 crc; + g_autoptr(FuStructPixartTpTfWritePacketCmd) st = + fu_struct_pixart_tp_tf_write_packet_cmd_new(); + + /* protocol overhead: packet_total (2) + packet_index (2) */ + fu_struct_pixart_tp_tf_write_packet_cmd_set_addr(st, addr); + fu_struct_pixart_tp_tf_write_packet_cmd_set_datalen(st, + (guint16)in_bufsz + + sizeof(guint16) * 2); + fu_struct_pixart_tp_tf_write_packet_cmd_set_packet_total(st, (guint16)packet_total); + fu_struct_pixart_tp_tf_write_packet_cmd_set_packet_index(st, (guint16)packet_index); + + if (in_bufsz > 0 && in_buf != NULL) + g_byte_array_append(st->buf, in_buf, (guint)in_bufsz); + + crc = fu_crc8(FU_CRC_KIND_B8_STANDARD, st->buf->data + crc_off, st->buf->len - crc_off); + g_byte_array_append(st->buf, &crc, 1); + fu_byte_array_append_uint8(st->buf, FU_PIXART_TP_TF_FRAME_CONST_TAIL); + fu_byte_array_set_size(st->buf, FU_PIXART_TP_TF_FRAME_SIZE_FEATURE_REPORT_LEN, 0x00); + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + return fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(proxy), + st->buf->data, + st->buf->len, + FU_IOCTL_FLAG_NONE, + error); +} + +static GByteArray * +fu_pixart_tp_haptic_device_tf_read_rmi(FuPixartTpHapticDevice *self, + guint16 addr, + const guint8 *in_buf, + gsize in_bufsz, + guint16 reply_len, + GError **error) +{ + FuDevice *proxy; + gsize datalen; + guint8 io_buf[FU_PIXART_TP_TF_FRAME_SIZE_FEATURE_REPORT_LEN] = {0}; + g_autoptr(FuStructPixartTpTfReadCmd) st_read = fu_struct_pixart_tp_tf_read_cmd_new(); + g_autoptr(FuStructPixartTpTfReplyHdr) st_hdr = NULL; + g_autoptr(GByteArray) buf = g_byte_array_new(); + + /* datalen = input length + 2 bytes reply length (low/high) */ + fu_struct_pixart_tp_tf_read_cmd_set_addr(st_read, addr); + fu_struct_pixart_tp_tf_read_cmd_set_datalen(st_read, (guint16)in_bufsz + sizeof(guint16)); + fu_struct_pixart_tp_tf_read_cmd_set_reply_len(st_read, reply_len); + + /* append payload (optional) */ + if (in_bufsz > 0) + g_byte_array_append(st_read->buf, in_buf, in_bufsz); + + /* append crc + tail */ + fu_byte_array_append_uint8( + st_read->buf, + fu_crc8(FU_CRC_KIND_B8_STANDARD, st_read->buf->data + 2, st_read->buf->len - 2)); + fu_byte_array_append_uint8(st_read->buf, FU_PIXART_TP_TF_FRAME_CONST_TAIL); + fu_byte_array_set_size(st_read->buf, FU_PIXART_TP_TF_FRAME_SIZE_FEATURE_REPORT_LEN, 0x00); + + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return NULL; + if (!fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(proxy), + st_read->buf->data, + st_read->buf->len, + FU_IOCTL_FLAG_NONE, + error)) + return NULL; + + fu_device_sleep(FU_DEVICE(self), 10); + + /* copy header to preserve emulation compat */ + if (!fu_memcpy_safe(io_buf, + sizeof(io_buf), + 0, + st_read->buf->data, + st_read->buf->len, + 0, + st_read->buf->len, + error)) + return NULL; + if (!fu_hidraw_device_get_feature(FU_HIDRAW_DEVICE(proxy), + io_buf, + sizeof(io_buf), + FU_IOCTL_FLAG_NONE, + error)) + return NULL; + + /* parse reply header */ + st_hdr = fu_struct_pixart_tp_tf_reply_hdr_parse(io_buf, sizeof(io_buf), 0, error); + if (st_hdr == NULL) + return NULL; + + if (fu_struct_pixart_tp_tf_reply_hdr_get_preamble(st_hdr) != + FU_PIXART_TP_TF_FRAME_CONST_PREAMBLE || + fu_struct_pixart_tp_tf_reply_hdr_get_target_addr(st_hdr) != + FU_PIXART_TP_TF_TARGET_ADDR_RMI_FRAME) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "invalid header 0x%02x 0x%02x", + fu_struct_pixart_tp_tf_reply_hdr_get_preamble(st_hdr), + fu_struct_pixart_tp_tf_reply_hdr_get_target_addr(st_hdr)); + return NULL; + } + + /* exception frame? */ + if ((fu_struct_pixart_tp_tf_reply_hdr_get_func(st_hdr) & + FU_PIXART_TP_TF_FRAME_CONST_EXCEPTION_FLAG) != 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "device returned exception 0x%02x", + fu_struct_pixart_tp_tf_reply_hdr_get_func(st_hdr)); + return NULL; + } + + datalen = fu_struct_pixart_tp_tf_reply_hdr_get_datalen(st_hdr); + if (datalen > sizeof(io_buf) - FU_STRUCT_PIXART_TP_TF_REPLY_HDR_SIZE) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "frame exceeds feature report size"); + return NULL; + } + + /* validate crc + tail */ + if (fu_crc8(FU_CRC_KIND_B8_STANDARD, io_buf + 2, datalen + 4) != io_buf[datalen + 6]) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "crc mismatch"); + return NULL; + } + if (io_buf[datalen + 7] != FU_PIXART_TP_TF_FRAME_CONST_TAIL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "tail mismatch"); + return NULL; + } + + /* success */ + if (!fu_byte_array_append_safe(buf, + io_buf, + sizeof(io_buf), + FU_STRUCT_PIXART_TP_TF_REPLY_HDR_SIZE, /* offset */ + datalen, + error)) + return NULL; + return g_steal_pointer(&buf); +} + +static gboolean +fu_pixart_tp_haptic_device_tf_read_firmware_version_cb(FuDevice *device, + gpointer user_data, + GError **error) +{ + FuPixartTpHapticDevice *self = FU_PIXART_TP_HAPTIC_DEVICE(device); + FuPixartTpTfReadFwVersionCtx *ctx = (FuPixartTpTfReadFwVersionCtx *)user_data; + g_autoptr(FuStructPixartTpTfVersionPayload) st = NULL; + g_autoptr(GByteArray) buf = NULL; + + buf = fu_pixart_tp_haptic_device_tf_read_rmi(self, + FU_PIXART_TP_TF_CMD_READ_VERSION, + &ctx->mode, + 1, + FU_STRUCT_PIXART_TP_TF_VERSION_PAYLOAD_SIZE, + error); + if (buf == NULL) + return FALSE; + st = fu_struct_pixart_tp_tf_version_payload_parse(buf->data, buf->len, 0x0, error); + if (st == NULL) + return FALSE; + + ctx->major = fu_struct_pixart_tp_tf_version_payload_get_major(st); + ctx->minor = fu_struct_pixart_tp_tf_version_payload_get_minor(st); + ctx->patch = fu_struct_pixart_tp_tf_version_payload_get_patch(st); + return TRUE; +} + +static gboolean +fu_pixart_tp_haptic_device_tf_ensure_status_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuPixartTpHapticDevice *self = FU_PIXART_TP_HAPTIC_DEVICE(device); + g_autoptr(FuStructPixartTpTfDownloadStatusPayload) st = NULL; + g_autoptr(GByteArray) buf = NULL; + + buf = fu_pixart_tp_haptic_device_tf_read_rmi( + self, + FU_PIXART_TP_TF_CMD_READ_UPGRADE_STATUS, + NULL, + 0, + FU_STRUCT_PIXART_TP_TF_DOWNLOAD_STATUS_PAYLOAD_SIZE, + error); + if (buf == NULL) + return FALSE; + + /* hdr + payload + trailer */ + st = fu_struct_pixart_tp_tf_download_status_payload_parse(buf->data, buf->len, 0x0, error); + if (st == NULL) + return FALSE; + + /* success */ + self->status = fu_struct_pixart_tp_tf_download_status_payload_get_status(st); + self->packet_number = fu_struct_pixart_tp_tf_download_status_payload_get_packet_number(st); + return TRUE; +} + +static gboolean +fu_pixart_tp_haptic_device_tf_write_packet_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuPixartTpHapticDevice *self = FU_PIXART_TP_HAPTIC_DEVICE(device); + FuPixartTpTfWritePacketCtx *ctx = (FuPixartTpTfWritePacketCtx *)user_data; + + return fu_pixart_tp_haptic_device_tf_write_rmi_with_packet( + self, + FU_PIXART_TP_TF_CMD_WRITE_UPGRADE_DATA, + ctx->packet_total, + ctx->packet_index, + ctx->chunk_data, + ctx->chunk_len, + error); +} + +static gboolean +fu_pixart_tp_haptic_device_tf_read_firmware_version(FuPixartTpHapticDevice *self, + FuPixartTpTfFwMode mode, + guint8 *major, + guint8 *minor, + guint8 *patch, + GError **error) +{ + FuPixartTpTfReadFwVersionCtx ctx = {.mode = mode, .major = 0, .minor = 0, .patch = 0}; + + if (!fu_device_retry_full(FU_DEVICE(self), + fu_pixart_tp_haptic_device_tf_read_firmware_version_cb, + FU_PIXART_TP_TF_RETRY_COUNT, + (guint)FU_PIXART_TP_TF_RETRY_INTERVAL_MS, + &ctx, + error)) { + g_prefix_error_literal(error, "failed to read firmware version: "); + return FALSE; + } + + /* success */ + if (major != NULL) + *major = ctx.major; + if (minor != NULL) + *minor = ctx.minor; + if (patch != NULL) + *patch = ctx.patch; + return TRUE; +} + +static gboolean +fu_pixart_tp_haptic_device_tf_ensure_status(FuPixartTpHapticDevice *self, GError **error) +{ + if (!fu_device_retry_full(FU_DEVICE(self), + fu_pixart_tp_haptic_device_tf_ensure_status_cb, + FU_PIXART_TP_TF_RETRY_COUNT, + (guint)FU_PIXART_TP_TF_RETRY_INTERVAL_MS, + NULL, + error)) { + g_prefix_error_literal(error, "failed to read download status: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_haptic_device_tf_exit_upgrade_mode(FuPixartTpHapticDevice *self, GError **error) +{ + return fu_pixart_tp_haptic_device_tf_write_rmi_cmd(self, + FU_PIXART_TP_TF_CMD_SET_UPGRADE_MODE, + FU_PIXART_TP_TF_UPGRADE_MODE_EXIT, + error); +} + +static gboolean +fu_pixart_tp_haptic_device_tf_write_packet(FuPixartTpHapticDevice *self, + FuChunk *chk, + guint num_chunks, + guint32 retry_interval, + GError **error) +{ + FuPixartTpTfWritePacketCtx ctx = { + .packet_total = num_chunks, + .packet_index = fu_chunk_get_idx(chk) + 1, + .chunk_data = fu_chunk_get_data(chk), + .chunk_len = fu_chunk_get_data_sz(chk), + }; + return fu_device_retry_full(FU_DEVICE(self), + fu_pixart_tp_haptic_device_tf_write_packet_cb, + FU_PIXART_TP_TF_RETRY_COUNT, + retry_interval, + &ctx, + error); +} + +static gboolean +fu_pixart_tp_haptic_device_tf_write_packets(FuPixartTpHapticDevice *self, + FuChunkArray *chunks, + guint32 send_interval, + FuProgress *progress, + GError **error) +{ + guint num_chunks; + + /* sanity check */ + num_chunks = fu_chunk_array_length(chunks); + if (num_chunks == 0) { + g_debug("no firmware data to write"); + return TRUE; + } + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, num_chunks); + for (guint i = 0; i < num_chunks; i++) { + g_autoptr(FuChunk) chk = NULL; + + chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) + return FALSE; + if (!fu_pixart_tp_haptic_device_tf_write_packet(self, + chk, + num_chunks, + (send_interval > 0) ? send_interval + : 50, + error)) + return FALSE; + + if (send_interval > 0) + fu_device_sleep(FU_DEVICE(self), send_interval); + fu_progress_step_done(progress); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_haptic_device_tf_write_firmware(FuPixartTpHapticDevice *self, + guint32 send_interval, + GBytes *blob, + FuProgress *progress, + GError **error) +{ + g_autoptr(FuChunkArray) chunks = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "disable-touch"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "enter-bootloader"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 9, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 86, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 0, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "exit-bootloader"); + + /* disabling touch */ + if (!fu_pixart_tp_haptic_device_tf_write_rmi_cmd(self, + FU_PIXART_TP_TF_CMD_TOUCH_CONTROL, + FU_PIXART_TP_TF_TOUCH_CONTROL_DISABLE, + error)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to disable touch"); + return FALSE; + } + fu_progress_step_done(progress); + + /* enter bootloader mode */ + if (!fu_pixart_tp_haptic_device_tf_write_rmi_cmd(self, + FU_PIXART_TP_TF_CMD_SET_UPGRADE_MODE, + FU_PIXART_TP_TF_UPGRADE_MODE_ENTER_BOOT, + error)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to enter bootloader mode"); + return FALSE; + } + fu_device_sleep(FU_DEVICE(self), 100); + fu_progress_step_done(progress); + + /* erase flash */ + if (!fu_pixart_tp_haptic_device_tf_write_rmi_cmd(self, + FU_PIXART_TP_TF_CMD_SET_UPGRADE_MODE, + FU_PIXART_TP_TF_UPGRADE_MODE_ERASE_FLASH, + error)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "failed to send erase flash command"); + return FALSE; + } + fu_device_sleep(FU_DEVICE(self), 2000); + fu_progress_step_done(progress); + + /* write packets */ + chunks = fu_chunk_array_new_from_bytes(blob, + FU_CHUNK_ADDR_OFFSET_NONE, + FU_CHUNK_PAGESZ_NONE, + 32); + if (!fu_pixart_tp_haptic_device_tf_write_packets(self, + chunks, + send_interval, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + + /* verify */ + if (!fu_pixart_tp_haptic_device_tf_ensure_status(self, error)) + return FALSE; + if (self->status != 0 || self->packet_number != fu_chunk_array_length(chunks)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "upgrade failed, status=%u, device_packets=%u, expected_packets=%u", + self->status, + self->packet_number, + fu_chunk_array_length(chunks)); + return FALSE; + } + fu_device_sleep(FU_DEVICE(self), 50); + fu_progress_step_done(progress); + + /* exit upgrade mode */ + if (!fu_pixart_tp_haptic_device_tf_exit_upgrade_mode(self, NULL)) + g_debug("failed to exit upgrade mode (ignored)"); + fu_device_sleep(FU_DEVICE(self), 1000); + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_haptic_device_tf_write_firmware_process(FuPixartTpHapticDevice *self, + guint32 send_interval, + GBytes *blob, + FuProgress *progress, + GError **error) +{ + FuDevice *proxy; + + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + + /* + * Workaround: + * Force the TP run mode to Force Run to prevent the TP from entering sleep during TF + * update, which can cause TF flashing to fail. + * + * Ideally, when the TP is switched to TF_UPDATE proxy mode it should stay awake. However, + * the current firmware cannot be changed, so we keep this as an AP-side workaround. + */ + if (!fu_pixart_tp_device_register_user_write(FU_PIXART_TP_DEVICE(proxy), + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_RUN_MODE, + FU_PIXART_TP_RUN_MODE_FORCE_RUN, + error)) { + return FALSE; + } + + if (!fu_pixart_tp_device_register_user_write(FU_PIXART_TP_DEVICE(proxy), + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_PROXY_MODE, + FU_PIXART_TP_PROXY_MODE_TF_UPDATE, + error)) + return FALSE; + + if (!fu_pixart_tp_haptic_device_tf_exit_upgrade_mode(self, NULL)) + g_debug("failed to exit upgrade mode (ignored)"); + + if (g_bytes_get_size(blob) < 128) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid firmware file: size too small for header check"); + return FALSE; + } + for (guint i = 6; i < 128; i++) { + const guint8 *data = g_bytes_get_data(blob, NULL); + if (data[i] != 0) { + g_set_error_literal( + error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "invalid firmware file, non-zero data in header region"); + return FALSE; + } + } + + return fu_pixart_tp_haptic_device_tf_write_firmware(self, + send_interval, + blob, + progress, + error); +} + +static gboolean +fu_pixart_tp_haptic_device_probe(FuDevice *device, GError **error) +{ + return fu_device_build_instance_id(device, + error, + "HIDRAW", + "VEN", + "DEV", + "COMPONENT", + NULL); +} + +static gboolean +fu_pixart_tp_haptic_device_setup(FuDevice *device, GError **error) +{ + FuPixartTpHapticDevice *self = FU_PIXART_TP_HAPTIC_DEVICE(device); + guint8 major = 0; + guint8 minor = 0; + guint8 patch = 0; + g_autofree gchar *ver_str = NULL; + g_autoptr(GError) error_local = NULL; + g_autoptr(GError) error_version = NULL; + + /* exit TF upgrade/engineer mode (best-effort) */ + if (!fu_pixart_tp_haptic_device_tf_exit_upgrade_mode(self, &error_local)) { + g_debug("haptic: ignoring failure to exit TF upgrade mode in setup: %s", + error_local->message); + } + + fu_device_sleep(FU_DEVICE(self), 1000); + + /* best-effort: if TF is not present or not responding, or respond error code, keep device + * online and need update */ + if (!fu_pixart_tp_haptic_device_tf_read_firmware_version(self, + FU_PIXART_TP_TF_FW_MODE_APP, + &major, + &minor, + &patch, + &error_version)) { + g_debug("failed to read TF firmware version: %s", error_version->message); + fu_device_set_version(device, "0.0.0"); + return TRUE; + } + + /* if TF version is 255.x.x, flash is empty / bootloader state, needs update */ + if (major == 0xFF) { + g_debug("TF in bootloader state (%u.%u.%u)", major, minor, patch); + fu_device_set_version(device, "0.0.0"); + return TRUE; + } + + /* success */ + ver_str = g_strdup_printf("%u.%u.%u", major, minor, patch); + fu_device_set_version(device, ver_str); + return TRUE; +} + +static FuFirmware * +fu_pixart_tp_haptic_device_prepare_firmware(FuDevice *device, + GInputStream *stream, + FuProgress *progress, + FuFirmwareParseFlags flags, + GError **error) +{ + g_autoptr(FuFirmware) firmware = fu_pixart_tp_firmware_new(); + + /* parse the TP FWHD firmware */ + if (!fu_firmware_parse_stream(firmware, stream, 0x0, flags, error)) + return NULL; + + /* find the TF_FORCE section image by ID */ + return fu_firmware_get_image_by_id(firmware, "tf-force", error); +} + +static gboolean +fu_pixart_tp_haptic_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuPixartTpHapticDevice *self = FU_PIXART_TP_HAPTIC_DEVICE(device); + guint32 send_interval = 0; + g_autoptr(GBytes) payload = NULL; + g_autoptr(GByteArray) reserved = NULL; + + /* read send interval from reserved bytes */ + reserved = fu_pixart_tp_section_get_reserved(FU_PIXART_TP_SECTION(firmware)); + if (reserved == NULL || reserved->len < 4) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "reserved bytes too short for TF_FORCE section"); + return FALSE; + } + + /* read TF payload */ + payload = fu_firmware_get_bytes(firmware, error); + if (payload == NULL) + return FALSE; + if (g_bytes_get_size(payload) == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "empty TF_FORCE payload"); + return FALSE; + } + + /* call TF updater */ + send_interval = (guint32)reserved->data[3]; /* ms */ + if (!fu_pixart_tp_haptic_device_tf_write_firmware_process(self, + send_interval, + payload, + progress, + error)) { + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_haptic_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuDevice *proxy; + + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + if (fu_device_has_flag(proxy, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "cannot update TF while TP parent is in bootloader mode; " + "please replug the device or update the TP firmware first"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_pixart_tp_haptic_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 96, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 4, "reload"); +} + +static gboolean +fu_pixart_tp_haptic_device_cleanup(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuPixartTpHapticDevice *self = FU_PIXART_TP_HAPTIC_DEVICE(device); + FuDevice *proxy; + g_autoptr(GError) error_local = NULL; + + /* exit TF upgrade/engineer mode (best-effort) */ + if (!fu_pixart_tp_haptic_device_tf_exit_upgrade_mode(self, &error_local)) { + g_debug("ignoring failure to exit TF upgrade mode in cleanup: %s", + error_local->message); + g_clear_error(&error_local); + } + + /* restore TP proxy mode back to normal (best-effort) */ + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; + if (!fu_pixart_tp_device_register_user_write(FU_PIXART_TP_DEVICE(proxy), + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_PROXY_MODE, + FU_PIXART_TP_PROXY_MODE_NORMAL, + &error_local)) { + g_debug("ignoring failure to restore proxy mode in cleanup: %s", + error_local->message); + return TRUE; + } + + /* restore the TP proxy run mode to normal */ + if (!fu_pixart_tp_device_register_user_write(FU_PIXART_TP_DEVICE(proxy), + FU_PIXART_TP_USER_BANK_BANK0, + FU_PIXART_TP_REG_USER0_RUN_MODE, + FU_PIXART_TP_RUN_MODE_AUTO, + &error_local)) { + g_debug("ignoring failure to restore proxy run mode in cleanup: %s", + error_local->message); + return TRUE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_haptic_device_reload(FuDevice *device, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* best-effort: do not fail the whole update just because reload failed */ + if (!fu_pixart_tp_haptic_device_setup(device, &error_local)) + g_debug("failed to refresh tf firmware version: %s", error_local->message); + + /* success */ + return TRUE; +} + +static void +fu_pixart_tp_haptic_device_class_init(FuPixartTpHapticDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->to_string = fu_pixart_tp_haptic_device_to_string; + device_class->probe = fu_pixart_tp_haptic_device_probe; + device_class->setup = fu_pixart_tp_haptic_device_setup; + device_class->prepare_firmware = fu_pixart_tp_haptic_device_prepare_firmware; + device_class->write_firmware = fu_pixart_tp_haptic_device_write_firmware; + device_class->detach = fu_pixart_tp_haptic_device_detach; + device_class->set_progress = fu_pixart_tp_haptic_device_set_progress; + device_class->cleanup = fu_pixart_tp_haptic_device_cleanup; + device_class->reload = fu_pixart_tp_haptic_device_reload; +} + +static void +fu_pixart_tp_haptic_device_init(FuPixartTpHapticDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "com.pixart.tp.haptic"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FOR_OPEN); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); + fu_device_add_instance_str(FU_DEVICE(self), "COMPONENT", "tf"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_logical_id(FU_DEVICE(self), "tf"); + fu_device_set_name(FU_DEVICE(self), "Touchpad Haptic"); + fu_device_set_summary(FU_DEVICE(self), "Force/haptic controller for touchpad"); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_PIXART_TP_DEVICE); + fu_device_add_icon(FU_DEVICE(self), "input-touchpad"); +} + +FuPixartTpHapticDevice * +fu_pixart_tp_haptic_device_new(FuDevice *proxy) +{ + return g_object_new(FU_TYPE_PIXART_TP_HAPTIC_DEVICE, "proxy", proxy, NULL); +} diff -Nru fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-haptic-device.h fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-haptic-device.h --- fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-haptic-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-haptic-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2025 Harris Tai + * Copyright 2025 Micky Hsieh + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_PIXART_TP_HAPTIC_DEVICE (fu_pixart_tp_haptic_device_get_type()) +G_DECLARE_FINAL_TYPE(FuPixartTpHapticDevice, + fu_pixart_tp_haptic_device, + FU, + PIXART_TP_HAPTIC_DEVICE, + FuDevice) + +FuPixartTpHapticDevice * +fu_pixart_tp_haptic_device_new(FuDevice *proxy) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-plugin.c fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-plugin.c --- fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,50 @@ +/* + * Copyright 2025 Harris Tai + * Copyright 2025 Micky Hsieh + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-pixart-tp-device.h" +#include "fu-pixart-tp-firmware.h" +#include "fu-pixart-tp-haptic-device.h" +#include "fu-pixart-tp-plugin.h" +#include "fu-pixart-tp-section.h" + +struct _FuPixartTpPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuPixartTpPlugin, fu_pixart_tp_plugin, FU_TYPE_PLUGIN) + +static void +fu_pixart_tp_plugin_init(FuPixartTpPlugin *self) +{ +} + +static void +fu_pixart_tp_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + FuContext *ctx = fu_plugin_get_context(plugin); + + fu_context_add_quirk_key(ctx, "PixartTpHidVersionBank"); + fu_context_add_quirk_key(ctx, "PixartTpHidVersionAddr"); + fu_context_add_quirk_key(ctx, "PixartTpSramSelect"); + fu_context_add_quirk_key(ctx, "PixartTpHasHaptic"); + + fu_plugin_add_udev_subsystem(plugin, "hidraw"); + fu_plugin_set_device_gtype_default(plugin, FU_TYPE_PIXART_TP_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_PIXART_TP_HAPTIC_DEVICE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_PIXART_TP_FIRMWARE); + fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_PIXART_TP_SECTION); /* coverage */ +} + +static void +fu_pixart_tp_plugin_class_init(FuPixartTpPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->constructed = fu_pixart_tp_plugin_constructed; +} diff -Nru fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-plugin.h fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-plugin.h --- fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,12 @@ +/* + * Copyright 2025 Harris Tai + * Copyright 2025 Micky Hsieh + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuPixartTpPlugin, fu_pixart_tp_plugin, FU, PIXART_TP_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-section.c fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-section.c --- fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-section.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-section.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,245 @@ +/* + * Copyright 2025 Harris Tai + * Copyright 2025 Micky Hsieh + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-pixart-tp-section.h" +#include "fu-pixart-tp-struct.h" + +struct _FuPixartTpSection { + FuFirmware parent_instance; + FuPixartTpUpdateType update_type; + FuPixartTpFirmwareFlags flags; + guint32 target_flash_start; + guint32 section_crc; + GByteArray *reserved; /* nullable */ +}; + +G_DEFINE_TYPE(FuPixartTpSection, fu_pixart_tp_section, FU_TYPE_FIRMWARE) + +FuPixartTpSection * +fu_pixart_tp_section_new(void) +{ + return g_object_new(FU_TYPE_PIXART_TP_SECTION, NULL); +} + +FuPixartTpUpdateType +fu_pixart_tp_section_get_update_type(FuPixartTpSection *self) +{ + g_return_val_if_fail(FU_IS_PIXART_TP_SECTION(self), 0); + return self->update_type; +} + +gboolean +fu_pixart_tp_section_has_flag(FuPixartTpSection *self, FuPixartTpFirmwareFlags flag) +{ + g_return_val_if_fail(FU_IS_PIXART_TP_SECTION(self), FALSE); + return (self->flags & flag) != 0; +} + +guint32 +fu_pixart_tp_section_get_target_flash_start(FuPixartTpSection *self) +{ + g_return_val_if_fail(FU_IS_PIXART_TP_SECTION(self), 0); + return self->target_flash_start; +} + +guint32 +fu_pixart_tp_section_get_crc(FuPixartTpSection *self) +{ + g_return_val_if_fail(FU_IS_PIXART_TP_SECTION(self), 0); + return self->section_crc; +} + +GByteArray * +fu_pixart_tp_section_get_reserved(FuPixartTpSection *self) +{ + g_return_val_if_fail(FU_IS_PIXART_TP_SECTION(self), NULL); + return g_byte_array_ref(self->reserved); +} + +static void +fu_pixart_tp_section_init(FuPixartTpSection *self) +{ + self->reserved = + g_byte_array_sized_new(FU_STRUCT_PIXART_TP_FIRMWARE_SECTION_HDR_N_ELEMENTS_SHARED); +} + +static void +fu_pixart_tp_section_export(FuFirmware *firmware, FuFirmwareExportFlags flags, XbBuilderNode *bn) +{ + FuPixartTpSection *self = FU_PIXART_TP_SECTION(firmware); + + fu_xmlb_builder_insert_kv(bn, + "update_type", + fu_pixart_tp_update_type_to_string(self->update_type)); + if (self->flags != FU_PIXART_TP_FIRMWARE_FLAG_NONE) { + g_autofree gchar *str = fu_pixart_tp_firmware_flags_to_string(self->flags); + fu_xmlb_builder_insert_kv(bn, "flags", str); + } + fu_xmlb_builder_insert_kx(bn, "target_flash_start", self->target_flash_start); + fu_xmlb_builder_insert_kx(bn, "section_crc", self->section_crc); + if (self->reserved != NULL) { + g_autofree gchar *str = fu_byte_array_to_string(self->reserved); + fu_xmlb_builder_insert_kv(bn, "reserved", str); + } +} + +static gboolean +fu_pixart_tp_section_build(FuFirmware *firmware, XbNode *n, GError **error) +{ + FuPixartTpSection *self = FU_PIXART_TP_SECTION(firmware); + const gchar *tmp; + guint64 tmp64 = 0; + + /* simple properties */ + tmp = xb_node_query_text(n, "update_type", NULL); + if (tmp != NULL) + self->update_type = fu_pixart_tp_update_type_from_string(tmp); + tmp = xb_node_query_text(n, "flags", NULL); + if (tmp != NULL) + self->flags = fu_pixart_tp_firmware_flags_from_string(tmp); + tmp = xb_node_query_text(n, "target_flash_start", NULL); + if (tmp != NULL) { + if (!fu_strtoull(tmp, &tmp64, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->target_flash_start = (guint32)tmp64; + } + tmp = xb_node_query_text(n, "section_crc", NULL); + if (tmp != NULL) { + if (!fu_strtoull(tmp, &tmp64, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + self->section_crc = (guint32)tmp64; + } + tmp = xb_node_query_text(n, "reserved", NULL); + if (tmp != NULL) { + if (self->reserved != NULL) + g_byte_array_unref(self->reserved); + self->reserved = fu_byte_array_from_string(tmp, error); + if (self->reserved == NULL) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_pixart_tp_section_parse(FuFirmware *firmware, + GInputStream *stream, + gsize offset, + FuFirmwareParseFlags flags, + GError **error) +{ + FuPixartTpSection *self = FU_PIXART_TP_SECTION(firmware); + const guint8 *reserved_src = NULL; + gsize reserved_len = 0; + guint32 section_length; + g_autofree gchar *extname = NULL; + g_autoptr(FuStructPixartTpFirmwareSectionHdr) st = NULL; + + g_return_val_if_fail(FU_IS_PIXART_TP_SECTION(self), FALSE); + g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE); + + st = fu_struct_pixart_tp_firmware_section_hdr_parse_stream(stream, offset, error); + if (st == NULL) + return FALSE; + + /* core fields */ + self->update_type = fu_struct_pixart_tp_firmware_section_hdr_get_update_type(st); + self->flags = fu_struct_pixart_tp_firmware_section_hdr_get_update_info(st); + self->target_flash_start = + fu_struct_pixart_tp_firmware_section_hdr_get_target_flash_start(st); + fu_firmware_set_offset( + firmware, + fu_struct_pixart_tp_firmware_section_hdr_get_internal_file_start(st)); + section_length = fu_struct_pixart_tp_firmware_section_hdr_get_section_length(st); + self->section_crc = fu_struct_pixart_tp_firmware_section_hdr_get_section_crc(st); + + /* reserved */ + reserved_src = fu_struct_pixart_tp_firmware_section_hdr_get_shared(st, &reserved_len); + g_byte_array_append(self->reserved, reserved_src, reserved_len); + + /* extname */ + extname = fu_struct_pixart_tp_firmware_section_hdr_get_extname(st); + if (extname != NULL) + fu_firmware_set_filename(FU_FIRMWARE(self), extname); + + /* data */ + if (section_length != 0) { + g_autoptr(GInputStream) partial_stream = NULL; + partial_stream = fu_partial_input_stream_new(stream, + fu_firmware_get_offset(firmware), + section_length, + error); + if (partial_stream == NULL) + return FALSE; + if (!fu_firmware_set_stream(FU_FIRMWARE(self), partial_stream, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static GByteArray * +fu_pixart_tp_section_write(FuFirmware *firmware, GError **error) +{ + FuPixartTpSection *self = FU_PIXART_TP_SECTION(firmware); + g_autoptr(FuStructPixartTpFirmwareSectionHdr) st = + fu_struct_pixart_tp_firmware_section_hdr_new(); + + fu_struct_pixart_tp_firmware_section_hdr_set_update_type(st, self->update_type); + fu_struct_pixart_tp_firmware_section_hdr_set_update_info(st, self->flags); + fu_struct_pixart_tp_firmware_section_hdr_set_target_flash_start(st, + self->target_flash_start); + fu_struct_pixart_tp_firmware_section_hdr_set_internal_file_start( + st, + fu_firmware_get_offset(firmware)); + fu_struct_pixart_tp_firmware_section_hdr_set_section_length(st, + fu_firmware_get_size(firmware)); + fu_struct_pixart_tp_firmware_section_hdr_set_section_crc(st, self->section_crc); + if (self->reserved != NULL) { + if (!fu_struct_pixart_tp_firmware_section_hdr_set_shared(st, + self->reserved->data, + self->reserved->len, + error)) + return NULL; + } + if (fu_firmware_get_filename(firmware) != NULL) { + if (!fu_struct_pixart_tp_firmware_section_hdr_set_extname( + st, + fu_firmware_get_filename(firmware), + error)) + return NULL; + } + + /* success */ + return g_steal_pointer(&st->buf); +} + +static void +fu_pixart_tp_section_finalize(GObject *obj) +{ + FuPixartTpSection *self = FU_PIXART_TP_SECTION(obj); + if (self->reserved != NULL) + g_byte_array_unref(self->reserved); + G_OBJECT_CLASS(fu_pixart_tp_section_parent_class)->finalize(obj); +} + +static void +fu_pixart_tp_section_class_init(FuPixartTpSectionClass *klass) +{ + FuFirmwareClass *firmware_class = FU_FIRMWARE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + firmware_class->export = fu_pixart_tp_section_export; + firmware_class->parse_full = fu_pixart_tp_section_parse; + firmware_class->build = fu_pixart_tp_section_build; + firmware_class->write = fu_pixart_tp_section_write; + object_class->finalize = fu_pixart_tp_section_finalize; +} diff -Nru fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-section.h fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-section.h --- fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp-section.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp-section.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,32 @@ +/* + * Copyright 2025 Harris Tai + * Copyright 2025 Micky Hsieh + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "config.h" + +#include + +#include "fu-pixart-tp-struct.h" + +#define FU_TYPE_PIXART_TP_SECTION (fu_pixart_tp_section_get_type()) +G_DECLARE_FINAL_TYPE(FuPixartTpSection, fu_pixart_tp_section, FU, PIXART_TP_SECTION, FuFirmware) + +FuPixartTpSection * +fu_pixart_tp_section_new(void) G_GNUC_WARN_UNUSED_RESULT; + +FuPixartTpUpdateType +fu_pixart_tp_section_get_update_type(FuPixartTpSection *self) G_GNUC_NON_NULL(1); +gboolean +fu_pixart_tp_section_has_flag(FuPixartTpSection *self, FuPixartTpFirmwareFlags flag) + G_GNUC_NON_NULL(1); +guint32 +fu_pixart_tp_section_get_target_flash_start(FuPixartTpSection *self) G_GNUC_NON_NULL(1); +guint32 +fu_pixart_tp_section_get_crc(FuPixartTpSection *self) G_GNUC_NON_NULL(1); +GByteArray * +fu_pixart_tp_section_get_reserved(FuPixartTpSection *self) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp.rs fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp.rs --- fwupd-2.0.8/plugins/pixart-tp/fu-pixart-tp.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/fu-pixart-tp.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,327 @@ +// Copyright 2025 Harris Tai +// Copyright 2025 Micky Hsieh +// SPDX-License-Identifier: LGPL-2.1-or-later + +// bank ids +#[repr(u8)] +enum FuPixartTpSystemBank { + Bank0 = 0x00, + Bank1 = 0x01, + Bank2 = 0x02, + Bank4 = 0x04, + Bank6 = 0x06, +} + +#[repr(u8)] +enum FuPixartTpUserBank { + Bank0 = 0x00, + Bank1 = 0x01, + Bank2 = 0x02, +} + +// system bank 4 (flash engine registers) +#[repr(u8)] +enum FuPixartTpRegSys4 { + FlashStatus = 0x1c, + SwapFlag = 0x29, + FlashInstCmd = 0x2c, + FlashBufAddr0 = 0x2e, + FlashBufAddr1 = 0x2f, + FlashCcr0 = 0x40, + FlashCcr1 = 0x41, + FlashCcr2 = 0x42, + FlashCcr3 = 0x43, + FlashDataCnt0 = 0x44, + FlashDataCnt1 = 0x45, + FlashAddr0 = 0x48, + FlashAddr1 = 0x49, + FlashAddr2 = 0x4a, + FlashAddr3 = 0x4b, + FlashExecute = 0x56, +} + +// system bank 6 (sram buffer) +#[repr(u8)] +enum FuPixartTpRegSys6 { + SramGainSelect = 0x08, + SramSelect = 0x09, + SramTrigger = 0x0a, + SramData = 0x0b, + SramChecksum = 0x0c, + SramAddr0 = 0x10, + SramAddr1 = 0x11, +} + +// system bank 1 (reset control registers) +#[repr(u8)] +enum FuPixartTpRegSys1 { + ClocksPowerUp = 0x0d, + ResetKey1 = 0x2c, + ResetKey2 = 0x2d, +} + +// user bank 0 (part id + crc registers) +#[repr(u8)] +enum FuPixartTpRegUser0 { + BootStaus = 0x00, + RunMode = 0x16, + ProxyMode = 0x56, + PartId0 = 0x78, + PartId1 = 0x79, + CrcCtrl = 0x82, + CrcResult0 = 0x84, + CrcResult1 = 0x85, + CrcResult2 = 0x86, + CrcResult3 = 0x87, +} + +#[repr(u8)] +enum FuPixartTpBootStatus{ + Rom = 0x8c, +} + +#[repr(u8)] +enum FuPixartTpRunMode { + Auto = 0x00, + ForceRun = 0x01, +} + +#[repr(u8)] +enum FuPixartTpResetMode { + Application, + Bootloader, +} + +#[repr(u8)] +enum FuPixartTpResetKey1 { + Suspend = 0xaa, +} + +#[repr(u8)] +enum FuPixartTpResetKey2 { + Regular = 0xbb, + Bootloader = 0xcc, +} + +#[repr(u8)] +enum FuPixartTpFlashInst { + None = 0, + Rd2RegBank = 1 << 0, + Program = 1 << 2, + InternalSramAccess = 1 << 7, +} + +#[repr(u8)] +enum FuPixartTpClocksPowerUp { + Cpu = 1 << 1, +} + +#[repr(u8)] +enum FuPixartTpFlashExecState { + Busy = 0x01, + Success = 0x00, +} + +#[repr(u8)] +enum FuPixartTpFlashWriteEnable { + Success = 0x02, +} + +#[repr(u8)] +enum FuPixartTpFlashStatus { + Busy = 0x01, +} + +#[repr(u32)] +enum FuPixartTpFlashCcr { + WriteEnable = 0x0000_0106, + ReadStatus = 0x0100_0105, + EraseSector = 0x0000_2520, + ProgramPage = 0x0100_2502, +} + +#[repr(u16)] +enum FuPixartTpPartId { + Pjp274 = 0x0274, +} + +#[repr(u8)] +enum FuPixartTpCrcCtrl { + FwBank0 = 0x02, + FwBank1 = 0x10, + ParamBank0 = 0x04, + ParamBank1 = 0x20, + Busy = 0x01, +} + +// host <-> tf pass-through proxy mode +#[repr(u8)] +enum FuPixartTpProxyMode { + Normal = 0x00, + TfUpdate = 0x01, +} + +// tf feature report / rmi frame constants +#[repr(u8)] +enum FuPixartTpTfFrameConst { + Preamble = 0x5a, + Tail = 0xa5, + ExceptionFlag = 0x80, +} + +// tf hid report ids +#[repr(u8)] +enum FuPixartTpTfReportId { + PassThrough = 0xcc, +} + +// tf rmi target address +#[repr(u8)] +enum FuPixartTpTfTargetAddr { + RmiFrame = 0x2c, +} + +// tf rmi function codes (func field) +#[repr(u8)] +enum FuPixartTpTfRmiFunc { + WriteSimple = 0x00, + WritePacket = 0x04, + Read = 0x0b, +} + +// tf function command ids (high-level tf commands) +enum FuPixartTpTfCmd { + SetUpgradeMode = 0x0001, + WriteUpgradeData = 0x0002, + ReadUpgradeStatus = 0x0003, + ReadVersion = 0x0007, + TouchControl = 0x0303, +} + +// tf upgrade mode payload +enum FuPixartTpTfUpgradeMode { + Exit = 0x00, + EnterBoot = 0x01, + EraseFlash = 0x02, +} + +// tf touch control +enum FuPixartTpTfTouchControl { + Enable = 0x00, + Disable = 0x01, +} + +// tf firmware version mode +enum FuPixartTpTfFwMode { + App = 1, + Boot = 2, + Algo = 3, +} + +// tf payload structs +#[derive(Parse, Getters)] +#[repr(C, packed)] +struct FuStructPixartTpTfVersionPayload { + major: u8, + minor: u8, + patch: u8, +} + +#[derive(Parse, Getters)] +#[repr(C, packed)] +struct FuStructPixartTpTfDownloadStatusPayload { + status: u8, + packet_number: u16le, +} + +// tf command / reply structs + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructPixartTpTfWriteSimpleCmd { + report_id: FuPixartTpTfReportId = PassThrough, + preamble: FuPixartTpTfFrameConst = Preamble, + target_addr: FuPixartTpTfTargetAddr = RmiFrame, + func: FuPixartTpTfRmiFunc = WriteSimple, + addr: u16le, + len: u16le, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructPixartTpTfWritePacketCmd { + report_id: FuPixartTpTfReportId = PassThrough, + preamble: FuPixartTpTfFrameConst = Preamble, + target_addr: FuPixartTpTfTargetAddr = RmiFrame, + func: FuPixartTpTfRmiFunc = WritePacket, + addr: u16le, + datalen: u16le, + packet_total: u16le, + packet_index: u16le, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructPixartTpTfReadCmd { + report_id: FuPixartTpTfReportId = PassThrough, + preamble: FuPixartTpTfFrameConst = Preamble, + target_addr: FuPixartTpTfTargetAddr = RmiFrame, + func: FuPixartTpTfRmiFunc = Read, + addr: u16le, + datalen: u16le, + reply_len: u16le, +} + +#[derive(Parse, Getters)] +#[repr(C, packed)] +struct FuStructPixartTpTfReplyHdr { + report_id: FuPixartTpTfReportId, + preamble: FuPixartTpTfFrameConst, + target_addr: FuPixartTpTfTargetAddr, + func: FuPixartTpTfRmiFunc, + datalen: u16le, +} + +#[derive(ToString, FromString)] +#[repr(u8)] +enum FuPixartTpUpdateType { + General = 0, + FwSection = 1, + Bootloader = 2, + Param = 3, + TfForce = 16, +} + +#[derive(ToString, FromString)] +#[repr(u32)] +enum FuPixartTpFirmwareFlags { + None = 0, + Valid = 1 << 0, + IsExternal = 1 << 1, +} + +#[derive(ParseStream, ValidateStream, New, Default)] +#[repr(C, packed)] +struct FuStructPixartTpFirmwareHdr { + magic: [char; 4] == "FWHD", + header_len: u16le == 0x0218, // for v1.0 + header_ver: u16le, + file_ver: u16le, + ic_part_id: u16le, + flash_sectors: u16le, + file_crc32: u32le, + num_sections: u16le, +} + +#[derive(ParseStream, New)] +#[repr(C, packed)] +struct FuStructPixartTpFirmwareSectionHdr { + update_type: FuPixartTpUpdateType, + update_info: u8, + target_flash_start: u32le, + internal_file_start: u32le, + section_length: u32le, + section_crc: u32le, + shared: [u8; 12], + extname: [char; 34], +} diff -Nru fwupd-2.0.8/plugins/pixart-tp/fu-self-test.c fwupd-2.0.20/plugins/pixart-tp/fu-self-test.c --- fwupd-2.0.8/plugins/pixart-tp/fu-self-test.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,58 @@ +/* + * Copyright 2025 Harris Tai + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-pixart-tp-firmware.h" + +static void +fu_pixart_tp_firmware_xml_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = fu_pixart_tp_firmware_new(); + g_autoptr(FuFirmware) firmware2 = fu_pixart_tp_firmware_new(); + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = g_test_build_filename(G_TEST_DIST, "tests", "pixart-tp.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "a9ed17b970a867c190f62be59338dbad89d07553"); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + +int +main(int argc, char **argv) +{ + (void)g_setenv("G_TEST_SRCDIR", SRCDIR, FALSE); + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + + /* tests go here */ + g_test_add_func("/pixart-tp/firmware{xml}", fu_pixart_tp_firmware_xml_func); + return g_test_run(); +} diff -Nru fwupd-2.0.8/plugins/pixart-tp/meson.build fwupd-2.0.20/plugins/pixart-tp/meson.build --- fwupd-2.0.8/plugins/pixart-tp/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,57 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginPixartTp"'] + +plugins += {meson.current_source_dir().split('/')[-1]: true} +plugin_quirks += files('pixart-tp.quirk') + +plugin_builtin_pixart_tp = static_library('fu_plugin_pixart_tp', + rustgen.process('fu-pixart-tp.rs'), + sources: [ + 'fu-pixart-tp-haptic-device.c', + 'fu-pixart-tp-device.c', + 'fu-pixart-tp-section.c', + 'fu-pixart-tp-firmware.c', + 'fu-pixart-tp-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) +plugin_builtins += plugin_builtin_pixart_tp + +device_tests += files('tests/pixart-tp.json') + +if get_option('tests') + install_data( + ['tests/pixart-tp.builder.xml'], + install_dir: join_paths(installed_test_datadir, 'tests'), + install_tag: 'tests', + ) + + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + + e = executable( + 'pixart-tp-self-test', + rustgen.process('fu-pixart-tp.rs'), + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: plugin_deps, + link_with: [ + plugin_libs, + plugin_builtin_pixart_tp, + ], + install: true, + install_rpath: libdir_pkg, + install_dir: installed_test_bindir, + install_tag: 'tests', + c_args: [ + '-DSRCDIR="' + meson.current_source_dir() + '"', + ], + ) + + test('pixart-tp-self-test', e, env: env) +endif diff -Nru fwupd-2.0.8/plugins/pixart-tp/pixart-tp.quirk fwupd-2.0.20/plugins/pixart-tp/pixart-tp.quirk --- fwupd-2.0.8/plugins/pixart-tp/pixart-tp.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/pixart-tp.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,14 @@ +[HIDRAW\VEN_093A&DEV_0274] +Plugin = pixart_tp +PixartTpSramSelect = 0x08 + +[HIDRAW\VEN_093A&DEV_0343] +Plugin = pixart_tp + +[HIDRAW\VEN_093A&DEV_3307] +Plugin = pixart_tp +PixartTpSramSelect = 0x08 + +[HIDRAW\VEN_093A&DEV_4F07] +Plugin = pixart_tp +PixartTpHasHaptic = true diff -Nru fwupd-2.0.8/plugins/pixart-tp/tests/pixart-tp.builder.xml fwupd-2.0.20/plugins/pixart-tp/tests/pixart-tp.builder.xml --- fwupd-2.0.8/plugins/pixart-tp/tests/pixart-tp.builder.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/tests/pixart-tp.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,31 @@ + + 0xff02 + 0x103 + 0x343 + 0x20 + + tf-force + aGVsbG8gd29ybGQ= + tf.bin + tf-force + valid + 01090e0a0000000000000000 + + + parameter + aGVsbG8gd29ybGQ= + param + valid + 0x1e000 + 0xb887bc55 + 000000000000000000000000 + + + payload + aGVsbG8gd29ybGQ= + fw-section + valid + 0xda33306b + 000000000000000000000000 + + diff -Nru fwupd-2.0.8/plugins/pixart-tp/tests/pixart-tp.json fwupd-2.0.20/plugins/pixart-tp/tests/pixart-tp.json --- fwupd-2.0.8/plugins/pixart-tp/tests/pixart-tp.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/pixart-tp/tests/pixart-tp.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +{ + "name": "PixArt POCO 4802 Touchpad", + "interactive": false, + "steps": [ + { + "url": "f4cfe7e15d7a33e66e8357c9fb924633bc85ef411ff9d4c789fb67478461d3b9-firmware.cab", + "emulation-url": "b8c9243c0a5dc14f36a56874564c57d0515168bb9da59dab7b1e957db36b606f-pxi-tp-update.zip", + "components": [ + { + "version": "0xff02", + "guids": [ + "86407e75-7d7f-5d09-97db-700776490493" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/powerd/fu-powerd-plugin.c fwupd-2.0.20/plugins/powerd/fu-powerd-plugin.c --- fwupd-2.0.8/plugins/powerd/fu-powerd-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/powerd/fu-powerd-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -36,15 +36,14 @@ static gboolean fu_powerd_plugin_create_suspend_file(GError **error) { - g_autofree gchar *lockdir = NULL; g_autofree gchar *inhibitsuspend_filename = NULL; g_autofree gchar *getpid_str = NULL; - lockdir = fu_path_from_kind(FU_PATH_KIND_LOCKDIR); - inhibitsuspend_filename = g_build_filename(lockdir, "power_override", "fwupd.lock", NULL); + inhibitsuspend_filename = + fu_path_build(FU_PATH_KIND_LOCKDIR, "power_override", "fwupd.lock", NULL); getpid_str = g_strdup_printf("%d", getpid()); if (!g_file_set_contents(inhibitsuspend_filename, getpid_str, -1, error)) { - g_prefix_error(error, "lock file unable to be created: "); + g_prefix_error_literal(error, "lock file unable to be created: "); return FALSE; } return TRUE; @@ -53,17 +52,17 @@ static gboolean fu_powerd_plugin_delete_suspend_file(GError **error) { - g_autoptr(GError) local_error = NULL; + g_autoptr(GError) error_local = NULL; g_autofree gchar *lockdir = NULL; g_autoptr(GFile) inhibitsuspend_file = NULL; lockdir = fu_path_from_kind(FU_PATH_KIND_LOCKDIR); inhibitsuspend_file = g_file_new_build_filename(lockdir, "power_override", "fwupd.lock", NULL); - if (!g_file_delete(inhibitsuspend_file, NULL, &local_error) && - !g_error_matches(local_error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { + if (!g_file_delete(inhibitsuspend_file, NULL, &error_local) && + !g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND)) { g_propagate_prefixed_error(error, - g_steal_pointer(&local_error), + g_steal_pointer(&error_local), "lock file unable to be deleted"); return FALSE; } @@ -88,38 +87,15 @@ /* plugged in */ if (power_type == FU_POWERD_EXTERNAL_POWER_AC || power_type == FU_POWERD_EXTERNAL_POWER_USB) { - switch (current_state) { - case FU_POWERD_BATTERY_STATE_CHARGING: - fu_context_set_power_state(ctx, FU_POWER_STATE_AC_CHARGING); - break; - case FU_POWERD_BATTERY_STATE_FULLY_CHARGED: - fu_context_set_power_state(ctx, FU_POWER_STATE_AC_FULLY_CHARGED); - break; - default: - fu_context_set_power_state(ctx, FU_POWER_STATE_AC); - break; - } + fu_context_set_power_state(ctx, FU_POWER_STATE_AC); return; } - /* fallback */ - switch (current_state) { - case FU_POWERD_BATTERY_STATE_CHARGING: - fu_context_set_power_state(ctx, FU_POWER_STATE_AC_CHARGING); - break; - case FU_POWERD_BATTERY_STATE_DISCHARGING: - fu_context_set_power_state(ctx, FU_POWER_STATE_BATTERY_DISCHARGING); - break; - case FU_POWERD_BATTERY_STATE_EMPTY: - fu_context_set_power_state(ctx, FU_POWER_STATE_BATTERY_EMPTY); - break; - case FU_POWERD_BATTERY_STATE_FULLY_CHARGED: - fu_context_set_power_state(ctx, FU_POWER_STATE_AC_FULLY_CHARGED); - break; - default: - fu_context_set_power_state(ctx, FU_POWER_STATE_UNKNOWN); - break; - } + if (current_state == FU_POWERD_BATTERY_STATE_FULLY_CHARGED || + current_state == FU_POWERD_BATTERY_STATE_CHARGING) + fu_context_set_power_state(ctx, FU_POWER_STATE_AC); + else + fu_context_set_power_state(ctx, FU_POWER_STATE_BATTERY); } static void @@ -154,7 +130,7 @@ error); if (self->proxy == NULL) { - g_prefix_error(error, "failed to connect to powerd: "); + g_prefix_error_literal(error, "failed to connect to powerd: "); return FALSE; } name_owner = g_dbus_proxy_get_name_owner(self->proxy); @@ -185,21 +161,13 @@ } static gboolean -fu_powerd_plugin_prepare(FuPlugin *plugin, - FuDevice *device, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) +fu_powerd_plugin_composite_prepare(FuPlugin *plugin, GPtrArray *devices, GError **error) { return fu_powerd_plugin_create_suspend_file(error); } static gboolean -fu_powerd_plugin_cleanup(FuPlugin *plugin, - FuDevice *device, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) +fu_powerd_plugin_composite_cleanup(FuPlugin *plugin, GPtrArray *devices, GError **error) { return fu_powerd_plugin_delete_suspend_file(error); } @@ -226,6 +194,6 @@ object_class->finalize = fu_powerd_plugin_finalize; plugin_class->startup = fu_powerd_plugin_startup; - plugin_class->cleanup = fu_powerd_plugin_cleanup; - plugin_class->prepare = fu_powerd_plugin_prepare; + plugin_class->composite_cleanup = fu_powerd_plugin_composite_cleanup; + plugin_class->composite_prepare = fu_powerd_plugin_composite_prepare; } diff -Nru fwupd-2.0.8/plugins/qc-firehose/README.md fwupd-2.0.20/plugins/qc-firehose/README.md --- fwupd-2.0.8/plugins/qc-firehose/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-firehose/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -71,10 +71,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.7`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Richard Hughes: @hughsie diff -Nru fwupd-2.0.8/plugins/qc-firehose/fu-qc-firehose-impl-common.c fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-impl-common.c --- fwupd-2.0.8/plugins/qc-firehose/fu-qc-firehose-impl-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-impl-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -48,7 +48,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "retry limit %u reached: ", + "retry limit %u reached", retry_cnt); return FALSE; } diff -Nru fwupd-2.0.8/plugins/qc-firehose/fu-qc-firehose-impl.c fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-impl.c --- fwupd-2.0.8/plugins/qc-firehose/fu-qc-firehose-impl.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-impl.c 2026-02-26 11:36:18.000000000 +0000 @@ -273,8 +273,9 @@ G_MAXUINT64, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, - "failed to parse MaxPayloadSizeToTargetInBytes:"); + g_prefix_error_literal( + error, + "failed to parse MaxPayloadSizeToTargetInBytes: "); return FALSE; } g_debug("max payload size now 0x%x", (guint)helper->max_payload_size); @@ -447,12 +448,25 @@ { g_autoptr(XbBuilderNode) bn = xb_builder_node_new("data"); g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, xb_node_get_element(xn), NULL); +#if LIBXMLB_CHECK_VERSION(0, 3, 24) + XbNodeAttrIter xn_iter; + const gchar *attr_name = NULL; + const gchar *attr_value = NULL; +#else const gchar *names[] = { "PAGES_PER_BLOCK", "SECTOR_SIZE_IN_BYTES", "num_partition_sectors", + "physical_partition_number", "start_sector", + /* these are Quectel-specific */ + "a_rawdata_start_sector", + "b_rawdata_num_sector", + "c_project_name", + "d_project_type", + "vendor", }; +#endif /* sanity check */ if (!fu_qc_firehose_impl_has_function(self, FU_QC_FIREHOSE_FUNCTIONS_ERASE)) { @@ -462,11 +476,20 @@ "erase is not supported"); return FALSE; } + +#if LIBXMLB_CHECK_VERSION(0, 3, 24) + /* copy over all attributes */ + xb_node_attr_iter_init(&xn_iter, xn); + while (xb_node_attr_iter_next(&xn_iter, &attr_name, &attr_value)) + xb_builder_node_set_attr(bc, attr_name, attr_value); +#else + /* copy over all *known* attributes */ for (guint i = 0; i < G_N_ELEMENTS(names); i++) { const gchar *value = xb_node_get_attr(xn, names[i]); if (value != NULL) xb_builder_node_set_attr(bc, names[i], value); } +#endif if (!fu_qc_firehose_impl_write_xml(self, bn, error)) return FALSE; return fu_qc_firehose_impl_read_xml(self, 30000, helper, error); @@ -537,6 +560,11 @@ g_autoptr(GBytes) blob_padded = NULL; g_autoptr(XbBuilderNode) bn = xb_builder_node_new("data"); g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, xb_node_get_element(xn), NULL); +#if LIBXMLB_CHECK_VERSION(0, 3, 24) + XbNodeAttrIter xn_iter; + const gchar *attr_name = NULL; + const gchar *attr_value = NULL; +#else const gchar *names[] = { "PAGES_PER_BLOCK", "SECTOR_SIZE_IN_BYTES", @@ -546,6 +574,7 @@ "start_sector", "last_sector", }; +#endif /* sanity check */ if (!fu_qc_firehose_impl_has_function(self, FU_QC_FIREHOSE_FUNCTIONS_PROGRAM)) { @@ -564,16 +593,23 @@ if (blob == NULL) return FALSE; - /* copy across */ +#if LIBXMLB_CHECK_VERSION(0, 3, 24) + /* copy over all attributes */ + xb_node_attr_iter_init(&xn_iter, xn); + while (xb_node_attr_iter_next(&xn_iter, &attr_name, &attr_value)) + xb_builder_node_set_attr(bc, attr_name, attr_value); +#else + /* copy over all *known* attributes */ for (guint i = 0; i < G_N_ELEMENTS(names); i++) { const gchar *value = xb_node_get_attr(xn, names[i]); if (value != NULL) xb_builder_node_set_attr(bc, names[i], value); } +#endif if (!fu_qc_firehose_impl_write_xml(self, bn, error)) return FALSE; if (!fu_qc_firehose_impl_read_xml(self, 2500, helper, error)) { - g_prefix_error(error, "failed to setup: "); + g_prefix_error_literal(error, "failed to setup: "); return FALSE; } @@ -629,6 +665,11 @@ { g_autoptr(XbBuilderNode) bn = xb_builder_node_new("data"); g_autoptr(XbBuilderNode) bc = xb_builder_node_insert(bn, xb_node_get_element(xn), NULL); +#if LIBXMLB_CHECK_VERSION(0, 3, 24) + XbNodeAttrIter xn_iter; + const gchar *attr_name = NULL; + const gchar *attr_value = NULL; +#else const gchar *names[] = { "SECTOR_SIZE_IN_BYTES", "byte_offset", @@ -638,6 +679,7 @@ "start_sector", "value", }; +#endif /* sanity check */ if (!fu_qc_firehose_impl_has_function(self, FU_QC_FIREHOSE_FUNCTIONS_PATCH)) { @@ -647,11 +689,20 @@ "patch is not supported"); return FALSE; } + +#if LIBXMLB_CHECK_VERSION(0, 3, 24) + /* copy over all attributes */ + xb_node_attr_iter_init(&xn_iter, xn); + while (xb_node_attr_iter_next(&xn_iter, &attr_name, &attr_value)) + xb_builder_node_set_attr(bc, attr_name, attr_value); +#else + /* copy over all *known* attributes */ for (guint i = 0; i < G_N_ELEMENTS(names); i++) { const gchar *value = xb_node_get_attr(xn, names[i]); if (value != NULL) xb_builder_node_set_attr(bc, names[i], value); } +#endif if (!fu_qc_firehose_impl_write_xml(self, bn, error)) return FALSE; return fu_qc_firehose_impl_read_xml(self, 5000, helper, error); @@ -685,7 +736,7 @@ g_autoptr(XbBuilderNode) bn = xb_builder_node_new("data"); FuQcFirehoseImplHelper helper = {0x0}; - /* */ xb_builder_node_insert_text(bn, "power", NULL, "value", "reset", NULL); if (!fu_qc_firehose_impl_write_xml(self, bn, error)) return FALSE; @@ -830,7 +881,7 @@ if (!fu_qc_firehose_impl_has_function(self, FU_QC_FIREHOSE_FUNCTIONS_CONFIGURE)) { helper.read_func = fu_qc_firehose_impl_read_xml_nop_cb; if (!fu_qc_firehose_impl_send_nop(self, &helper, error)) { - g_prefix_error(error, "failed to send NOP: "); + g_prefix_error_literal(error, "failed to send NOP: "); return FALSE; } } @@ -863,10 +914,9 @@ /* progress */ fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 20, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 80, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 4, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 94, NULL); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "patch"); /* load XML */ @@ -890,7 +940,7 @@ /* hardcode storage */ if (!fu_qc_firehose_impl_configure(self, "nand", &helper, error)) { - g_prefix_error(error, "failed to configure: "); + g_prefix_error_literal(error, "failed to configure: "); return FALSE; } fu_progress_step_done(progress); @@ -903,7 +953,7 @@ &helper, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to erase targets: "); + g_prefix_error_literal(error, "failed to erase targets: "); return FALSE; } } @@ -917,7 +967,7 @@ &helper, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to program targets: "); + g_prefix_error_literal(error, "failed to program targets: "); return FALSE; } } @@ -931,7 +981,7 @@ &helper, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to patch targets: "); + g_prefix_error_literal(error, "failed to patch targets: "); return FALSE; } } @@ -948,7 +998,7 @@ (guint)bootable, &helper, error)) { - g_prefix_error(error, "failed to set bootable: "); + g_prefix_error_literal(error, "failed to set bootable: "); return FALSE; } } diff -Nru fwupd-2.0.8/plugins/qc-firehose/fu-qc-firehose-raw-device.c fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-raw-device.c --- fwupd-2.0.8/plugins/qc-firehose/fu-qc-firehose-raw-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-raw-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -58,7 +58,7 @@ FuQcFirehoseRawDevice *self = FU_QC_FIREHOSE_RAW_DEVICE(device); if (self->supported_functions == FU_QC_FIREHOSE_FUNCTIONS_NONE) { if (!fu_qc_firehose_impl_setup(FU_QC_FIREHOSE_IMPL(self), error)) { - g_prefix_error(error, "failed to setup before write: "); + g_prefix_error_literal(error, "failed to setup before write: "); return FALSE; } } @@ -162,7 +162,7 @@ timeout_ms, FU_IO_CHANNEL_FLAG_SINGLE_SHOT, error)) { - g_prefix_error(error, "failed to do bulk transfer (read): "); + g_prefix_error_literal(error, "failed to do bulk transfer (read): "); return NULL; } @@ -209,7 +209,7 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_ARCHIVE_FIRMWARE); - fu_device_set_remove_delay(FU_DEVICE(self), 60000); + fu_device_set_remove_delay(FU_DEVICE(self), 90000); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); } diff -Nru fwupd-2.0.8/plugins/qc-firehose/fu-qc-firehose-sahara-impl.c fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-sahara-impl.c --- fwupd-2.0.8/plugins/qc-firehose/fu-qc-firehose-sahara-impl.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-sahara-impl.c 2026-02-26 11:36:18.000000000 +0000 @@ -72,7 +72,7 @@ return FALSE; fu_qc_firehose_sahara_pkt_hello_resp_set_mode(st_resp, fu_qc_firehose_sahara_pkt_hello_get_mode(st)); - return fu_qc_firehose_sahara_impl_write(self, st_resp->data, st_resp->len, error); + return fu_qc_firehose_sahara_impl_write(self, st_resp->buf->data, st_resp->buf->len, error); } static gboolean @@ -93,7 +93,7 @@ fu_qc_firehose_sahara_pkt_read_get_length(st), error); if (blob_chunk == NULL) { - g_prefix_error(error, "failed to get bootloader chunk: "); + g_prefix_error_literal(error, "failed to get bootloader chunk: "); return FALSE; } return fu_qc_firehose_sahara_impl_write(self, @@ -120,7 +120,7 @@ fu_qc_firehose_sahara_pkt_read64_get_length(st), error); if (blob_chunk == NULL) { - g_prefix_error(error, "failed to get bootloader chunk: "); + g_prefix_error_literal(error, "failed to get bootloader chunk: "); return FALSE; } return fu_qc_firehose_sahara_impl_write(self, @@ -150,7 +150,7 @@ fu_qc_firehose_sahara_status_to_string(status)); return FALSE; } - return fu_qc_firehose_sahara_impl_write(self, st_resp->data, st_resp->len, error); + return fu_qc_firehose_sahara_impl_write(self, st_resp->buf->data, st_resp->buf->len, error); } static gboolean @@ -198,7 +198,7 @@ buf = fu_qc_firehose_sahara_impl_read(self, error); if (buf == NULL) { - g_prefix_error(error, "failed to get device response: "); + g_prefix_error_literal(error, "failed to get device response: "); return FALSE; } diff -Nru fwupd-2.0.8/plugins/qc-firehose/fu-qc-firehose-usb-device.c fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-usb-device.c --- fwupd-2.0.8/plugins/qc-firehose/fu-qc-firehose-usb-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose-usb-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -80,7 +80,7 @@ timeout_ms, NULL, error)) { - g_prefix_error(error, "failed to do bulk transfer (read): "); + g_prefix_error_literal(error, "failed to do bulk transfer (read): "); return NULL; } @@ -100,6 +100,15 @@ g_autoptr(GPtrArray) chunks = NULL; g_autoptr(GByteArray) bufmut = g_byte_array_sized_new(sz); + /* sanity check */ + if (self->maxpktsize_out == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no max packet size defined"); + return FALSE; + } + /* copy const data to mutable GByteArray */ g_byte_array_append(bufmut, buf, sz); chunks = fu_chunk_array_mutable_new(bufmut->data, bufmut->len, 0, 0, self->maxpktsize_out); @@ -120,15 +129,16 @@ timeout_ms, NULL, error)) { - g_prefix_error(error, "failed to do bulk transfer (write data): "); + g_prefix_error_literal(error, "failed to do bulk transfer (write data): "); return FALSE; } if (actual_len != fu_chunk_get_data_sz(chk)) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "only wrote %" G_GSIZE_FORMAT "bytes", - actual_len); + "only wrote 0x%x of 0x%x bytes", + (guint)actual_len, + (guint)fu_chunk_get_data_sz(chk)); return FALSE; } } @@ -144,7 +154,7 @@ timeout_ms, NULL, error)) { - g_prefix_error(error, "failed to do bulk transfer (write zlp): "); + g_prefix_error_literal(error, "failed to do bulk transfer (write zlp): "); return FALSE; } } @@ -263,12 +273,14 @@ static void fu_qc_firehose_usb_device_replace(FuDevice *device, FuDevice *donor) { + if (!FU_IS_QC_FIREHOSE_USB_DEVICE(donor)) + return; if (fu_device_has_private_flag(donor, FU_QC_FIREHOSE_USB_DEVICE_NO_ZLP)) fu_device_add_private_flag(device, FU_QC_FIREHOSE_USB_DEVICE_NO_ZLP); } static void -fu_qc_firehose_usb_device_set_progress(FuDevice *self, FuProgress *progress) +fu_qc_firehose_usb_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -342,7 +354,7 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_ARCHIVE_FIRMWARE); - fu_device_set_remove_delay(FU_DEVICE(self), 60000); + fu_device_set_remove_delay(FU_DEVICE(self), 90000); fu_device_register_private_flag(FU_DEVICE(self), FU_QC_FIREHOSE_USB_DEVICE_NO_ZLP); fu_usb_device_add_interface(FU_USB_DEVICE(self), 0x00); } diff -Nru fwupd-2.0.8/plugins/qc-firehose/fu-qc-firehose.rs fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose.rs --- fwupd-2.0.8/plugins/qc-firehose/fu-qc-firehose.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-firehose/fu-qc-firehose.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,7 +1,7 @@ // Copyright 2025 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later -#[derive(ToBitString, FromString)] +#[derive(ToString, FromString)] enum FuQcFirehoseFunctions { None = 0, Program = 1 << 0, diff -Nru fwupd-2.0.8/plugins/qc-firehose/fu-self-test.c fwupd-2.0.20/plugins/qc-firehose/fu-self-test.c --- fwupd-2.0.8/plugins/qc-firehose/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-firehose/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,7 +12,7 @@ guint cnt; guint cnt_done; FwupdError error_code; -} FooHelper; +} FuQcFirehoseHelper; static gboolean fu_qc_firehose_retry_cb(FuQcFirehoseImpl *self, @@ -21,7 +21,7 @@ gpointer user_data, GError **error) { - FooHelper *helper = (FooHelper *)user_data; + FuQcFirehoseHelper *helper = (FuQcFirehoseHelper *)user_data; helper->cnt++; if (helper->cnt_done > 0 && helper->cnt == helper->cnt_done) *done = TRUE; @@ -33,7 +33,7 @@ { gboolean ret; g_autoptr(GError) error = NULL; - FooHelper helper = {0}; + FuQcFirehoseHelper helper = {0}; ret = fu_qc_firehose_impl_retry(NULL, 2500, fu_qc_firehose_retry_cb, &helper, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL); @@ -46,7 +46,7 @@ { gboolean ret; g_autoptr(GError) error = NULL; - FooHelper helper = { + FuQcFirehoseHelper helper = { .cnt_done = 10, }; @@ -63,7 +63,7 @@ gpointer user_data, GError **error) { - FooHelper *helper = (FooHelper *)user_data; + FuQcFirehoseHelper *helper = (FuQcFirehoseHelper *)user_data; helper->cnt++; g_set_error_literal(error, FWUPD_ERROR, (gint)helper->error_code, "timeout"); return FALSE; @@ -74,7 +74,7 @@ { gboolean ret; g_autoptr(GError) error = NULL; - FooHelper helper = { + FuQcFirehoseHelper helper = { .error_code = FWUPD_ERROR_TIMED_OUT, }; @@ -89,7 +89,7 @@ { gboolean ret; g_autoptr(GError) error = NULL; - FooHelper helper = { + FuQcFirehoseHelper helper = { .error_code = FWUPD_ERROR_INVALID_DATA, }; diff -Nru fwupd-2.0.8/plugins/qc-firehose/meson.build fwupd-2.0.20/plugins/qc-firehose/meson.build --- fwupd-2.0.8/plugins/qc-firehose/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-firehose/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,3 @@ -if get_option('plugin_qc_firehose') cargs = ['-DG_LOG_DOMAIN="FuPluginQcFirehose"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -56,5 +55,3 @@ ) test('qc-firehose-self-test', e, env: env) endif - -endif diff -Nru fwupd-2.0.8/plugins/qc-firehose/tests/qc-eg25ggc.json fwupd-2.0.20/plugins/qc-firehose/tests/qc-eg25ggc.json --- fwupd-2.0.8/plugins/qc-firehose/tests/qc-eg25ggc.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-firehose/tests/qc-eg25ggc.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/b1cd72c75053f5c29e55784ef30e5d4be722c1aacc2400995b1ee248f07be7d6-EG25GGC-0.0.cab", - "emulation-url": "https://fwupd.org/downloads/0f21d648b33736fa0f8d1ec36805627291428c5a6197cc2443988ccb80df0fa4-emulation.zip", + "url": "b1cd72c75053f5c29e55784ef30e5d4be722c1aacc2400995b1ee248f07be7d6-EG25GGC-0.0.cab", + "emulation-url": "7868d8239184b8d3c63ba9936441f86410447411a650f2ba0169c3f870d63dcf-emulation.zip", "components": [ { "version": "0.0", diff -Nru fwupd-2.0.8/plugins/qc-s5gen2/README.md fwupd-2.0.20/plugins/qc-s5gen2/README.md --- fwupd-2.0.8/plugins/qc-s5gen2/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-s5gen2/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -73,10 +73,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.9.16`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Denis Pynkin: @d4s diff -Nru fwupd-2.0.8/plugins/qc-s5gen2/fu-qc-s5gen2-ble-device.c fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-ble-device.c --- fwupd-2.0.8/plugins/qc-s5gen2/fu-qc-s5gen2-ble-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-ble-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -33,14 +33,14 @@ typedef struct { guint8 core; guint8 dfu; -} gaia_features_version_t; +} FuQcS5gen2GaiaFeaturesVersion; struct _FuQcS5gen2BleDevice { FuBluezDevice parent_instance; guint16 vid_v3; FuIOChannel *io_cmd; gint32 mtu; - gaia_features_version_t feature; + FuQcS5gen2GaiaFeaturesVersion feature; }; static void @@ -125,21 +125,22 @@ FuQcS5gen2BleDevice *self = FU_QC_S5GEN2_BLE_DEVICE(impl); guint8 buf[FU_QC_S5GEN2_BLE_DEVICE_BUFFER_SZ] = {0}; gsize read_len; - g_autoptr(GByteArray) req = fu_struct_qc_gaia_v3_upgrade_control_cmd_new(); - g_autoptr(GByteArray) validate = NULL; + g_autoptr(FuStructQcGaiaV3UpgradeControlCmd) st_req = + fu_struct_qc_gaia_v3_upgrade_control_cmd_new(); + g_autoptr(FuStructQcGaiaV3UpgradeControlAck) st_res = NULL; - fu_struct_qc_gaia_v3_upgrade_control_cmd_set_vendor_id(req, self->vid_v3); + fu_struct_qc_gaia_v3_upgrade_control_cmd_set_vendor_id(st_req, self->vid_v3); - g_byte_array_append(req, data, data_len); + g_byte_array_append(st_req->buf, data, data_len); - if (!fu_qc_s5gen2_ble_device_send(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_ble_device_send(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_ble_device_recv(self, buf, sizeof(buf), &read_len, error)) return FALSE; - validate = fu_struct_qc_gaia_v3_upgrade_control_ack_parse(buf, read_len, 0, error); - if (validate == NULL) { + st_res = fu_struct_qc_gaia_v3_upgrade_control_ack_parse(buf, read_len, 0, error); + if (st_res == NULL) { return FALSE; } @@ -202,22 +203,23 @@ FuQcS5gen2BleDevice *self = FU_QC_S5GEN2_BLE_DEVICE(impl); guint8 buf[FU_QC_S5GEN2_BLE_DEVICE_BUFFER_SZ] = {0}; gsize read_len; - g_autoptr(GByteArray) req = fu_struct_qc_gaia_v3_upgrade_connect_cmd_new(); - g_autoptr(GByteArray) validate = NULL; + g_autoptr(FuStructQcGaiaV3UpgradeConnectCmd) st_req = + fu_struct_qc_gaia_v3_upgrade_connect_cmd_new(); + g_autoptr(FuStructQcGaiaV3UpgradeConnectAck) st_res = NULL; - fu_struct_qc_gaia_v3_upgrade_connect_cmd_set_vendor_id(req, self->vid_v3); + fu_struct_qc_gaia_v3_upgrade_connect_cmd_set_vendor_id(st_req, self->vid_v3); if (!fu_qc_s5gen2_ble_device_notify_acquire(self, error)) return FALSE; - if (!fu_qc_s5gen2_ble_device_send(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_ble_device_send(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_ble_device_recv(self, buf, sizeof(buf), &read_len, error)) return FALSE; - validate = fu_struct_qc_gaia_v3_upgrade_connect_ack_parse(buf, read_len, 0, error); - if (validate == NULL) + st_res = fu_struct_qc_gaia_v3_upgrade_connect_ack_parse(buf, read_len, 0, error); + if (st_res == NULL) return FALSE; return TRUE; @@ -229,19 +231,20 @@ FuQcS5gen2BleDevice *self = FU_QC_S5GEN2_BLE_DEVICE(impl); guint8 buf[FU_QC_S5GEN2_BLE_DEVICE_BUFFER_SZ] = {0}; gsize read_len; - g_autoptr(GByteArray) req = fu_struct_qc_gaia_v3_upgrade_disconnect_cmd_new(); - g_autoptr(GByteArray) validate = NULL; + g_autoptr(FuStructQcGaiaV3UpgradeDisconnectCmd) st_req = + fu_struct_qc_gaia_v3_upgrade_disconnect_cmd_new(); + g_autoptr(FuStructQcGaiaV3UpgradeDisconnectAck) st_res = NULL; - fu_struct_qc_gaia_v3_upgrade_disconnect_cmd_set_vendor_id(req, self->vid_v3); + fu_struct_qc_gaia_v3_upgrade_disconnect_cmd_set_vendor_id(st_req, self->vid_v3); - if (!fu_qc_s5gen2_ble_device_send(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_ble_device_send(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_ble_device_recv(self, buf, sizeof(buf), &read_len, error)) return FALSE; - validate = fu_struct_qc_gaia_v3_upgrade_disconnect_ack_parse(buf, read_len, 0, error); - if (validate == NULL) + st_res = fu_struct_qc_gaia_v3_upgrade_disconnect_ack_parse(buf, read_len, 0, error); + if (st_res == NULL) return FALSE; return fu_qc_s5gen2_ble_device_notify_release(self, error); @@ -273,23 +276,23 @@ gsize read_len; guint8 api_major; guint8 api_minor; - g_autoptr(GByteArray) req = fu_struct_qc_gaia_v3_api_req_new(); - g_autoptr(GByteArray) resp = NULL; + g_autoptr(FuStructQcGaiaV3ApiReq) st_req = fu_struct_qc_gaia_v3_api_req_new(); + g_autoptr(FuStructQcGaiaV3Api) st_res = NULL; - fu_struct_qc_gaia_v3_api_req_set_vendor_id(req, self->vid_v3); + fu_struct_qc_gaia_v3_api_req_set_vendor_id(st_req, self->vid_v3); - if (!fu_qc_s5gen2_ble_device_send(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_ble_device_send(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_ble_device_recv(self, buf, sizeof(buf), &read_len, error)) return FALSE; - resp = fu_struct_qc_gaia_v3_api_parse(buf, read_len, 0, error); - if (resp == NULL) + st_res = fu_struct_qc_gaia_v3_api_parse(buf, read_len, 0, error); + if (st_res == NULL) return FALSE; - api_major = fu_struct_qc_gaia_v3_api_get_major(resp); - api_minor = fu_struct_qc_gaia_v3_api_get_minor(resp); + api_major = fu_struct_qc_gaia_v3_api_get_major(st_res); + api_minor = fu_struct_qc_gaia_v3_api_get_minor(st_res); if (api_major < FU_QC_S5GEN2_GAIA_V3_SUPPORTED_VERSION_MAJOR) { g_set_error(error, @@ -310,24 +313,25 @@ { guint8 buf[FU_QC_S5GEN2_BLE_DEVICE_BUFFER_SZ] = {0}; gsize read_len; - g_autoptr(GByteArray) req = fu_struct_qc_gaia_v3_supported_features_req_new(); - g_autoptr(GByteArray) resp = NULL; + g_autoptr(FuStructQcGaiaV3SupportedFeaturesReq) st_req = + fu_struct_qc_gaia_v3_supported_features_req_new(); + g_autoptr(FuStructQcGaiaV3SupportedFeatures) st_res = NULL; - fu_struct_qc_gaia_v3_supported_features_req_set_vendor_id(req, self->vid_v3); + fu_struct_qc_gaia_v3_supported_features_req_set_vendor_id(st_req, self->vid_v3); fu_struct_qc_gaia_v3_supported_features_req_set_command( - req, + st_req, (next == FALSE) ? FU_QC_GAIA_V3_CMD_GET_SUPPORTED_FEATURES_REQ : FU_QC_GAIA_V3_CMD_GET_SUPPORTED_FEATURES_NEXT_REQ); - if (!fu_qc_s5gen2_ble_device_send(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_ble_device_send(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_ble_device_recv(self, buf, sizeof(buf), &read_len, error)) return FALSE; - resp = fu_struct_qc_gaia_v3_supported_features_parse(buf, read_len, 0, error); - if (resp == NULL) + st_res = fu_struct_qc_gaia_v3_supported_features_parse(buf, read_len, 0, error); + if (st_res == NULL) return FALSE; /* must be odd: header 5B + feature pairs */ @@ -346,7 +350,7 @@ switch (buf[i]) { case FU_QC_GAIA_V3_FEATURES_CORE: self->feature.core = buf[i + 1]; - g_debug("Core feature version: %u", self->feature.core); + g_debug("core feature version: %u", self->feature.core); break; case FU_QC_GAIA_V3_FEATURES_DFU: self->feature.dfu = buf[i + 1]; @@ -358,7 +362,7 @@ } /* request the rest of the list */ - if (fu_struct_qc_gaia_v3_supported_features_get_more_features(resp) == FU_QC_MORE_MORE) + if (fu_struct_qc_gaia_v3_supported_features_get_more_features(st_res) == FU_QC_MORE_MORE) return fu_qc_s5gen2_ble_device_get_features(self, TRUE, error); return TRUE; @@ -369,22 +373,21 @@ { guint8 buf[FU_QC_S5GEN2_BLE_DEVICE_BUFFER_SZ] = {0}; gsize read_len; - g_autoptr(GByteArray) req = fu_struct_qc_gaia_v3_serial_req_new(); - g_autoptr(GByteArray) validate = NULL; + g_autoptr(FuStructQcGaiaV3SerialReq) st_req = fu_struct_qc_gaia_v3_serial_req_new(); + g_autoptr(FuStructQcGaiaV3Serial) st_res = NULL; g_autofree gchar *serial = NULL; - fu_struct_qc_gaia_v3_serial_req_set_vendor_id(req, self->vid_v3); + fu_struct_qc_gaia_v3_serial_req_set_vendor_id(st_req, self->vid_v3); - if (!fu_qc_s5gen2_ble_device_send(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_ble_device_send(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_ble_device_recv(self, buf, sizeof(buf), &read_len, error)) return FALSE; - /* Check if response is valid */ - validate = - fu_struct_qc_gaia_v3_serial_parse(buf, FU_STRUCT_QC_GAIA_V3_SERIAL_SIZE, 0, error); - if (validate == NULL) + /* check if response is valid */ + st_res = fu_struct_qc_gaia_v3_serial_parse(buf, FU_STRUCT_QC_GAIA_V3_SERIAL_SIZE, 0, error); + if (st_res == NULL) return FALSE; serial = fu_strsafe((gchar *)(buf + FU_STRUCT_QC_GAIA_V3_SERIAL_SIZE), @@ -401,22 +404,22 @@ { guint8 buf[FU_QC_S5GEN2_BLE_DEVICE_BUFFER_SZ] = {0}; gsize read_len; - g_autoptr(GByteArray) req = fu_struct_qc_gaia_v3_variant_req_new(); - g_autoptr(GByteArray) validate = NULL; + g_autoptr(FuStructQcGaiaV3VariantReq) st_req = fu_struct_qc_gaia_v3_variant_req_new(); + g_autoptr(FuStructQcGaiaV3Variant) st_res = NULL; g_autofree gchar *variant = NULL; - fu_struct_qc_gaia_v3_variant_req_set_vendor_id(req, self->vid_v3); + fu_struct_qc_gaia_v3_variant_req_set_vendor_id(st_req, self->vid_v3); - if (!fu_qc_s5gen2_ble_device_send(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_ble_device_send(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_ble_device_recv(self, buf, sizeof(buf), &read_len, error)) return FALSE; /* check if response is valid */ - validate = + st_res = fu_struct_qc_gaia_v3_variant_parse(buf, FU_STRUCT_QC_GAIA_V3_VARIANT_SIZE, 0, error); - if (validate == NULL) + if (st_res == NULL) return FALSE; variant = fu_strsafe((gchar *)(buf + FU_STRUCT_QC_GAIA_V3_VARIANT_SIZE), @@ -445,25 +448,26 @@ { guint8 buf[FU_QC_S5GEN2_BLE_DEVICE_BUFFER_SZ] = {0}; gsize read_len = 0; - g_autoptr(GByteArray) req = fu_struct_qc_gaia_v3_register_notification_cmd_new(); - g_autoptr(GByteArray) validate = NULL; + g_autoptr(FuStructQcGaiaV3RegisterNotificationCmd) st_req = + fu_struct_qc_gaia_v3_register_notification_cmd_new(); + g_autoptr(FuStructQcGaiaV3RegisterNotificationAck) st_res = NULL; /* register only for update feature */ - fu_struct_qc_gaia_v3_register_notification_cmd_set_vendor_id(req, self->vid_v3); + fu_struct_qc_gaia_v3_register_notification_cmd_set_vendor_id(st_req, self->vid_v3); - if (!fu_qc_s5gen2_ble_device_send(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_ble_device_send(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_ble_device_recv(self, buf, sizeof(buf), &read_len, error)) return FALSE; - /* Check if response is valid */ - validate = fu_struct_qc_gaia_v3_register_notification_ack_parse( + /* check if response is valid */ + st_res = fu_struct_qc_gaia_v3_register_notification_ack_parse( buf, FU_STRUCT_QC_GAIA_V3_REGISTER_NOTIFICATION_ACK_SIZE, 0, error); - if (validate == NULL) + if (st_res == NULL) return FALSE; return TRUE; @@ -476,21 +480,22 @@ { guint8 buf[FU_QC_S5GEN2_BLE_DEVICE_BUFFER_SZ] = {0}; gsize read_len; - g_autoptr(GByteArray) req = fu_struct_qc_gaia_v3_set_transport_info_req_new(); - g_autoptr(GByteArray) validate = NULL; - - fu_struct_qc_gaia_v3_set_transport_info_req_set_vendor_id(req, self->vid_v3); - fu_struct_qc_gaia_v3_set_transport_info_req_set_key(req, 0x07); - fu_struct_qc_gaia_v3_set_transport_info_req_set_value(req, version); + g_autoptr(FuStructQcGaiaV3SetTransportInfoReq) st_req = + fu_struct_qc_gaia_v3_set_transport_info_req_new(); + g_autoptr(FuStructQcGaiaV3SetTransportInfo) st_res = NULL; + + fu_struct_qc_gaia_v3_set_transport_info_req_set_vendor_id(st_req, self->vid_v3); + fu_struct_qc_gaia_v3_set_transport_info_req_set_key(st_req, 0x07); + fu_struct_qc_gaia_v3_set_transport_info_req_set_value(st_req, version); - if (!fu_qc_s5gen2_ble_device_send(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_ble_device_send(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_ble_device_recv(self, buf, sizeof(buf), &read_len, error)) return FALSE; - validate = fu_struct_qc_gaia_v3_set_transport_info_parse(buf, read_len, 0, error); - if (validate == NULL) + st_res = fu_struct_qc_gaia_v3_set_transport_info_parse(buf, read_len, 0, error); + if (st_res == NULL) return FALSE; return TRUE; @@ -573,7 +578,7 @@ FuQcS5gen2BleDevice *self = FU_QC_S5GEN2_BLE_DEVICE(device); guint64 tmp = 0; - if (g_strcmp0(key, "AudioS5gen2Gaia3VendorId") == 0) { + if (g_strcmp0(key, "QcS5gen2Gaia3VendorId") == 0) { if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, FU_INTEGER_BASE_AUTO, error)) return FALSE; self->vid_v3 = tmp; diff -Nru fwupd-2.0.8/plugins/qc-s5gen2/fu-qc-s5gen2-device.c fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-device.c --- fwupd-2.0.8/plugins/qc-s5gen2/fu-qc-s5gen2-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,6 +17,10 @@ /* 100ms delay requested by device as a rule */ #define FU_QC_S5GEN2_DEVICE_VALIDATION_RETRIES (60000 / 100) +/* usually takes around 10-11 sec, let's be tolerant */ +#define FU_QC_S5GEN2_DEVICE_ABORT_DELAY 300 +#define FU_QC_S5GEN2_DEVICE_ABORT_RETRIES (20 * 1000 / FU_QC_S5GEN2_DEVICE_ABORT_DELAY) + struct _FuQcS5gen2Device { FuDevice parent_instance; guint32 file_id; @@ -39,11 +43,9 @@ static gboolean fu_qc_s5gen2_device_msg_out(FuQcS5gen2Device *self, guint8 *data, gsize data_len, GError **error) { - FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self)); - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) return FALSE; - } return fu_qc_s5gen2_impl_msg_out(FU_QC_S5GEN2_IMPL(proxy), data, data_len, error); } @@ -54,14 +56,13 @@ gsize *read_len, GError **error) { - FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self)); - g_autoptr(GByteArray) err_msg = NULL; + FuDevice *proxy; + g_autoptr(FuStructQcErrorInd) st_err = NULL; g_autoptr(GError) error_local = NULL; - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) return FALSE; - } if (!fu_qc_s5gen2_impl_msg_in(FU_QC_S5GEN2_IMPL(proxy), buf, bufsz, read_len, error)) return FALSE; @@ -76,16 +77,16 @@ } /* error detected */ - err_msg = fu_struct_qc_error_ind_parse(buf, *read_len, 0, &error_local); - if (err_msg != NULL) { - guint16 code = fu_struct_qc_error_ind_get_error_code(err_msg); - g_autoptr(GByteArray) confirm = fu_struct_qc_error_res_new(); + st_err = fu_struct_qc_error_ind_parse(buf, *read_len, 0, &error_local); + if (st_err != NULL) { + guint16 code = fu_struct_qc_error_ind_get_error_code(st_err); + g_autoptr(FuStructQcErrorRes) st_confirm = fu_struct_qc_error_res_new(); /* confirm and stop */ - fu_struct_qc_error_res_set_error_code(confirm, code); + fu_struct_qc_error_res_set_error_code(st_confirm, code); if (!fu_qc_s5gen2_impl_msg_out(FU_QC_S5GEN2_IMPL(proxy), - confirm->data, - confirm->len, + st_confirm->buf->data, + st_confirm->buf->len, error)) return FALSE; @@ -103,22 +104,18 @@ static gboolean fu_qc_s5gen2_device_cmd_req_disconnect(FuQcS5gen2Device *self, GError **error) { - FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self)); - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) return FALSE; - } return fu_qc_s5gen2_impl_req_disconnect(FU_QC_S5GEN2_IMPL(proxy), error); } static gboolean fu_qc_s5gen2_device_cmd_req_connect(FuQcS5gen2Device *self, GError **error) { - FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self)); - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) return FALSE; - } return fu_qc_s5gen2_impl_req_connect(FU_QC_S5GEN2_IMPL(proxy), error); } @@ -126,63 +123,74 @@ static gboolean fu_qc_s5gen2_device_data_size(FuQcS5gen2Device *self, gsize *data_sz, GError **error) { - FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self)); - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) return FALSE; - } return fu_qc_s5gen2_impl_data_size(FU_QC_S5GEN2_IMPL(proxy), data_sz, error); } static gboolean -fu_qc_s5gen2_device_cmd_abort(FuQcS5gen2Device *self, GError **error) +fu_qc_s5gen2_device_cmd_abort_cb(FuDevice *device, gpointer user_data, GError **error) { + FuQcS5gen2Device *self = FU_QC_S5GEN2_DEVICE(device); guint8 data[FU_STRUCT_QC_ABORT_SIZE] = {0}; gsize read_len; - g_autoptr(GByteArray) req = fu_struct_qc_abort_req_new(); - g_autoptr(GByteArray) reply = NULL; + g_autoptr(FuStructQcAbort) st_res = NULL; - if (!fu_qc_s5gen2_device_msg_out(self, req->data, req->len, error)) - return FALSE; if (!fu_qc_s5gen2_device_msg_in(self, data, sizeof(data), &read_len, error)) return FALSE; - reply = fu_struct_qc_abort_parse(data, read_len, 0, error); - if (reply == NULL) + st_res = fu_struct_qc_abort_parse(data, read_len, 0, error); + if (st_res == NULL) return FALSE; return TRUE; } static gboolean +fu_qc_s5gen2_device_cmd_abort(FuQcS5gen2Device *self, GError **error) +{ + g_autoptr(FuStructQcAbortReq) st_req = fu_struct_qc_abort_req_new(); + + if (!fu_qc_s5gen2_device_msg_out(self, st_req->buf->data, st_req->buf->len, error)) + return FALSE; + return fu_device_retry_full(FU_DEVICE(self), + fu_qc_s5gen2_device_cmd_abort_cb, + FU_QC_S5GEN2_DEVICE_ABORT_RETRIES, + FU_QC_S5GEN2_DEVICE_ABORT_DELAY, + NULL, + error); +} + +static gboolean fu_qc_s5gen2_device_cmd_sync(FuQcS5gen2Device *self, GError **error) { guint8 data[FU_STRUCT_QC_SYNC_SIZE] = {0}; gsize read_len; - g_autoptr(GByteArray) req = fu_struct_qc_sync_req_new(); - g_autoptr(GByteArray) reply = NULL; + g_autoptr(FuStructQcSyncReq) st_req = fu_struct_qc_sync_req_new(); + g_autoptr(FuStructQcSync) st_res = NULL; - fu_struct_qc_sync_req_set_file_id(req, self->file_id); - if (!fu_qc_s5gen2_device_msg_out(self, req->data, req->len, error)) + fu_struct_qc_sync_req_set_file_id(st_req, self->file_id); + if (!fu_qc_s5gen2_device_msg_out(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_device_msg_in(self, data, sizeof(data), &read_len, error)) return FALSE; - reply = fu_struct_qc_sync_parse(data, read_len, 0, error); - if (reply == NULL) + st_res = fu_struct_qc_sync_parse(data, read_len, 0, error); + if (st_res == NULL) return FALSE; - if (self->file_version != fu_struct_qc_sync_get_protocol_version(reply)) { + if (self->file_version != fu_struct_qc_sync_get_protocol_version(st_res)) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "unsupported firmware protocol version on device %u, expected %u", - fu_struct_qc_sync_get_protocol_version(reply), + fu_struct_qc_sync_get_protocol_version(st_res), self->file_version); return FALSE; } - if (self->file_id != fu_struct_qc_sync_get_file_id(reply)) { + if (self->file_id != fu_struct_qc_sync_get_file_id(st_res)) { g_autoptr(GError) error_local = NULL; /* reset the update state */ if (!fu_qc_s5gen2_device_cmd_abort(self, &error_local)) @@ -192,13 +200,13 @@ FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "unexpected file ID from the device (%u), expected (%u)", - fu_struct_qc_sync_get_file_id(reply), + fu_struct_qc_sync_get_file_id(st_res), self->file_id); return FALSE; } - self->resume_point = fu_struct_qc_sync_get_resume_point(reply); + self->resume_point = fu_struct_qc_sync_get_resume_point(st_res); return TRUE; } @@ -208,19 +216,19 @@ guint8 data[FU_STRUCT_QC_START_SIZE] = {0}; gsize read_len; FuQcStartStatus status; - g_autoptr(GByteArray) req = fu_struct_qc_start_req_new(); - g_autoptr(GByteArray) reply = NULL; + g_autoptr(FuStructQcStartReq) st_req = fu_struct_qc_start_req_new(); + g_autoptr(FuStructQcStart) st_res = NULL; - if (!fu_qc_s5gen2_device_msg_out(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_device_msg_out(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_device_msg_in(self, data, sizeof(data), &read_len, error)) return FALSE; - reply = fu_struct_qc_start_parse(data, read_len, 0, error); - if (reply == NULL) + st_res = fu_struct_qc_start_parse(data, read_len, 0, error); + if (st_res == NULL) return FALSE; - status = fu_struct_qc_start_get_status(reply); + status = fu_struct_qc_start_get_status(st_res); if (status != FU_QC_START_STATUS_SUCCESS) { g_set_error(error, FWUPD_ERROR, @@ -231,7 +239,7 @@ } /* mostly for debug: save raw battery level */ - self->battery_raw = fu_struct_qc_start_get_battery_level(reply); + self->battery_raw = fu_struct_qc_start_get_battery_level(st_res); return TRUE; } @@ -239,9 +247,9 @@ static gboolean fu_qc_s5gen2_device_cmd_start_data(FuQcS5gen2Device *self, GError **error) { - g_autoptr(GByteArray) req = fu_struct_qc_start_data_req_new(); + g_autoptr(FuStructQcStartDataReq) st_req = fu_struct_qc_start_data_req_new(); - if (!fu_qc_s5gen2_device_msg_out(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_device_msg_out(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; fu_device_sleep(FU_DEVICE(self), FU_QC_S5GEN2_DEVICE_DATA_REQ_SLEEP); @@ -255,11 +263,12 @@ guint16 delay_ms; guint8 data[FU_STRUCT_QC_IS_VALIDATION_DONE_SIZE] = {0}; gsize read_len = 0; - g_autoptr(GByteArray) req = fu_struct_qc_validation_req_new(); - g_autoptr(GByteArray) reply = NULL; + g_autoptr(FuStructQcValidationReq) st_req = fu_struct_qc_validation_req_new(); + g_autoptr(FuStructQcTransferCompleteInd) st_res = NULL; + g_autoptr(FuStructQcIsValidationDone) st_res2 = NULL; g_autoptr(GError) error_local = NULL; - if (!fu_qc_s5gen2_device_msg_out(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_device_msg_out(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_device_msg_in(self, data, sizeof(data), &read_len, error)) return FALSE; @@ -275,16 +284,16 @@ } /* ignore the error */ - reply = fu_struct_qc_transfer_complete_ind_parse(data, sizeof(data), 0, &error_local); + st_res = fu_struct_qc_transfer_complete_ind_parse(data, sizeof(data), 0, &error_local); /* check if validation is complete */ - if (reply != NULL) + if (st_res != NULL) return TRUE; - reply = fu_struct_qc_is_validation_done_parse(data, sizeof(data), 0, error); - if (reply == NULL) + st_res2 = fu_struct_qc_is_validation_done_parse(data, sizeof(data), 0, error); + if (st_res2 == NULL) return FALSE; - delay_ms = fu_struct_qc_is_validation_done_get_delay(reply); + delay_ms = fu_struct_qc_is_validation_done_get_delay(st_res2); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, @@ -305,12 +314,12 @@ { /* reboot immediately */ FuQcTransferAction action = FU_QC_TRANSFER_ACTION_INTERACTIVE; - g_autoptr(GByteArray) req = fu_struct_qc_transfer_complete_new(); + g_autoptr(FuStructQcTransferComplete) st_req = fu_struct_qc_transfer_complete_new(); - fu_struct_qc_transfer_complete_set_action(req, action); + fu_struct_qc_transfer_complete_set_action(st_req, action); /* if reboot immediately, the write might return error */ - return fu_qc_s5gen2_device_msg_out(self, req->data, req->len, error); + return fu_qc_s5gen2_device_msg_out(self, st_req->buf->data, st_req->buf->len, error); } static gboolean @@ -318,18 +327,18 @@ { guint8 data[FU_STRUCT_QC_COMMIT_REQ_SIZE] = {0}; gsize read_len; - g_autoptr(GByteArray) req = fu_struct_qc_proceed_to_commit_new(); - g_autoptr(GByteArray) reply = NULL; + g_autoptr(FuStructQcProceedToCommit) st_req = fu_struct_qc_proceed_to_commit_new(); + g_autoptr(FuStructQcCommitReq) st_res = NULL; - fu_struct_qc_proceed_to_commit_set_action(req, FU_QC_COMMIT_ACTION_PROCEED); + fu_struct_qc_proceed_to_commit_set_action(st_req, FU_QC_COMMIT_ACTION_PROCEED); - if (!fu_qc_s5gen2_device_msg_out(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_device_msg_out(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_device_msg_in(self, data, sizeof(data), &read_len, error)) return FALSE; - reply = fu_struct_qc_commit_req_parse(data, read_len, 0, error); - if (reply == NULL) + st_res = fu_struct_qc_commit_req_parse(data, read_len, 0, error); + if (st_res == NULL) return FALSE; return TRUE; @@ -340,21 +349,21 @@ { guint8 data[FU_STRUCT_QC_COMPLETE_SIZE] = {0}; gsize read_len; - g_autoptr(GByteArray) req = fu_struct_qc_commit_cfm_new(); - g_autoptr(GByteArray) reply = NULL; + g_autoptr(FuStructQcCommitCfm) st_req = fu_struct_qc_commit_cfm_new(); + g_autoptr(FuStructQcComplete) st_res = NULL; - fu_struct_qc_commit_cfm_set_action(req, FU_QC_COMMIT_CFM_ACTION_UPGRADE); + fu_struct_qc_commit_cfm_set_action(st_req, FU_QC_COMMIT_CFM_ACTION_UPGRADE); if (self->resume_point != FU_QC_RESUME_POINT_POST_COMMIT) { - if (!fu_qc_s5gen2_device_msg_out(self, req->data, req->len, error)) + if (!fu_qc_s5gen2_device_msg_out(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; } if (!fu_qc_s5gen2_device_msg_in(self, data, sizeof(data), &read_len, error)) return FALSE; - reply = fu_struct_qc_complete_parse(data, read_len, 0, error); - if (reply == NULL) + st_res = fu_struct_qc_complete_parse(data, read_len, 0, error); + if (st_res == NULL) return FALSE; return TRUE; @@ -367,8 +376,8 @@ g_autofree gchar *ver_str = NULL; gsize read_len; g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(GByteArray) version = NULL; - g_autoptr(GByteArray) version_req = fu_struct_qc_version_req_new(); + g_autoptr(FuStructQcVersion) st_res = NULL; + g_autoptr(FuStructQcVersionReq) st_req = fu_struct_qc_version_req_new(); locker = fu_device_locker_new_full(FU_DEVICE(self), @@ -378,18 +387,18 @@ if (locker == NULL) return FALSE; - if (!fu_qc_s5gen2_device_msg_out(self, version_req->data, version_req->len, error)) + if (!fu_qc_s5gen2_device_msg_out(self, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_device_msg_in(self, ver_raw, sizeof(ver_raw), &read_len, error)) return FALSE; - version = fu_struct_qc_version_parse(ver_raw, read_len, 0, error); - if (version == NULL) + st_res = fu_struct_qc_version_parse(ver_raw, read_len, 0, error); + if (st_res == NULL) return FALSE; ver_str = g_strdup_printf("%u.%u.%u", - fu_struct_qc_version_get_major(version), - fu_struct_qc_version_get_minor(version), - fu_struct_qc_version_get_config(version)); + fu_struct_qc_version_get_major(st_res), + fu_struct_qc_version_get_minor(st_res), + fu_struct_qc_version_get_config(st_res)); fu_device_set_version(FU_DEVICE(self), ver_str); return TRUE; } @@ -406,15 +415,15 @@ (FuDeviceLockerFunc)fu_qc_s5gen2_device_cmd_req_disconnect, error); if (locker == NULL) { - g_prefix_error(error, "failed to connect: "); + g_prefix_error_literal(error, "failed to connect: "); return FALSE; } if (!fu_qc_s5gen2_device_cmd_sync(self, error)) { - g_prefix_error(error, "failed to cmd-sync: "); + g_prefix_error_literal(error, "failed to cmd-sync: "); return FALSE; } if (!fu_qc_s5gen2_device_cmd_start(self, error)) { - g_prefix_error(error, "failed to cmd-start: "); + g_prefix_error_literal(error, "failed to cmd-start: "); return FALSE; } @@ -437,7 +446,7 @@ if (self->resume_point == FU_QC_RESUME_POINT_POST_REBOOT) { if (!fu_qc_s5gen2_device_cmd_proceed_to_commit(self, error)) { - g_prefix_error(error, "failed to cmd-proceed-to-commit: "); + g_prefix_error_literal(error, "failed to cmd-proceed-to-commit: "); return FALSE; } self->resume_point = FU_QC_RESUME_POINT_COMMIT; @@ -445,7 +454,7 @@ g_debug("resume point: %s", fu_qc_resume_point_to_string(self->resume_point)); if (!fu_qc_s5gen2_device_cmd_commit_cfm(self, error)) { - g_prefix_error(error, "failed to cmd-commit: "); + g_prefix_error_literal(error, "failed to cmd-commit: "); return FALSE; } self->resume_point = FU_QC_RESUME_POINT_POST_COMMIT; @@ -460,7 +469,7 @@ { FuQcS5gen2Device *self = FU_QC_S5GEN2_DEVICE(device); if (!fu_qc_s5gen2_device_ensure_version(self, error)) { - g_prefix_error(error, "failed to ensure version on reload: "); + g_prefix_error_literal(error, "failed to ensure version on reload: "); return FALSE; } return TRUE; @@ -471,7 +480,7 @@ { FuQcS5gen2Device *self = FU_QC_S5GEN2_DEVICE(device); if (!fu_qc_s5gen2_device_ensure_version(self, error)) { - g_prefix_error(error, "failed to ensure version: "); + g_prefix_error_literal(error, "failed to ensure version: "); return FALSE; } return TRUE; @@ -495,25 +504,23 @@ data_sz); for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - g_autoptr(GByteArray) pkt = fu_struct_qc_data_new(); + g_autoptr(FuStructQcData) st_pkt = fu_struct_qc_data_new(); g_autoptr(FuChunk) chk = fu_chunk_array_index(chunks, i, error); if (chk == NULL) return FALSE; - fu_struct_qc_data_set_data_len(pkt, fu_chunk_get_data_sz(chk) + 1); + fu_struct_qc_data_set_data_len(st_pkt, fu_chunk_get_data_sz(chk) + 1); /* only the last block of the last bucket should have flag LAST */ if ((i + 1) == fu_chunk_array_length(chunks)) - fu_struct_qc_data_set_last_packet(pkt, last); + fu_struct_qc_data_set_last_packet(st_pkt, last); else - fu_struct_qc_data_set_last_packet(pkt, FU_QC_MORE_DATA_MORE); + fu_struct_qc_data_set_last_packet(st_pkt, FU_QC_MORE_DATA_MORE); - pkt = g_byte_array_append(pkt, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); - if (pkt == NULL) - return FALSE; + g_byte_array_append(st_pkt->buf, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk)); if (!fu_qc_s5gen2_device_msg_out(self, - pkt->data, + st_pkt->buf->data, FU_STRUCT_QC_DATA_SIZE + fu_chunk_get_data_sz(chk), error)) return FALSE; @@ -543,18 +550,18 @@ gsize read_len; guint32 data_sz; guint32 data_offset; - g_autoptr(GByteArray) data_req = NULL; + g_autoptr(FuStructQcDataReq) st_req = NULL; g_autoptr(GBytes) data_out = NULL; if (!fu_qc_s5gen2_device_msg_in(self, buf_in, sizeof(buf_in), &read_len, error)) return FALSE; - data_req = fu_struct_qc_data_req_parse(buf_in, read_len, 0, error); - if (data_req == NULL) + st_req = fu_struct_qc_data_req_parse(buf_in, read_len, 0, error); + if (st_req == NULL) return FALSE; /* requested data */ - data_sz = fu_struct_qc_data_req_get_fw_data_len(data_req); - data_offset = fu_struct_qc_data_req_get_fw_data_offset(data_req); + data_sz = fu_struct_qc_data_req_get_fw_data_len(st_req); + data_offset = fu_struct_qc_data_req_get_fw_data_offset(st_req); /* FIXME: abort for now */ if (data_sz == 0) { @@ -609,7 +616,7 @@ fu_qc_s5gen2_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuQcS5gen2Device *self = FU_QC_S5GEN2_DEVICE(device); @@ -700,9 +707,7 @@ g_autoptr(GError) error_local = NULL; fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - fu_qc_s5gen2_device_cmd_transfer_complete(self, &error_local); - - if (error_local != NULL) + if (!fu_qc_s5gen2_device_cmd_transfer_complete(self, &error_local)) g_debug("expected error during auto reboot: %s", error_local->message); self->resume_point = FU_QC_RESUME_POINT_POST_REBOOT; @@ -712,7 +717,7 @@ } static void -fu_qc_s5gen2_device_set_progress(FuDevice *self, FuProgress *progress) +fu_qc_s5gen2_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -738,6 +743,7 @@ { fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); fu_device_set_remove_delay(FU_DEVICE(self), FU_QC_S5GEN2_DEVICE_REMOVE_DELAY); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_QC_S5GEN2_IMPL); fu_device_add_protocol(FU_DEVICE(self), "com.qualcomm.s5gen2"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); diff -Nru fwupd-2.0.8/plugins/qc-s5gen2/fu-qc-s5gen2-firmware.c fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-firmware.c --- fwupd-2.0.8/plugins/qc-s5gen2/fu-qc-s5gen2-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -54,7 +54,7 @@ static gboolean fu_qc_s5gen2_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuQcS5gen2Firmware *self = FU_QC_S5GEN2_FIRMWARE(firmware); @@ -62,25 +62,25 @@ gsize config_offset = 26; guint16 config_ver; g_autofree gchar *ver_str = NULL; - g_autoptr(GByteArray) hdr = NULL; + g_autoptr(FuStructQcFwUpdateHdr) st = NULL; /* FIXME: deal with encrypted? */ - hdr = fu_struct_qc_fw_update_hdr_parse_stream(stream, 0x0, error); - if (hdr == NULL) + st = fu_struct_qc_fw_update_hdr_parse_stream(stream, 0x0, error); + if (st == NULL) return FALSE; /* protocol version */ - self->protocol_ver = fu_struct_qc_fw_update_hdr_get_protocol(hdr) - '0'; - device_variant = fu_struct_qc_fw_update_hdr_get_dev_variant(hdr, NULL); + self->protocol_ver = fu_struct_qc_fw_update_hdr_get_protocol(st) - '0'; + device_variant = fu_struct_qc_fw_update_hdr_get_dev_variant(st, NULL); self->device_variant = fu_strsafe((const gchar *)device_variant, 8); - config_offset += fu_struct_qc_fw_update_hdr_get_upgrades(hdr) * 4; + config_offset += fu_struct_qc_fw_update_hdr_get_upgrades(st) * 4; if (!fu_input_stream_read_u16(stream, config_offset, &config_ver, G_BIG_ENDIAN, error)) return FALSE; ver_str = g_strdup_printf("%u.%u.%u", - fu_struct_qc_fw_update_hdr_get_major(hdr), - fu_struct_qc_fw_update_hdr_get_minor(hdr), + fu_struct_qc_fw_update_hdr_get_major(st), + fu_struct_qc_fw_update_hdr_get_minor(st), config_ver); fu_firmware_set_version(firmware, ver_str); diff -Nru fwupd-2.0.8/plugins/qc-s5gen2/fu-qc-s5gen2-hid-device.c fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-hid-device.c --- fwupd-2.0.8/plugins/qc-s5gen2/fu-qc-s5gen2-hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,9 +17,7 @@ #define HID_EP_IN 0x82 #define HID_EP_OUT 0x01 -#define FU_QC_S5GEN2_HID_DEVICE_TIMEOUT 0 /* ms */ - -#define FU_QC_S5GEN2_HID_DEVICE_MAX_TRANSFER_SIZE 255 +#define FU_QC_S5GEN2_HID_DEVICE_TIMEOUT 500 /* ms */ struct _FuQcS5gen2HidDevice { FuHidDevice parent_instance; @@ -38,16 +36,16 @@ fu_qc_s5gen2_hid_device_msg_out(FuQcS5gen2Impl *impl, guint8 *data, gsize data_len, GError **error) { FuQcS5gen2HidDevice *self = FU_QC_S5GEN2_HID_DEVICE(impl); - g_autoptr(GByteArray) msg = fu_struct_qc_hid_data_transfer_new(); + g_autoptr(FuStructQcHidDataTransfer) st_req = fu_struct_qc_hid_data_transfer_new(); - fu_struct_qc_hid_data_transfer_set_payload_len(msg, data_len); - if (!fu_struct_qc_hid_data_transfer_set_payload(msg, data, data_len, error)) + fu_struct_qc_hid_data_transfer_set_payload_len(st_req, data_len); + if (!fu_struct_qc_hid_data_transfer_set_payload(st_req, data, data_len, error)) return FALSE; return fu_hid_device_set_report(FU_HID_DEVICE(self), 0x00, - msg->data, - FU_STRUCT_QC_HID_DATA_TRANSFER_SIZE, + st_req->buf->data, + st_req->buf->len, FU_QC_S5GEN2_HID_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, error); @@ -62,32 +60,32 @@ { FuQcS5gen2HidDevice *self = FU_QC_S5GEN2_HID_DEVICE(impl); guint8 buf[FU_STRUCT_QC_HID_RESPONSE_SIZE] = {0x0}; - g_autoptr(GByteArray) msg = NULL; + g_autoptr(FuStructQcHidResponse) st_rsp = NULL; if (!fu_hid_device_get_report(FU_HID_DEVICE(self), 0x00, buf, - FU_STRUCT_QC_HID_RESPONSE_SIZE, + sizeof(buf), FU_QC_S5GEN2_HID_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, error)) return FALSE; - msg = fu_struct_qc_hid_response_parse(buf, FU_STRUCT_QC_HID_RESPONSE_SIZE, 0, error); - if (msg == NULL) + st_rsp = fu_struct_qc_hid_response_parse(buf, FU_STRUCT_QC_HID_RESPONSE_SIZE, 0, error); + if (st_rsp == NULL) return FALSE; if (!fu_memcpy_safe(data, data_len, 0, - msg->data, - msg->len, + st_rsp->buf->data, + st_rsp->buf->len, FU_STRUCT_QC_HID_RESPONSE_OFFSET_PAYLOAD, - fu_struct_qc_hid_response_get_payload_len(msg), + fu_struct_qc_hid_response_get_payload_len(st_rsp), error)) return FALSE; - *read_len = fu_struct_qc_hid_response_get_payload_len(msg); + *read_len = fu_struct_qc_hid_response_get_payload_len(st_rsp); return TRUE; } @@ -96,16 +94,16 @@ fu_qc_s5gen2_hid_device_msg_cmd(FuQcS5gen2Impl *impl, guint8 *data, gsize data_len, GError **error) { FuQcS5gen2HidDevice *self = FU_QC_S5GEN2_HID_DEVICE(impl); - g_autoptr(GByteArray) msg = fu_struct_qc_hid_command_new(); + g_autoptr(FuStructQcHidCommand) st_req = fu_struct_qc_hid_command_new(); - fu_struct_qc_hid_command_set_payload_len(msg, data_len); - if (!fu_struct_qc_hid_command_set_payload(msg, data, data_len, error)) + fu_struct_qc_hid_command_set_payload_len(st_req, data_len); + if (!fu_struct_qc_hid_command_set_payload(st_req, data, data_len, error)) return FALSE; return fu_hid_device_set_report(FU_HID_DEVICE(self), 0x03, - msg->data, - FU_STRUCT_QC_HID_COMMAND_SIZE, + st_req->buf->data, + st_req->buf->len, 0, FU_HID_DEVICE_FLAG_IS_FEATURE, error); @@ -114,8 +112,8 @@ static gboolean fu_qc_s5gen2_hid_device_cmd_req_disconnect(FuQcS5gen2Impl *impl, GError **error) { - g_autoptr(GByteArray) req = fu_struct_qc_disconnect_req_new(); - return fu_qc_s5gen2_hid_device_msg_cmd(impl, req->data, req->len, error); + g_autoptr(FuStructQcDisconnectReq) st_req = fu_struct_qc_disconnect_req_new(); + return fu_qc_s5gen2_hid_device_msg_cmd(impl, st_req->buf->data, st_req->buf->len, error); } static gboolean @@ -124,10 +122,10 @@ guint8 data_in[FU_STRUCT_QC_UPDATE_STATUS_SIZE] = {0x0}; gsize read_len; FuQcStatus update_status; - g_autoptr(GByteArray) req = fu_struct_qc_connect_req_new(); - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructQcConnectReq) st_req = fu_struct_qc_connect_req_new(); + g_autoptr(FuStructQcUpdateStatus) st = NULL; - if (!fu_qc_s5gen2_hid_device_msg_cmd(impl, req->data, req->len, error)) + if (!fu_qc_s5gen2_hid_device_msg_cmd(impl, st_req->buf->data, st_req->buf->len, error)) return FALSE; if (!fu_qc_s5gen2_hid_device_msg_in(impl, data_in, sizeof(data_in), &read_len, error)) return FALSE; @@ -157,7 +155,7 @@ static gboolean fu_qc_s5gen2_hid_device_data_size(FuQcS5gen2Impl *impl, gsize *data_sz, GError **error) { - if (FU_QC_S5GEN2_HID_DEVICE_MAX_TRANSFER_SIZE <= FU_STRUCT_QC_DATA_SIZE + 2) { + if (FU_STRUCT_QC_HID_DATA_TRANSFER_SIZE <= FU_STRUCT_QC_DATA_SIZE + 2) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, @@ -165,7 +163,7 @@ return FALSE; } - *data_sz = FU_QC_S5GEN2_HID_DEVICE_MAX_TRANSFER_SIZE - FU_STRUCT_QC_DATA_SIZE - 2; + *data_sz = FU_STRUCT_QC_HID_DATA_TRANSFER_SIZE - FU_STRUCT_QC_DATA_SIZE - 2; return TRUE; } diff -Nru fwupd-2.0.8/plugins/qc-s5gen2/fu-qc-s5gen2-plugin.c fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-plugin.c --- fwupd-2.0.8/plugins/qc-s5gen2/fu-qc-s5gen2-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-s5gen2/fu-qc-s5gen2-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,6 +21,7 @@ static void fu_qc_s5gen2_plugin_init(FuQcS5gen2Plugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void @@ -28,7 +29,8 @@ { FuPlugin *plugin = FU_PLUGIN(obj); FuContext *ctx = fu_plugin_get_context(plugin); - fu_context_add_quirk_key(ctx, "AudioS5gen2Gaia3VendorId"); + fu_context_add_quirk_key(ctx, "QcS5gen2Gaia3VendorId"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_QC_S5GEN2_BLE_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_QC_S5GEN2_HID_DEVICE); fu_plugin_set_device_gtype_default(plugin, FU_TYPE_QC_S5GEN2_DEVICE); diff -Nru fwupd-2.0.8/plugins/qc-s5gen2/meson.build fwupd-2.0.20/plugins/qc-s5gen2/meson.build --- fwupd-2.0.8/plugins/qc-s5gen2/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-s5gen2/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -21,4 +21,12 @@ dependencies: plugin_deps, ) -device_tests += files('tests/qualcomm-qcc5171.json') +enumeration_data += files( + 'tests/ugreen-15765a-setup.json', +) + +device_tests += files( + 'tests/google-gid8.json', + 'tests/qualcomm-qcc5171.json', + 'tests/ugreen-15765a.json', +) diff -Nru fwupd-2.0.8/plugins/qc-s5gen2/qc-s5gen2.quirk fwupd-2.0.20/plugins/qc-s5gen2/qc-s5gen2.quirk --- fwupd-2.0.8/plugins/qc-s5gen2/qc-s5gen2.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-s5gen2/qc-s5gen2.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -9,13 +9,13 @@ Plugin = qc_s5gen2 Flags = enforce-requires ProxyGType = FuQcS5gen2BleDevice -AudioS5gen2Gaia3VendorId = 0x001D +QcS5gen2Gaia3VendorId = 0x001D # by GAIA primary service with default vendor ID [BLUETOOTH\GATT_00001100-d102-11e1-9b23-00025b00a5a5] Plugin = qc_s5gen2 ProxyGType = FuQcS5gen2BleDevice -AudioS5gen2Gaia3VendorId = 0x001D +QcS5gen2Gaia3VendorId = 0x001D # GID8 headset [USB\VID_18D1&PID_800C] diff -Nru fwupd-2.0.8/plugins/qc-s5gen2/tests/google-gid8.json fwupd-2.0.20/plugins/qc-s5gen2/tests/google-gid8.json --- fwupd-2.0.8/plugins/qc-s5gen2/tests/google-gid8.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-s5gen2/tests/google-gid8.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +{ + "name": "Google GID8", + "interactive": false, + "steps": [ + { + "url": "866846396ac6e4b869651d405707186d6d003db97bb9e67f1d0f26503bbd0b60-GID8-Firmware-v300.cab", + "emulation-url": "dd55e2b2af04411c6ed7805362967b9ac3e0f4adb75b06a4524b1b4b23e3e210-gid8-reinstall-3.0.0-3.0.0.zip", + "components": [ + { + "version": "3.0.0", + "guids": [ + "7be16507-61bf-5931-808a-495277003392" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/qc-s5gen2/tests/qualcomm-qcc5171.json fwupd-2.0.20/plugins/qc-s5gen2/tests/qualcomm-qcc5171.json --- fwupd-2.0.8/plugins/qc-s5gen2/tests/qualcomm-qcc5171.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-s5gen2/tests/qualcomm-qcc5171.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/084ebf794bfcb56b7c749238b4c6ed211342738cece664d02b0cbe285eb646f8-Qualcomm_qcc517X_1.1.2.cab", + "url": "084ebf794bfcb56b7c749238b4c6ed211342738cece664d02b0cbe285eb646f8-Qualcomm_qcc517X_1.1.2.cab", "components": [ { "version": "1.1.2", diff -Nru fwupd-2.0.8/plugins/qc-s5gen2/tests/ugreen-15765a-setup.json fwupd-2.0.20/plugins/qc-s5gen2/tests/ugreen-15765a-setup.json --- fwupd-2.0.8/plugins/qc-s5gen2/tests/ugreen-15765a-setup.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-s5gen2/tests/ugreen-15765a-setup.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,287 @@ +{ + "FwupdVersion": "2.0.18", + "UsbDevices": [ + { + "Created": "2025-11-16T16:51:28.061576Z", + "GType": "FuUsbDevice", + "PlatformId": "1-2.1", + "IdVendor": 2578, + "IdProduct": 16391, + "Device": 10534, + "USB": 512, + "Manufacturer": 1, + "Product": 2, + "SerialNumber": 5, + "UsbConfigDescriptors": [ + { + "ConfigurationValue": 1 + } + ], + "UsbHidDescriptors": [ + "BgD/CQGhARUAJv8AdQgJApY+AIUBkQIJApb+AIUFkQIJApUQhQKBAgkClgwAhQaBAgkClT6FA7ECCQKVPoUEsQLACQOhAQkClr4BhQeRAgkClr4BhQiBAgkClQuFCYECwA==" + ], + "UsbInterfaces": [ + { + "Length": 9, + "DescriptorType": 4, + "InterfaceClass": 3, + "UsbEndpoints": [ + { + "DescriptorType": 5, + "EndpointAddress": 129, + "Interval": 1, + "MaxPacketSize": 64 + } + ] + }, + { + "Length": 9, + "DescriptorType": 4, + "InterfaceNumber": 1, + "InterfaceClass": 3, + "UsbEndpoints": [ + { + "DescriptorType": 5, + "EndpointAddress": 1, + "Interval": 1, + "MaxPacketSize": 64 + }, + { + "DescriptorType": 5, + "EndpointAddress": 130, + "Interval": 1, + "MaxPacketSize": 64 + } + ] + }, + { + "Length": 9, + "DescriptorType": 4, + "InterfaceNumber": 2, + "InterfaceClass": 1, + "InterfaceSubClass": 1 + }, + { + "Length": 9, + "DescriptorType": 4, + "InterfaceNumber": 3, + "InterfaceClass": 1, + "InterfaceSubClass": 2 + }, + { + "Length": 9, + "DescriptorType": 4, + "InterfaceNumber": 3, + "AlternateSetting": 1, + "InterfaceClass": 1, + "InterfaceSubClass": 2, + "UsbEndpoints": [ + { + "DescriptorType": 5, + "EndpointAddress": 131, + "Interval": 1, + "MaxPacketSize": 96 + } + ] + }, + { + "Length": 9, + "DescriptorType": 4, + "InterfaceNumber": 4, + "InterfaceClass": 1, + "InterfaceSubClass": 2 + }, + { + "Length": 9, + "DescriptorType": 4, + "InterfaceNumber": 4, + "AlternateSetting": 1, + "InterfaceClass": 1, + "InterfaceSubClass": 2, + "UsbEndpoints": [ + { + "DescriptorType": 5, + "EndpointAddress": 2, + "Interval": 1, + "MaxPacketSize": 384 + } + ] + }, + { + "Length": 9, + "DescriptorType": 4, + "InterfaceNumber": 4, + "AlternateSetting": 2, + "InterfaceClass": 1, + "InterfaceSubClass": 2, + "UsbEndpoints": [ + { + "DescriptorType": 5, + "EndpointAddress": 2, + "Interval": 1, + "MaxPacketSize": 576 + } + ] + } + ], + "UsbEvents": [ + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "usb" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "usb" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=57\nDEVNAME=bus/usb/001/058\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=a12/4007/2926\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=058" + }, + { + "Id": "ReadProp:Key=DEVNAME", + "Data": "bus/usb/001/058" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadProp:Key=BUSNUM", + "Data": "001" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=57\nDEVNAME=bus/usb/001/058\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=a12/4007/2926\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=058" + }, + { + "Id": "ReadProp:Key=DEVNUM", + "Data": "058" + }, + { + "Id": "LoadDescriptor:basename=descriptors", + "Data": "EgEAAgAAAEASCgdAJikBAgUBCQI6AQUBAMAACQQAAAEDAAAACSERAQABIrAABwWBA0AAAQkEAQACAwAAAAkhEQEAASJhAAcFAQNAAAEHBYIDQAABCQQCAAABAQAACiQBAAFHAAIDBAwkAgQCBAABAQAAAAkkBgUEAQEAAAkkAwYBAQAFAAwkAgcBAQACAwAAAAokBggHAQMAAAAJJAMJAgMACAAJBAMAAAECAAAJBAMBAQECAAAHJAEGAAEAFCQCAQECEASAuwAAfQCAPgBAHwAHJQEBAgAACQWDAWAAAQAACQQEAAABAgAACQQEAQEBAgAAByQBBwABABEkAgECAhADAHcBgLsARKwAByUBAQIAAAkFAgGAAQEAAAkEBAIBAQIAAAckAQcAAQARJAIBAgMYAwB3AYC7AESsAAclAQECAAAJBQIBQAIBAAA=" + }, + { + "Id": "LoadDescriptor:basename=bos_descriptors" + }, + { + "Id": "GetSymlinkTarget:Attr=subsystem", + "Data": "usb" + }, + { + "Id": "GetSymlinkTarget:Attr=driver", + "Data": "usb" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=57\nDEVNAME=bus/usb/001/058\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=a12/4007/2926\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=058" + }, + { + "Id": "ReadProp:Key=DEVNAME", + "Data": "bus/usb/001/058" + }, + { + "Id": "ReadAttr:Attr=vendor" + }, + { + "Id": "ReadAttr:Attr=device" + }, + { + "Id": "ReadAttr:Attr=class" + }, + { + "Id": "ReadProp:Key=DEVTYPE", + "Data": "usb_device" + }, + { + "Id": "ReadProp:Key=BUSNUM", + "Data": "001" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=57\nDEVNAME=bus/usb/001/058\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=a12/4007/2926\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=058" + }, + { + "Id": "ReadProp:Key=DEVNUM", + "Data": "058" + }, + { + "Id": "LoadDescriptor:basename=descriptors", + "Data": "EgEAAgAAAEASCgdAJikBAgUBCQI6AQUBAMAACQQAAAEDAAAACSERAQABIrAABwWBA0AAAQkEAQACAwAAAAkhEQEAASJhAAcFAQNAAAEHBYIDQAABCQQCAAABAQAACiQBAAFHAAIDBAwkAgQCBAABAQAAAAkkBgUEAQEAAAkkAwYBAQAFAAwkAgcBAQACAwAAAAokBggHAQMAAAAJJAMJAgMACAAJBAMAAAECAAAJBAMBAQECAAAHJAEGAAEAFCQCAQECEASAuwAAfQCAPgBAHwAHJQEBAgAACQWDAWAAAQAACQQEAAABAgAACQQEAQEBAgAAByQBBwABABEkAgECAhADAHcBgLsARKwAByUBAQIAAAkFAgGAAQEAAAkEBAIBAQIAAAckAQcAAQARJAIBAgMYAwB3AYC7AESsAAclAQECAAAJBQIBQAIBAAA=" + }, + { + "Id": "LoadDescriptor:basename=bos_descriptors" + }, + { + "Id": "ReadProp:Key=BUSNUM", + "Data": "001" + }, + { + "Id": "ReadAttr:Attr=uevent", + "Data": "MAJOR=189\nMINOR=57\nDEVNAME=bus/usb/001/058\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=a12/4007/2926\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=058" + }, + { + "Id": "ReadProp:Key=DEVNUM", + "Data": "058" + }, + { + "Id": "GetStringDescriptor:DescIndex=0x05", + "Data": "ODUyMUU3RUQzMzNBMjI4RUU2ODkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + }, + { + "Id": "GetStringDescriptor:DescIndex=0x01", + "Data": "VGFpWWlMaWFuAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + }, + { + "Id": "GetStringDescriptor:DescIndex=0x02", + "Data": "VUdSRUVOLTE1NzY1QQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + }, + { + "Id": "ControlTransfer:Direction=0x01,RequestType=0x01,Recipient=0x01,Request=0x09,Value=0x0303,Idx=0x0001,Data=AwMCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,Length=0x3f", + "Data": "AwMCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + { + "Id": "InterruptTransfer:Endpoint=0x82,Data=AAAAAAAAAAAAAAAAAA==,Length=0xd", + "Data": "BgEAAAAAAAAAAAAAAA==" + }, + { + "Id": "InterruptTransfer:Endpoint=0x01,Data=BQMZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,Length=0xff", + "Data": "BQMZAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + { + "Id": "InterruptTransfer:Endpoint=0x82,Data=AAAAAAAAAAAAAAAAAA==,Length=0xd", + "Data": "BgkaAAYAAQAAAAEAAA==" + }, + { + "Id": "ControlTransfer:Direction=0x01,RequestType=0x01,Recipient=0x01,Request=0x09,Value=0x0303,Idx=0x0001,Data=AwMHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA,Length=0x3f", + "Data": "AwMHAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + }, + { + "Id": "ControlTransfer:Direction=0x00,RequestType=0x00,Recipient=0x01,Request=0x06,Value=0x2200,Idx=0x0000,Data=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=,Length=0xb0", + "Error": -1 + }, + { + "Id": "ControlTransfer:Direction=0x00,RequestType=0x00,Recipient=0x01,Request=0x06,Value=0x2200,Idx=0x0001,Data=AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==,Length=0x61", + "Data": "BgD/CQGhARUAJv8AdQgJApY+AIUBkQIJApb+AIUFkQIJApUQhQKBAgkClgwAhQaBAgkClT6FA7ECCQKVPoUEsQLACQOhAQkClr4BhQeRAgkClr4BhQiBAgkClQuFCYECwA==" + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/qc-s5gen2/tests/ugreen-15765a.json fwupd-2.0.20/plugins/qc-s5gen2/tests/ugreen-15765a.json --- fwupd-2.0.8/plugins/qc-s5gen2/tests/ugreen-15765a.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/qc-s5gen2/tests/ugreen-15765a.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +{ + "name": "UGREEN 15765A", + "interactive": false, + "steps": [ + { + "emulation-file": "@enumeration_datadir@/ugreen-15765a-setup.json", + "components": [ + { + "version": "1.0.1", + "guids": [ + "ea64faca-1a31-5b76-9d03-c9455c962b4c" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/qsi-dock/README.md fwupd-2.0.20/plugins/qsi-dock/README.md --- fwupd-2.0.8/plugins/qsi-dock/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qsi-dock/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -34,10 +34,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.8.8`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Kevin Chen: @hssinf diff -Nru fwupd-2.0.8/plugins/qsi-dock/fu-qsi-dock-child-device.c fwupd-2.0.20/plugins/qsi-dock/fu-qsi-dock-child-device.c --- fwupd-2.0.8/plugins/qsi-dock/fu-qsi-dock-child-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qsi-dock/fu-qsi-dock-child-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -34,14 +34,12 @@ fu_qsi_dock_child_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { - FuDevice *parent = fu_device_get_parent(device); - if (parent == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no parent"); + FuDevice *parent = fu_device_get_parent(device, error); + if (parent == NULL) return NULL; - } return fu_device_prepare_firmware(parent, stream, progress, flags, error); } @@ -54,11 +52,9 @@ GError **error) { FuQsiDockChildDevice *self = FU_QSI_DOCK_CHILD_DEVICE(device); - FuDevice *parent = fu_device_get_parent(device); - if (parent == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no parent"); + FuDevice *parent = fu_device_get_parent(device, error); + if (parent == NULL) return FALSE; - } return fu_qsi_dock_mcu_device_write_firmware_with_idx(FU_QSI_DOCK_MCU_DEVICE(parent), firmware, self->chip_idx, diff -Nru fwupd-2.0.8/plugins/qsi-dock/fu-qsi-dock-mcu-device.c fwupd-2.0.20/plugins/qsi-dock/fu-qsi-dock-mcu-device.c --- fwupd-2.0.8/plugins/qsi-dock/fu-qsi-dock-mcu-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qsi-dock/fu-qsi-dock-mcu-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -45,7 +45,7 @@ static gboolean fu_qsi_dock_mcu_device_rx(FuQsiDockMcuDevice *self, guint8 *outbuf, gsize outbufsz, GError **error) { - guint8 buf[64]; + guint8 buf[64] = {0}; if (!fu_hid_device_get_report(FU_HID_DEVICE(self), FU_QSI_DOCK_REPORT_ID, @@ -206,8 +206,8 @@ FU_QSI_DOCK_CMD1_SPI, FU_QSI_DOCK_CMD2_SPI_EXTERNAL_FLASH_CHECKSUM, 0}; - guint8 fw_length[4]; - guint8 checksum_val[2]; + guint8 fw_length[4] = {0}; + guint8 checksum_val[2] = {0}; fu_memwrite_uint32(fw_length, length, G_LITTLE_ENDIAN); fu_memwrite_uint16(checksum_val, checksum, G_LITTLE_ENDIAN); @@ -255,7 +255,10 @@ /* MCU Checksum Compare Result 0:Pass 1:Fail */ if (buf[2] != 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "checksum did not match"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "checksum did not match"); return FALSE; } @@ -423,8 +426,8 @@ FU_QSI_DOCK_CMD1_SPI, FU_QSI_DOCK_CMD2_SPI_EXTERNAL_FLASH_ERASE}; guint32 offset = 0; - guint8 fw_length[4]; - guint8 flash_offset[4]; + guint8 fw_length[4] = {0}; + guint8 flash_offset[4] = {0}; fu_memwrite_uint32(fw_length, length, G_LITTLE_ENDIAN); fu_memwrite_uint32(flash_offset, offset, G_LITTLE_ENDIAN); @@ -503,7 +506,7 @@ 30, NULL, error)) { - g_prefix_error(error, "failed to wait for initial: "); + g_prefix_error_literal(error, "failed to wait for initial: "); return FALSE; } if (!fu_qsi_dock_mcu_device_wait_for_spi_erase_ready_cb(self, @@ -562,7 +565,7 @@ } static void -fu_qsi_dock_mcu_device_set_progress(FuDevice *self, FuProgress *progress) +fu_qsi_dock_mcu_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); diff -Nru fwupd-2.0.8/plugins/qsi-dock/fu-qsi-dock-plugin.c fwupd-2.0.20/plugins/qsi-dock/fu-qsi-dock-plugin.c --- fwupd-2.0.8/plugins/qsi-dock/fu-qsi-dock-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/qsi-dock/fu-qsi-dock-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -20,12 +20,14 @@ static void fu_qsi_dock_plugin_init(FuQsiDockPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void fu_qsi_dock_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_set_device_gtype_default(plugin, FU_TYPE_QSI_DOCK_MCU_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_QSI_DOCK_CHILD_DEVICE); /* coverage */ } diff -Nru fwupd-2.0.8/plugins/realtek-mst/README.md fwupd-2.0.20/plugins/realtek-mst/README.md --- fwupd-2.0.8/plugins/realtek-mst/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/realtek-mst/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -16,8 +16,9 @@ Device OUI fields of DPCD (DisplayPort Configuration Data), they do not have unique Device Identification strings. -This plugin was neither written, verified, supported or endorsed by Realtek -Semiconductor Corp. +> [!CAUTION] +> This plugin was neither written, verified, supported or endorsed by Realtek +> Semiconductor Corp. ## Firmware Format @@ -51,10 +52,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.6.2`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Peter Marheine: @tari diff -Nru fwupd-2.0.8/plugins/realtek-mst/fu-realtek-mst-device.c fwupd-2.0.20/plugins/realtek-mst/fu-realtek-mst-device.c --- fwupd-2.0.8/plugins/realtek-mst/fu-realtek-mst-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/realtek-mst/fu-realtek-mst-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -234,7 +234,7 @@ g_autofree gchar *version = NULL; if (!fu_i2c_device_set_address(FU_I2C_DEVICE(self), I2C_ADDR_DEBUG, FALSE, error)) { - g_prefix_error(error, "failed to ensure address: "); + g_prefix_error_literal(error, "failed to ensure address: "); return FALSE; } @@ -509,7 +509,7 @@ MCU_MODE_WRITE_BUF, 10, error)) { - g_prefix_error(error, "failed waiting for write buffer to clear: "); + g_prefix_error_literal(error, "failed waiting for write buffer to clear: "); return FALSE; } /* write data into FIFO */ @@ -550,7 +550,7 @@ if (!fu_i2c_device_set_address(FU_I2C_DEVICE(self), I2C_ADDR_ISP, FALSE, error)) return FALSE; - /* Switch to programming mode (stops regular operation) */ + /* switch to programming mode (stops regular operation) */ if (!fu_realtek_mst_device_write_register(self, FU_REALTEK_MST_REG_MCU_MODE, MCU_MODE_ISP, @@ -653,10 +653,10 @@ error)) return FALSE; if (memcmp(g_bytes_get_data(firmware_bytes, NULL), readback_buf, FLASH_USER_SIZE) != 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "flash contents after write do not match firmware image"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_WRITE, + "flash contents do not match firmware image"); return FALSE; } fu_progress_step_done(progress); @@ -793,7 +793,7 @@ } static void -fu_realtek_mst_device_set_progress(FuDevice *self, FuProgress *progress) +fu_realtek_mst_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -816,10 +816,9 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_NO_GENERIC_GUIDS); fu_device_add_protocol(FU_DEVICE(self), "com.realtek.rtd2142"); - fu_device_set_vendor(FU_DEVICE(self), "Realtek"); fu_device_build_vendor_id_u16(FU_DEVICE(self), "PCI", 0x10EC); fu_device_set_summary(FU_DEVICE(self), "DisplayPort MST hub"); - fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_VIDEO_DISPLAY); fu_device_set_firmware_size(FU_DEVICE(self), FLASH_USER_SIZE); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); } diff -Nru fwupd-2.0.8/plugins/realtek-mst/meson.build fwupd-2.0.20/plugins/realtek-mst/meson.build --- fwupd-2.0.8/plugins/realtek-mst/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/realtek-mst/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginRealtekMst"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -19,4 +20,3 @@ ], dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/redfish/fu-ipmi-device.c fwupd-2.0.20/plugins/redfish/fu-ipmi-device.c --- fwupd-2.0.8/plugins/redfish/fu-ipmi-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-ipmi-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -34,7 +34,8 @@ #define IPMI_PASSWORD_SET_PASSWORD 0x02 #define IPMI_PASSWORD_TEST_PASSWORD 0x03 -/* these are not provided in ipmi_msgdefs.h */ +/* these are not provided in ipmi_msgdefs.h, + * nocheck:magic-defines=50 */ #define IPMI_INVALID_COMMAND_ON_LUN_ERR 0xC2 #define IPMI_OUT_OF_SPACE_ERR 0xC4 #define IPMI_CANCELLED_OR_INVALID_ERR 0xC5 @@ -190,7 +191,7 @@ } static gboolean -fu_ipmi_device_lock(GObject *device, GError **error) +fu_ipmi_device_lock_cb(FuDevice *device, GError **error) { FuIpmiDevice *self = FU_IPMI_DEVICE(device); FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); @@ -206,7 +207,7 @@ } static gboolean -fu_ipmi_device_unlock(GObject *device, GError **error) +fu_ipmi_device_unlock_cb(FuDevice *device, GError **error) { FuIpmiDevice *self = FU_IPMI_DEVICE(device); FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); @@ -336,7 +337,10 @@ g_autoptr(FuDeviceLocker) lock = NULL; g_autofree guint8 *resp_buf2 = g_malloc0(resp_buf2sz); - lock = fu_device_locker_new_full(self, fu_ipmi_device_lock, fu_ipmi_device_unlock, error); + lock = fu_device_locker_new_full(device, + fu_ipmi_device_lock_cb, + fu_ipmi_device_unlock_cb, + error); if (lock == NULL) return FALSE; @@ -560,7 +564,7 @@ &resp_len, FU_IPMI_DEVICE_TIMEOUT, error)) { - g_prefix_error(error, "failed to get username: "); + g_prefix_error_literal(error, "failed to get username: "); return NULL; } if (resp_len != sizeof(resp)) { @@ -599,7 +603,7 @@ 0x0, /* src */ username_sz, error)) { - g_prefix_error(error, "username invalid: "); + g_prefix_error_literal(error, "username invalid: "); return FALSE; } @@ -673,7 +677,7 @@ 0x0, /* src */ password_sz, error)) { - g_prefix_error(error, "password invalid: "); + g_prefix_error_literal(error, "password invalid: "); return FALSE; } @@ -769,7 +773,7 @@ { fu_device_set_name(FU_DEVICE(self), "IPMI"); fu_device_set_summary(FU_DEVICE(self), "Intelligent Platform Management Interface"); - fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-backend.c fwupd-2.0.20/plugins/redfish/fu-redfish-backend.c --- fwupd-2.0.8/plugins/redfish/fu-redfish-backend.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-backend.c 2026-02-26 11:36:18.000000000 +0000 @@ -118,8 +118,13 @@ NULL); /* Dell specific currently */ - if (self->system_id != NULL) + if (self->system_id != NULL) { fu_device_add_instance_str(dev, "SYSTEMID", self->system_id); + /* Ensure the reboot is not done immediately after installation, and only after a + * wanted reboot */ + fu_redfish_multipart_device_set_apply_time(FU_REDFISH_MULTIPART_DEVICE(dev), + "OnReset"); + } /* some vendors do not specify the Targets array when updating */ if (self->wildcard_targets) @@ -244,7 +249,7 @@ FuRedfishBackend *self = FU_REDFISH_BACKEND(userdata); if ((size * nmemb) > 16 && g_ascii_strncasecmp(ptr, "X-Auth-Token:", 13) == 0) { g_autofree gchar *session_key = NULL; - /* The string also includes \r\n at the end */ + /* string also includes \r\n at the end */ session_key = g_strndup(ptr + 14, (size * nmemb) - 16); fu_redfish_backend_set_session_key(self, session_key); } @@ -278,7 +283,10 @@ error)) return FALSE; if (fu_redfish_backend_get_session_key(self) == NULL) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed to get session key"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to get session key"); return FALSE; } diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-common.c fwupd-2.0.20/plugins/redfish/fu-redfish-common.c --- fwupd-2.0.8/plugins/redfish/fu-redfish-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -62,7 +62,7 @@ if (g_strcmp0(version, "-*") == 0) return NULL; - /* find the section preficed with "v" */ + /* find the section prefixed with "v" */ split = g_strsplit(version, " ", -1); for (guint i = 0; split[i] != NULL; i++) { if (g_str_has_prefix(split[i], "v")) { @@ -95,29 +95,35 @@ /* sanity check */ if (g_strv_length(versplit) != 2) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "not two sections"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "not two sections"); return FALSE; } if (strlen(versplit[0]) != 3) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid length first section"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "invalid length first section"); return FALSE; } /* milestone */ if (!g_ascii_isdigit(versplit[0][0]) || !g_ascii_isdigit(versplit[0][1])) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "milestone number invalid"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "milestone number invalid"); return FALSE; } /* build is only one letter from A -> Z */ if (!g_ascii_isalpha(versplit[0][2])) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "build letter invalid"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "build letter invalid"); return FALSE; } diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-device.c fwupd-2.0.20/plugins/redfish/fu-redfish-device.c --- fwupd-2.0.8/plugins/redfish/fu-redfish-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -40,23 +40,23 @@ fu_redfish_device_set_device_class(FuRedfishDevice *self, const gchar *tmp) { if (g_strcmp0(tmp, "NetworkController") == 0) { - fu_device_add_icon(FU_DEVICE(self), "network-wired"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_NETWORK_WIRED); return; } if (g_strcmp0(tmp, "MassStorageController") == 0) { - fu_device_add_icon(FU_DEVICE(self), "drive-multidisk"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_DRIVE_MULTIDISK); return; } if (g_strcmp0(tmp, "DisplayController") == 0) { - fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_VIDEO_DISPLAY); return; } if (g_strcmp0(tmp, "DockingStation") == 0) { - fu_device_add_icon(FU_DEVICE(self), "dock"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_DOCK); return; } if (g_strcmp0(tmp, "WirelessController") == 0) { - fu_device_add_icon(FU_DEVICE(self), "network-wireless"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_NETWORK_WIRELESS); return; } g_debug("no icon mapping for %s", tmp); @@ -264,7 +264,10 @@ /* build is only one letter from A -> Z */ if (!g_ascii_isalpha(out_build[2])) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "build letter invalid"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "build letter invalid"); return FALSE; } priv->build = g_strndup(out_build + 2, 1); @@ -330,10 +333,10 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); } else if (g_str_has_prefix(name, "DISK-")) { name += 5; - fu_device_add_icon(FU_DEVICE(self), "drive-harddisk"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_DRIVE_HARDDISK); } else if (g_str_has_prefix(name, "POWER-")) { name += 6; - fu_device_add_icon(FU_DEVICE(self), "ac-adapter"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_AC_ADAPTER); fu_device_set_summary(FU_DEVICE(self), "Redfish power supply unit"); } else { fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); @@ -365,14 +368,18 @@ fu_device_build_vendor_id(FU_DEVICE(self), "REDFISH", vendor_upper); } -static void -fu_redfish_device_smc_license_check(FuRedfishDevice *self) +static gboolean +fu_redfish_device_smc_license_check(FuRedfishDevice *self, GError **error) { - FuRedfishBackend *backend = fu_redfish_device_get_backend(self); - g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(backend); + FuRedfishBackend *backend; + g_autoptr(FuRedfishRequest) request = NULL; g_autoptr(GError) error_local = NULL; /* see if we don't get an license error */ + backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self), error); + if (backend == NULL) + return FALSE; + request = fu_redfish_backend_request_new(backend); if (!fu_redfish_request_perform(request, fu_redfish_backend_get_push_uri_path(backend), FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, @@ -384,6 +391,7 @@ g_debug("supermicro license check returned %s", error_local->message); } } + return TRUE; } static gboolean @@ -421,6 +429,17 @@ FU_REDFISH_DEVICE_FLAG_IS_BACKUP); } + if (json_object_has_member(software_info, "Id")) { + const gchar *status = json_object_get_string_member(software_info, "Id"); + if (g_ascii_strncasecmp(status, "DCIM:INSTALLED", 12) != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware is in repository"); + return FALSE; + } + } + /* it does not seem that Dell allows targeting a device when updating */ fu_device_add_private_flag(FU_DEVICE(self), FU_REDFISH_DEVICE_FLAG_WILDCARD_TARGETS); @@ -572,10 +591,16 @@ if (json_object_get_boolean_member(member, "Updateable")) fu_device_add_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE); } - if (fu_device_has_private_flag(dev, FU_REDFISH_DEVICE_FLAG_IS_BACKUP)) - fu_device_inhibit(dev, "is-backup", "Is a backup partition"); - else - fu_device_uninhibit(dev, "is-backup"); + + /* not useful to export */ + if (fu_device_has_private_flag(dev, FU_REDFISH_DEVICE_FLAG_IS_BACKUP)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "%s is a backup partition", + fu_device_get_backend_id(dev)); + return FALSE; + } /* use related items to set extra instance IDs */ if (fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE) && @@ -594,17 +619,23 @@ } /* for Supermicro check whether we have a proper Redfish license installed */ - if (g_strcmp0("SMCI", fu_device_get_vendor(dev)) == 0) - fu_redfish_device_smc_license_check(self); + if (g_strcmp0("SMCI", fu_device_get_vendor(dev)) == 0) { + if (!fu_redfish_device_smc_license_check(self, error)) + return FALSE; + } /* success */ return TRUE; } FuRedfishBackend * -fu_redfish_device_get_backend(FuRedfishDevice *self) +fu_redfish_device_get_backend(FuRedfishDevice *self, GError **error) { FuRedfishDevicePrivate *priv = GET_PRIVATE(self); + if (priv->backend == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no backend"); + return NULL; + } return priv->backend; } @@ -630,7 +661,9 @@ return TRUE; /* set flags */ - if (g_pattern_match_simple("Base.*.ResetRequired", message_id)) { + if (g_pattern_match_simple("Base.*.ResetRequired", message_id) || + g_pattern_match_simple("IDRAC.*.JCP001", message_id) || + g_pattern_match_simple("IDRAC.*.RED014", message_id)) { fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); return TRUE; } @@ -779,7 +812,8 @@ } state_tmp = json_object_get_string_member(json_obj, "TaskState"); g_debug("TaskState now %s", state_tmp); - if (g_strcmp0(state_tmp, "Completed") == 0) { + if (g_strcmp0(state_tmp, "Completed") == 0 || + fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) { ctx->completed = TRUE; return TRUE; } diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-device.h fwupd-2.0.20/plugins/redfish/fu-redfish-device.h --- fwupd-2.0.8/plugins/redfish/fu-redfish-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -24,7 +24,7 @@ #define FU_REDFISH_DEVICE_FLAG_NO_MANAGER_RESET_REQUEST "no-manager-reset-request" FuRedfishBackend * -fu_redfish_device_get_backend(FuRedfishDevice *self); +fu_redfish_device_get_backend(FuRedfishDevice *self, GError **error); gboolean fu_redfish_device_parse_message_id(FuRedfishDevice *self, const gchar *message_id, diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-hpe-device.c fwupd-2.0.20/plugins/redfish/fu-redfish-hpe-device.c --- fwupd-2.0.8/plugins/redfish/fu-redfish-hpe-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-hpe-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -24,11 +24,15 @@ fu_redfish_hpe_device_attach(FuDevice *dev, FuProgress *progress, GError **error) { FuRedfishHpeDevice *self = FU_REDFISH_HPE_DEVICE(dev); - FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); + FuRedfishBackend *backend; JsonObject *json_obj; - g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(backend); + g_autoptr(FuRedfishRequest) request = NULL; /* create URI and poll */ + backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self), error); + if (backend == NULL) + return FALSE; + request = fu_redfish_backend_request_new(backend); if (!fu_redfish_request_perform(request, "/redfish/v1/UpdateService", FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, @@ -48,12 +52,12 @@ g_strcmp0(status, "Complete") == 0) { return TRUE; } - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "device is busy"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "device is busy"); return FALSE; } } - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unknown failure"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unknown failure"); return FALSE; } @@ -87,12 +91,16 @@ FuRedfishHpeDevicePollCtx *ctx, GError **error) { - FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); + FuRedfishBackend *backend; JsonObject *json_obj; const gchar *message = "Unknown failure"; - g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(backend); + g_autoptr(FuRedfishRequest) request = NULL; /* create URI and poll */ + backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self), error); + if (backend == NULL) + return FALSE; + request = fu_redfish_backend_request_new(backend); if (!fu_redfish_request_perform(request, "/redfish/v1/UpdateService", FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, @@ -196,7 +204,7 @@ GError **error) { FuRedfishHpeDevice *self = FU_REDFISH_HPE_DEVICE(device); - FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); + FuRedfishBackend *backend; CURL *curl; const gchar *sessionkey; curl_mimepart *part; @@ -223,6 +231,9 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 82, NULL); /* create session */ + backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self), error); + if (backend == NULL) + return FALSE; if (!fu_redfish_backend_create_session(backend, error)) return FALSE; sessionkey = fu_redfish_backend_get_session_key(backend); @@ -299,7 +310,7 @@ } static void -fu_redfish_hpe_device_set_progress(FuDevice *self, FuProgress *progress) +fu_redfish_hpe_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 3, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-legacy-device.c fwupd-2.0.20/plugins/redfish/fu-redfish-legacy-device.c --- fwupd-2.0.8/plugins/redfish/fu-redfish-legacy-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-legacy-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,8 +21,8 @@ fu_redfish_legacy_device_detach(FuDevice *dev, FuProgress *progress, GError **error) { FuRedfishLegacyDevice *self = FU_REDFISH_LEGACY_DEVICE(dev); - FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); - g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(backend); + FuRedfishBackend *backend; + g_autoptr(FuRedfishRequest) request = NULL; g_autoptr(JsonBuilder) builder = json_builder_new(); /* create header */ @@ -36,6 +36,10 @@ json_builder_end_object(builder); /* patch the two fields */ + backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self), error); + if (backend == NULL) + return FALSE; + request = fu_redfish_backend_request_new(backend); return fu_redfish_request_perform_full(request, "/redfish/v1/UpdateService", "PATCH", @@ -49,8 +53,8 @@ fu_redfish_legacy_device_attach(FuDevice *dev, FuProgress *progress, GError **error) { FuRedfishLegacyDevice *self = FU_REDFISH_LEGACY_DEVICE(dev); - FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); - g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(backend); + FuRedfishBackend *backend; + g_autoptr(FuRedfishRequest) request = NULL; g_autoptr(JsonBuilder) builder = json_builder_new(); /* create header */ @@ -63,6 +67,10 @@ json_builder_end_object(builder); /* patch the two fields */ + backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self), error); + if (backend == NULL) + return FALSE; + request = fu_redfish_backend_request_new(backend); return fu_redfish_request_perform_full(request, "/redfish/v1/UpdateService", "PATCH", @@ -80,7 +88,7 @@ GError **error) { FuRedfishLegacyDevice *self = FU_REDFISH_LEGACY_DEVICE(device); - FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); + FuRedfishBackend *backend; CURL *curl; JsonObject *json_obj; const gchar *location; @@ -93,6 +101,9 @@ return FALSE; /* POST data */ + backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self), error); + if (backend == NULL) + return FALSE; request = fu_redfish_backend_request_new(backend); curl = fu_redfish_request_get_curl(request); (void)curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST"); @@ -120,7 +131,7 @@ } static void -fu_redfish_legacy_device_set_progress(FuDevice *self, FuProgress *progress) +fu_redfish_legacy_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-multipart-device.c fwupd-2.0.20/plugins/redfish/fu-redfish-multipart-device.c --- fwupd-2.0.8/plugins/redfish/fu-redfish-multipart-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-multipart-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,12 +14,31 @@ struct _FuRedfishMultipartDevice { FuRedfishDevice parent_instance; + gchar *apply_time; }; G_DEFINE_TYPE(FuRedfishMultipartDevice, fu_redfish_multipart_device, FU_TYPE_REDFISH_DEVICE) G_DEFINE_AUTOPTR_CLEANUP_FUNC(curl_mime, curl_mime_free) +static void +fu_redfish_multipart_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuRedfishMultipartDevice *self = FU_REDFISH_MULTIPART_DEVICE(device); + fwupd_codec_string_append(str, idt, "ApplyTime", self->apply_time); +} + +void +fu_redfish_multipart_device_set_apply_time(FuRedfishMultipartDevice *self, const gchar *apply_time) +{ + g_return_if_fail(FU_IS_REDFISH_MULTIPART_DEVICE(self)); + g_return_if_fail(apply_time != NULL); + if (g_strcmp0(self->apply_time, apply_time) == 0) + return; + g_free(self->apply_time); + self->apply_time = g_strdup(apply_time); +} + static GString * fu_redfish_multipart_device_get_parameters(FuRedfishMultipartDevice *self) { @@ -37,8 +56,10 @@ json_builder_add_string_value(builder, logical_id); } json_builder_end_array(builder); - json_builder_set_member_name(builder, "@Redfish.OperationApplyTime"); - json_builder_add_string_value(builder, "Immediate"); + if (self->apply_time != NULL) { + json_builder_set_member_name(builder, "@Redfish.OperationApplyTime"); + json_builder_add_string_value(builder, self->apply_time); + } json_builder_end_object(builder); /* export as a string */ @@ -49,6 +70,20 @@ return g_steal_pointer(&str); } +static size_t +fu_redfish_multipart_device_location_headers_callback(char *ptr, + size_t size, + size_t nmemb, + void *location) +{ + char **loc_str = (char **)location; + if ((size * nmemb) > 16 && g_ascii_strncasecmp(ptr, "Location:", 9) == 0) { + /* The string also includes \r\n at the end */ + *loc_str = g_strndup(ptr + 10, (size * nmemb) - 12); + } + return size * nmemb; +} + static gboolean fu_redfish_multipart_device_write_firmware(FuDevice *device, FuFirmware *firmware, @@ -57,7 +92,7 @@ GError **error) { FuRedfishMultipartDevice *self = FU_REDFISH_MULTIPART_DEVICE(device); - FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); + FuRedfishBackend *backend; CURL *curl; JsonObject *json_obj; curl_mimepart *part; @@ -73,6 +108,9 @@ return FALSE; /* create the multipart request */ + backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self), error); + if (backend == NULL) + return FALSE; request = fu_redfish_backend_request_new(backend); curl = fu_redfish_request_get_curl(request); mime = curl_mime_init(curl); @@ -87,10 +125,15 @@ part = curl_mime_addpart(mime); curl_mime_name(part, "UpdateFile"); (void)curl_mime_type(part, "application/octet-stream"); - (void)curl_mime_filename(part, "firmware.bin"); + (void)curl_mime_filename(part, fu_firmware_get_filename(firmware)); + (void)curl_mime_data(part, g_bytes_get_data(fw, NULL), g_bytes_get_size(fw)); (void)curl_easy_setopt(curl, CURLOPT_MIMEPOST, mime); + (void)curl_easy_setopt(curl, + CURLOPT_HEADERFUNCTION, + fu_redfish_multipart_device_location_headers_callback); + (void)curl_easy_setopt(fu_redfish_request_get_curl(request), CURLOPT_HEADERDATA, &location); fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); if (!fu_redfish_request_perform(request, @@ -98,6 +141,7 @@ FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, error)) return FALSE; + if (fu_redfish_request_get_status_code(request) != 202) { g_set_error(error, FWUPD_ERROR, @@ -108,27 +152,29 @@ } /* prefer the header, otherwise fall back to the response */ - json_obj = fu_redfish_request_get_json_object(request); - if (json_object_has_member(json_obj, "TaskMonitor")) { - const gchar *tmp = json_object_get_string_member(json_obj, "TaskMonitor"); - g_debug("task manager for cleanup is %s", tmp); + if (location == NULL || g_utf8_strlen(location, 1) == 0) { + json_obj = fu_redfish_request_get_json_object(request); + if (json_object_has_member(json_obj, "TaskMonitor")) { + const gchar *tmp = json_object_get_string_member(json_obj, "TaskMonitor"); + g_debug("task manager for cleanup is %s", tmp); + } + + /* poll the task for progress */ + if (!json_object_has_member(json_obj, "@odata.id")) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no task returned for %s", + fu_redfish_backend_get_push_uri_path(backend)); + return FALSE; + } + location = json_object_get_string_member(json_obj, "@odata.id"); } - - /* poll the task for progress */ - if (!json_object_has_member(json_obj, "@odata.id")) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no task returned for %s", - fu_redfish_backend_get_push_uri_path(backend)); - return FALSE; - } - location = json_object_get_string_member(json_obj, "@odata.id"); return fu_redfish_device_poll_task(FU_REDFISH_DEVICE(self), location, progress, error); } static void -fu_redfish_multipart_device_set_progress(FuDevice *self, FuProgress *progress) +fu_redfish_multipart_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -141,13 +187,26 @@ static void fu_redfish_multipart_device_init(FuRedfishMultipartDevice *self) { + fu_redfish_multipart_device_set_apply_time(self, "Immediate"); fu_device_set_summary(FU_DEVICE(self), "Redfish multipart device"); } static void +fu_redfish_multipart_device_finalize(GObject *obj) +{ + FuRedfishMultipartDevice *self = FU_REDFISH_MULTIPART_DEVICE(obj); + g_free(self->apply_time); + G_OBJECT_CLASS(fu_redfish_multipart_device_parent_class)->finalize(obj); +} + +static void fu_redfish_multipart_device_class_init(FuRedfishMultipartDeviceClass *klass) { FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + + object_class->finalize = fu_redfish_multipart_device_finalize; + device_class->to_string = fu_redfish_multipart_device_to_string; device_class->write_firmware = fu_redfish_multipart_device_write_firmware; device_class->set_progress = fu_redfish_multipart_device_set_progress; } diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-multipart-device.h fwupd-2.0.20/plugins/redfish/fu-redfish-multipart-device.h --- fwupd-2.0.8/plugins/redfish/fu-redfish-multipart-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-multipart-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -14,3 +14,7 @@ FU, REDFISH_MULTIPART_DEVICE, FuRedfishDevice) + +void +fu_redfish_multipart_device_set_apply_time(FuRedfishMultipartDevice *self, const gchar *apply_time) + G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-plugin.c fwupd-2.0.20/plugins/redfish/fu-redfish-plugin.c --- fwupd-2.0.8/plugins/redfish/fu-redfish-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,6 @@ #include "fu-ipmi-device.h" #endif -#include "fu-ipmi-device.h" #include "fu-redfish-backend.h" #include "fu-redfish-common.h" #include "fu-redfish-device.h" @@ -230,7 +229,7 @@ if (smbios_data_fn != NULL) { g_autoptr(FuRedfishSmbios) smbios = fu_redfish_smbios_new(); if (!fu_firmware_build_from_filename(FU_FIRMWARE(smbios), smbios_data_fn, error)) { - g_prefix_error(error, "failed to build SMBIOS entry type 42: "); + g_prefix_error_literal(error, "failed to build SMBIOS entry type 42: "); return FALSE; } g_set_object(&self->smbios, smbios); @@ -250,9 +249,9 @@ if (!fu_firmware_parse_bytes(FU_FIRMWARE(smbios), type42_blob, 0x0, - FWUPD_INSTALL_FLAG_NO_SEARCH, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) { - g_prefix_error(error, "failed to parse SMBIOS entry type 42: "); + g_prefix_error_literal(error, "failed to parse SMBIOS entry type 42: "); return FALSE; } if (fu_redfish_smbios_get_interface_type(smbios) == @@ -347,7 +346,7 @@ g_autoptr(JsonBuilder) builder = json_builder_new(); /* create device */ - locker = fu_device_locker_new(device, error); + locker = fu_device_locker_new(FU_DEVICE(device), error); if (locker == NULL) return FALSE; @@ -620,7 +619,7 @@ builder, FU_REDFISH_REQUEST_PERFORM_FLAG_NONE, error)) { - g_prefix_error(error, "failed to reset manager: "); + g_prefix_error_literal(error, "failed to reset manager: "); return FALSE; } } @@ -657,7 +656,7 @@ FU_REDFISH_PLUGIN_CLEANUP_RETRIES_DELAY * 1000, self, error)) { - g_prefix_error(error, "manager failed to come back from setup: "); + g_prefix_error_literal(error, "manager failed to come back from setup: "); return FALSE; } fu_progress_step_done(progress); @@ -675,7 +674,7 @@ FU_REDFISH_PLUGIN_CLEANUP_RETRIES_DELAY * 1000, self, error)) { - g_prefix_error(error, "manager failed to come back from coldplug: "); + g_prefix_error_literal(error, "manager failed to come back from coldplug: "); return FALSE; } fu_progress_step_done(progress); diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-request.c fwupd-2.0.20/plugins/redfish/fu-redfish-request.c --- fwupd-2.0.8/plugins/redfish/fu-redfish-request.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-request.c 2026-02-26 11:36:18.000000000 +0000 @@ -135,7 +135,8 @@ else if (g_strcmp0(id, "SMC.1.0.OemLicenseNotPassed") == 0) error_code = FWUPD_ERROR_NOT_SUPPORTED; else if (g_strcmp0(id, "SMC.1.0.OemFirmwareAlreadyInUpdateMode") == 0 || - g_strcmp0(id, "SMC.1.0.OemBiosUpdateIsInProgress") == 0) + g_strcmp0(id, "SMC.1.0.OemBiosUpdateIsInProgress") == 0 || + g_pattern_match_simple("IDRAC.*.RED014", id)) error_code = FWUPD_ERROR_ALREADY_PENDING; g_set_error_literal(error, FWUPD_ERROR, error_code, msg); return FALSE; @@ -201,7 +202,7 @@ } /* load JSON */ - if (flags & FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON) { + if (flags & FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON && self->buf->len > 0) { if (!fu_redfish_request_load_json(self, self->buf, error)) { g_prefix_error(error, "failed to parse %s: ", uri_str); return FALSE; @@ -253,7 +254,7 @@ path, FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, error)) { - g_prefix_error(error, "failed to request etag: "); + g_prefix_error_literal(error, "failed to request etag: "); return FALSE; } json_obj = fu_redfish_request_get_json_object(self); @@ -263,7 +264,7 @@ json_object_get_string_member(json_obj, "@odata.etag")); } - /* allow us to re-use the request */ + /* allow us to reuse the request */ fu_redfish_request_reset(self); } diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-request.h fwupd-2.0.20/plugins/redfish/fu-redfish-request.h --- fwupd-2.0.8/plugins/redfish/fu-redfish-request.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-request.h 2026-02-26 11:36:18.000000000 +0000 @@ -10,16 +10,11 @@ #include +#include "fu-redfish-struct.h" + #define FU_TYPE_REDFISH_REQUEST (fu_redfish_request_get_type()) G_DECLARE_FINAL_TYPE(FuRedfishRequest, fu_redfish_request, FU, REDFISH_REQUEST, GObject) -typedef enum { - FU_REDFISH_REQUEST_PERFORM_FLAG_NONE = 0, - FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON = 1 << 0, - FU_REDFISH_REQUEST_PERFORM_FLAG_USE_CACHE = 1 << 1, - FU_REDFISH_REQUEST_PERFORM_FLAG_USE_ETAG = 1 << 2, -} FuRedfishRequestPerformFlags; - gboolean fu_redfish_request_perform(FuRedfishRequest *self, const gchar *path, diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-smbios.c fwupd-2.0.20/plugins/redfish/fu-redfish-smbios.c --- fwupd-2.0.8/plugins/redfish/fu-redfish-smbios.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-smbios.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,7 @@ #include "fu-redfish-struct.h" struct _FuRedfishSmbios { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; FuRedfishSmbiosInterfaceType interface_type; guint16 port; gchar *hostname; @@ -217,7 +217,7 @@ { guint8 hostname_length; guint8 service_ip_address_format; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructRedfishProtocolOverIp) st = NULL; /* port + IP address */ st = fu_struct_redfish_protocol_over_ip_parse_stream(stream, offset, error); @@ -252,8 +252,8 @@ if (!fu_input_stream_read_safe(stream, (guint8 *)hostname, hostname_length, - 0x0, /* dst */ - offset + st->len, /* seek */ + 0x0, /* dst */ + offset + st->buf->len, /* seek */ hostname_length, error)) return FALSE; @@ -267,13 +267,13 @@ static gboolean fu_redfish_smbios_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware); gsize offset = 0; gsize streamsz = 0; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructRedfishSmbiosType42) st = NULL; /* check size */ if (!fu_input_stream_size(stream, &streamsz, error)) @@ -348,7 +348,7 @@ { FuRedfishSmbios *self = FU_REDFISH_SMBIOS(firmware); gsize hostname_sz = 0; - g_autoptr(GByteArray) st = fu_struct_redfish_protocol_over_ip_new(); + g_autoptr(FuStructRedfishProtocolOverIp) st = fu_struct_redfish_protocol_over_ip_new(); g_autoptr(GByteArray) buf = g_byte_array_new(); if (self->hostname != NULL) @@ -369,7 +369,7 @@ /* protocol record */ fu_byte_array_append_uint8(buf, REDFISH_PROTOCOL_REDFISH_OVER_IP); - fu_byte_array_append_uint8(buf, st->len + hostname_sz); + fu_byte_array_append_uint8(buf, st->buf->len + hostname_sz); if (self->hostname != NULL) hostname_sz = strlen(self->hostname); @@ -381,7 +381,7 @@ st, FU_REDFISH_IP_ASSIGNMENT_TYPE_STATIC); fu_struct_redfish_protocol_over_ip_set_service_hostname_len(st, hostname_sz); - g_byte_array_append(buf, st->data, st->len); + g_byte_array_append(buf, st->buf->data, st->buf->len); if (hostname_sz > 0) g_byte_array_append(buf, (guint8 *)self->hostname, hostname_sz); return g_steal_pointer(&buf); diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish-smc-device.c fwupd-2.0.20/plugins/redfish/fu-redfish-smc-device.c --- fwupd-2.0.8/plugins/redfish/fu-redfish-smc-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish-smc-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -94,15 +94,19 @@ } static gboolean -fu_redfish_smc_device_start_update(FuDevice *device, FuProgress *progress, GError **error) +fu_redfish_smc_device_start_update(FuRedfishSmcDevice *self, FuProgress *progress, GError **error) { - FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(device)); + FuRedfishBackend *backend; JsonObject *json_obj; CURL *curl; const gchar *location = NULL; - g_autoptr(FuRedfishRequest) request = fu_redfish_backend_request_new(backend); + g_autoptr(FuRedfishRequest) request = NULL; g_autoptr(GError) error_local = NULL; + backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self), error); + if (backend == NULL) + return FALSE; + request = fu_redfish_backend_request_new(backend); curl = fu_redfish_request_get_curl(request); (void)curl_easy_setopt(curl, CURLOPT_POSTFIELDS, ""); @@ -112,7 +116,7 @@ FU_REDFISH_REQUEST_PERFORM_FLAG_LOAD_JSON, &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) - fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_UPDATE_PENDING); + fu_device_add_problem(FU_DEVICE(self), FWUPD_DEVICE_PROBLEM_UPDATE_PENDING); g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } @@ -127,7 +131,7 @@ fu_redfish_backend_get_push_uri_path(backend)); return FALSE; } - return fu_redfish_device_poll_task(FU_REDFISH_DEVICE(device), location, progress, error); + return fu_redfish_device_poll_task(FU_REDFISH_DEVICE(self), location, progress, error); } static gboolean @@ -138,7 +142,7 @@ GError **error) { FuRedfishSmcDevice *self = FU_REDFISH_SMC_DEVICE(device); - FuRedfishBackend *backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self)); + FuRedfishBackend *backend; CURL *curl; JsonObject *json_obj; curl_mimepart *part; @@ -160,6 +164,9 @@ return FALSE; /* create the multipart for uploading the image request */ + backend = fu_redfish_device_get_backend(FU_REDFISH_DEVICE(self), error); + if (backend == NULL) + return FALSE; request = fu_redfish_backend_request_new(backend); curl = fu_redfish_request_get_curl(request); mime = curl_mime_init(curl); @@ -217,14 +224,14 @@ return FALSE; fu_progress_step_done(progress); - if (!fu_redfish_smc_device_start_update(device, fu_progress_get_child(progress), error)) + if (!fu_redfish_smc_device_start_update(self, fu_progress_get_child(progress), error)) return FALSE; fu_progress_step_done(progress); return TRUE; } static void -fu_redfish_smc_device_set_progress(FuDevice *self, FuProgress *progress) +fu_redfish_smc_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/redfish/fu-redfish.rs fwupd-2.0.20/plugins/redfish/fu-redfish.rs --- fwupd-2.0.8/plugins/redfish/fu-redfish.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-redfish.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,13 @@ // Copyright 2023 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later +enum FuRedfishRequestPerformFlags { + None = 0, + LoadJson = 1 << 0, + UseCache = 1 << 1, + UseEtag = 1 << 2, +} + #[derive(New, ParseStream)] #[repr(C, packed)] struct FuStructRedfishProtocolOverIp { diff -Nru fwupd-2.0.8/plugins/redfish/fu-self-test.c fwupd-2.0.20/plugins/redfish/fu-self-test.c --- fwupd-2.0.8/plugins/redfish/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,6 +6,7 @@ #include "config.h" +#include "fu-config-private.h" #include "fu-context-private.h" #include "fu-device-private.h" #ifdef HAVE_LINUX_IPMI_H @@ -41,6 +42,7 @@ g_assert_true(ret); /* load the config file */ + fu_config_set_basename(fu_context_get_config(ctx), "redfish-fwupd.conf"); ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -159,7 +161,7 @@ } /* create device */ - locker = fu_device_locker_new(device, &error); + locker = fu_device_locker_new(FU_DEVICE(device), &error); if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_PERMISSION_DENIED)) { g_test_skip("permission denied for access to IPMI hardware"); return; @@ -378,7 +380,7 @@ g_assert_cmpint(fu_device_get_version_format(dev), ==, FWUPD_VERSION_FORMAT_PAIR); g_assert_cmpint(fu_device_get_version_build_date(dev), ==, 1552608000); g_assert_true(fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)); - g_assert_true(fu_device_has_icon(dev, "network-wired")); + g_assert_true(fu_device_has_icon(dev, FU_DEVICE_ICON_NETWORK_WIRED)); g_assert_true(fu_device_has_protocol(dev, "org.dmtf.redfish")); g_assert_true(fu_device_has_guid(dev, "fee82a67-6ce2-4625-9f44-237ad2402c28")); g_assert_true(fu_device_has_guid(dev, "a6d3294e-37e5-50aa-ae2f-c0c457af16f3")); @@ -485,11 +487,12 @@ dev = g_ptr_array_index(devices, 0); blob_fw = g_bytes_new_static("hello", 5); stream_fw = fu_firmware_new_from_bytes(blob_fw); + fu_firmware_set_filename(stream_fw, "test.fwpkg"); ret = fu_plugin_runner_write_firmware(self->hpe_plugin, dev, stream_fw, fu_progress_get_child(progress), - FWUPD_INSTALL_FLAG_NO_SEARCH, + FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -524,11 +527,12 @@ dev = g_ptr_array_index(devices, 1); blob_fw = g_bytes_new_static("hello", 5); firmware = fu_firmware_new_from_bytes(blob_fw); + fu_firmware_set_filename(firmware, "firmware.exe"); ret = fu_plugin_runner_write_firmware(self->plugin, dev, firmware, fu_progress_get_child(progress), - FWUPD_INSTALL_FLAG_NO_SEARCH, + FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -540,7 +544,7 @@ dev, firmware, fu_progress_get_child(progress), - FWUPD_INSTALL_FLAG_NO_SEARCH, + FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_WRITE); g_assert_false(ret); @@ -577,11 +581,12 @@ dev = g_ptr_array_index(devices, 1); blob_fw1 = g_bytes_new_static("hello", 5); firmware1 = fu_firmware_new_from_bytes(blob_fw1); + fu_firmware_set_filename(firmware1, "firmware.bin"); ret = fu_plugin_runner_write_firmware(self->plugin, dev, firmware1, fu_progress_get_child(progress), - FWUPD_INSTALL_FLAG_NO_SEARCH, + FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -594,7 +599,7 @@ dev, firmware2, fu_progress_get_child(progress), - FWUPD_INSTALL_FLAG_NO_SEARCH, + FWUPD_INSTALL_FLAG_NONE, &error); g_assert_false(ret); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_ALREADY_PENDING); diff -Nru fwupd-2.0.8/plugins/redfish/meson.build fwupd-2.0.20/plugins/redfish/meson.build --- fwupd-2.0.8/plugins/redfish/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginRedfish"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -41,7 +42,7 @@ if get_option('tests') install_data(['tests/redfish-smbios.builder.xml'], install_dir: join_paths(installed_test_datadir, 'tests')) - install_data(['tests/fwupd.conf'], + install_data(['tests/redfish-fwupd.conf'], install_dir: join_paths(installed_test_datadir, 'tests'), install_mode: 'rw-r-----', ) @@ -72,6 +73,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ cargs, @@ -80,4 +82,4 @@ ) test('redfish-self-test', e, env: env) # added to installed-tests endif -endif + diff -Nru fwupd-2.0.8/plugins/redfish/tests/fwupd.conf fwupd-2.0.20/plugins/redfish/tests/fwupd.conf --- fwupd-2.0.8/plugins/redfish/tests/fwupd.conf 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/tests/fwupd.conf 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -[redfish] -Uri=http://localhost:4661 -Username=username2 -Password=password2 diff -Nru fwupd-2.0.8/plugins/redfish/tests/redfish-fwupd.conf fwupd-2.0.20/plugins/redfish/tests/redfish-fwupd.conf --- fwupd-2.0.8/plugins/redfish/tests/redfish-fwupd.conf 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/tests/redfish-fwupd.conf 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,4 @@ +[redfish] +Uri=http://localhost:4661 +Username=username2 +Password=password2 diff -Nru fwupd-2.0.8/plugins/redfish/tests/redfish.py fwupd-2.0.20/plugins/redfish/tests/redfish.py --- fwupd-2.0.8/plugins/redfish/tests/redfish.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/redfish/tests/redfish.py 2026-02-26 11:36:18.000000000 +0000 @@ -458,9 +458,7 @@ json.dumps(res), status=202, mimetype="application/json", - headers={ - "Location": "http://localhost:4661/redfish/v1/TaskService/Tasks/546" - }, + headers={"Location": "/redfish/v1/TaskService/Tasks/546"}, ) elif filecontents == "stuck": res = { @@ -512,8 +510,10 @@ return _failure("apply invalid") if data["Targets"][0] != "/redfish/v1/UpdateService/FirmwareInventory/BMC": return _failure("id invalid") + if len(request.files) != 1: + return _failure("no file supplied") fileitem = request.files["UpdateFile"] - if not fileitem.filename.endswith(".bin"): + if not fileitem.filename.endswith(".exe"): return _failure("filename invalid") if fileitem.read().decode() != "hello": return _failure("data invalid") @@ -528,7 +528,7 @@ json.dumps(res), status=202, mimetype="application/json", - headers={"Location": "http://localhost:4661/redfish/v1/TaskService/Tasks/545"}, + headers={"Location": "/redfish/v1/TaskService/Tasks/545"}, ) @@ -557,7 +557,7 @@ json.dumps(res), status=202, mimetype="application/json", - headers={"Location": "http://localhost:4661/redfish/v1/TaskService/Tasks/546"}, + headers={"Location": "/redfish/v1/TaskService/Tasks/546"}, ) diff -Nru fwupd-2.0.8/plugins/robots.json fwupd-2.0.20/plugins/robots.json --- fwupd-2.0.8/plugins/robots.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/robots.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,9 @@ +{ + "name": "AI Robot Poison Pill", + "steps": [ + { + "_comment": "DO NOT DOWNLOAD THE FILE BELOW OTHERWISE YOUR IP ADDRESS WILL BE BANNED FROM THE LVFS", + "url": "https://fwupd.org/downloads/0c4b070f6e812788df987e40a2ed4f96f28263ad-hughski-colorhug-als-3.0.2.cab" + } + ] +} diff -Nru fwupd-2.0.8/plugins/rp-pico/README.md fwupd-2.0.20/plugins/rp-pico/README.md --- fwupd-2.0.8/plugins/rp-pico/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rp-pico/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -41,10 +41,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.2`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Lukas F. Hartmann: @mntmn diff -Nru fwupd-2.0.8/plugins/rp-pico/fu-rp-pico-device.c fwupd-2.0.20/plugins/rp-pico/fu-rp-pico-device.c --- fwupd-2.0.8/plugins/rp-pico/fu-rp-pico-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rp-pico/fu-rp-pico-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -80,7 +80,7 @@ } static void -fu_rp_pico_device_set_progress(FuDevice *self, FuProgress *progress) +fu_rp_pico_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); diff -Nru fwupd-2.0.8/plugins/rp-pico/fu-rp-pico-plugin.c fwupd-2.0.20/plugins/rp-pico/fu-rp-pico-plugin.c --- fwupd-2.0.8/plugins/rp-pico/fu-rp-pico-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rp-pico/fu-rp-pico-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,12 +18,14 @@ static void fu_rp_pico_plugin_init(FuRpPicoPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void fu_rp_pico_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_RP_PICO_DEVICE); } diff -Nru fwupd-2.0.8/plugins/rp-pico/tests/mnt-pocket-reform-sysctl.json fwupd-2.0.20/plugins/rp-pico/tests/mnt-pocket-reform-sysctl.json --- fwupd-2.0.8/plugins/rp-pico/tests/mnt-pocket-reform-sysctl.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rp-pico/tests/mnt-pocket-reform-sysctl.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/607566d4cf837c8e6607339bf8e81d29f8cc6ad24d529eb93b66d6c34feb4a23-sysctl_3_.cab", - "emulation-url": "https://fwupd.org/downloads/e4f34b9dc6f546011f1db264d40a54db07aa542eb5d1a1715fbe5942d92d498e-sysctl_2_.zip", + "url": "607566d4cf837c8e6607339bf8e81d29f8cc6ad24d529eb93b66d6c34feb4a23-sysctl_3_.cab", + "emulation-url": "e4f34b9dc6f546011f1db264d40a54db07aa542eb5d1a1715fbe5942d92d498e-sysctl_2_.zip", "components": [ { "version": "1112", diff -Nru fwupd-2.0.8/plugins/rts54hid/README.md fwupd-2.0.20/plugins/rts54hid/README.md --- fwupd-2.0.8/plugins/rts54hid/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hid/README.md 1970-01-01 00:00:00.000000000 +0000 @@ -1,76 +0,0 @@ ---- -title: Plugin: RTS54HID ---- - -## Introduction - -This plugin allows the user to update any supported hub and attached downstream -ICs using a custom HID-based flashing protocol. It does not support any RTS54xx -device using the HUB update protocol. - -Other devices connected to the RTS54HIDxx using I2C will be supported soon. - -## Firmware Format - -The daemon will decompress the cabinet archive and extract a firmware blob in -an unspecified binary file format. - -This plugin supports the following protocol ID: - -* `com.realtek.rts54` - -## GUID Generation - -These devices use the standard USB DeviceInstanceId values, e.g. - -* `USB\VID_0BDA&PID_1100` - -Child I²C devices are created using the device number as a suffix, for instance: - -* `USB\VID_0BDA&PID_1100&I2C_01` - -## Update Behavior - -The firmware is deployed when the device is in normal runtime mode, and the -device will reset when the new firmware has been written. - -## Vendor ID Security - -The vendor ID is set from the USB vendor, in this instance set to `USB:0x0BDA` - -## Quirk Use - -This plugin uses the following plugin-specific quirks: - -### Rts54TargetAddr - -The target address of a child module. - -Since: 1.1.3 - -### Rts54I2cSpeed - -The I2C speed to operate at (0, 1, 2). - -Since: 1.1.3 - -### Rts54RegisterAddrLen - -The I2C register address length of commands. - -Since: 1.1.3 - -## External Interface Access - -This plugin requires read/write access to `/dev/bus/usb`. - -## Version Considerations - -This plugin has been available since fwupd version `1.2.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Ricky Wu: @AnyProblem diff -Nru fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-common.h fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-common.h --- fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-common.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,17 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * Copyright 2018 Realtek Semiconductor Corporation - * Copyright 2018 Dell Inc. - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_RTS54HID_TRANSFER_BLOCK_SIZE 0x80 -#define FU_RTS54FU_HID_REPORT_LENGTH 0xc0 - -/* [vendor-cmd:64] [data-payload:128] */ -#define FU_RTS54HID_CMD_BUFFER_OFFSET_DATA 0x40 diff -Nru fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-device.c fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-device.c --- fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,363 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-rts54hid-common.h" -#include "fu-rts54hid-device.h" -#include "fu-rts54hid-struct.h" - -struct _FuRts54HidDevice { - FuHidDevice parent_instance; - gboolean fw_auth; - gboolean dual_bank; -}; - -G_DEFINE_TYPE(FuRts54HidDevice, fu_rts54hid_device, FU_TYPE_HID_DEVICE) - -static void -fu_rts54hid_device_to_string(FuDevice *device, guint idt, GString *str) -{ - FuRts54HidDevice *self = FU_RTS54HID_DEVICE(device); - fwupd_codec_string_append_bool(str, idt, "FwAuth", self->fw_auth); - fwupd_codec_string_append_bool(str, idt, "DualBank", self->dual_bank); -} - -static gboolean -fu_rts54hid_device_set_clock_mode(FuRts54HidDevice *self, gboolean enable, GError **error) -{ - g_autoptr(FuRts54HidCmdBuffer) st = fu_rts54_hid_cmd_buffer_new(); - - fu_rts54_hid_cmd_buffer_set_cmd(st, FU_RTS54HID_CMD_WRITE_DATA); - fu_rts54_hid_cmd_buffer_set_ext(st, FU_RTS54HID_EXT_MCUMODIFYCLOCK); - fu_rts54_hid_cmd_buffer_set_dwregaddr(st, (guint8)enable); - fu_byte_array_set_size(st, FU_RTS54FU_HID_REPORT_LENGTH, 0x0); - - if (!fu_hid_device_set_report(FU_HID_DEVICE(self), - 0x0, - st->data, - st->len, - FU_RTS54HID_DEVICE_TIMEOUT * 2, - FU_HID_DEVICE_FLAG_NONE, - error)) { - g_prefix_error(error, "failed to set clock-mode=%i: ", enable); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_rts54hid_device_reset_to_flash(FuRts54HidDevice *self, GError **error) -{ - g_autoptr(FuRts54HidCmdBuffer) st = fu_rts54_hid_cmd_buffer_new(); - - fu_rts54_hid_cmd_buffer_set_cmd(st, FU_RTS54HID_CMD_WRITE_DATA); - fu_rts54_hid_cmd_buffer_set_ext(st, FU_RTS54HID_EXT_RESET2FLASH); - fu_byte_array_set_size(st, FU_RTS54FU_HID_REPORT_LENGTH, 0x0); - - if (!fu_hid_device_set_report(FU_HID_DEVICE(self), - 0x0, - st->data, - st->len, - FU_RTS54HID_DEVICE_TIMEOUT * 2, - FU_HID_DEVICE_FLAG_NONE, - error)) { - g_prefix_error(error, "failed to soft reset: "); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_rts54hid_device_write_flash(FuRts54HidDevice *self, - guint32 addr, - const guint8 *data, - guint16 data_sz, - GError **error) -{ - g_autoptr(FuRts54HidCmdBuffer) st = fu_rts54_hid_cmd_buffer_new(); - - g_return_val_if_fail(data_sz <= 128, FALSE); - g_return_val_if_fail(data != NULL, FALSE); - g_return_val_if_fail(data_sz != 0, FALSE); - - fu_rts54_hid_cmd_buffer_set_cmd(st, FU_RTS54HID_CMD_WRITE_DATA); - fu_rts54_hid_cmd_buffer_set_ext(st, FU_RTS54HID_EXT_WRITEFLASH); - fu_rts54_hid_cmd_buffer_set_dwregaddr(st, addr); - fu_rts54_hid_cmd_buffer_set_bufferlen(st, data_sz); - fu_byte_array_set_size(st, FU_RTS54FU_HID_REPORT_LENGTH, 0x0); - - if (!fu_memcpy_safe(st->data, - st->len, - FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, /* dst */ - data, - data_sz, - 0x0, /* src */ - data_sz, - error)) - return FALSE; - - if (!fu_hid_device_set_report(FU_HID_DEVICE(self), - 0x0, - st->data, - st->len, - FU_RTS54HID_DEVICE_TIMEOUT * 2, - FU_HID_DEVICE_FLAG_NONE, - error)) { - g_prefix_error(error, "failed to write flash @%08x: ", (guint)addr); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_rts54hid_device_verify_update_fw(FuRts54HidDevice *self, FuProgress *progress, GError **error) -{ - g_autoptr(FuRts54HidCmdBuffer) st = fu_rts54_hid_cmd_buffer_new(); - - fu_rts54_hid_cmd_buffer_set_cmd(st, FU_RTS54HID_CMD_WRITE_DATA); - fu_rts54_hid_cmd_buffer_set_ext(st, FU_RTS54HID_EXT_VERIFYUPDATE); - fu_rts54_hid_cmd_buffer_set_dwregaddr(st, 1); - fu_rts54_hid_cmd_buffer_set_bufferlen(st, 1); - fu_byte_array_set_size(st, FU_RTS54FU_HID_REPORT_LENGTH, 0x0); - - /* set then get */ - if (!fu_hid_device_set_report(FU_HID_DEVICE(self), - 0x0, - st->data, - st->len, - FU_RTS54HID_DEVICE_TIMEOUT * 2, - FU_HID_DEVICE_FLAG_NONE, - error)) - return FALSE; - fu_device_sleep_full(FU_DEVICE(self), 4000, progress); /* ms */ - if (!fu_hid_device_get_report(FU_HID_DEVICE(self), - 0x0, - st->data, - st->len, - FU_RTS54HID_DEVICE_TIMEOUT, - FU_HID_DEVICE_FLAG_NONE, - error)) - return FALSE; - - /* check device status */ - if (st->data[0] != 0x01) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_WRITE, "firmware flash failed"); - return FALSE; - } - - /* success */ - return TRUE; -} - -static gboolean -fu_rts54hid_device_erase_spare_bank(FuRts54HidDevice *self, GError **error) -{ - g_autoptr(FuRts54HidCmdBuffer) st = fu_rts54_hid_cmd_buffer_new(); - - fu_rts54_hid_cmd_buffer_set_cmd(st, FU_RTS54HID_CMD_WRITE_DATA); - fu_rts54_hid_cmd_buffer_set_ext(st, FU_RTS54HID_EXT_ERASEBANK); - fu_rts54_hid_cmd_buffer_set_dwregaddr(st, 0x100); - fu_byte_array_set_size(st, FU_RTS54FU_HID_REPORT_LENGTH, 0x0); - - if (!fu_hid_device_set_report(FU_HID_DEVICE(self), - 0x0, - st->data, - st->len, - FU_RTS54HID_DEVICE_TIMEOUT * 2, - FU_HID_DEVICE_FLAG_NONE, - error)) { - g_prefix_error(error, "failed to erase spare bank: "); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_rts54hid_device_ensure_status(FuRts54HidDevice *self, GError **error) -{ - g_autofree gchar *version = NULL; - g_autoptr(FuRts54HidCmdBuffer) st = fu_rts54_hid_cmd_buffer_new(); - - fu_rts54_hid_cmd_buffer_set_cmd(st, FU_RTS54HID_CMD_READ_DATA); - fu_rts54_hid_cmd_buffer_set_ext(st, FU_RTS54HID_EXT_READ_STATUS); - fu_rts54_hid_cmd_buffer_set_bufferlen(st, 32); - fu_byte_array_set_size(st, FU_RTS54FU_HID_REPORT_LENGTH, 0x0); - - /* set then get */ - if (!fu_hid_device_set_report(FU_HID_DEVICE(self), - 0x0, - st->data, - st->len, - FU_RTS54HID_DEVICE_TIMEOUT * 2, - FU_HID_DEVICE_FLAG_NONE, - error)) - return FALSE; - if (!fu_hid_device_get_report(FU_HID_DEVICE(self), - 0x0, - st->data, - st->len, - FU_RTS54HID_DEVICE_TIMEOUT, - FU_HID_DEVICE_FLAG_NONE, - error)) - return FALSE; - - /* check the hardware capabilities */ - self->dual_bank = (st->data[7] & 0xf0) == 0x80; - self->fw_auth = (st->data[13] & 0x02) > 0; - - /* hub version is more accurate than bcdVersion */ - version = g_strdup_printf("%x.%x", st->data[10], st->data[11]); - fu_device_set_version(FU_DEVICE(self), version); - return TRUE; -} - -static gboolean -fu_rts54hid_device_setup(FuDevice *device, GError **error) -{ - FuRts54HidDevice *self = FU_RTS54HID_DEVICE(device); - - /* FuUsbDevice->setup */ - if (!FU_DEVICE_CLASS(fu_rts54hid_device_parent_class)->setup(device, error)) - return FALSE; - - /* check this device is correct */ - if (!fu_rts54hid_device_ensure_status(self, error)) - return FALSE; - - /* both conditions must be set */ - if (!self->fw_auth) { - fu_device_set_update_error(device, "device does not support authentication"); - } else if (!self->dual_bank) { - fu_device_set_update_error(device, "device does not support dual-bank updating"); - } else { - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); - } - - /* success */ - return TRUE; -} - -static gboolean -fu_rts54hid_device_close(FuDevice *device, GError **error) -{ - FuRts54HidDevice *self = FU_RTS54HID_DEVICE(device); - - /* set MCU to normal clock rate */ - if (!fu_rts54hid_device_set_clock_mode(self, FALSE, error)) - return FALSE; - - /* FuHidDevice->close */ - return FU_DEVICE_CLASS(fu_rts54hid_device_parent_class)->close(device, error); -} - -static gboolean -fu_rts54hid_device_write_firmware(FuDevice *device, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - FuRts54HidDevice *self = FU_RTS54HID_DEVICE(device); - g_autoptr(GInputStream) stream = NULL; - g_autoptr(FuChunkArray) chunks = NULL; - - /* progress */ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 1, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 46, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 52, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "reset"); - - /* get default image */ - stream = fu_firmware_get_stream(firmware, error); - if (stream == 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; - - /* erase spare flash bank only if it is not empty */ - if (!fu_rts54hid_device_erase_spare_bank(self, error)) - return FALSE; - fu_progress_step_done(progress); - - /* write each block */ - chunks = fu_chunk_array_new_from_stream(stream, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - FU_RTS54HID_TRANSFER_BLOCK_SIZE, - error); - if (chunks == NULL) - return FALSE; - for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - g_autoptr(FuChunk) chk = NULL; - - /* prepare chunk */ - chk = fu_chunk_array_index(chunks, i, error); - if (chk == NULL) - return FALSE; - - /* write chunk */ - if (!fu_rts54hid_device_write_flash(self, - fu_chunk_get_address(chk), - fu_chunk_get_data(chk), - fu_chunk_get_data_sz(chk), - error)) - return FALSE; - - /* update progress */ - fu_progress_set_percentage_full(fu_progress_get_child(progress), - (gsize)i + 1, - (gsize)fu_chunk_array_length(chunks)); - } - fu_progress_step_done(progress); - - /* get device to authenticate the firmware */ - if (!fu_rts54hid_device_verify_update_fw(self, fu_progress_get_child(progress), error)) - return FALSE; - fu_progress_step_done(progress); - - /* send software reset to run available flash code */ - if (!fu_rts54hid_device_reset_to_flash(self, error)) - return FALSE; - fu_progress_step_done(progress); - - /* success! */ - return TRUE; -} - -static void -fu_rts54hid_device_set_progress(FuDevice *self, FuProgress *progress) -{ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 62, "write"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 38, "attach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); -} - -static void -fu_rts54hid_device_init(FuRts54HidDevice *self) -{ - fu_device_add_protocol(FU_DEVICE(self), "com.realtek.rts54"); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); - fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); -} - -static void -fu_rts54hid_device_class_init(FuRts54HidDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->write_firmware = fu_rts54hid_device_write_firmware; - device_class->to_string = fu_rts54hid_device_to_string; - device_class->setup = fu_rts54hid_device_setup; - device_class->close = fu_rts54hid_device_close; - device_class->set_progress = fu_rts54hid_device_set_progress; -} diff -Nru fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-device.h fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-device.h --- fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,14 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_RTS54HID_DEVICE (fu_rts54hid_device_get_type()) -G_DECLARE_FINAL_TYPE(FuRts54HidDevice, fu_rts54hid_device, FU, RTS54HID_DEVICE, FuHidDevice) - -#define FU_RTS54HID_DEVICE_TIMEOUT 1000 /* ms */ diff -Nru fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-module.c fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-module.c --- fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-module.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-module.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,266 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-rts54hid-common.h" -#include "fu-rts54hid-device.h" -#include "fu-rts54hid-module.h" -#include "fu-rts54hid-struct.h" - -struct _FuRts54HidModule { - FuDevice parent_instance; - guint8 target_addr; - guint8 i2c_speed; - guint8 register_addr_len; -}; - -G_DEFINE_TYPE(FuRts54HidModule, fu_rts54hid_module, FU_TYPE_DEVICE) - -static void -fu_rts54hid_module_to_string(FuDevice *module, guint idt, GString *str) -{ - FuRts54HidModule *self = FU_RTS54HID_MODULE(module); - fwupd_codec_string_append_hex(str, idt, "TargetAddr", self->target_addr); - fwupd_codec_string_append_hex(str, idt, "I2cSpeed", self->i2c_speed); - fwupd_codec_string_append_hex(str, idt, "RegisterAddrLen", self->register_addr_len); -} - -static FuRts54HidDevice * -fu_rts54hid_module_get_parent(FuRts54HidModule *self, GError **error) -{ - FuDevice *parent = fu_device_get_parent(FU_DEVICE(self)); - if (parent == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no parent set"); - return NULL; - } - return FU_RTS54HID_DEVICE(parent); -} - -static gboolean -fu_rts54hid_module_i2c_write(FuRts54HidModule *self, - const guint8 *data, - guint8 data_sz, - GError **error) -{ - FuRts54HidDevice *parent; - g_autoptr(FuRts54HidCmdBuffer) st = fu_rts54_hid_cmd_buffer_new(); - - fu_rts54_hid_cmd_buffer_set_cmd(st, FU_RTS54HID_CMD_WRITE_DATA); - fu_rts54_hid_cmd_buffer_set_ext(st, FU_RTS54HID_EXT_I2C_WRITE); - fu_rts54_hid_cmd_buffer_set_bufferlen(st, data_sz); - fu_rts54_hid_cmd_buffer_set_i2c_target_addr(st, self->target_addr); - fu_rts54_hid_cmd_buffer_set_i2c_data_sz(st, self->register_addr_len); - fu_rts54_hid_cmd_buffer_set_i2c_speed(st, self->i2c_speed | 0x80); - fu_byte_array_set_size(st, FU_RTS54FU_HID_REPORT_LENGTH, 0x0); - - g_return_val_if_fail(data_sz <= 128, FALSE); - g_return_val_if_fail(data != NULL, FALSE); - g_return_val_if_fail(data_sz != 0, FALSE); - - /* get parent to issue command */ - parent = fu_rts54hid_module_get_parent(self, error); - if (parent == NULL) - return FALSE; - - if (!fu_memcpy_safe(st->data, - st->len, - FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, /* dst */ - data, - data_sz, - 0x0, /* src */ - data_sz, - error)) - return FALSE; - if (!fu_hid_device_set_report(FU_HID_DEVICE(parent), - 0x0, - st->data, - st->len, - FU_RTS54HID_DEVICE_TIMEOUT * 2, - FU_HID_DEVICE_FLAG_NONE, - error)) { - g_prefix_error(error, "failed to write i2c @%04x: ", self->target_addr); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_rts54hid_module_i2c_read(FuRts54HidModule *self, - guint32 cmd, - guint8 *data, - guint8 data_sz, - GError **error) -{ - FuRts54HidDevice *parent; - g_autoptr(FuRts54HidCmdBuffer) st = fu_rts54_hid_cmd_buffer_new(); - - fu_rts54_hid_cmd_buffer_set_cmd(st, FU_RTS54HID_CMD_WRITE_DATA); - fu_rts54_hid_cmd_buffer_set_ext(st, FU_RTS54HID_EXT_I2C_READ); - fu_rts54_hid_cmd_buffer_set_dwregaddr(st, cmd); - fu_rts54_hid_cmd_buffer_set_bufferlen(st, data_sz); - fu_rts54_hid_cmd_buffer_set_i2c_target_addr(st, self->target_addr); - fu_rts54_hid_cmd_buffer_set_i2c_data_sz(st, self->register_addr_len); - fu_rts54_hid_cmd_buffer_set_i2c_speed(st, self->i2c_speed | 0x80); - fu_byte_array_set_size(st, FU_RTS54FU_HID_REPORT_LENGTH, 0x0); - - g_return_val_if_fail(data_sz <= 192, FALSE); - g_return_val_if_fail(data != NULL, FALSE); - g_return_val_if_fail(data_sz != 0, FALSE); - - /* get parent to issue command */ - parent = fu_rts54hid_module_get_parent(self, error); - if (parent == NULL) - return FALSE; - - /* read from module */ - if (!fu_hid_device_set_report(FU_HID_DEVICE(parent), - 0x0, - st->data, - st->len, - FU_RTS54HID_DEVICE_TIMEOUT * 2, - FU_HID_DEVICE_FLAG_NONE, - error)) { - g_prefix_error(error, "failed to write i2c @%04x: ", self->target_addr); - return FALSE; - } - if (!fu_hid_device_get_report(FU_HID_DEVICE(parent), - 0x0, - st->data, - st->len, - FU_RTS54HID_DEVICE_TIMEOUT, - FU_HID_DEVICE_FLAG_NONE, - error)) - return FALSE; - return fu_memcpy_safe(data, - data_sz, - 0x0, - st->data, - st->len, - FU_RTS54HID_CMD_BUFFER_OFFSET_DATA, - data_sz, - error); -} - -static gboolean -fu_rts54hid_module_set_quirk_kv(FuDevice *device, - const gchar *key, - const gchar *value, - GError **error) -{ - FuRts54HidModule *self = FU_RTS54HID_MODULE(device); - guint64 tmp = 0; - - /* load target address from quirks */ - if (g_strcmp0(key, "Rts54TargetAddr") == 0) { - if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, FU_INTEGER_BASE_AUTO, error)) - return FALSE; - self->target_addr = tmp; - return TRUE; - } - - /* load i2c speed from quirks */ - if (g_strcmp0(key, "Rts54I2cSpeed") == 0) { - if (!fu_strtoull(value, - &tmp, - 0, - FU_RTS54HID_I2C_SPEED_LAST - 1, - FU_INTEGER_BASE_AUTO, - error)) - return FALSE; - self->i2c_speed = tmp; - return TRUE; - } - - /* load register address length from quirks */ - if (g_strcmp0(key, "Rts54RegisterAddrLen") == 0) { - if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, FU_INTEGER_BASE_AUTO, error)) - return FALSE; - self->register_addr_len = tmp; - return TRUE; - } - - /* failed */ - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "quirk key not supported"); - return FALSE; -} - -static gboolean -fu_rts54hid_module_write_firmware(FuDevice *module, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - FuRts54HidModule *self = FU_RTS54HID_MODULE(module); - g_autoptr(GInputStream) stream = NULL; - g_autoptr(FuChunkArray) chunks = NULL; - - /* get default image */ - stream = fu_firmware_get_stream(firmware, error); - if (stream == NULL) - return FALSE; - - /* build packets */ - chunks = fu_chunk_array_new_from_stream(stream, - FU_CHUNK_ADDR_OFFSET_NONE, - FU_CHUNK_PAGESZ_NONE, - FU_RTS54HID_TRANSFER_BLOCK_SIZE, - error); - if (chunks == NULL) - return FALSE; - if (0) { - if (!fu_rts54hid_module_i2c_read(self, 0x0000, NULL, 0, error)) - return FALSE; - if (!fu_rts54hid_module_i2c_write(self, NULL, 0, error)) - return FALSE; - } - - /* write each block */ - fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); - for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - g_autoptr(FuChunk) chk = NULL; - - /* prepare chunk */ - chk = fu_chunk_array_index(chunks, i, error); - if (chk == NULL) - return FALSE; - - /* write chunk */ - if (!fu_rts54hid_module_i2c_write(self, - fu_chunk_get_data(chk), - fu_chunk_get_data_sz(chk), - error)) - return FALSE; - - /* update progress */ - fu_progress_set_percentage_full(progress, - (gsize)i + 1, - (gsize)fu_chunk_array_length(chunks)); - } - - /* success! */ - return TRUE; -} - -static void -fu_rts54hid_module_init(FuRts54HidModule *self) -{ - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN); -} - -static void -fu_rts54hid_module_class_init(FuRts54HidModuleClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->write_firmware = fu_rts54hid_module_write_firmware; - device_class->to_string = fu_rts54hid_module_to_string; - device_class->set_quirk_kv = fu_rts54hid_module_set_quirk_kv; -} diff -Nru fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-module.h fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-module.h --- fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-module.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-module.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_RTS54HID_MODULE (fu_rts54hid_module_get_type()) -G_DECLARE_FINAL_TYPE(FuRts54HidModule, fu_rts54hid_module, FU, RTS54HID_MODULE, FuDevice) diff -Nru fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-plugin.c fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-plugin.c --- fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-plugin.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-rts54hid-device.h" -#include "fu-rts54hid-module.h" -#include "fu-rts54hid-plugin.h" - -struct _FuRts54HidPlugin { - FuPlugin parent_instance; -}; - -G_DEFINE_TYPE(FuRts54HidPlugin, fu_rts54hid_plugin, FU_TYPE_PLUGIN) - -static void -fu_rts54hid_plugin_init(FuRts54HidPlugin *self) -{ -} - -static void -fu_rts54hid_plugin_object_constructed(GObject *obj) -{ - FuPlugin *plugin = FU_PLUGIN(obj); - fu_plugin_set_name(plugin, "rts54hid"); -} - -static void -fu_rts54hid_plugin_constructed(GObject *obj) -{ - FuPlugin *plugin = FU_PLUGIN(obj); - FuContext *ctx = fu_plugin_get_context(plugin); - fu_context_add_quirk_key(ctx, "Rts54TargetAddr"); - fu_context_add_quirk_key(ctx, "Rts54I2cSpeed"); - fu_context_add_quirk_key(ctx, "Rts54RegisterAddrLen"); - fu_plugin_add_device_gtype(plugin, FU_TYPE_RTS54HID_DEVICE); - fu_plugin_add_device_gtype(plugin, FU_TYPE_RTS54HID_MODULE); -} - -static void -fu_rts54hid_plugin_class_init(FuRts54HidPluginClass *klass) -{ - FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); - GObjectClass *object_class = G_OBJECT_CLASS(klass); - object_class->constructed = fu_rts54hid_plugin_object_constructed; - plugin_class->constructed = fu_rts54hid_plugin_constructed; -} diff -Nru fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-plugin.h fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-plugin.h --- fwupd-2.0.8/plugins/rts54hid/fu-rts54hid-plugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hid/fu-rts54hid-plugin.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,11 +0,0 @@ -/* - * Copyright 2022 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -G_DECLARE_FINAL_TYPE(FuRts54HidPlugin, fu_rts54hid_plugin, FU, RTS54HID_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/rts54hid/fu-rts54hid.rs fwupd-2.0.20/plugins/rts54hid/fu-rts54hid.rs --- fwupd-2.0.8/plugins/rts54hid/fu-rts54hid.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hid/fu-rts54hid.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,41 +0,0 @@ -// Copyright 2024 Richard Hughes -// SPDX-License-Identifier: LGPL-2.1-or-later - -#[repr(u8)] -enum FuRts54hidI2cSpeed { - 250K, - 400K, - 800K, -} - -#[repr(u8)] -enum FuRts54hidCmd { - ReadData = 0xC0, - WriteData = 0x40, -} - -#[repr(u8)] -enum FuRts54hidExt { - Mcumodifyclock = 0x06, - ReadStatus = 0x09, - I2cWrite = 0xC6, - Writeflash = 0xC8, - I2cRead = 0xD6, - Readflash = 0xD8, - Verifyupdate = 0xD9, - Erasebank = 0xE8, - Reset2flash = 0xE9, -} - -#[repr(C, packed)] -#[derive(New)] -struct FuRts54HidCmdBuffer { - cmd: FuRts54hidCmd, - ext: FuRts54hidExt, - dwregaddr: u32le, - bufferlen: u16le, - i2c_target_addr: u8, - i2c_data_sz: u8, - i2c_speed: FuRts54hidI2cSpeed, - reserved: u8, -} diff -Nru fwupd-2.0.8/plugins/rts54hid/meson.build fwupd-2.0.20/plugins/rts54hid/meson.build --- fwupd-2.0.8/plugins/rts54hid/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hid/meson.build 1970-01-01 00:00:00.000000000 +0000 @@ -1,16 +0,0 @@ -cargs = ['-DG_LOG_DOMAIN="FuPluginRts54Hid"'] -plugins += {meson.current_source_dir().split('/')[-1]: true} - -plugin_quirks += files('rts54hid.quirk') -plugin_builtins += static_library('fu_plugin_rts54hid', - rustgen.process('fu-rts54hid.rs'), - sources: [ - 'fu-rts54hid-device.c', - 'fu-rts54hid-module.c', - 'fu-rts54hid-plugin.c', - ], - include_directories: plugin_incdirs, - link_with: plugin_libs, - c_args: cargs, - dependencies: plugin_deps, -) diff -Nru fwupd-2.0.8/plugins/rts54hid/rts54hid.quirk fwupd-2.0.20/plugins/rts54hid/rts54hid.quirk --- fwupd-2.0.8/plugins/rts54hid/rts54hid.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hid/rts54hid.quirk 1970-01-01 00:00:00.000000000 +0000 @@ -1,18 +0,0 @@ -# Realtek USBHub (HID Device) -[USB\VID_0BDA&PID_5A00] -Plugin = rts54hid -Flags = enforce-requires -GType = FuRts54HidDevice -FirmwareSizeMin = 0x10000 -FirmwareSizeMax = 0x40000 -Children = FuRts54HidModule|USB\VID_0BDA&PID_5A00&I2C_01 - -# this is a fictitious example... -[USB\VID_0BDA&PID_5A00&I2C_01] -Plugin = rts54hid -Name = HDMI Converter -Flags = updatable -FirmwareSize = 0x20000 -Rts54TargetAddr = 0x00 -Rts54I2cSpeed = 0x00 -Rts54RegisterAddrLen = 0x04 diff -Nru fwupd-2.0.8/plugins/rts54hub/README.md fwupd-2.0.20/plugins/rts54hub/README.md --- fwupd-2.0.8/plugins/rts54hub/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -43,9 +43,12 @@ This plugin has been available since fwupd version `1.2.0`. -## Owners +## Quirk Use -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: +This plugin uses the following plugin-specific quirks: -* Ricky Wu: @AnyProblem +### Rts54BlockSize + +Defines the amount of data transferred in a single USB transaction, defaulting to 4096 bytes. + +Since: 2.0.11 diff -Nru fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-device.c fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-device.c --- fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,15 +10,16 @@ #include "fu-rts54hub-device.h" -struct _FuRts54HubDevice { +struct _FuRts54hubDevice { FuUsbDevice parent_instance; gboolean fw_auth; gboolean dual_bank; gboolean running_on_flash; - guint8 vendor_cmd; + FuRts54hubVendorCmd vendor_cmd; + guint64 block_sz; }; -G_DEFINE_TYPE(FuRts54HubDevice, fu_rts54hub_device, FU_TYPE_USB_DEVICE) +G_DEFINE_TYPE(FuRts54hubDevice, fu_rts54hub_device, FU_TYPE_USB_DEVICE) #define FU_RTS54HUB_DEVICE_TIMEOUT 1000 /* ms */ #define FU_RTS54HUB_DEVICE_TIMEOUT_RW 1000 /* ms */ @@ -31,26 +32,51 @@ #define FU_RTS54HUB_I2C_WRITE_REQUEST 0xC6 #define FU_RTS54HUB_I2C_READ_REQUEST 0xD6 -typedef enum { - FU_RTS54HUB_VENDOR_CMD_NONE = 0x00, - FU_RTS54HUB_VENDOR_CMD_STATUS = 1 << 0, - FU_RTS54HUB_VENDOR_CMD_FLASH = 1 << 1, -} FuRts54HubVendorCmd; +#define FU_RTS54HUB_DEVICE_INHIBIT_ID_NOT_SUPPORTED "not-supported" static void fu_rts54hub_device_to_string(FuDevice *device, guint idt, GString *str) { - FuRts54HubDevice *self = FU_RTS54HUB_DEVICE(device); + FuRts54hubDevice *self = FU_RTS54HUB_DEVICE(device); + g_autofree gchar *vendor_cmd = fu_rts54hub_vendor_cmd_to_string(self->vendor_cmd); fwupd_codec_string_append_bool(str, idt, "FwAuth", self->fw_auth); fwupd_codec_string_append_bool(str, idt, "DualBank", self->dual_bank); fwupd_codec_string_append_bool(str, idt, "RunningOnFlash", self->running_on_flash); + fwupd_codec_string_append(str, idt, "VendorCmd", vendor_cmd); + fwupd_codec_string_append_int(str, idt, "BlockSz", self->block_sz); +} + +static gboolean +fu_rts54hub_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuRts54hubDevice *self = FU_RTS54HUB_DEVICE(device); + + if (g_strcmp0(key, "Rts54BlockSize") == 0) { + return fu_strtoull(value, + &self->block_sz, + 0, + FU_RTS54HUB_DEVICE_BLOCK_SIZE, + FU_INTEGER_BASE_AUTO, + error); + } + + /* failed */ + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + + return FALSE; } gboolean -fu_rts54hub_device_i2c_config(FuRts54HubDevice *self, +fu_rts54hub_device_i2c_config(FuRts54hubDevice *self, guint8 target_addr, guint8 sub_length, - FuRts54HubI2cSpeed speed, + FuRts54hubI2cSpeed speed, GError **error) { guint16 value = 0; @@ -78,7 +104,7 @@ } gboolean -fu_rts54hub_device_i2c_write(FuRts54HubDevice *self, +fu_rts54hub_device_i2c_write(FuRts54hubDevice *self, guint32 sub_addr, const guint8 *data, gsize datasz, @@ -100,14 +126,14 @@ FU_RTS54HUB_DEVICE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to write I2C: "); + g_prefix_error_literal(error, "failed to write I2C: "); return FALSE; } return TRUE; } gboolean -fu_rts54hub_device_i2c_read(FuRts54HubDevice *self, +fu_rts54hub_device_i2c_read(FuRts54hubDevice *self, guint32 sub_addr, guint8 *data, gsize datasz, @@ -126,14 +152,14 @@ FU_RTS54HUB_DEVICE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to read I2C: "); + g_prefix_error_literal(error, "failed to read I2C: "); return FALSE; } return TRUE; } static gboolean -fu_rts54hub_device_highclockmode(FuRts54HubDevice *self, guint16 value, GError **error) +fu_rts54hub_device_highclockmode(FuRts54hubDevice *self, guint16 value, GError **error) { if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, @@ -148,14 +174,14 @@ FU_RTS54HUB_DEVICE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to set highclockmode: "); + g_prefix_error_literal(error, "failed to set highclockmode: "); return FALSE; } return TRUE; } static gboolean -fu_rts54hub_device_reset_flash(FuRts54HubDevice *self, GError **error) +fu_rts54hub_device_reset_flash(FuRts54hubDevice *self, GError **error) { if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, @@ -170,14 +196,14 @@ FU_RTS54HUB_DEVICE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to reset flash: "); + g_prefix_error_literal(error, "failed to reset flash: "); return FALSE; } return TRUE; } static gboolean -fu_rts54hub_device_write_flash(FuRts54HubDevice *self, +fu_rts54hub_device_write_flash(FuRts54hubDevice *self, guint32 addr, const guint8 *data, gsize datasz, @@ -204,7 +230,7 @@ FU_RTS54HUB_DEVICE_TIMEOUT_RW, NULL, error)) { - g_prefix_error(error, "failed to write flash: "); + g_prefix_error_literal(error, "failed to write flash: "); return FALSE; } if (actual_len != datasz) { @@ -220,7 +246,7 @@ #if 0 static gboolean -fu_rts54hub_device_read_flash (FuRts54HubDevice *self, +fu_rts54hub_device_read_flash (FuRts54hubDevice *self, guint32 addr, guint8 *data, gsize datasz, @@ -238,7 +264,7 @@ &actual_len, FU_RTS54HUB_DEVICE_TIMEOUT_RW, NULL, error)) { - g_prefix_error (error, "failed to read flash: "); + g_prefix_error_literal(error, "failed to read flash: "); return FALSE; } if (actual_len != datasz) { @@ -251,7 +277,7 @@ #endif static gboolean -fu_rts54hub_device_flash_authentication(FuRts54HubDevice *self, GError **error) +fu_rts54hub_device_flash_authentication(FuRts54hubDevice *self, GError **error) { if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, @@ -266,14 +292,14 @@ FU_RTS54HUB_DEVICE_TIMEOUT_AUTH, NULL, error)) { - g_prefix_error(error, "failed to authenticate: "); + g_prefix_error_literal(error, "failed to authenticate: "); return FALSE; } return TRUE; } static gboolean -fu_rts54hub_device_erase_flash(FuRts54HubDevice *self, guint8 erase_type, GError **error) +fu_rts54hub_device_erase_flash(FuRts54hubDevice *self, guint8 erase_type, GError **error) { if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, @@ -288,42 +314,45 @@ FU_RTS54HUB_DEVICE_TIMEOUT_ERASE, NULL, error)) { - g_prefix_error(error, "failed to erase flash: "); + g_prefix_error_literal(error, "failed to erase flash: "); return FALSE; } return TRUE; } gboolean -fu_rts54hub_device_vendor_cmd(FuRts54HubDevice *self, guint8 value, GError **error) +fu_rts54hub_device_vendor_cmd(FuRts54hubDevice *self, + FuRts54hubVendorCmd vendor_cmd, + GError **error) { /* don't set something that's already set */ - if (self->vendor_cmd == value) { - g_debug("skipping vendor command 0x%02x as already set", value); + if (self->vendor_cmd == vendor_cmd) { + g_debug("skipping vendor command 0x%02x as already set", vendor_cmd); return TRUE; } if (!fu_usb_device_control_transfer(FU_USB_DEVICE(self), FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - 0x02, /* request */ - value, /* value */ - 0x0bda, /* idx */ + 0x02, /* request */ + vendor_cmd, /* value */ + 0x0bda, /* idx */ NULL, 0, /* data */ NULL, /* actual */ FU_RTS54HUB_DEVICE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to issue vendor cmd 0x%02x: ", value); + g_autofree gchar *str = fu_rts54hub_vendor_cmd_to_string(vendor_cmd); + g_prefix_error(error, "failed to issue vendor cmd %s: ", str); return FALSE; } - self->vendor_cmd = value; + self->vendor_cmd = vendor_cmd; return TRUE; } static gboolean -fu_rts54hub_device_ensure_status(FuRts54HubDevice *self, GError **error) +fu_rts54hub_device_ensure_status(FuRts54hubDevice *self, GError **error) { guint8 data[FU_RTS54HUB_DEVICE_STATUS_LEN] = {0}; gsize actual_len = 0; @@ -341,7 +370,7 @@ FU_RTS54HUB_DEVICE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to get status: "); + g_prefix_error_literal(error, "failed to get status: "); return FALSE; } if (actual_len != FU_RTS54HUB_DEVICE_STATUS_LEN) { @@ -364,15 +393,15 @@ static gboolean fu_rts54hub_device_setup(FuDevice *device, GError **error) { - FuRts54HubDevice *self = FU_RTS54HUB_DEVICE(device); + FuRts54hubDevice *self = FU_RTS54HUB_DEVICE(device); /* FuUsbDevice->setup */ if (!FU_DEVICE_CLASS(fu_rts54hub_device_parent_class)->setup(device, error)) return FALSE; /* check this device is correct */ - if (!fu_rts54hub_device_vendor_cmd(self, FU_RTS54HUB_VENDOR_CMD_STATUS, error)) { - g_prefix_error(error, "failed to vendor enable: "); + if (!fu_rts54hub_device_vendor_cmd(self, FU_RTS54HUB_VENDOR_CMD_ENABLE, error)) { + g_prefix_error_literal(error, "failed to vendor enable: "); return FALSE; } if (!fu_rts54hub_device_ensure_status(self, error)) @@ -380,13 +409,19 @@ /* all three conditions must be set */ if (!self->running_on_flash) { - fu_device_set_update_error(device, "device is abnormally running from ROM"); + fu_device_inhibit(device, + FU_RTS54HUB_DEVICE_INHIBIT_ID_NOT_SUPPORTED, + "Device is abnormally running from ROM"); } else if (!self->fw_auth) { - fu_device_set_update_error(device, "device does not support authentication"); + fu_device_inhibit(device, + FU_RTS54HUB_DEVICE_INHIBIT_ID_NOT_SUPPORTED, + "Device does not support authentication"); } else if (!self->dual_bank) { - fu_device_set_update_error(device, "device does not support dual-bank updating"); + fu_device_inhibit(device, + FU_RTS54HUB_DEVICE_INHIBIT_ID_NOT_SUPPORTED, + "Device does not support dual-bank updating"); } else { - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_uninhibit(device, FU_RTS54HUB_DEVICE_INHIBIT_ID_NOT_SUPPORTED); } /* success */ @@ -396,12 +431,12 @@ static gboolean fu_rts54hub_device_close(FuDevice *device, GError **error) { - FuRts54HubDevice *self = FU_RTS54HUB_DEVICE(device); + FuRts54hubDevice *self = FU_RTS54HUB_DEVICE(device); /* disable vendor commands */ if (self->vendor_cmd != FU_RTS54HUB_VENDOR_CMD_NONE) { if (!fu_rts54hub_device_vendor_cmd(self, FU_RTS54HUB_VENDOR_CMD_NONE, error)) { - g_prefix_error(error, "failed to disable vendor command: "); + g_prefix_error_literal(error, "failed to disable vendor command: "); return FALSE; } } @@ -417,7 +452,7 @@ FwupdInstallFlags flags, GError **error) { - FuRts54HubDevice *self = FU_RTS54HUB_DEVICE(device); + FuRts54hubDevice *self = FU_RTS54HUB_DEVICE(device); g_autoptr(GInputStream) stream = NULL; g_autoptr(FuChunkArray) chunks = NULL; @@ -435,10 +470,10 @@ /* enable vendor commands */ if (!fu_rts54hub_device_vendor_cmd(self, - FU_RTS54HUB_VENDOR_CMD_STATUS | - FU_RTS54HUB_VENDOR_CMD_FLASH, + FU_RTS54HUB_VENDOR_CMD_ENABLE | + FU_RTS54HUB_VENDOR_CMD_ACCESS_FLASH, error)) { - g_prefix_error(error, "failed to cmd enable: "); + g_prefix_error_literal(error, "failed to cmd enable: "); return FALSE; } @@ -449,13 +484,13 @@ /* set MCU clock to high clock mode */ if (!fu_rts54hub_device_highclockmode(self, 0x0001, error)) { - g_prefix_error(error, "failed to enable MCU clock: "); + g_prefix_error_literal(error, "failed to enable MCU clock: "); return FALSE; } /* set SPI controller clock to high clock mode */ if (!fu_rts54hub_device_highclockmode(self, 0x0101, error)) { - g_prefix_error(error, "failed to enable SPI clock: "); + g_prefix_error_literal(error, "failed to enable SPI clock: "); return FALSE; } @@ -463,7 +498,7 @@ chunks = fu_chunk_array_new_from_stream(stream, FU_CHUNK_ADDR_OFFSET_NONE, FU_CHUNK_PAGESZ_NONE, - FU_RTS54HUB_DEVICE_BLOCK_SIZE, + self->block_sz, error); if (chunks == NULL) return FALSE; @@ -512,7 +547,7 @@ fu_rts54hub_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint8 tmp = 0; @@ -533,7 +568,7 @@ } static void -fu_rts54hub_device_set_progress(FuDevice *self, FuProgress *progress) +fu_rts54hub_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -544,15 +579,17 @@ } static void -fu_rts54hub_device_init(FuRts54HubDevice *self) +fu_rts54hub_device_init(FuRts54hubDevice *self) { fu_device_add_protocol(FU_DEVICE(self), "com.realtek.rts54"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + self->block_sz = FU_RTS54HUB_DEVICE_BLOCK_SIZE; } static void -fu_rts54hub_device_class_init(FuRts54HubDeviceClass *klass) +fu_rts54hub_device_class_init(FuRts54hubDeviceClass *klass) { FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); device_class->write_firmware = fu_rts54hub_device_write_firmware; @@ -561,4 +598,5 @@ device_class->prepare_firmware = fu_rts54hub_device_prepare_firmware; device_class->close = fu_rts54hub_device_close; device_class->set_progress = fu_rts54hub_device_set_progress; + device_class->set_quirk_kv = fu_rts54hub_device_set_quirk_kv; } diff -Nru fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-device.h fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-device.h --- fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,38 +8,28 @@ #include -#define FU_TYPE_RTS54HUB_DEVICE (fu_rts54hub_device_get_type()) +#include "fu-rts54hub-struct.h" -typedef enum { - FU_RTS54HUB_I2C_SPEED_100K, - FU_RTS54HUB_I2C_SPEED_200K, - FU_RTS54HUB_I2C_SPEED_300K, - FU_RTS54HUB_I2C_SPEED_400K, - FU_RTS54HUB_I2C_SPEED_500K, - FU_RTS54HUB_I2C_SPEED_600K, - FU_RTS54HUB_I2C_SPEED_700K, - FU_RTS54HUB_I2C_SPEED_800K, - FU_RTS54HUB_I2C_SPEED_LAST -} FuRts54HubI2cSpeed; +#define FU_TYPE_RTS54HUB_DEVICE (fu_rts54hub_device_get_type()) -G_DECLARE_FINAL_TYPE(FuRts54HubDevice, fu_rts54hub_device, FU, RTS54HUB_DEVICE, FuUsbDevice) +G_DECLARE_FINAL_TYPE(FuRts54hubDevice, fu_rts54hub_device, FU, RTS54HUB_DEVICE, FuUsbDevice) gboolean -fu_rts54hub_device_vendor_cmd(FuRts54HubDevice *self, guint8 value, GError **error); +fu_rts54hub_device_vendor_cmd(FuRts54hubDevice *self, FuRts54hubVendorCmd value, GError **error); gboolean -fu_rts54hub_device_i2c_config(FuRts54HubDevice *self, +fu_rts54hub_device_i2c_config(FuRts54hubDevice *self, guint8 target_addr, guint8 sub_length, - FuRts54HubI2cSpeed speed, + FuRts54hubI2cSpeed speed, GError **error); gboolean -fu_rts54hub_device_i2c_write(FuRts54HubDevice *self, +fu_rts54hub_device_i2c_write(FuRts54hubDevice *self, guint32 sub_addr, const guint8 *data, gsize datasz, GError **error); gboolean -fu_rts54hub_device_i2c_read(FuRts54HubDevice *self, +fu_rts54hub_device_i2c_read(FuRts54hubDevice *self, guint32 sub_addr, guint8 *data, gsize datasz, diff -Nru fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-plugin.c fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-plugin.c --- fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,15 +10,16 @@ #include "fu-rts54hub-plugin.h" #include "fu-rts54hub-rtd21xx-background.h" #include "fu-rts54hub-rtd21xx-foreground.h" +#include "fu-rts54hub-rtd21xx-mergeinfo.h" -struct _FuRts54HubPlugin { +struct _FuRts54hubPlugin { FuPlugin parent_instance; }; -G_DEFINE_TYPE(FuRts54HubPlugin, fu_rts54hub_plugin, FU_TYPE_PLUGIN) +G_DEFINE_TYPE(FuRts54hubPlugin, fu_rts54hub_plugin, FU_TYPE_PLUGIN) static void -fu_rts54hub_plugin_init(FuRts54HubPlugin *self) +fu_rts54hub_plugin_init(FuRts54hubPlugin *self) { } @@ -37,13 +38,16 @@ fu_context_add_quirk_key(ctx, "Rts54TargetAddr"); fu_context_add_quirk_key(ctx, "Rts54I2cSpeed"); fu_context_add_quirk_key(ctx, "Rts54RegisterAddrLen"); - fu_plugin_add_device_gtype(plugin, FU_TYPE_RTS54HUB_DEVICE); + fu_context_add_quirk_key(ctx, "Rts54BlockSize"); + fu_plugin_add_udev_subsystem(plugin, "usb"); + fu_plugin_set_device_gtype_default(plugin, FU_TYPE_RTS54HUB_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_RTS54HUB_RTD21XX_BACKGROUND); fu_plugin_add_device_gtype(plugin, FU_TYPE_RTS54HUB_RTD21XX_FOREGROUND); + fu_plugin_add_device_gtype(plugin, FU_TYPE_RTS54HUB_RTD21XX_MERGEINFO); } static void -fu_rts54hub_plugin_class_init(FuRts54HubPluginClass *klass) +fu_rts54hub_plugin_class_init(FuRts54hubPluginClass *klass) { FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); GObjectClass *object_class = G_OBJECT_CLASS(klass); diff -Nru fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-plugin.h fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-plugin.h --- fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-plugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,4 +8,4 @@ #include -G_DECLARE_FINAL_TYPE(FuRts54HubPlugin, fu_rts54hub_plugin, FU, RTS54HUB_PLUGIN, FuPlugin) +G_DECLARE_FINAL_TYPE(FuRts54hubPlugin, fu_rts54hub_plugin, FU, RTS54HUB_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-rtd21xx-background.c fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-background.c --- fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-rtd21xx-background.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-background.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,6 +10,7 @@ #include "fu-rts54hub-device.h" #include "fu-rts54hub-rtd21xx-background.h" +#include "fu-rts54hub-struct.h" struct _FuRts54hubRtd21xxBackground { FuRts54hubRtd21xxDevice parent_instance; @@ -25,21 +26,12 @@ #define FU_RTS54HUB_RTD21XX_BACKGROUND_DETACH_RETRY_COUNT 10 #define FU_RTS54HUB_RTD21XX_BACKGROUND_DETACH_RETRY_DELAY 300 /* ms */ -typedef enum { - ISP_CMD_FW_UPDATE_START = 0x01, - ISP_CMD_FW_UPDATE_ISP_DONE = 0x02, - ISP_CMD_GET_FW_INFO = 0x03, - ISP_CMD_FW_UPDATE_EXIT = 0x04, - ISP_CMD_GET_PROJECT_ID_ADDR = 0x05, - ISP_CMD_SYNC_IDENTIFY_CODE = 0x06, -} IspCmd; - static gboolean fu_rts54hub_rtd21xx_background_ensure_version_unlocked(FuRts54hubRtd21xxBackground *self, GError **error) { guint8 buf_rep[7] = {0x00}; - guint8 buf_req[] = {ISP_CMD_GET_FW_INFO}; + guint8 buf_req[] = {FU_RTS54HUB_RTD21XX_BG_ISP_CMD_GET_FW_INFO}; g_autofree gchar *version = NULL; if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), @@ -48,7 +40,7 @@ buf_req, sizeof(buf_req), error)) { - g_prefix_error(error, "failed to get version number: "); + g_prefix_error_literal(error, "failed to get version number: "); return FALSE; } if (!fu_rts54hub_rtd21xx_device_i2c_read(FU_RTS54HUB_RTD21XX_DEVICE(self), @@ -57,7 +49,7 @@ buf_rep, sizeof(buf_rep), error)) { - g_prefix_error(error, "failed to get version number: "); + g_prefix_error_literal(error, "failed to get version number: "); return FALSE; } @@ -71,14 +63,14 @@ static gboolean fu_rts54hub_rtd21xx_background_detach_raw(FuRts54hubRtd21xxBackground *self, GError **error) { - guint8 buf[] = {ISP_CMD_FW_UPDATE_ISP_DONE}; + guint8 buf[] = {FU_RTS54HUB_RTD21XX_BG_ISP_CMD_FW_UPDATE_ISP_DONE}; if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), 0x6A, UC_BACKGROUND_OPCODE, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to detach: "); + g_prefix_error_literal(error, "failed to detach: "); return FALSE; } return TRUE; @@ -96,7 +88,7 @@ &status, error)) return FALSE; - if (status != ISP_STATUS_IDLE_SUCCESS) { + if (status != FU_RTS54HUB_RTD21XX_ISP_STATUS_IDLE_SUCCESS) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -112,10 +104,13 @@ static gboolean fu_rts54hub_rtd21xx_background_detach(FuDevice *device, FuProgress *progress, GError **error) { - FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; g_autoptr(FuDeviceLocker) locker = NULL; /* open device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -130,12 +125,15 @@ static gboolean fu_rts54hub_rtd21xx_background_attach(FuDevice *device, FuProgress *progress, GError **error) { - FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; FuRts54hubRtd21xxDevice *self = FU_RTS54HUB_RTD21XX_DEVICE(device); g_autoptr(FuDeviceLocker) locker = NULL; - guint8 buf[] = {ISP_CMD_FW_UPDATE_EXIT}; + guint8 buf[] = {FU_RTS54HUB_RTD21XX_BG_ISP_CMD_FW_UPDATE_EXIT}; /* open device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -145,11 +143,22 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to attach: "); + g_prefix_error_literal(error, "failed to attach: "); return FALSE; } fu_device_sleep_full(device, 1000, progress); /* ms */ + /* target addr change to 0x6A, need check if target addr ack*/ + if (!fu_rts54hub_rtd21xx_device_i2c_read(FU_RTS54HUB_RTD21XX_DEVICE(self), + 0x6A, + 0x23, + buf, + 1, + error)) { + g_prefix_error_literal(error, "failed to change target addr: "); + return FALSE; + } + /* success */ return TRUE; } @@ -177,10 +186,13 @@ static gboolean fu_rts54hub_rtd21xx_background_reload(FuDevice *device, GError **error) { - FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; g_autoptr(FuDeviceLocker) locker = NULL; /* open parent device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -211,7 +223,7 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 5, "exit"); /* open device */ - locker = fu_device_locker_new(self, error); + locker = fu_device_locker_new(device, error); if (locker == NULL) return FALSE; @@ -221,14 +233,14 @@ return FALSE; /* get project ID address */ - write_buf[0] = ISP_CMD_GET_PROJECT_ID_ADDR; + write_buf[0] = FU_RTS54HUB_RTD21XX_BG_ISP_CMD_GET_PROJECT_ID_ADDR; if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), UC_ISP_TARGET_ADDR, UC_BACKGROUND_OPCODE, write_buf, 1, error)) { - g_prefix_error(error, "failed to get project ID address: "); + g_prefix_error_literal(error, "failed to get project ID address: "); return FALSE; } @@ -240,14 +252,14 @@ read_buf, 6, error)) { - g_prefix_error(error, "failed to read project ID: "); + g_prefix_error_literal(error, "failed to read project ID: "); return FALSE; } - if (read_buf[0] != ISP_STATUS_IDLE_SUCCESS) { + if (read_buf[0] != FU_RTS54HUB_RTD21XX_ISP_STATUS_IDLE_SUCCESS) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "failed project ID with error 0x%02x: ", + "failed project ID with error 0x%02x", read_buf[0]); return FALSE; } @@ -255,7 +267,7 @@ /* verify project ID */ project_addr = fu_memread_uint32(read_buf + 1, G_BIG_ENDIAN); project_id_count = read_buf[5]; - write_buf[0] = ISP_CMD_SYNC_IDENTIFY_CODE; + write_buf[0] = FU_RTS54HUB_RTD21XX_BG_ISP_CMD_SYNC_IDENTIFY_CODE; if (!fu_input_stream_read_safe(stream, write_buf, sizeof(write_buf), @@ -272,14 +284,14 @@ write_buf, project_id_count + 1, error)) { - g_prefix_error(error, "failed to send fw update start cmd: "); + g_prefix_error_literal(error, "failed to send fw update start cmd: "); return FALSE; } if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), NULL, error)) return FALSE; /* background FW update start command */ - write_buf[0] = ISP_CMD_FW_UPDATE_START; + write_buf[0] = FU_RTS54HUB_RTD21XX_BG_ISP_CMD_FW_UPDATE_START; fu_memwrite_uint16(write_buf + 1, ISP_DATA_BLOCKSIZE, G_BIG_ENDIAN); if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), UC_ISP_TARGET_ADDR, @@ -287,7 +299,7 @@ write_buf, 3, error)) { - g_prefix_error(error, "failed to send fw update start cmd: "); + g_prefix_error_literal(error, "failed to send fw update start cmd: "); return FALSE; } fu_progress_step_done(progress); @@ -333,14 +345,14 @@ /* update finish command */ if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), NULL, error)) return FALSE; - write_buf[0] = ISP_CMD_FW_UPDATE_ISP_DONE; + write_buf[0] = FU_RTS54HUB_RTD21XX_BG_ISP_CMD_FW_UPDATE_ISP_DONE; if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), UC_ISP_TARGET_ADDR, UC_BACKGROUND_OPCODE, write_buf, 1, error)) { - g_prefix_error(error, "failed update finish cmd: "); + g_prefix_error_literal(error, "failed update finish cmd: "); return FALSE; } diff -Nru fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-rtd21xx-device.c fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-device.c --- fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-rtd21xx-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,6 +10,7 @@ #include "fu-rts54hub-device.h" #include "fu-rts54hub-rtd21xx-device.h" +#include "fu-rts54hub-struct.h" typedef struct { guint8 target_addr; @@ -17,15 +18,11 @@ guint8 register_addr_len; } FuRts54hubRtd21xxDevicePrivate; +#define FU_RTS54HUB_DDCCI_BUFFER_MAXSZ 256 + G_DEFINE_TYPE_WITH_PRIVATE(FuRts54hubRtd21xxDevice, fu_rts54hub_rtd21xx_device, FU_TYPE_DEVICE) #define GET_PRIVATE(o) (fu_rts54hub_rtd21xx_device_get_instance_private(o)) -typedef enum { - VENDOR_CMD_DISABLE = 0x00, - VENDOR_CMD_ENABLE = 0x01, - VENDOR_CMD_ACCESS_FLASH = 0x02, -} VendorCmd; - static void fu_rts54hub_rtd21xx_device_to_string(FuDevice *module, guint idt, GString *str) { @@ -36,17 +33,6 @@ fwupd_codec_string_append_hex(str, idt, "RegisterAddrLen", priv->register_addr_len); } -static FuRts54HubDevice * -fu_rts54hub_rtd21xx_device_get_parent(FuRts54hubRtd21xxDevice *self, GError **error) -{ - FuDevice *parent = fu_device_get_parent(FU_DEVICE(self)); - if (parent == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no parent set"); - return NULL; - } - return FU_RTS54HUB_DEVICE(parent); -} - static gboolean fu_rts54hub_rtd21xx_device_set_quirk_kv(FuDevice *device, const gchar *key, @@ -102,13 +88,13 @@ gsize datasz, GError **error) { - FuRts54HubDevice *parent; + FuRts54hubDevice *parent; FuRts54hubRtd21xxDevicePrivate *priv = GET_PRIVATE(self); - parent = fu_rts54hub_rtd21xx_device_get_parent(self, error); + parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(FU_DEVICE(self), error)); if (parent == NULL) return FALSE; - if (!fu_rts54hub_device_vendor_cmd(parent, VENDOR_CMD_ENABLE, error)) + if (!fu_rts54hub_device_vendor_cmd(parent, FU_RTS54HUB_VENDOR_CMD_ENABLE, error)) return FALSE; if (target_addr != priv->target_addr) { @@ -128,6 +114,52 @@ return TRUE; } +static guint8 +_fu_xor8(const guint8 *buf, gsize bufsz) +{ + guint8 tmp = 0; + for (guint i = 0; i < bufsz; i++) + tmp ^= buf[i]; + return tmp; +} + +gboolean +fu_rts54hub_rtd21xx_device_ddcci_write(FuRts54hubRtd21xxDevice *self, + guint8 target_addr, + guint8 sub_addr, + const guint8 *data, + gsize datasz, + GError **error) +{ + g_autoptr(GByteArray) buf = g_byte_array_new(); + + if (datasz > FU_RTS54HUB_DDCCI_BUFFER_MAXSZ) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "DDC/CI write length exceed max length: "); + return FALSE; + } + + fu_byte_array_append_uint8(buf, target_addr); + fu_byte_array_append_uint8(buf, sub_addr); + fu_byte_array_append_uint8(buf, (guint8)datasz | 0x80); + g_byte_array_append(buf, data, datasz); + fu_byte_array_append_uint8(buf, _fu_xor8(buf->data, buf->len)); + + if (!fu_rts54hub_rtd21xx_device_i2c_write(self, + target_addr, + sub_addr, + buf->data + 2, + buf->len - 2, + error)) { + g_prefix_error_literal(error, "failed to DDC/CI write: "); + return FALSE; + } + + return TRUE; +} + gboolean fu_rts54hub_rtd21xx_device_i2c_read(FuRts54hubRtd21xxDevice *self, guint8 target_addr, @@ -136,13 +168,13 @@ gsize datasz, GError **error) { - FuRts54HubDevice *parent; + FuRts54hubDevice *parent; FuRts54hubRtd21xxDevicePrivate *priv = GET_PRIVATE(self); - parent = fu_rts54hub_rtd21xx_device_get_parent(self, error); + parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(FU_DEVICE(self), error)); if (parent == NULL) return FALSE; - if (!fu_rts54hub_device_vendor_cmd(parent, VENDOR_CMD_ENABLE, error)) + if (!fu_rts54hub_device_vendor_cmd(parent, FU_RTS54HUB_VENDOR_CMD_ENABLE, error)) return FALSE; if (target_addr != priv->target_addr) { if (!fu_rts54hub_device_i2c_config(parent, @@ -154,13 +186,76 @@ priv->target_addr = target_addr; } if (!fu_rts54hub_device_i2c_read(parent, sub_addr, data, datasz, error)) { - g_prefix_error(error, "failed to read I2C: "); + g_prefix_error_literal(error, "failed to read I2C: "); return FALSE; } return TRUE; } gboolean +fu_rts54hub_rtd21xx_device_ddcci_read(FuRts54hubRtd21xxDevice *self, + guint8 target_addr, + guint8 sub_addr, + guint8 *data, + gsize datasz, + GError **error) +{ + guint8 checksum = 0x50; + guint8 buf[FU_RTS54HUB_DDCCI_BUFFER_MAXSZ] = {0x00}; + gsize length; + + if (datasz > FU_RTS54HUB_DDCCI_BUFFER_MAXSZ) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "DDC/CI read length exceed max length: "); + return FALSE; + } + + if (!fu_rts54hub_rtd21xx_device_i2c_read(self, target_addr, sub_addr, buf, datasz, error)) { + g_prefix_error_literal(error, "failed to DDC/CI read I2C: "); + return FALSE; + } + + if (buf[0] != target_addr) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to DDC/CI read I2C target addr invalid: "); + return FALSE; + } + + length = buf[1] & 0x7F; + if (length + 3 > sizeof(buf)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "DDC/CI read cmd length exceed max length: "); + return FALSE; + } + + /* verify checksum */ + checksum = 0x50 ^ _fu_xor8(buf, length + 2); + if (checksum != buf[length + 2]) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to DDCCI read I2C checksum error: "); + return FALSE; + } + + /* success */ + return fu_memcpy_safe(data, + datasz, + 0x0, /* dst */ + buf, + sizeof(buf), + 0x0, /* src */ + length + 3, + error); +} + +gboolean fu_rts54hub_rtd21xx_device_read_status_raw(FuRts54hubRtd21xxDevice *self, guint8 *status, GError **error) @@ -185,7 +280,7 @@ guint8 status = 0xfd; if (!fu_rts54hub_rtd21xx_device_read_status_raw(self, &status, error)) return FALSE; - if (status == ISP_STATUS_BUSY) { + if (status == FU_RTS54HUB_RTD21XX_ISP_STATUS_BUSY) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "status was 0x%02x", status); return FALSE; } @@ -207,7 +302,7 @@ static void fu_rts54hub_rtd21xx_device_init(FuRts54hubRtd21xxDevice *self) { - fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_VIDEO_DISPLAY); fu_device_add_protocol(FU_DEVICE(self), "com.realtek.rts54.i2c"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); diff -Nru fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-rtd21xx-device.h fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-device.h --- fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-rtd21xx-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -29,12 +29,6 @@ #define UC_BACKGROUND_OPCODE 0x31 #define UC_BACKGROUND_ISP_DATA_OPCODE 0x32 -typedef enum { - ISP_STATUS_BUSY = 0xBB, /* host must wait for device */ - ISP_STATUS_IDLE_SUCCESS = 0x11, /* previous command was OK */ - ISP_STATUS_IDLE_FAILURE = 0x12, /* previous command failed */ -} IspStatus; - gboolean fu_rts54hub_rtd21xx_device_read_status(FuRts54hubRtd21xxDevice *self, guint8 *status, @@ -43,6 +37,22 @@ fu_rts54hub_rtd21xx_device_read_status_raw(FuRts54hubRtd21xxDevice *self, guint8 *status, GError **error); + +gboolean +fu_rts54hub_rtd21xx_device_i2c_write(FuRts54hubRtd21xxDevice *self, + guint8 target_addr, + guint8 sub_addr, + const guint8 *data, + gsize datasz, + GError **error); + +gboolean +fu_rts54hub_rtd21xx_device_ddcci_write(FuRts54hubRtd21xxDevice *self, + guint8 target_addr, + guint8 sub_addr, + const guint8 *data, + gsize datasz, + GError **error); gboolean fu_rts54hub_rtd21xx_device_i2c_read(FuRts54hubRtd21xxDevice *self, guint8 target_addr, @@ -51,9 +61,9 @@ gsize datasz, GError **error); gboolean -fu_rts54hub_rtd21xx_device_i2c_write(FuRts54hubRtd21xxDevice *self, - guint8 target_addr, - guint8 sub_addr, - const guint8 *data, - gsize datasz, - GError **error); +fu_rts54hub_rtd21xx_device_ddcci_read(FuRts54hubRtd21xxDevice *self, + guint8 target_addr, + guint8 sub_addr, + guint8 *data, + gsize datasz, + GError **error); diff -Nru fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-rtd21xx-foreground.c fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-foreground.c --- fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-rtd21xx-foreground.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-foreground.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,6 +10,7 @@ #include "fu-rts54hub-device.h" #include "fu-rts54hub-rtd21xx-foreground.h" +#include "fu-rts54hub-struct.h" struct _FuRts54hubRtd21xxForeground { FuRts54hubRtd21xxDevice parent_instance; @@ -22,23 +23,12 @@ #define ISP_DATA_BLOCKSIZE 256 #define ISP_PACKET_SIZE 257 -typedef enum { - ISP_CMD_ENTER_FW_UPDATE = 0x01, - ISP_CMD_GET_PROJECT_ID_ADDR = 0x02, - ISP_CMD_SYNC_IDENTIFY_CODE = 0x03, - ISP_CMD_GET_FW_INFO = 0x04, - ISP_CMD_FW_UPDATE_START = 0x05, - ISP_CMD_FW_UPDATE_ISP_DONE = 0x06, - ISP_CMD_FW_UPDATE_RESET = 0x07, - ISP_CMD_FW_UPDATE_EXIT = 0x08, -} IspCmd; - static gboolean fu_rts54hub_rtd21xx_foreground_ensure_version_unlocked(FuRts54hubRtd21xxForeground *self, GError **error) { guint8 buf_rep[7] = {0x00}; - guint8 buf_req[] = {ISP_CMD_GET_FW_INFO}; + guint8 buf_req[] = {FU_RTS54HUB_RTD21XX_FG_ISP_CMD_GET_FW_INFO}; g_autofree gchar *version = NULL; if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), @@ -47,7 +37,7 @@ buf_req, sizeof(buf_req), error)) { - g_prefix_error(error, "failed to get version number: "); + g_prefix_error_literal(error, "failed to get version number: "); return FALSE; } @@ -59,7 +49,7 @@ buf_rep, sizeof(buf_rep), error)) { - g_prefix_error(error, "failed to get version number: "); + g_prefix_error_literal(error, "failed to get version number: "); return FALSE; } /* set version */ @@ -78,7 +68,7 @@ &buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to detach: "); + g_prefix_error_literal(error, "failed to detach: "); return FALSE; } /* wait for device ready */ @@ -98,7 +88,7 @@ &status, error)) return FALSE; - if (status != ISP_STATUS_IDLE_SUCCESS) { + if (status != FU_RTS54HUB_RTD21XX_ISP_STATUS_IDLE_SUCCESS) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -114,10 +104,13 @@ static gboolean fu_rts54hub_rtd21xx_foreground_detach(FuDevice *device, FuProgress *progress, GError **error) { - FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; g_autoptr(FuDeviceLocker) locker = NULL; /* open device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -127,12 +120,15 @@ static gboolean fu_rts54hub_rtd21xx_foreground_attach(FuDevice *device, FuProgress *progress, GError **error) { - FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; FuRts54hubRtd21xxForeground *self = FU_RTS54HUB_RTD21XX_FOREGROUND(device); - guint8 buf[] = {ISP_CMD_FW_UPDATE_RESET}; + guint8 buf[] = {FU_RTS54HUB_RTD21XX_FG_ISP_CMD_FW_UPDATE_RESET}; g_autoptr(FuDeviceLocker) locker = NULL; /* open device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -146,7 +142,9 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to ISP_CMD_FW_UPDATE_RESET: "); + g_prefix_error_literal( + error, + "failed to FU_RTS54HUB_RTD21XX_FG_ISP_CMD_FW_UPDATE_RESET: "); return FALSE; } @@ -154,19 +152,33 @@ * it can be queried again */ fu_device_sleep_full(device, 60000, progress); /* ms */ + /* target addr change to 0x6A, need check if target addr ack*/ + if (!fu_rts54hub_rtd21xx_device_i2c_read(FU_RTS54HUB_RTD21XX_DEVICE(self), + 0x6A, + 0x23, + buf, + 1, + error)) { + g_prefix_error_literal(error, "failed to change target addr: "); + return FALSE; + } + /* success */ return TRUE; } static gboolean -fu_rts54hub_rtd21xx_foreground_exit(FuDevice *device, GError **error) +fu_rts54hub_rtd21xx_foreground_exit_cb(FuDevice *device, GError **error) { - FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; FuRts54hubRtd21xxForeground *self = FU_RTS54HUB_RTD21XX_FOREGROUND(device); - guint8 buf[] = {ISP_CMD_FW_UPDATE_EXIT}; + guint8 buf[] = {FU_RTS54HUB_RTD21XX_FG_ISP_CMD_FW_UPDATE_EXIT}; g_autoptr(FuDeviceLocker) locker = NULL; /* open device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -177,7 +189,8 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to ISP_CMD_FW_UPDATE_EXIT: "); + g_prefix_error_literal(error, + "failed to FU_RTS54HUB_RTD21XX_FG_ISP_CMD_FW_UPDATE_EXIT: "); return FALSE; } @@ -192,10 +205,11 @@ g_autoptr(FuDeviceLocker) locker = NULL; /* get version */ - locker = fu_device_locker_new_full(device, - (FuDeviceLockerFunc)fu_device_detach, - (FuDeviceLockerFunc)fu_rts54hub_rtd21xx_foreground_exit, - error); + locker = + fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_device_detach, + (FuDeviceLockerFunc)fu_rts54hub_rtd21xx_foreground_exit_cb, + error); if (locker == NULL) return FALSE; if (!fu_rts54hub_rtd21xx_foreground_ensure_version_unlocked(self, error)) @@ -208,10 +222,13 @@ static gboolean fu_rts54hub_rtd21xx_foreground_reload(FuDevice *device, GError **error) { - FuRts54HubDevice *parent = FU_RTS54HUB_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; g_autoptr(FuDeviceLocker) locker = NULL; /* open parent device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -242,7 +259,7 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 5, "finish"); /* open device */ - locker = fu_device_locker_new(self, error); + locker = fu_device_locker_new(FU_DEVICE(self), error); if (locker == NULL) return FALSE; @@ -252,7 +269,7 @@ return FALSE; /* enable ISP high priority */ - write_buf[0] = ISP_CMD_ENTER_FW_UPDATE; + write_buf[0] = FU_RTS54HUB_RTD21XX_FG_ISP_CMD_ENTER_FW_UPDATE; write_buf[1] = 0x01; if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), UC_ISP_TARGET_ADDR, @@ -260,21 +277,21 @@ write_buf, 2, error)) { - g_prefix_error(error, "failed to enable ISP: "); + g_prefix_error_literal(error, "failed to enable ISP: "); return FALSE; } if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), NULL, error)) return FALSE; /* get project ID address */ - write_buf[0] = ISP_CMD_GET_PROJECT_ID_ADDR; + write_buf[0] = FU_RTS54HUB_RTD21XX_FG_ISP_CMD_GET_PROJECT_ID_ADDR; if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), UC_ISP_TARGET_ADDR, UC_FOREGROUND_OPCODE, write_buf, 1, error)) { - g_prefix_error(error, "failed to get project ID address: "); + g_prefix_error_literal(error, "failed to get project ID address: "); return FALSE; } @@ -286,14 +303,14 @@ read_buf, 6, error)) { - g_prefix_error(error, "failed to read project ID: "); + g_prefix_error_literal(error, "failed to read project ID: "); return FALSE; } - if (read_buf[0] != ISP_STATUS_IDLE_SUCCESS) { + if (read_buf[0] != FU_RTS54HUB_RTD21XX_ISP_STATUS_IDLE_SUCCESS) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "failed project ID with error 0x%02x: ", + "failed project ID with error 0x%02x", read_buf[0]); return FALSE; } @@ -301,7 +318,7 @@ /* verify project ID */ project_addr = fu_memread_uint32(read_buf + 1, G_BIG_ENDIAN); project_id_count = read_buf[5]; - write_buf[0] = ISP_CMD_SYNC_IDENTIFY_CODE; + write_buf[0] = FU_RTS54HUB_RTD21XX_FG_ISP_CMD_SYNC_IDENTIFY_CODE; if (!fu_input_stream_read_safe(stream, write_buf, sizeof(write_buf), @@ -318,14 +335,14 @@ write_buf, project_id_count + 1, error)) { - g_prefix_error(error, "failed to send fw update start cmd: "); + g_prefix_error_literal(error, "failed to send fw update start cmd: "); return FALSE; } if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), NULL, error)) return FALSE; /* foreground FW update start command */ - write_buf[0] = ISP_CMD_FW_UPDATE_START; + write_buf[0] = FU_RTS54HUB_RTD21XX_FG_ISP_CMD_FW_UPDATE_START; fu_memwrite_uint16(write_buf + 1, ISP_DATA_BLOCKSIZE, G_BIG_ENDIAN); if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), UC_ISP_TARGET_ADDR, @@ -333,7 +350,7 @@ write_buf, 3, error)) { - g_prefix_error(error, "failed to send fw update start cmd: "); + g_prefix_error_literal(error, "failed to send fw update start cmd: "); return FALSE; } fu_progress_step_done(progress); @@ -379,14 +396,14 @@ /* update finish command */ if (!fu_rts54hub_rtd21xx_device_read_status(FU_RTS54HUB_RTD21XX_DEVICE(self), NULL, error)) return FALSE; - write_buf[0] = ISP_CMD_FW_UPDATE_ISP_DONE; + write_buf[0] = FU_RTS54HUB_RTD21XX_FG_ISP_CMD_FW_UPDATE_ISP_DONE; if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), UC_ISP_TARGET_ADDR, UC_FOREGROUND_OPCODE, write_buf, 1, error)) { - g_prefix_error(error, "failed update finish cmd: "); + g_prefix_error_literal(error, "failed update finish cmd: "); return FALSE; } fu_progress_step_done(progress); diff -Nru fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-rtd21xx-mergeinfo.c fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-mergeinfo.c --- fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-rtd21xx-mergeinfo.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-mergeinfo.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,493 @@ +/* + * Copyright 2025 Realtek Corporation + * Copyright 2025 Shadow Zhang + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include + +#include "fu-rts54hub-device.h" +#include "fu-rts54hub-rtd21xx-mergeinfo.h" +#include "fu-rts54hub-struct.h" + +struct _FuRts54hubRtd21xxMergeinfo { + FuRts54hubRtd21xxDevice parent_instance; +}; + +G_DEFINE_TYPE(FuRts54hubRtd21xxMergeinfo, + fu_rts54hub_rtd21xx_mergeinfo, + FU_TYPE_RTS54HUB_RTD21XX_DEVICE) + +#define FU_RTS54HUB_MERGEINFO_ADDR_DEBUG_TARGET 0x6A +#define FU_RTS54HUB_MERGEINFO_ADDR_DDCCI_TARGET 0x6E + +#define FU_RTS54HUB_MERGEINFO_OPCODE_CHANGE_TO_DDCCI_MODE 0x71 + +#define FU_RTS54HUB_MERGEINFO_SUB_ADDR_DDCCI_COMM 0x71 +#define FU_RTS54HUB_MERGEINFO_SUB_ADDR_CHECK_ACK 0x23 + +#define FU_RTS54HUB_MERGEINFO_VERSION_BUFSZ 4 + +#define FU_RTS54HUB_MERGEINFO_DDCCI_CHECK_TARGET_VALUE 0x90 + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_ddcci_mode(FuRts54hubRtd21xxMergeinfo *self, GError **error) +{ + guint8 temp = 0x01; + + /* change debug mode to DDC/CI mode */ + if (!fu_rts54hub_rtd21xx_device_i2c_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + FU_RTS54HUB_MERGEINFO_ADDR_DEBUG_TARGET, + FU_RTS54HUB_MERGEINFO_OPCODE_CHANGE_TO_DDCCI_MODE, + &temp, + sizeof(temp), + error)) { + g_prefix_error_literal(error, "failed to change debug mode to DDC/CI mode: "); + return FALSE; + } + + /* wait for device ready */ + fu_device_sleep(FU_DEVICE(self), 300); /* ms */ + + if (!fu_rts54hub_rtd21xx_device_i2c_read(FU_RTS54HUB_RTD21XX_DEVICE(self), + FU_RTS54HUB_MERGEINFO_ADDR_DDCCI_TARGET, + FU_RTS54HUB_MERGEINFO_SUB_ADDR_CHECK_ACK, + &temp, + sizeof(temp), + error)) { + g_prefix_error_literal(error, "failed to change debug mode to DDC/CI mode: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_check_ddcci(FuRts54hubRtd21xxMergeinfo *self, GError **error) +{ + guint8 buf_reply[16] = {0x00}; + g_autoptr(FuStructRts54HubDdcPkt) st = fu_struct_rts54_hub_ddc_pkt_new(); + + /* check DDC/CI communication */ + fu_struct_rts54_hub_ddc_pkt_set_second_opcode( + st, + FU_RTS54_HUB_MERGE_INFO_DDCCI_OPCODE_COMMUNICATION); + + if (!fu_rts54hub_rtd21xx_device_ddcci_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + FU_RTS54HUB_MERGEINFO_ADDR_DDCCI_TARGET, + FU_RTS54HUB_MERGEINFO_SUB_ADDR_DDCCI_COMM, + st->buf->data, + st->buf->len, + error)) { + g_prefix_error_literal(error, "failed to DDC/CI communication with fw: "); + return FALSE; + } + + /* wait for device ready */ + fu_device_sleep(FU_DEVICE(self), 300); /* ms */ + + if (!fu_rts54hub_rtd21xx_device_ddcci_read(FU_RTS54HUB_RTD21XX_DEVICE(self), + FU_RTS54HUB_MERGEINFO_ADDR_DDCCI_TARGET, + FU_RTS54HUB_MERGEINFO_SUB_ADDR_DDCCI_COMM, + buf_reply, + sizeof(buf_reply), + error)) { + g_prefix_error_literal(error, "failed to DDC/CI communication with fw: "); + return FALSE; + } + + if (buf_reply[4] != FU_RTS54HUB_MERGEINFO_DDCCI_CHECK_TARGET_VALUE) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to DDC/CI communication with fw: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_ensure_version(FuRts54hubRtd21xxMergeinfo *self, GError **error) +{ + guint8 buf_reply[16] = {0x00}; + guint32 version_raw = 0; + g_autoptr(FuStructRts54HubDdcPkt) st = fu_struct_rts54_hub_ddc_pkt_new(); + + /* read merge version */ + fu_struct_rts54_hub_ddc_pkt_set_second_opcode( + st, + FU_RTS54_HUB_MERGE_INFO_DDCCI_OPCODE_GET_VERSION); + + if (!fu_rts54hub_rtd21xx_device_ddcci_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + FU_RTS54HUB_MERGEINFO_ADDR_DDCCI_TARGET, + FU_RTS54HUB_MERGEINFO_SUB_ADDR_DDCCI_COMM, + st->buf->data, + st->buf->len, + error)) { + g_prefix_error_literal(error, "failed to DDC/CI communication with fw: "); + return FALSE; + } + + /* wait for device ready */ + fu_device_sleep(FU_DEVICE(self), 300); /* ms */ + + if (!fu_rts54hub_rtd21xx_device_ddcci_read(FU_RTS54HUB_RTD21XX_DEVICE(self), + FU_RTS54HUB_MERGEINFO_ADDR_DDCCI_TARGET, + FU_RTS54HUB_MERGEINFO_SUB_ADDR_DDCCI_COMM, + buf_reply, + sizeof(buf_reply), + error)) { + g_prefix_error_literal(error, "failed to DDC/CI communication with fw: "); + return FALSE; + } + + if (!fu_memread_uint32_safe(buf_reply, + sizeof(buf_reply), + 4, + &version_raw, + G_BIG_ENDIAN, + error)) { + g_prefix_error_literal(error, "converting version to uint32 failed: "); + return FALSE; + } + fu_device_set_version_raw(FU_DEVICE(self), version_raw); + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_read_version(FuRts54hubRtd21xxMergeinfo *self, + guint8 *buf_version, + gsize buf_version_sz, + GError **error) +{ + guint8 buf_reply[16] = {0x00}; + g_autoptr(FuStructRts54HubDdcPkt) st = fu_struct_rts54_hub_ddc_pkt_new(); + + /* read merge version */ + fu_struct_rts54_hub_ddc_pkt_set_second_opcode( + st, + FU_RTS54_HUB_MERGE_INFO_DDCCI_OPCODE_GET_VERSION); + + if (!fu_rts54hub_rtd21xx_device_ddcci_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + FU_RTS54HUB_MERGEINFO_ADDR_DDCCI_TARGET, + FU_RTS54HUB_MERGEINFO_SUB_ADDR_DDCCI_COMM, + st->buf->data, + st->buf->len, + error)) { + g_prefix_error_literal(error, "failed to DDC/CI communication with fw: "); + return FALSE; + } + + /* wait for device ready */ + fu_device_sleep(FU_DEVICE(self), 300); /* ms */ + + if (!fu_rts54hub_rtd21xx_device_ddcci_read(FU_RTS54HUB_RTD21XX_DEVICE(self), + FU_RTS54HUB_MERGEINFO_ADDR_DDCCI_TARGET, + FU_RTS54HUB_MERGEINFO_SUB_ADDR_DDCCI_COMM, + buf_reply, + sizeof(buf_reply), + error)) { + g_prefix_error_literal(error, "failed to DDC/CI communication with fw: "); + return FALSE; + } + + if (!fu_memcpy_safe(buf_version, + buf_version_sz, + 0x0, /* dst */ + buf_reply, + 16, + 4, /* src */ + buf_version_sz, + error)) { + g_prefix_error_literal(error, "memcpy merge version fail: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_write_version(FuRts54hubRtd21xxMergeinfo *self, + const guint8 *buf_version, + gsize buf_version_sz, + GError **error) +{ + g_autoptr(FuStructRts54HubDdcWriteMergeInfoPkt) st = + fu_struct_rts54_hub_ddc_write_merge_info_pkt_new(); + + if (buf_version_sz != FU_RTS54HUB_MERGEINFO_VERSION_BUFSZ) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to check version buffer size: "); + return FALSE; + } + + /* write merge version */ + fu_struct_rts54_hub_ddc_write_merge_info_pkt_set_second_opcode( + st, + FU_RTS54_HUB_MERGE_INFO_DDCCI_OPCODE_SET_VERSION); + fu_struct_rts54_hub_ddc_write_merge_info_pkt_set_major_version(st, buf_version[0]); + fu_struct_rts54_hub_ddc_write_merge_info_pkt_set_minor_version(st, buf_version[1]); + fu_struct_rts54_hub_ddc_write_merge_info_pkt_set_patch_version(st, buf_version[2]); + fu_struct_rts54_hub_ddc_write_merge_info_pkt_set_build_version(st, buf_version[3]); + + if (!fu_rts54hub_rtd21xx_device_ddcci_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + FU_RTS54HUB_MERGEINFO_ADDR_DDCCI_TARGET, + FU_RTS54HUB_MERGEINFO_SUB_ADDR_DDCCI_COMM, + st->buf->data, + st->buf->len, + error)) { + g_prefix_error_literal(error, "failed to write merge fw version: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_restore_state(FuRts54hubRtd21xxMergeinfo *self, GError **error) +{ + guint8 temp = 0; + g_autoptr(FuStructRts54HubDdcPkt) st = fu_struct_rts54_hub_ddc_pkt_new(); + + fu_struct_rts54_hub_ddc_pkt_set_second_opcode( + st, + FU_RTS54_HUB_MERGE_INFO_DDCCI_OPCODE_DDCCI_TO_DEBUG); + + if (!fu_rts54hub_rtd21xx_device_ddcci_write(FU_RTS54HUB_RTD21XX_DEVICE(self), + FU_RTS54HUB_MERGEINFO_ADDR_DDCCI_TARGET, + FU_RTS54HUB_MERGEINFO_SUB_ADDR_DDCCI_COMM, + st->buf->data, + st->buf->len, + error)) { + g_prefix_error_literal(error, "failed to DDC/CI communication with fw: "); + return FALSE; + } + + /* wait for device ready */ + fu_device_sleep(FU_DEVICE(self), 500); /* ms */ + + if (!fu_rts54hub_rtd21xx_device_i2c_read(FU_RTS54HUB_RTD21XX_DEVICE(self), + FU_RTS54HUB_MERGEINFO_ADDR_DEBUG_TARGET, + FU_RTS54HUB_MERGEINFO_SUB_ADDR_CHECK_ACK, + &temp, + 1, + error)) { + g_prefix_error_literal(error, "failed to change to debug target addr: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_detach_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuRts54hubRtd21xxMergeinfo *self = FU_RTS54HUB_RTD21XX_MERGEINFO(device); + + /* wait for device ready */ + if (!fu_rts54hub_rtd21xx_mergeinfo_ddcci_mode(self, error)) { + g_prefix_error_literal(error, "change to DDC/CI mode fail: "); + return FALSE; + } + fu_device_sleep(FU_DEVICE(self), 300); /* ms */ + if (!fu_rts54hub_rtd21xx_mergeinfo_check_ddcci(self, error)) { + g_prefix_error_literal(error, "check DDC/CI mode fail: "); + return FALSE; + } + fu_device_sleep(FU_DEVICE(self), 300); /* ms */ + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + return fu_device_retry_full(device, + fu_rts54hub_rtd21xx_mergeinfo_detach_cb, + 10, + 300, + NULL, + error); +} + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuRts54hubRtd21xxMergeinfo *self = FU_RTS54HUB_RTD21XX_MERGEINFO(device); + + if (!fu_rts54hub_rtd21xx_mergeinfo_restore_state(self, error)) { + g_prefix_error_literal(error, "failed to restore state in attach: "); + return FALSE; + } + /* the device needs some time to restart with the new firmware before + * it can be queried again */ + fu_device_sleep_full(device, 1000, progress); /* ms */ + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_exit_cb(FuDevice *device, GError **error) +{ + FuRts54hubRtd21xxMergeinfo *self = FU_RTS54HUB_RTD21XX_MERGEINFO(device); + + if (!fu_rts54hub_rtd21xx_mergeinfo_restore_state(self, error)) { + g_prefix_error_literal(error, "failed to restore state in attach: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_setup(FuDevice *device, GError **error) +{ + FuRts54hubRtd21xxMergeinfo *self = FU_RTS54HUB_RTD21XX_MERGEINFO(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* get version */ + locker = + fu_device_locker_new_full(device, + (FuDeviceLockerFunc)fu_device_detach, + (FuDeviceLockerFunc)fu_rts54hub_rtd21xx_mergeinfo_exit_cb, + error); + if (locker == NULL) + return FALSE; + if (!fu_rts54hub_rtd21xx_mergeinfo_ensure_version(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_reload(FuDevice *device, GError **error) +{ + return fu_rts54hub_rtd21xx_mergeinfo_setup(device, error); +} + +static gboolean +fu_rts54hub_rtd21xx_mergeinfo_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuRts54hubRtd21xxMergeinfo *self = FU_RTS54HUB_RTD21XX_MERGEINFO(device); + guint8 buf[FU_RTS54HUB_MERGEINFO_VERSION_BUFSZ] = {0x0}; + guint8 buf_version[FU_RTS54HUB_MERGEINFO_VERSION_BUFSZ] = {0x00}; + const gchar *version_str = NULL; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 50, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 50, "read"); + + /* get version x.x.x.x */ + version_str = fu_firmware_get_version(firmware); + if (version_str == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "get version in write firmware fail: "); + return FALSE; + } + + /* convert x.x.x.x to buf_version */ + if (fu_device_get_version_format(FU_DEVICE(self)) == FWUPD_VERSION_FORMAT_QUAD) { + if (sscanf(version_str, + "%hhu.%hhu.%hhu.%hhu", + &buf_version[0], + &buf_version[1], + &buf_version[2], + &buf_version[3]) != 4) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to parse version str: "); + return FALSE; + } + } else { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "failed to get version format: "); + return FALSE; + } + + /* write version*/ + if (!fu_rts54hub_rtd21xx_mergeinfo_write_version(self, + buf_version, + sizeof(buf_version), + error)) { + g_prefix_error_literal(error, "failed to write merge version: "); + return FALSE; + } + + /* wait for device ready */ + fu_device_sleep(FU_DEVICE(self), 1000); /* ms */ + fu_progress_step_done(progress); + + if (!fu_rts54hub_rtd21xx_mergeinfo_read_version(self, buf, sizeof(buf), error)) { + g_prefix_error_literal(error, "failed to read merge version: "); + return FALSE; + } + if (!fu_memcmp_safe(buf, + sizeof(buf), + 0x0, + buf_version, + sizeof(buf_version), + 0x0, + FU_RTS54HUB_MERGEINFO_VERSION_BUFSZ, + error)) { + g_prefix_error_literal(error, "failed to compare merge version: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* success */ + return TRUE; +} + +static gchar * +fu_rts54hub_rtd21xx_mergeinfo_convert_version(FuDevice *device, guint64 version_raw) +{ + return fu_version_from_uint32(version_raw, fu_device_get_version_format(device)); +} + +static void +fu_rts54hub_rtd21xx_mergeinfo_init(FuRts54hubRtd21xxMergeinfo *self) +{ + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); +} + +static void +fu_rts54hub_rtd21xx_mergeinfo_class_init(FuRts54hubRtd21xxMergeinfoClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->setup = fu_rts54hub_rtd21xx_mergeinfo_setup; + device_class->reload = fu_rts54hub_rtd21xx_mergeinfo_reload; + device_class->attach = fu_rts54hub_rtd21xx_mergeinfo_attach; + device_class->detach = fu_rts54hub_rtd21xx_mergeinfo_detach; + device_class->write_firmware = fu_rts54hub_rtd21xx_mergeinfo_write_firmware; + device_class->convert_version = fu_rts54hub_rtd21xx_mergeinfo_convert_version; +} diff -Nru fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-rtd21xx-mergeinfo.h fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-mergeinfo.h --- fwupd-2.0.8/plugins/rts54hub/fu-rts54hub-rtd21xx-mergeinfo.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/fu-rts54hub-rtd21xx-mergeinfo.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2025 Realtek Corporation + * Copyright 2025 Shadow Zhang + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#include "fu-rts54hub-rtd21xx-device.h" + +#define FU_TYPE_RTS54HUB_RTD21XX_MERGEINFO (fu_rts54hub_rtd21xx_mergeinfo_get_type()) +G_DECLARE_FINAL_TYPE(FuRts54hubRtd21xxMergeinfo, + fu_rts54hub_rtd21xx_mergeinfo, + FU, + RTS54HUB_RTD21XX_MERGEINFO, + FuRts54hubRtd21xxDevice) diff -Nru fwupd-2.0.8/plugins/rts54hub/fu-rts54hub.rs fwupd-2.0.20/plugins/rts54hub/fu-rts54hub.rs --- fwupd-2.0.8/plugins/rts54hub/fu-rts54hub.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/fu-rts54hub.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,76 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +enum FuRts54hubRtd21xxIspStatus { + Busy = 0xbb, // host must wait for device + IdleSuccess = 0x11, // previous command was OK + IdleFailure = 0x12, // previous command failed +} + +enum FuRts54hubI2cSpeed { + 100K, + 200K, + 300K, + 400K, + 500K, + 600K, + 700K, + 800K, +} + +#[repr(u8)] +#[derive(Bitfield, ToString)] +enum FuRts54hubVendorCmd { + None = 0, + Enable = 1 << 0, + AccessFlash = 1 << 1, +} + +enum FuRts54hubRtd21xxBgIspCmd { + None, + FwUpdateStart, + FwUpdateIspDone, + GetFwInfo, + FwUpdateExit, + GetProjectIdAddr, + SyncIdentifyCode, +} + +enum FuRts54hubRtd21xxFgIspCmd { + None, + EnterFwUpdate, + GetProjectIdAddr, + SyncIdentifyCode, + GetFwInfo, + FwUpdateStart, + FwUpdateIspDone, + FwUpdateReset, + FwUpdateExit, +} + +#[repr(u8)] +enum FuRts54HubMergeInfoDdcciOpcode { + Communication = 0x11, + DdcciToDebug = 0x55, + First = 0x77, + GetVersion = 0x99, + SetVersion = 0xBB, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructRts54HubDdcPkt { + first_opcode: FuRts54HubMergeInfoDdcciOpcode = First, + second_opcode: u8, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructRts54HubDdcWriteMergeInfoPkt { + first_opcode: FuRts54HubMergeInfoDdcciOpcode = First, + second_opcode: u8, + major_version: u8, + minor_version: u8, + patch_version: u8, + build_version: u8, +} diff -Nru fwupd-2.0.8/plugins/rts54hub/meson.build fwupd-2.0.20/plugins/rts54hub/meson.build --- fwupd-2.0.8/plugins/rts54hub/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,13 +1,15 @@ -cargs = ['-DG_LOG_DOMAIN="FuPluginRts54Hub"'] +cargs = ['-DG_LOG_DOMAIN="FuPluginRts54hub"'] plugins += {meson.current_source_dir().split('/')[-1]: true} plugin_quirks += files('rts54hub.quirk') plugin_builtins += static_library('fu_plugin_rts54hub', + rustgen.process('fu-rts54hub.rs'), sources: [ 'fu-rts54hub-device.c', 'fu-rts54hub-rtd21xx-device.c', 'fu-rts54hub-rtd21xx-background.c', 'fu-rts54hub-rtd21xx-foreground.c', + 'fu-rts54hub-rtd21xx-mergeinfo.c', 'fu-rts54hub-plugin.c', ], include_directories: plugin_incdirs, @@ -18,4 +20,5 @@ device_tests += files( 'tests/realtek-rts5423.json', + 'tests/realtek-br1u3aa.json', ) diff -Nru fwupd-2.0.8/plugins/rts54hub/rts54hub.quirk fwupd-2.0.20/plugins/rts54hub/rts54hub.quirk --- fwupd-2.0.8/plugins/rts54hub/rts54hub.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/rts54hub.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -2,22 +2,20 @@ [USB\VID_0BDA&PID_5B00] Plugin = rts54hub Flags = enforce-requires -GType = FuRts54HubDevice +GType = FuRts54hubDevice FirmwareSizeMin = 0x20000 FirmwareSizeMax = 0x40000 # Lenovo HotRod [USB\VID_17EF&PID_30BF] Plugin = rts54hub -GType = FuRts54HubDevice -Vendor = Lenovo +GType = FuRts54hubDevice FirmwareSizeMin = 0x20000 FirmwareSizeMax = 0x40000 Children = FuRts54hubRtd21xxForeground|USB\VID_17EF&PID_30BF&I2C_01 [USB\VID_17EF&PID_30BF&I2C_01] Plugin = rts54hub Name = HDMI Converter -Flags = updatable FirmwareSize = 0x30000 Rts54TargetAddr = 0x20 Rts54I2cSpeed = 0x2 @@ -26,7 +24,7 @@ # Acer D501 Dock [USB\VID_2BEF&PID_1009] Plugin = rts54hub -GType = FuRts54HubDevice +GType = FuRts54hubDevice Name = Acer D501 Dock USB Hub FirmwareSizeMin = 0x10000 FirmwareSizeMax = 0x40000 @@ -35,86 +33,109 @@ Plugin = rts54hub Vendor = Realtek Name = Acer D501 Dock HDMI Converter -Flags = updatable FirmwareSizeMin = 0x10000 FirmwareSizeMax = 0x90000 Rts54TargetAddr = 0x20 Rts54I2cSpeed = 0x2 Rts54RegisterAddrLen = 0x04 -#Acer T34 Dock gen1 +# Acer T34 Dock gen1 [USB\VID_0502&PID_0702] Plugin = rts54hub -Flags = updatable -GType = FuRts54HubDevice +GType = FuRts54hubDevice FirmwareSizeMin = 0x10000 FirmwareSizeMax = 0x20000 -#Acer T34 Dock gen2 +# Acer T34 Dock gen2 [USB\VID_0502&PID_0701] Plugin = rts54hub -Flags = updatable -GType = FuRts54HubDevice +GType = FuRts54hubDevice FirmwareSizeMin = 0x10000 FirmwareSizeMax = 0x20000 -#Acer U33 Dock gen2 +# Acer U33 Dock gen2 [USB\VID_0502&PID_0801] Plugin = rts54hub -Flags = updatable -GType = FuRts54HubDevice +GType = FuRts54hubDevice FirmwareSizeMin = 0x10000 FirmwareSizeMax = 0x20000 -#Acer U33 Dock gen1 +# Acer U33 Dock gen1 [USB\VID_0502&PID_0802] Plugin = rts54hub -Flags = updatable -GType = FuRts54HubDevice +GType = FuRts54hubDevice FirmwareSizeMin = 0x10000 FirmwareSizeMax = 0x20000 -#Acer U32 Dock level 1 +# Acer U32 Dock level 1 [USB\VID_0502&PID_0804] Plugin = rts54hub Name = Acer Universal Dock U32 Lv1 -Flags = updatable -GType = FuRts54HubDevice +GType = FuRts54hubDevice FirmwareSizeMin = 0x10000 FirmwareSizeMax = 0x20000 -#Acer U32 Dock level 2 +# Acer U32 Dock level 2 [USB\VID_0502&PID_0806] Plugin = rts54hub Name = Acer Universal Dock U32 Lv2 -Flags = updatable -GType = FuRts54HubDevice +GType = FuRts54hubDevice FirmwareSizeMin = 0x10000 FirmwareSizeMax = 0x20000 -#UCDDS Dock level 1 +# UCDDS Dock level 1 [USB\VID_3749&PID_050B] Plugin = rts54hub Name = UCDDS1080P Lv1 -Flags = updatable -GType = FuRts54HubDevice +GType = FuRts54hubDevice FirmwareSizeMin = 0x10000 FirmwareSizeMax = 0x20000 -#UCDDS Dock level 2 +# UCDDS Dock level 2 [USB\VID_3749&PID_050D] Plugin = rts54hub Name = UCDDS1080P Lv2 -Flags = updatable -GType = FuRts54HubDevice +GType = FuRts54hubDevice FirmwareSizeMin = 0x10000 FirmwareSizeMax = 0x20000 -#USB-C Dual Display Dock +# USB-C Dual Display Dock [USB\VID_065F&PID_2260] Plugin = rts54hub Name = USB-C Dual Display Dock -Flags = updatable -GType = FuRts54HubDevice +GType = FuRts54hubDevice FirmwareSizeMin = 0x10000 FirmwareSizeMax = 0x20000 + +# HP Portable USB-C Hub +[USB\VID_03F0&PID_AE4A] +Plugin = rts54hub +Name = HP Portable USB-C Hub +GType = FuRts54hubDevice +FirmwareSizeMin = 0x10000 +FirmwareSizeMax = 0x30000 +Rts54BlockSize = 0x100 + +# HP Cypher +[USB\VID_03F0&PID_AC4A] +Plugin = rts54hub +Name = HP Portable USB-C 4K HDMI Hub - USB Hub +GType = FuRts54hubDevice +FirmwareSizeMin = 0x10000 +FirmwareSizeMax = 0x30000 +Rts54BlockSize = 0x100 +Children = FuRts54hubRtd21xxForeground|USB\VID_03F0&PID_AB4A&I2C_01,FuRts54hubRtd21xxMergeinfo|USB\VID_03F0&PID_AB4A&I2C_02 +[USB\VID_03F0&PID_AB4A&I2C_01] +Plugin = rts54hub +Name = HP Portable USB-C 4K HDMI Hub - HDMI Converter +FirmwareSize = 0x30000 +Rts54TargetAddr = 0x20 +Rts54I2cSpeed = 0x2 +Rts54RegisterAddrLen = 0x04 +[USB\VID_03F0&PID_AB4A&I2C_02] +Plugin = rts54hub +Name = HP Portable USB-C 4K HDMI Hub - Package Version +FirmwareSize = 4 +Rts54TargetAddr = 0x20 +Rts54I2cSpeed = 0x2 +Rts54RegisterAddrLen = 0x04 diff -Nru fwupd-2.0.8/plugins/rts54hub/tests/realtek-br1u3aa.json fwupd-2.0.20/plugins/rts54hub/tests/realtek-br1u3aa.json --- fwupd-2.0.8/plugins/rts54hub/tests/realtek-br1u3aa.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/tests/realtek-br1u3aa.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +{ + "name": "HP Portable USB-C 4K HDMI Hub (BR1U3AA)", + "interactive": false, + "steps": [ + { + "url": "c931d9ae8085355c1372df70ee97c947a4efd00d0b6e60c6c3a65a0ede73dbbd-HP_Cypher_v09_v030_2p1_0004_test.cab", + "emulation-url": "f21b635cb7cfea451c9bcb2ac9656266bdffe66091b5bc43534e8a471d94c56a-rts54hub_addmergeinfofeature_20251126.zip", + "components": [ + { + "version": "0.0.0.4", + "guids": [ + "f6b32c07-9d67-5258-9264-4bb41bc486c7" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/rts54hub/tests/realtek-rts5423.json fwupd-2.0.20/plugins/rts54hub/tests/realtek-rts5423.json --- fwupd-2.0.8/plugins/rts54hub/tests/realtek-rts5423.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/rts54hub/tests/realtek-rts5423.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/460a18d93f5d6118908d8437009ebbd6eceb3c6a0cdfbaf31f8a96df008564da-Realtek-RTS5423-1.56.cab", - "emulation-url": "https://fwupd.org/downloads/a203b727b0dae3d721605dbd1551e90c5c8d2fe5f8e7a7059300f1cc94658186-Realtek-RTS5423-1.56.zip", + "url": "460a18d93f5d6118908d8437009ebbd6eceb3c6a0cdfbaf31f8a96df008564da-Realtek-RTS5423-1.56.cab", + "emulation-url": "a203b727b0dae3d721605dbd1551e90c5c8d2fe5f8e7a7059300f1cc94658186-Realtek-RTS5423-1.56.zip", "components": [ { "version": "1.56", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/659e721b0efbe2dfc003d3d30ea5b771c00113cc9a162333ee5a8918c4515f69-Realtek-RTS5423-1.57.cab", - "emulation-url": "https://fwupd.org/downloads/85ca3b102ca29c42d2e14bbeaa355d0f26d3cf05f5db10716efd52e48bff1496-Realtek-RTS5423-1.57.zip", + "url": "659e721b0efbe2dfc003d3d30ea5b771c00113cc9a162333ee5a8918c4515f69-Realtek-RTS5423-1.57.cab", + "emulation-url": "85ca3b102ca29c42d2e14bbeaa355d0f26d3cf05f5db10716efd52e48bff1496-Realtek-RTS5423-1.57.zip", "components": [ { "version": "1.57", diff -Nru fwupd-2.0.8/plugins/scsi/fu-scsi-device.c fwupd-2.0.20/plugins/scsi/fu-scsi-device.c --- fwupd-2.0.8/plugins/scsi/fu-scsi-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/scsi/fu-scsi-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -98,7 +98,7 @@ FU_UDEV_DEVICE_ATTR_READ_TIMEOUT_DEFAULT, error); if (attr_ffu_timeout == NULL) { - g_prefix_error(error, "no ffu timeout specified: "); + g_prefix_error_literal(error, "no ffu timeout specified: "); return FALSE; } if (!fu_strtoull(attr_ffu_timeout, @@ -180,7 +180,7 @@ fu_scsi_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) firmware = fu_firmware_new(); @@ -298,7 +298,7 @@ sizeof(buf), SG_DXFER_FROM_DEV, error)) { - g_prefix_error(error, "SG_IO INQUIRY_CMD data error: "); + g_prefix_error_literal(error, "SG_IO INQUIRY_CMD data error: "); return FALSE; } @@ -317,9 +317,9 @@ fu_device_set_version(device, revision); /* add GUIDs */ - fu_device_add_instance_str(device, "VEN", vendor); - fu_device_add_instance_str(device, "DEV", model); - fu_device_add_instance_str(device, "REV", revision); + fu_device_add_instance_strsafe(device, "VEN", vendor); + fu_device_add_instance_strsafe(device, "DEV", model); + fu_device_add_instance_strsafe(device, "REV", revision); if (!fu_device_build_instance_id_full(device, FU_DEVICE_INSTANCE_FLAG_QUIRKS, error, @@ -434,7 +434,7 @@ } static void -fu_scsi_device_set_progress(FuDevice *self, FuProgress *progress) +fu_scsi_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -447,7 +447,7 @@ static void fu_scsi_device_init(FuScsiDevice *self) { - fu_device_add_icon(FU_DEVICE(self), "drive-harddisk"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_DRIVE_HARDDISK); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); fu_device_set_summary(FU_DEVICE(self), "SCSI device"); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_ADD_INSTANCE_ID_REV); diff -Nru fwupd-2.0.8/plugins/scsi/meson.build fwupd-2.0.20/plugins/scsi/meson.build --- fwupd-2.0.8/plugins/scsi/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/scsi/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginScsi"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -17,4 +18,3 @@ link_with: plugin_libs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/scsi/scsi.quirk fwupd-2.0.20/plugins/scsi/scsi.quirk --- fwupd-2.0.8/plugins/scsi/scsi.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/scsi/scsi.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -20,6 +20,6 @@ [SCSI\VEN_HP&DEV_MO1600JVYPR&REV_HPD6] Issue = MTX-6f54e8621f4c490a918defdab6 -#Samsung +# Samsung [SCSI\VEN_SAMSUNG&DEV_KLUDG4UHGC-B0E1] ScsiWriteBufferSize = 524288 diff -Nru fwupd-2.0.8/plugins/snapd-uefi/README.md fwupd-2.0.20/plugins/snapd-uefi/README.md --- fwupd-2.0.8/plugins/snapd-uefi/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/snapd-uefi/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +--- +title: Plugin: Snapd UEFI Integration +--- + +## Introduction + +This plugin allows integration with snapd, to allow db, dbx, KEK and PK updates to be deployed +without the user having to use the volume recovery key on next boot. + +## External Interface Access + +This plugin requires access to either `/run/snapd-snap.socket` or `/run/snapd.socket`. + +## Version Considerations + +This plugin has been available since fwupd version `2.0.19`. diff -Nru fwupd-2.0.8/plugins/snapd-uefi/fu-self-test.c fwupd-2.0.20/plugins/snapd-uefi/fu-self-test.c --- fwupd-2.0.8/plugins/snapd-uefi/fu-self-test.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/snapd-uefi/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,732 @@ +/* + * Copyright 2024 Maciej Borzecki + * Copyright 2025 Simon Johnsson + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include +#include + +#include "fwupd-error.h" + +#include "fu-context-private.h" +#include "fu-plugin-private.h" +#include "fu-snapd-uefi-plugin.h" +#include "fu-uefi-device-private.h" + +typedef struct { + gboolean snapd_fde_detected; + gboolean snapd_supported; + const gchar *mock_snapd_scenario; +} FuTestCase; + +typedef struct { + FuContext *ctx; + + gboolean mock_snapd_available; + + CURL *mock_snapd_curl; + struct curl_slist *mock_curl_hdrs; +} FuTestFixture; + +static gboolean +fu_self_test_mock_snapd_easy_post_request(FuTestFixture *fixture, + const gchar *endpoint, + const gchar *data, + gsize len) +{ + CURL *curl = curl_easy_duphandle(fixture->mock_snapd_curl); + CURLcode res = -1; + glong status_code = 0; + g_autofree gchar *endpoint_str = g_strdup_printf("http://localhost%s", endpoint); + + g_debug("mock snapd request to %s with data: '%s'", endpoint_str, data); + + /* use snap dedicated socket when running inside a snap */ + (void)curl_easy_setopt(curl, CURLOPT_URL, endpoint_str); + (void)curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len); + (void)curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); + res = curl_easy_perform(curl); + g_debug("curl error: %u %s", res, curl_easy_strerror(res)); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code); + curl_easy_cleanup(curl); + return res == CURLE_OK && status_code == 200; +} + +static size_t +fu_self_test_curl_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + GByteArray *bufarr = (GByteArray *)userdata; + gsize sz = size * nmemb; + g_byte_array_append(bufarr, (const guint8 *)ptr, sz); + return sz; +} + +static GBytes * +fu_self_test_mock_snapd_easy_get_request(FuTestFixture *fixture, const gchar *endpoint) +{ + CURL *curl = curl_easy_duphandle(fixture->mock_snapd_curl); + CURLcode res = -1; + glong status_code = 0; + g_autoptr(GByteArray) buf = g_byte_array_new(); + g_autofree gchar *endpoint_str = g_strdup_printf("http://localhost%s", endpoint); + + /* use snap dedicated socket when running inside a snap */ + (void)curl_easy_setopt(curl, CURLOPT_URL, endpoint_str); + (void)curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fu_self_test_curl_write_callback); + (void)curl_easy_setopt(curl, CURLOPT_WRITEDATA, buf); + res = curl_easy_perform(curl); + g_debug("curl error: %u %s", res, curl_easy_strerror(res)); + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code); + curl_easy_cleanup(curl); + + g_assert_true(res == CURLE_OK); + g_assert_true(status_code == 200); + + g_debug("rsp:%s", buf->data); + + return g_bytes_new(buf->data, buf->len); +} + +typedef struct { + guint startup; + guint prepare; + guint cleanup; +} FuTestSnapdCalls; + +static void +fu_self_test_mock_snapd_assert_calls(FuTestFixture *fixture, FuTestSnapdCalls calls) +{ + guint64 val = 0xffffff; + g_autoptr(GBytes) rsp = NULL; + g_autoptr(GKeyFile) kf = g_key_file_new(); + g_autoptr(GError) error = NULL; + + rsp = fu_self_test_mock_snapd_easy_get_request(fixture, "/test/stats"); + + g_key_file_load_from_bytes(kf, rsp, 0, &error); + g_assert_no_error(error); + + val = g_key_file_get_uint64(kf, "stats", "efi-secureboot-update-startup", &error); + g_assert_no_error(error); + g_assert_cmpuint(val, ==, calls.startup); + + val = g_key_file_get_uint64(kf, "stats", "efi-secureboot-update-db-prepare", &error); + g_assert_no_error(error); + g_assert_cmpuint(val, ==, calls.prepare); + + val = g_key_file_get_uint64(kf, "stats", "efi-secureboot-update-db-cleanup", &error); + g_assert_no_error(error); + g_assert_cmpuint(val, ==, calls.cleanup); +} + +static gboolean +fu_self_test_mock_snapd_reset(FuTestFixture *fixture) +{ + return fu_self_test_mock_snapd_easy_post_request(fixture, "/test/reset", NULL, 0); +} + +static gboolean +fu_self_test_mock_snapd_setup_scenario(FuTestFixture *fixture, const gchar *scenario) +{ + g_autofree gchar *scenario_request = g_strdup_printf("{\"scenario\":\"%s\"}", scenario); + + return fu_self_test_mock_snapd_easy_post_request(fixture, + "/test/setup", + scenario_request, + strlen(scenario_request)); +} + +static gboolean +fu_self_test_mock_snapd_init(FuTestFixture *fixture) +{ + CURL *curl = curl_easy_init(); + struct curl_slist *req_hdrs = NULL; + const char *mock_snapd_snap_socket = g_getenv("FWUPD_SNAPD_SNAP_SOCKET"); + + g_assert_nonnull(mock_snapd_snap_socket); + + /* use snap dedicated socket when running inside a snap */ + (void)curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, mock_snapd_snap_socket); + (void)curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); + req_hdrs = curl_slist_append(req_hdrs, "Content-Type: application/json"); + (void)curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req_hdrs); + + fixture->mock_snapd_curl = curl; + fixture->mock_curl_hdrs = req_hdrs; + + return fu_self_test_mock_snapd_reset(fixture); +} + +static void +fu_self_test_set_up(FuTestFixture *fixture, gconstpointer user_data) +{ + FuTestCase *tc = (FuTestCase *)user_data; + gboolean ret; + g_autoptr(GError) error = NULL; + + fixture->ctx = fu_context_new(); + + if (tc->snapd_fde_detected && fu_self_test_mock_snapd_init(fixture)) { + fixture->mock_snapd_available = TRUE; + + /* snapd-uefi expects to be running in a snap */ + (void)g_setenv("SNAP", "fwupd", TRUE); + g_debug("set SNAP environment variable for snapd integration tests"); + + if (tc->snapd_fde_detected) + fu_context_add_flag(fixture->ctx, FU_CONTEXT_FLAG_FDE_SNAPD); + + fu_self_test_mock_snapd_setup_scenario(fixture, tc->mock_snapd_scenario); + } + + fu_context_add_flag(fixture->ctx, FU_CONTEXT_FLAG_INHIBIT_VOLUME_MOUNT); + fu_context_add_flag(fixture->ctx, FU_CONTEXT_FLAG_FDE_SNAPD); + ret = fu_context_load_quirks(fixture->ctx, + FU_QUIRKS_LOAD_FLAG_NO_CACHE | FU_QUIRKS_LOAD_FLAG_NO_VERIFY, + &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_self_test_tear_down(FuTestFixture *fixture, gconstpointer user_data) +{ + FuTestCase *tc = (FuTestCase *)user_data; + /* test should always run in a snap */ + g_unsetenv("SNAP"); + if (tc->snapd_fde_detected) { + if (fixture->mock_snapd_available) + fu_self_test_mock_snapd_reset(fixture); + + if (fixture->mock_snapd_curl) + curl_easy_cleanup(fixture->mock_snapd_curl); + + if (fixture->mock_curl_hdrs) + curl_slist_free_all(fixture->mock_curl_hdrs); + } + + g_object_unref(fixture->ctx); +} + +static gboolean +fu_test_mock_efivar_content(FuEfivars *efivars, + const gchar *guid, + const gchar *name, + const gchar *path, + GError **error) +{ + gchar *mock_blob = NULL; + gsize mock_blob_size = 0; + g_autoptr(GBytes) mock_bytes = NULL; + + if (!g_file_get_contents(path, &mock_blob, &mock_blob_size, error)) + return FALSE; + + mock_bytes = g_bytes_new_take(mock_blob, mock_blob_size); + + return fu_efivars_set_data_bytes(efivars, guid, name, mock_bytes, 0, error); +} + +static gboolean +fu_test_mock_dbx_efivars(FuEfivars *efivars, GError **error) +{ + g_autofree gchar *mock_kek_path = + g_test_build_filename(G_TEST_DIST, + "tests/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c", + NULL); + g_autofree gchar *mock_dbx_path = + g_test_build_filename(G_TEST_DIST, + "tests/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f", + NULL); + + if (!fu_test_mock_efivar_content(efivars, + FU_EFIVARS_GUID_EFI_GLOBAL, + "KEK", + mock_kek_path, + error)) + return FALSE; + + return fu_test_mock_efivar_content(efivars, + FU_EFIVARS_GUID_SECURITY_DATABASE, + "dbx", + mock_dbx_path, + error); +} + +static FuFirmware * +fu_test_mock_dbx_update_firmware(void) +{ + gboolean ret; + gchar *mock_blob = NULL; + gsize mock_blob_size = 0; + g_autoptr(GError) error = NULL; + g_autoptr(GBytes) mock_bytes = NULL; + g_autofree gchar *mock_dbx_update_path = + g_test_build_filename(G_TEST_DIST, "tests/dbx-update.auth", NULL); + + ret = g_file_get_contents(mock_dbx_update_path, &mock_blob, &mock_blob_size, &error); + g_assert_no_error(error); + g_assert_true(ret); + + mock_bytes = g_bytes_new_take(mock_blob, mock_blob_size); + return fu_firmware_new_from_bytes(mock_bytes); +} + +static void +fu_uefi_dbx_test_plugin_update(FuTestFixture *fixture, gconstpointer user_data) +{ + /* run though an update */ + gboolean ret; + FuContext *ctx = fixture->ctx; + FuEfivars *efivars = fu_context_get_efivars(ctx); + g_autoptr(GError) error = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuPlugin) plugin = fu_plugin_new_from_gtype(FU_TYPE_SNAPD_UEFI_PLUGIN, ctx); + g_autoptr(FuUefiDevice) uefi_device = NULL; + + /* progress */ + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 33, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 33, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 33, NULL); + + if (!fixture->mock_snapd_available) { + g_test_skip("mock snapd not available"); + return; + } + + if (!fu_test_mock_dbx_efivars(efivars, &error) && + g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + g_test_skip("test assets unavailable"); + return; + } + g_assert_no_error(error); + + ret = fu_plugin_runner_startup(plugin, fu_progress_get_child(progress), &error); + g_assert_no_error(error); + g_assert_true(ret); + fu_progress_step_done(progress); + + uefi_device = g_object_new(FU_TYPE_UEFI_DEVICE, "context", ctx, NULL); + fu_uefi_device_set_guid(FU_UEFI_DEVICE(uefi_device), FU_EFIVARS_GUID_EFI_GLOBAL); + fu_uefi_device_set_name(FU_UEFI_DEVICE(uefi_device), "KEK"); + ret = fu_plugin_runner_device_created(plugin, FU_DEVICE(uefi_device), &error); + g_assert_no_error(error); + g_assert_true(ret); + + firmware = fu_test_mock_dbx_update_firmware(); + ret = fu_plugin_runner_composite_peek_firmware(plugin, + FU_DEVICE(uefi_device), + firmware, + fu_progress_get_child(progress), + /* skip verification of ESP binaries*/ + FWUPD_INSTALL_FLAG_FORCE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_true(fu_device_has_flag(FU_DEVICE(uefi_device), FWUPD_DEVICE_FLAG_NEEDS_REBOOT)); + fu_progress_step_done(progress); + + /* this is normally invoked by FuEngine */ + ret = fu_device_cleanup(FU_DEVICE(uefi_device), fu_progress_get_child(progress), 0, &error); + g_assert_true(ret); + g_assert_no_error(error); + fu_progress_step_done(progress); + + fu_self_test_mock_snapd_assert_calls(fixture, + (FuTestSnapdCalls){ + .startup = 1, + .prepare = 1, + .cleanup = 1, + }); +} + +static void +fu_uefi_dbx_test_plugin_failed_update(FuTestFixture *fixture, gconstpointer user_data) +{ + /* update which fails at either write or cleanup steps, this test can only + * properly mock the environment when using snapd integration */ + + FuTestCase *tc = (FuTestCase *)user_data; + gboolean ret; + FuContext *ctx = fixture->ctx; + FuEfivars *efivars = fu_context_get_efivars(ctx); + g_autoptr(GError) error = NULL; + g_autoptr(FuFirmware) firmware = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuProgress) progress_write = fu_progress_new(G_STRLOC); + g_autoptr(FuPlugin) plugin = fu_plugin_new_from_gtype(FU_TYPE_SNAPD_UEFI_PLUGIN, ctx); + g_autoptr(FuUefiDevice) uefi_device = NULL; + + if (!tc->snapd_fde_detected) { + g_test_skip("only supports snapd integration variant"); + return; + } + + if (!fixture->mock_snapd_available) { + g_test_skip("mock snapd not available"); + return; + } + + if (!fu_test_mock_dbx_efivars(efivars, &error) && + g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + g_test_skip("test assets unavailable"); + return; + } + g_assert_no_error(error); + + ret = fu_plugin_runner_startup(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + uefi_device = g_object_new(FU_TYPE_UEFI_DEVICE, "context", ctx, NULL); + fu_uefi_device_set_guid(FU_UEFI_DEVICE(uefi_device), FU_EFIVARS_GUID_EFI_GLOBAL); + fu_uefi_device_set_name(FU_UEFI_DEVICE(uefi_device), "KEK"); + ret = fu_plugin_runner_device_created(plugin, FU_DEVICE(uefi_device), &error); + g_assert_no_error(error); + g_assert_true(ret); + + firmware = fu_test_mock_dbx_update_firmware(); + ret = fu_plugin_runner_composite_peek_firmware(plugin, + FU_DEVICE(uefi_device), + firmware, + progress_write, + /* skip verification of ESP binaries*/ + FWUPD_INSTALL_FLAG_FORCE, + &error); + if (g_str_equal(tc->mock_snapd_scenario, "failed-prepare")) { + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL); + g_clear_error(&error); + } else { + g_assert_no_error(error); + g_assert_true(ret); + } + + /* engine invokes cleanup */ + ret = fu_device_cleanup(FU_DEVICE(uefi_device), progress, 0, &error); + if (g_str_equal(tc->mock_snapd_scenario, "failed-cleanup")) { + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL); + } else { + g_assert_no_error(error); + g_assert_true(ret); + } + + fu_self_test_mock_snapd_assert_calls(fixture, + (FuTestSnapdCalls){ + .startup = 1, + .prepare = 1, + .cleanup = 1, + }); +} + +static void +fu_uefi_dbx_test_plugin_coldplug_probed_device(FuTestFixture *fixture, gconstpointer user_data) +{ + FuTestCase *tc = (FuTestCase *)user_data; + gboolean ret; + FuContext *ctx = fixture->ctx; + FuEfivars *efivars = fu_context_get_efivars(ctx); + g_autoptr(GError) error = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuPlugin) plugin = fu_plugin_new_from_gtype(FU_TYPE_SNAPD_UEFI_PLUGIN, ctx); + g_autoptr(FuUefiDevice) uefi_device = NULL; + + if (!fixture->mock_snapd_available) { + g_test_skip("mock snapd not available"); + return; + } + + if (!fu_test_mock_dbx_efivars(efivars, &error) && + g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { + g_test_skip("test assets unavailable"); + return; + } + g_assert_no_error(error); + + ret = fu_plugin_runner_startup(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + uefi_device = g_object_new(FU_TYPE_UEFI_DEVICE, "context", ctx, NULL); + fu_uefi_device_set_guid(FU_UEFI_DEVICE(uefi_device), FU_EFIVARS_GUID_EFI_GLOBAL); + fu_uefi_device_set_name(FU_UEFI_DEVICE(uefi_device), "KEK"); + ret = fu_plugin_runner_device_created(plugin, FU_DEVICE(uefi_device), &error); + g_assert_no_error(error); + g_assert_true(ret); + + ret = fu_device_has_inhibit(FU_DEVICE(uefi_device), "no-snapd-dbx"); + if (tc->snapd_supported && g_str_equal(tc->mock_snapd_scenario, "failed-startup")) { + /* startup failed for whatever reason, device updates are inhibited */ + g_assert_true(ret); + } else + g_assert_false(ret); + + fu_self_test_mock_snapd_assert_calls(fixture, + (FuTestSnapdCalls){ + .startup = 1, + }); +} + +// NOTE: I think this is failing because it is not supposed to run in a snap, +// but the snapd_uefi_plugin startup function assumes it is running in a snap? +static void +fu_uefi_dbx_test_plugin_startup(FuTestFixture *fixture, gconstpointer user_data) +{ + gboolean ret; + FuContext *ctx = fixture->ctx; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(GError) error = NULL; + g_autoptr(FuPlugin) plugin = fu_plugin_new_from_gtype(FU_TYPE_SNAPD_UEFI_PLUGIN, ctx); + + if (!fixture->mock_snapd_available) { + g_test_skip("mock snapd not available"); + return; + } + + ret = fu_context_load_quirks(ctx, + FU_QUIRKS_LOAD_FLAG_NO_CACHE | FU_QUIRKS_LOAD_FLAG_NO_VERIFY, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + ret = fu_plugin_runner_startup(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + fu_self_test_mock_snapd_assert_calls(fixture, + (FuTestSnapdCalls){ + .startup = 1, + }); +} + +int +main(int argc, char **argv) +{ + FuTestCase simple = { + }; + /* test variants with snapd, see tests/snapd.py for what a specific scenario + * supports */ + FuTestCase running_in_snap = { + .snapd_supported = TRUE, + .mock_snapd_scenario = "happy-startup", + }; + FuTestCase snapd_fde_detected = { + .snapd_fde_detected = TRUE, + .snapd_supported = TRUE, + .mock_snapd_scenario = "happy-startup", + }; + FuTestCase running_in_snap_snapd_fde_detected = { + .snapd_fde_detected = TRUE, + .snapd_supported = TRUE, + .mock_snapd_scenario = "happy-startup", + }; + FuTestCase running_in_snap_no_support = { + .snapd_supported = FALSE, + .mock_snapd_scenario = "not-supported", + }; + FuTestCase snapd_fde_detected_no_support = { + .snapd_fde_detected = TRUE, + .snapd_supported = FALSE, + .mock_snapd_scenario = "not-supported", + }; + FuTestCase running_in_snap_bad_startup = { + .snapd_supported = TRUE, + .mock_snapd_scenario = "failed-startup", + }; + FuTestCase snapd_fde_detected_bad_startup = { + .snapd_fde_detected = TRUE, + .snapd_supported = TRUE, + .mock_snapd_scenario = "failed-startup", + }; + FuTestCase running_in_snap_update = { + .snapd_supported = TRUE, + .mock_snapd_scenario = "happy-update", + }; + FuTestCase snapd_fde_detected_update = { + .snapd_fde_detected = TRUE, + .snapd_supported = TRUE, + .mock_snapd_scenario = "happy-update", + }; + FuTestCase running_in_snap_update_failed_prepare = { + .snapd_supported = TRUE, + .mock_snapd_scenario = "failed-prepare", + }; + FuTestCase snapd_fde_detected_update_failed_prepare = { + .snapd_fde_detected = TRUE, + .snapd_supported = TRUE, + .mock_snapd_scenario = "failed-prepare", + }; + FuTestCase running_in_snap_update_failed_cleanup = { + .snapd_supported = TRUE, + .mock_snapd_scenario = "failed-cleanup", + }; + FuTestCase snapd_fde_detected_update_failed_cleanup = { + .snapd_fde_detected = TRUE, + .snapd_supported = TRUE, + .mock_snapd_scenario = "failed-cleanup", + }; + g_autofree gchar *testdatadir = NULL; + + (void)g_setenv("G_TEST_SRCDIR", SRCDIR, FALSE); + g_test_init(&argc, &argv, NULL); + + /* only critical and error are fatal */ + g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); + (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); + + testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + + (void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_EFIVARS", "dummy", TRUE); + (void)g_setenv("FWUPD_SYSFSDRIVERDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_SNAPD_SNAP_SOCKET", "/tmp/mock-snapd-test.sock", TRUE); + + /* tests go here */ + g_test_add("/uefi-dbx/startup", + FuTestFixture, + &simple, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_startup, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/update", + FuTestFixture, + &simple, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_update, + fu_self_test_tear_down); + + g_test_add("/uefi-dbx/startup/snapd/running-in-snap/supported", + FuTestFixture, + &running_in_snap, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_startup, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/startup/snapd/fde-detected/supported", + FuTestFixture, + &snapd_fde_detected, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_startup, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/startup/snapd/running-in-snap-and-fde-detected/supported", + FuTestFixture, + &running_in_snap_snapd_fde_detected, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_startup, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/startup/snapd/running-in-snap/not-supported", + FuTestFixture, + &running_in_snap_no_support, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_startup, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/startup/snapd/fde-detected/not-supported", + FuTestFixture, + &snapd_fde_detected_no_support, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_startup, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/startup/snapd/running-in-snap/supported-failure", + FuTestFixture, + &running_in_snap_bad_startup, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_startup, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/startup/snapd/fde-detected/supported-failure", + FuTestFixture, + &snapd_fde_detected_bad_startup, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_startup, + fu_self_test_tear_down); + + g_test_add("/uefi-dbx/coldplug/with-device", + FuTestFixture, + &simple, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_coldplug_probed_device, + fu_self_test_tear_down); + + g_test_add("/uefi-dbx/coldplug/snapd/running-in-snap/supported", + FuTestFixture, + &running_in_snap, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_coldplug_probed_device, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/coldplug/snapd/fde-detected/supported", + FuTestFixture, + &snapd_fde_detected, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_coldplug_probed_device, + fu_self_test_tear_down); + + g_test_add("/uefi-dbx/coldplug/snapd/running-in-snap/not-supported", + FuTestFixture, + &running_in_snap_no_support, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_coldplug_probed_device, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/coldplug/snapd/fde-detected/not-supported", + FuTestFixture, + &snapd_fde_detected_no_support, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_coldplug_probed_device, + fu_self_test_tear_down); + + g_test_add("/uefi-dbx/coldplug/snapd/running-in-snap/supported-failure", + FuTestFixture, + &running_in_snap_bad_startup, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_coldplug_probed_device, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/coldplug/snapd/fde-detected/supported-failure", + FuTestFixture, + &snapd_fde_detected_bad_startup, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_coldplug_probed_device, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/update/snapd/running-in-snap/success", + FuTestFixture, + &running_in_snap_update, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_update, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/update/snapd/fde-detected/success", + FuTestFixture, + &snapd_fde_detected_update, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_update, + fu_self_test_tear_down); + + g_test_add("/uefi-dbx/update/snapd/running-in-snap/failed-prepare", + FuTestFixture, + &running_in_snap_update_failed_prepare, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_failed_update, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/update/snapd/fde-detected/failed-prepare", + FuTestFixture, + &snapd_fde_detected_update_failed_prepare, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_failed_update, + fu_self_test_tear_down); + + g_test_add("/uefi-dbx/update/snapd/running-in-snap/failed-cleanup", + FuTestFixture, + &running_in_snap_update_failed_cleanup, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_failed_update, + fu_self_test_tear_down); + g_test_add("/uefi-dbx/update/snapd/fde-detected/failed-cleanup", + FuTestFixture, + &snapd_fde_detected_update_failed_cleanup, + fu_self_test_set_up, + fu_uefi_dbx_test_plugin_failed_update, + fu_self_test_tear_down); + + return g_test_run(); +} diff -Nru fwupd-2.0.8/plugins/snapd-uefi/fu-snapd-uefi-plugin.c fwupd-2.0.20/plugins/snapd-uefi/fu-snapd-uefi-plugin.c --- fwupd-2.0.8/plugins/snapd-uefi/fu-snapd-uefi-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/snapd-uefi/fu-snapd-uefi-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,319 @@ +/* + * Copyright 2024 Maciej Borzecki + * Copyright 2025 Simon Johnsson + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include +#include + +#include "fu-snapd-uefi-plugin.h" + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURL, curl_easy_cleanup); + +struct _FuSnapPlugin { + FuPlugin parent_instance; + gboolean snapd_integration_supported; + CURL *curl_template; + struct curl_slist *req_hdrs; +}; + +G_DEFINE_TYPE(FuSnapPlugin, fu_snapd_uefi_plugin, FU_TYPE_PLUGIN) + +static const gchar * +fu_snapd_uefi_plugin_device_to_key_database(FuSnapPlugin *self, FuDevice *device) +{ + const gchar *plugin = fu_device_get_plugin(device); + if (g_strcmp0(plugin, "uefi_dbx") == 0) + return "DBX"; + if (g_strcmp0(plugin, "uefi_db") == 0) + return "DB"; + if (g_strcmp0(plugin, "uefi_kek") == 0) + return "KEK"; + if (g_strcmp0(plugin, "uefi_pk") == 0) + return "PK"; + return NULL; +} + +static void +fu_snapd_uefi_plugin_device_registered(FuPlugin *plugin, FuDevice *device) +{ + FuSnapPlugin *self = FU_SNAPD_UEFI_PLUGIN(plugin); + + /* is not UEFI update */ + if (fu_snapd_uefi_plugin_device_to_key_database(self, device) == NULL) + return; + + /* if snapd integration is supported, but we are unable to use snapd, inhibit updates */ + if (!self->snapd_integration_supported) { + fu_device_inhibit(FU_DEVICE(device), + "no-snapd", + "snapd integration for UEFI update is not available"); + } +} + +/* see CURLOPT_WRITEFUNCTION(3) */ +static size_t +fu_snapd_uefi_plugin_rsp_cb(char *ptr, size_t size, size_t nmemb, void *userdata) +{ + GByteArray *bufarr = (GByteArray *)userdata; + gsize sz = size * nmemb; + g_byte_array_append(bufarr, (const guint8 *)ptr, sz); + return sz; +} + +static gboolean +fu_snapd_uefi_plugin_simple_req(FuSnapPlugin *self, + const gchar *endpoint, + const gchar *data, + gsize len, + GError **error) +{ + CURLcode res = -1; + glong status_code = 0; + g_autoptr(CURL) curl = NULL; + g_autofree gchar *endpoint_str = NULL; + g_autoptr(GByteArray) rsp_buf = g_byte_array_new(); + + /* duplicate a preconfigured curl handle */ + curl = curl_easy_duphandle(self->curl_template); + + endpoint_str = g_strdup_printf("http://localhost%s", endpoint); + + (void)curl_easy_setopt(curl, CURLOPT_URL, endpoint_str); + + (void)curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len); + (void)curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); + + /* collect response for debugging */ + (void)curl_easy_setopt(curl, CURLOPT_WRITEDATA, rsp_buf); + (void)curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fu_snapd_uefi_plugin_rsp_cb); + + res = curl_easy_perform(curl); + if (res != CURLE_OK) { + /* TODO inspect the error */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to communicate with snapd: %s", + curl_easy_strerror(res)); + return FALSE; + } + + curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code); + if (status_code == 404) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "snapd notification endpoint not supported by snapd API"); + return FALSE; + } + + if (status_code != 200) { + g_autofree gchar *rsp = NULL; + if (rsp_buf->len > 0) { + /* make sure the response is printable */ + rsp = fu_strsafe((const char *)rsp_buf->data, rsp_buf->len); + } + + /* TODO check whether the response is even printable? */ + g_info("snapd request failed with status %ld, response: %s", + (glong)status_code, + rsp != NULL ? rsp : ""); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "snapd request failed with status %ld", + (glong)status_code); + return FALSE; + } + + return TRUE; +} + +static gboolean +fu_snapd_uefi_plugin_is_in_snap(void) +{ + return getenv("SNAP") != NULL; +} + +static gboolean +fu_snapd_uefi_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) +{ + FuSnapPlugin *self = FU_SNAPD_UEFI_PLUGIN(plugin); + FuContext *ctx = fu_plugin_get_context(plugin); + const gchar *startup_msg = "{\"action\":\"efi-secureboot-update-startup\"}"; + const gchar *snapd_snap_socket_override = g_getenv("FWUPD_SNAPD_SNAP_SOCKET"); + g_autoptr(GError) error_local = NULL; + + /* only enable snapd integration if either running inside a snap or we detect that this is a + snapd FDE setup. either of these cases makes snapd integration mandatory */ + if (!fu_snapd_uefi_plugin_is_in_snap() && + !fu_context_has_flag(ctx, FU_CONTEXT_FLAG_FDE_SNAPD)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not run as a snap and no snap FDE"); + return FALSE; + } + + /* default path is different inside the snap sandbox vs out */ + self->curl_template = curl_easy_init(); + if (snapd_snap_socket_override != NULL) { + (void)curl_easy_setopt(self->curl_template, + CURLOPT_UNIX_SOCKET_PATH, + snapd_snap_socket_override); + } else { + const gchar *snapd_snap_socket = fu_snapd_uefi_plugin_is_in_snap() + ? "/run/snapd-snap.socket" + : "/run/snapd.socket"; + /* use snap dedicated socket when running inside a snap */ + (void)curl_easy_setopt(self->curl_template, + CURLOPT_UNIX_SOCKET_PATH, + snapd_snap_socket); + } + + /* set up curl */ + self->req_hdrs = curl_slist_append(self->req_hdrs, "Content-Type: application/json"); + (void)curl_easy_setopt(self->curl_template, CURLOPT_HTTPHEADER, self->req_hdrs); + + /* notify snapd of that the DBX manager has started */ + if (!fu_snapd_uefi_plugin_simple_req(self, + "/v2/system-secureboot", + startup_msg, + strlen(startup_msg), + &error_local)) { + /* unless we got specific error indicating lack of relevant APIs, snapd integration + * is considered to be supported, even if snapd itself cannot be reached */ + self->snapd_integration_supported = + !g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_info("snapd integration non-functional: %s", error_local->message); + } else { + g_info("snapd integration enabled"); + self->snapd_integration_supported = TRUE; + } + return TRUE; +} + +static gboolean +fu_snapd_uefi_plugin_cleanup(FuSnapPlugin *self, FuDevice *device, GError **error) +{ + const gchar *msg = "{\"action\":\"efi-secureboot-update-db-cleanup\"}"; + + /* notify of an completed update to one of secureboot key databases -- + * a successful call shall result in completion of a corresponding change on the + * snapd side */ + if (!fu_snapd_uefi_plugin_simple_req(self, + "/v2/system-secureboot", + msg, + strlen(msg), + error)) { + g_prefix_error_literal(error, "failed to notify snapd of cleanup: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_snapd_uefi_plugin_composite_cleanup(FuPlugin *plugin, GPtrArray *devices, GError **error) +{ + FuSnapPlugin *self = FU_SNAPD_UEFI_PLUGIN(plugin); + + /* only for UEFI updates */ + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + if (fu_snapd_uefi_plugin_device_to_key_database(self, device) == NULL) + continue; + if (!fu_snapd_uefi_plugin_cleanup(self, device, error)) + return FALSE; + } + return TRUE; +} + +static gboolean +fu_snapd_uefi_plugin_composite_peek_firmware(FuPlugin *plugin, + FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuSnapPlugin *self = FU_SNAPD_UEFI_PLUGIN(plugin); + gsize bufsz = 0; + const gchar *key_database; + const guint8 *buf; + g_autofree gchar *b64data = NULL; + g_autofree gchar *msg = NULL; + g_autoptr(GBytes) fw = NULL; + + /* not interesting */ + key_database = fu_snapd_uefi_plugin_device_to_key_database(self, device); + if (key_database == NULL) + return TRUE; + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + buf = g_bytes_get_data(fw, &bufsz); + b64data = g_base64_encode(buf, bufsz); + msg = g_strdup_printf("{" + "\"action\":\"efi-secureboot-update-db-prepare\"," + "\"key-database\":\"%s\"," + "\"payload\":\"%s\"" + "}", + key_database, + b64data); + + /* Notify of an upcoming update to the DBX. A successful call shall initiate a + * change tracking an update to the DBX on the snapd side */ + if (!fu_snapd_uefi_plugin_simple_req(self, + "/v2/system-secureboot", + msg, + strlen(msg), + error)) { + g_prefix_error_literal(error, "failed to notify snapd of prepare: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static void +fu_snapd_uefi_plugin_init(FuSnapPlugin *self) +{ +} + +static void +fu_snapd_uefi_plugin_finalize(GObject *object) +{ + FuSnapPlugin *self = FU_SNAPD_UEFI_PLUGIN(object); + + if (self->req_hdrs != NULL) + curl_slist_free_all(self->req_hdrs); + if (self->curl_template != NULL) + curl_easy_cleanup(self->curl_template); + + G_OBJECT_CLASS(fu_snapd_uefi_plugin_parent_class)->finalize(object); +} + +static void +fu_snapd_uefi_plugin_class_init(FuSnapPluginClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS(klass); + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + + plugin_class->startup = fu_snapd_uefi_plugin_startup; + plugin_class->device_registered = fu_snapd_uefi_plugin_device_registered; + plugin_class->composite_cleanup = fu_snapd_uefi_plugin_composite_cleanup; + plugin_class->composite_peek_firmware = fu_snapd_uefi_plugin_composite_peek_firmware; + + object_class->finalize = fu_snapd_uefi_plugin_finalize; +} diff -Nru fwupd-2.0.8/plugins/snapd-uefi/fu-snapd-uefi-plugin.h fwupd-2.0.20/plugins/snapd-uefi/fu-snapd-uefi-plugin.h --- fwupd-2.0.8/plugins/snapd-uefi/fu-snapd-uefi-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/snapd-uefi/fu-snapd-uefi-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,13 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_SNAPD_UEFI_PLUGIN (fu_snapd_uefi_plugin_get_type()) + +G_DECLARE_FINAL_TYPE(FuSnapPlugin, fu_snapd_uefi_plugin, FU, SNAPD_UEFI_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/snapd-uefi/meson.build fwupd-2.0.20/plugins/snapd-uefi/meson.build --- fwupd-2.0.8/plugins/snapd-uefi/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/snapd-uefi/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,50 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginSnapdUefi"'] +plugins += {meson.current_source_dir().split('/')[-1]: true} + +plugin_builtin_snapd_uefi = static_library('fu_plugin_snapd_uefi', + sources: [ + 'fu-snapd-uefi-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: [ + plugin_deps, + libcurl, + ], +) +plugin_builtins += plugin_builtin_snapd_uefi + +if get_option('tests') + install_data(['tests/snapd.py'], + install_dir: join_paths(installed_test_datadir, 'tests')) + + env = environment() + env.set('G_TEST_SRCDIR', meson.current_source_dir()) + env.set('G_TEST_BUILDDIR', meson.current_build_dir()) + env.set('G_DEBUG', 'fatal-criticals') + e = executable( + 'snap-self-test', + sources: [ + 'fu-self-test.c', + ], + include_directories: plugin_incdirs, + dependencies: [ + plugin_deps, + libcurl, + ], + link_with: [ + plugin_libs, + plugin_builtin_snapd_uefi, + ], + install: true, + install_rpath: libdir_pkg, + install_tag: 'tests', + install_dir: installed_test_bindir, + c_args: [ + cargs, + '-DSRCDIR="' + meson.current_source_dir() + '"', + ], + ) + test('snap-self-test', e, env: env) # added to installed-tests +endif diff -Nru fwupd-2.0.8/plugins/snapd-uefi/tests/snapd.py fwupd-2.0.20/plugins/snapd-uefi/tests/snapd.py --- fwupd-2.0.8/plugins/snapd-uefi/tests/snapd.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/snapd-uefi/tests/snapd.py 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,272 @@ +#!/usr/bin/env python3 +# +# Copyright 2024 Maciej Borzecki +# +# SPDX-License-Identifier: LGPL-2.1-or-later + +"""The app provides a mock snapd API for use with fwupd unit tests. The +/v2/system-secureboot endpoint mimics the behavior of snapd wrt. DBX updates. + +The app exposes a couple of test specific endpoinds, /test/scenario, which picks +one of the scenarios (expected to be called in when setting a test fixture), +/test/reset, for resetting the state (usually called in tear down), and +/test/stats which provides the count of actions invoked since last reset. +""" + +import argparse +import logging +import base64 +import json +import os.path +from io import StringIO +from dataclasses import dataclass +from collections.abc import Callable +from typing import Any, Optional + +from flask import Flask, Response, request, current_app + + +@dataclass +class Scenario: + handler: Callable[[dict[str, Any]], Response] + + +@dataclass +class State: + scenario: Optional[str] + stats: dict[str, int] + + def __init__(self): + self.scenario = None + self.stats = {k: 0 for k in supported_actions} + + +app = Flask(__name__) + +supported_actions = [ + "efi-secureboot-update-startup", + "efi-secureboot-update-db-cleanup", + "efi-secureboot-update-db-prepare", +] + + +def assert_no_extra_keys(d: dict[str, Any], allowed: list[str]): + unexpected = [a for a in d.keys() if a not in allowed] + assert len(unexpected) == 0, f"unexpected keys in request data: {unexpected}" + + +def assert_prepare_req(req: dict[str, Any]): + assert req.get("action") == "efi-secureboot-update-db-prepare", "unexpected action" + assert "payload" in req, "missing payload field" + assert "key-database" in req, "missing key-database field" + assert req.get("key-database") == "DBX", "unexpected key database" + + payload = req.get("payload") + assert_no_extra_keys(req, ["action", "payload", "key-database"]) + assert payload is not None, "no update payload" + # payload must be valid base64 + try: + raw_payload = base64.urlsafe_b64decode(payload) + with open(os.path.join(current_app.datadir, "dbx-update.auth"), "rb") as inf: + reference_payload = inf.read() + + assert raw_payload == reference_payload, "unexpected payload content" + except Exception as err: + raise AssertionError("invalid base64 data in request") from err + + +def assert_startup_req(req: dict[str, Any]): + assert req.get("action") == "efi-secureboot-update-startup", "unexpected action" + assert_no_extra_keys(req, ["action"]) + + +def assert_cleanup_req(req: dict[str, Any]): + assert req.get("action") == "efi-secureboot-update-db-cleanup", "unexpected action" + assert_no_extra_keys(req, ["action"]) + + +def happy_startup(req: dict[str, Any]) -> Response: + assert_startup_req(req) + return Response(None, status=200) + + +def not_supported(req: dict[str, Any]) -> Response: + assert_startup_req(req) + # pretend relevant APIs are missing, hence 404 + return Response("", status=404) + + +def failed_startup(req: dict[str, Any]) -> Response: + assert_startup_req(req) + return Response(None, status=400) + + +def happy_prepare(req: dict[str, Any]) -> Response: + assert_prepare_req(req) + return Response("", status=200) + + +def failed_prepare(req: dict[str, Any]) -> Response: + action = req.get("action") + if action == "efi-secureboot-update-db-cleanup": + return happy_cleanup(req) + if action == "efi-secureboot-update-db-prepare": + assert_prepare_req(req) + return Response( + json.dumps( + { + "status": "500", + "error": { + "kind": "internal-error", + "message": "cannot reseal keys in prepare", + }, + } + ), + status=500, + ) + if action == "efi-secureboot-update-startup": + return happy_startup(req) + + raise AssertionError(f"unexpected action {action}") + + +def failed_cleanup(req: dict[str, Any]) -> Response: + action = req.get("action") + if action == "efi-secureboot-update-db-cleanup": + assert_cleanup_req(req) + return Response( + json.dumps( + { + "status": "500", + "error": { + "kind": "internal-error", + "message": "cannot reseal keys in cleanup", + }, + } + ), + status=500, + ) + if action == "efi-secureboot-update-db-prepare": + return happy_prepare(req) + if action == "efi-secureboot-update-startup": + return happy_startup(req) + + raise AssertionError(f"unexpected action {action}") + + +def happy_cleanup(req: dict[str, Any]) -> Response: + assert_cleanup_req(req) + return Response("", status=200) + + +def happy_update(req: dict[str, Any]) -> Response: + action = req.get("action") + if action == "efi-secureboot-update-db-cleanup": + return happy_cleanup(req) + if action == "efi-secureboot-update-db-prepare": + return happy_prepare(req) + if action == "efi-secureboot-update-startup": + return happy_startup(req) + + raise AssertionError(f"unexpected action {action}") + + +playbook: dict[str, Scenario] = { + # successful startup + "happy-startup": Scenario(handler=happy_startup), + # startup with mock failure + "failed-startup": Scenario(handler=failed_startup), + # 404 on the API endpoint, indicating lack of support on the snapd side + "not-supported": Scenario(handler=not_supported), + # prepare step fails + "failed-prepare": Scenario(handler=failed_prepare), + # prepare is successful, but cleanup fails + "failed-cleanup": Scenario(handler=failed_cleanup), + # successful update cycle + "happy-update": Scenario(handler=happy_update), +} + + +def assert_scenario(f): + def do(): + with app.app_context(): + assert current_app.state.scenario is not None, "test scenario is not set" + return f() + + return do + + +def app_init_state(): + with app.app_context(): + current_app.state = State() + + +@app.route("/v2/system-secureboot", methods=["POST"]) +@assert_scenario +def system_secureboot(): + assert len(request.view_args) == 0 + assert request.headers.get("Content-Type") == "application/json" + + req = request.get_json() + logging.debug("req: %r", req) + action = req.get("action") + + assert action in supported_actions, f"unknown action {action}" + + current_app.state.stats[action] += 1 + + return playbook[current_app.state.scenario].handler(req) + + +@app.route("/test/setup", methods=["POST"]) +def test_setup(): + req = request.get_json() + logging.debug("req: %r", req) + scenario = req.get("scenario") + + assert scenario in playbook, f"unknown scenario {scenario}" + + logging.debug("setting scenario to '%s'", scenario) + current_app.state.scenario = scenario + return Response("", status=200) + + +@app.route("/test/reset", methods=["POST"]) +def test_reset(): + app_init_state() + return Response("", status=200) + + +@app.route("/test/stats") +def test_stats(): + out = StringIO() + # use a key-file format so that glib side parsing is easy + out.write("[stats]\n") + for action in sorted(current_app.state.stats.keys()): + out.write(f"{action}={current_app.state.stats[action]}\n") + return Response(out.getvalue(), status=200) + + +def parse_arguments(): + parser = argparse.ArgumentParser(description="mock snapd APIs") + parser.add_argument( + "--socket", help="socket path", default="/tmp/mock-snapd-test.sock" + ) + parser.add_argument( + "--datadir", + default=os.path.dirname(__file__), + help="path to directory with test data files", + ) + + return parser.parse_args() + + +if __name__ == "__main__": + opts = parse_arguments() + logging.basicConfig(level=logging.DEBUG) + logging.debug("socket path: %s", opts.socket) + logging.debug("data dir: %s", opts.datadir) + with app.app_context(): + current_app.datadir = opts.datadir + app_init_state() + app.run(host="unix://" + opts.socket) diff -Nru fwupd-2.0.8/plugins/steelseries/README.md fwupd-2.0.20/plugins/steelseries/README.md --- fwupd-2.0.8/plugins/steelseries/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -4,17 +4,20 @@ ## Introduction -This plugin allows to update firmware on SteelSeries gamepads: +This plugin allows to update firmware on SteelSeries peripherals: * Stratus Duo * Stratus Duo USB wireless adapter * Stratus+ * Aerox 3 Wireless * Rival 3 Wireless +* Arctis Nova 5 headset and dongle +* Arctis Nova 3P headset and dongle -SteelSeries Rival 100 gaming mice support is limited by getting the correct -version number. These mice have updatable firmware but so far no updates are -available from the vendor. +> [!NOTE] +> SteelSeries Rival 100 gaming mice support is limited by getting the correct +> version number. +> These mice have updatable firmware but so far no updates are available from the vendor. This plugin supports the following protocol IDs: @@ -38,6 +41,19 @@ Since 1.8.1 +### SteelSeriesCmdInterface + +USB HID command interface number. + +Since 2.0.14 + +### SteelSeriesFizzProtocolRevision + +Defines the revision of the FIZZ protocol. +Some replies should be parsed differently depending on the revision. + +Since 2.0.14 + ## Update Behavior ### Gamepad @@ -63,6 +79,14 @@ Wireless adapter must be connected to host; or the mouse must be connected to host via the USB-A to USB-C cable. +### Arctis Nova 5 headset + +The headset can only be updated via a direct USB-C connection. + +### Arctis Nova 3P headset and dongle + +The headset can only be updated via a direct USB-C connection. + ## Vendor ID Security The vendor ID is set from the USB vendor, in this instance set to `USB:0x1038` diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-device.c fwupd-2.0.20/plugins/steelseries/fu-steelseries-device.c --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,7 @@ #include "fu-steelseries-device.h" typedef struct { - gint iface_idx_offset; + gint iface_number; guint8 iface_idx; guint8 ep; gsize ep_in_size; @@ -20,10 +20,10 @@ /* @iface_idx_offset can be negative to specify from the end */ void -fu_steelseries_device_set_iface_idx_offset(FuSteelseriesDevice *self, gint iface_idx_offset) +fu_steelseries_device_set_iface_number(FuSteelseriesDevice *self, gint iface_number) { FuSteelseriesDevicePrivate *priv = GET_PRIVATE(self); - priv->iface_idx_offset = iface_idx_offset; + priv->iface_number = iface_number; } gboolean @@ -52,7 +52,7 @@ FU_STEELSERIES_TRANSACTION_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to do control transfer: "); + g_prefix_error_literal(error, "failed to do control transfer: "); return FALSE; } if (actual_len != buf_padded->len) { @@ -84,7 +84,7 @@ FU_STEELSERIES_TRANSACTION_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to do EP transfer: "); + g_prefix_error_literal(error, "failed to do EP transfer: "); return NULL; } fu_dump_raw(G_LOG_DOMAIN, "Response", buf->data, actual_len); @@ -102,13 +102,31 @@ } static gboolean +fu_steelseries_device_find_cmd_iface(gconstpointer item, gconstpointer number) +{ + FuUsbInterface *iface = (FuUsbInterface *)item; + gint iface_number_requested = *(gint *)number; + + /* must be HID */ + if (fu_usb_interface_get_class(iface) != FU_USB_CLASS_HID) + return FALSE; + + /* in case of autodetection first found fits */ + if (iface_number_requested != -1 && + iface_number_requested != (gint)fu_usb_interface_get_number(iface)) + return FALSE; + + return TRUE; +} + +static gboolean fu_steelseries_device_probe(FuDevice *device, GError **error) { FuSteelseriesDevice *self = FU_STEELSERIES_DEVICE(device); FuSteelseriesDevicePrivate *priv = GET_PRIVATE(self); FuUsbInterface *iface = NULL; FuUsbEndpoint *ep = NULL; - guint8 iface_idx; + guint iface_idx; guint8 ep_id; guint16 packet_size; g_autoptr(GPtrArray) ifaces = NULL; @@ -118,20 +136,18 @@ if (ifaces == NULL) return FALSE; - /* use the correct interface for interrupt transfer, either specifying an absolute offset, - * or a negative offset value for the "last" one */ - if (priv->iface_idx_offset >= 0) { - if ((guint)priv->iface_idx_offset > ifaces->len) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "update interface 0x%x not found", - (guint)priv->iface_idx_offset); - return FALSE; - } - iface_idx = priv->iface_idx_offset; - } else { - iface_idx = ifaces->len - 1; + /* use the correct interface for interrupt transfer, either specifying an absolute + * offset, or a negative offset value for the default HID interface autodetection */ + if (!g_ptr_array_find_with_equal_func(ifaces, + priv, + fu_steelseries_device_find_cmd_iface, + &iface_idx)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "update interface 0x%x not found", + (guint)priv->iface_number); + return FALSE; } iface = g_ptr_array_index(ifaces, iface_idx); priv->iface_idx = fu_usb_interface_get_number(iface); @@ -168,10 +184,32 @@ fwupd_codec_string_append_hex(str, idt, "Endpoint", priv->ep); } +static gboolean +fu_steelseries_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuSteelseriesDevice *self = FU_STEELSERIES_DEVICE(device); + guint64 tmp = 0; + + if (g_strcmp0(key, "SteelSeriesCmdInterface") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + + fu_steelseries_device_set_iface_number(FU_STEELSERIES_DEVICE(self), tmp); + return TRUE; + } + + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not supported"); + return FALSE; +} + static void fu_steelseries_device_init(FuSteelseriesDevice *self) { fu_device_register_private_flag(FU_DEVICE(self), FU_STEELSERIES_DEVICE_FLAG_IS_RECEIVER); + fu_steelseries_device_set_iface_number(FU_STEELSERIES_DEVICE(self), -1); } static void @@ -180,4 +218,5 @@ FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); device_class->to_string = fu_steelseries_device_to_string; device_class->probe = fu_steelseries_device_probe; + device_class->set_quirk_kv = fu_steelseries_device_set_quirk_kv; } diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-device.h fwupd-2.0.20/plugins/steelseries/fu-steelseries-device.h --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -31,7 +31,7 @@ #define FU_STEELSERIES_DEVICE_FLAG_DETACH_BOOTLOADER "detach-bootloader" void -fu_steelseries_device_set_iface_idx_offset(FuSteelseriesDevice *self, gint iface_idx_offset); +fu_steelseries_device_set_iface_number(FuSteelseriesDevice *self, gint iface_number); gboolean fu_steelseries_device_request(FuSteelseriesDevice *self, const GByteArray *buf, GError **error); GByteArray * diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-firmware.c fwupd-2.0.20/plugins/steelseries/fu-steelseries-firmware.c --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,7 @@ #include "fu-steelseries-firmware.h" struct _FuSteelseriesFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; guint32 checksum; }; @@ -18,7 +18,7 @@ static gboolean fu_steelseries_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSteelseriesFirmware *self = FU_STEELSERIES_FIRMWARE(firmware); @@ -52,7 +52,7 @@ error)) return FALSE; if (checksum_tmp != checksum) { - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz-gen1.c fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-gen1.c --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz-gen1.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-gen1.c 2026-02-26 11:36:18.000000000 +0000 @@ -46,7 +46,7 @@ st_req = fu_struct_steelseries_fizz_version_req_new(); fu_struct_steelseries_fizz_version_req_set_cmd(st_req, cmd); - if (!fu_steelseries_fizz_gen1_request(self, st_req, error)) + if (!fu_steelseries_fizz_gen1_request(self, st_req->buf, error)) return NULL; buf_res = fu_steelseries_fizz_gen1_response(self, error); if (buf_res == NULL) @@ -83,7 +83,7 @@ g_autoptr(FuStructSteelseriesPairedStatusRes) st_res = NULL; g_autoptr(GByteArray) buf_res = NULL; - if (!fu_steelseries_fizz_gen1_request(self, st_req, error)) + if (!fu_steelseries_fizz_gen1_request(self, st_req->buf, error)) return FALSE; buf_res = fu_steelseries_fizz_gen1_response(self, error); if (buf_res == NULL) @@ -106,7 +106,7 @@ g_autoptr(FuStructSteelseriesConnectionStatusRes) st_res = NULL; g_autoptr(GByteArray) buf_res = NULL; - if (!fu_steelseries_fizz_gen1_request(self, st_req, error)) + if (!fu_steelseries_fizz_gen1_request(self, st_req->buf, error)) return FALSE; buf_res = fu_steelseries_fizz_gen1_response(self, error); if (buf_res == NULL) @@ -137,7 +137,7 @@ st_req = fu_struct_steelseries_battery_level_req_new(); fu_struct_steelseries_battery_level_req_set_cmd(st_req, cmd); - if (!fu_steelseries_fizz_gen1_request(self, st_req, error)) + if (!fu_steelseries_fizz_gen1_request(self, st_req->buf, error)) return FALSE; buf_res = fu_steelseries_fizz_gen1_response(self, error); if (buf_res == NULL) @@ -198,5 +198,5 @@ static void fu_steelseries_fizz_gen1_init(FuSteelseriesFizzGen1 *self) { - fu_steelseries_device_set_iface_idx_offset(FU_STEELSERIES_DEVICE(self), 0x03); + fu_steelseries_device_set_iface_number(FU_STEELSERIES_DEVICE(self), 0x03); } diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz-gen2.c fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-gen2.c --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz-gen2.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-gen2.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,6 +19,7 @@ struct _FuSteelseriesFizzGen2 { FuSteelseriesDevice parent_instance; + guint protocol_revision; }; static void @@ -43,35 +44,30 @@ } static gchar * -fu_steelseries_fizz_gen2_get_version(FuSteelseriesFizzImpl *self, gboolean tunnel, GError **error) +fu_steelseries_fizz_gen2_get_version2(FuSteelseriesFizzImpl *self, + gboolean tunnel, + GByteArray *buf_res, + GError **error) { g_autofree gchar *version_raw = NULL; - g_autoptr(FuStructSteelseriesFizzVersion2Req) st_req = - fu_struct_steelseries_fizz_version2_req_new(); - g_autoptr(FuStructSteelseriesVersion2Res) st_res = NULL; - g_autoptr(GByteArray) buf_res = NULL; - - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) - return NULL; - buf_res = fu_steelseries_device_response(FU_STEELSERIES_DEVICE(self), error); - if (buf_res == NULL) - return NULL; + g_autoptr(FuStructSteelseriesVersion2Res2) st_res = NULL; - st_res = fu_struct_steelseries_version2_res_parse(buf_res->data, buf_res->len, 0x0, error); + st_res = fu_struct_steelseries_version2_res2_parse(buf_res->data, buf_res->len, 0x0, error); if (st_res == NULL) return NULL; + /* versions of receiver and device are swapped depending how queried */ if (tunnel) - version_raw = fu_struct_steelseries_version2_res_get_version_device(st_res); + version_raw = fu_struct_steelseries_version2_res2_get_version2(st_res); else - version_raw = fu_struct_steelseries_version2_res_get_version_receiver(st_res); + version_raw = fu_struct_steelseries_version2_res2_get_version1(st_res); if (version_raw == NULL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "version number provided"); + "version number not provided"); return NULL; } - if (strlen(version_raw) != FU_STRUCT_STEELSERIES_VERSION2_RES_SIZE_VERSION_RECEIVER) { + if (strlen(version_raw) != FU_STRUCT_STEELSERIES_VERSION2_RES2_SIZE_VERSION1) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -80,7 +76,7 @@ return NULL; } - /* very interesting version format */ + /* custom version format */ if (version_raw[1] == 0x2E && version_raw[4] == 0x2E && version_raw[8] == 0x2E) { /* format triple */ return g_strdup_printf( @@ -96,6 +92,53 @@ ((guint32)(version_raw[10] - 0x30) << 4) + (version_raw[11] - 0x30)); } +static gchar * +fu_steelseries_fizz_gen2_get_version3(FuSteelseriesFizzImpl *self, + gboolean tunnel, + GByteArray *buf_res, + GError **error) +{ + g_autofree gchar *version_raw = NULL; + g_autoptr(FuStructSteelseriesVersion2Res3) st_res = NULL; + + st_res = fu_struct_steelseries_version2_res3_parse(buf_res->data, buf_res->len, 0x0, error); + if (st_res == NULL) + return NULL; + /* versions of receiver and device are swapped depending how queried */ + if (tunnel) + version_raw = fu_struct_steelseries_version2_res3_get_version2(st_res); + else + version_raw = fu_struct_steelseries_version2_res3_get_version1(st_res); + if (version_raw == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "version number not provided"); + return NULL; + } + return g_steal_pointer(&version_raw); +} + +static gchar * +fu_steelseries_fizz_gen2_get_version(FuSteelseriesFizzImpl *self, gboolean tunnel, GError **error) +{ + FuSteelseriesFizzGen2 *device = FU_STEELSERIES_FIZZ_GEN2(self); + g_autoptr(FuStructSteelseriesFizzVersion2Req) st_req = + fu_struct_steelseries_fizz_version2_req_new(); + g_autoptr(GByteArray) buf_res = NULL; + + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) + return NULL; + buf_res = fu_steelseries_device_response(FU_STEELSERIES_DEVICE(self), error); + if (buf_res == NULL) + return NULL; + + if (device->protocol_revision > 2) + return fu_steelseries_fizz_gen2_get_version3(self, tunnel, buf_res, error); + /* fallback to initial variant */ + return fu_steelseries_fizz_gen2_get_version2(self, tunnel, buf_res, error); +} + static gboolean fu_steelseries_fizz_gen2_get_battery_level(FuSteelseriesFizzImpl *self, gboolean tunnel, @@ -107,7 +150,7 @@ g_autoptr(FuStructSteelseriesBatteryLevel2Res) st_res = NULL; g_autoptr(GByteArray) buf_res = NULL; - if (!fu_steelseries_fizz_gen2_request(self, st_req, error)) + if (!fu_steelseries_fizz_gen2_request(self, st_req->buf, error)) return FALSE; buf_res = fu_steelseries_fizz_gen2_response(self, error); if (buf_res == NULL) @@ -146,7 +189,7 @@ g_autoptr(FuStructSteelseriesConnectionStatus2Res) st_res = NULL; g_autoptr(GByteArray) buf_res = NULL; - if (!fu_steelseries_fizz_gen2_request(self, st_req, error)) + if (!fu_steelseries_fizz_gen2_request(self, st_req->buf, error)) return FALSE; buf_res = fu_steelseries_fizz_gen2_response(self, error); if (buf_res == NULL) @@ -182,7 +225,7 @@ g_autoptr(FuStructSteelseriesConnectionStatus2Res) st_res = NULL; g_autoptr(GByteArray) buf_res = NULL; - if (!fu_steelseries_fizz_gen2_request(self, st_req, error)) + if (!fu_steelseries_fizz_gen2_request(self, st_req->buf, error)) return FALSE; buf_res = fu_steelseries_fizz_gen2_response(self, error); if (buf_res == NULL) @@ -205,22 +248,45 @@ } static gchar * -fu_steelseries_fizz_gen2_get_serial(FuSteelseriesFizzImpl *self, gboolean tunnel, GError **error) +fu_steelseries_fizz_gen2_get_serial2(FuSteelseriesFizzImpl *self, + gboolean tunnel, + GByteArray *buf_res, + GError **error) { g_autofree gchar *serial = NULL; - g_autoptr(FuStructSteelseriesSerial2Req) st_req = fu_struct_steelseries_serial2_req_new(); - g_autoptr(FuStructSteelseriesSerial2Res) st_res = NULL; - g_autoptr(GByteArray) buf_res = NULL; + g_autoptr(FuStructSteelseriesSerial2Res2) st_res = NULL; - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) + st_res = fu_struct_steelseries_serial2_res2_parse(buf_res->data, buf_res->len, 0x0, error); + if (st_res == NULL) return NULL; - buf_res = fu_steelseries_device_response(FU_STEELSERIES_DEVICE(self), error); - if (buf_res == NULL) + serial = fu_struct_steelseries_serial2_res2_get_serial(st_res); + if (serial == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no serial number provided"); return NULL; - st_res = fu_struct_steelseries_serial2_res_parse(buf_res->data, buf_res->len, 0x0, error); + } + return g_steal_pointer(&serial); +} + +static gchar * +fu_steelseries_fizz_gen2_get_serial3(FuSteelseriesFizzImpl *self, + gboolean tunnel, + GByteArray *buf_res, + GError **error) +{ + g_autofree gchar *serial = NULL; + g_autoptr(FuStructSteelseriesSerial2Res3) st_res = NULL; + + st_res = fu_struct_steelseries_serial2_res3_parse(buf_res->data, buf_res->len, 0x0, error); if (st_res == NULL) return NULL; - serial = fu_struct_steelseries_serial2_res_get_serial(st_res); + /* versions of receiver and device are swapped depending how queried */ + if (tunnel) + serial = fu_struct_steelseries_serial2_res3_get_serial2(st_res); + else + serial = fu_struct_steelseries_serial2_res3_get_serial1(st_res); if (serial == NULL) { g_set_error_literal(error, FWUPD_ERROR, @@ -231,6 +297,25 @@ return g_steal_pointer(&serial); } +static gchar * +fu_steelseries_fizz_gen2_get_serial(FuSteelseriesFizzImpl *self, gboolean tunnel, GError **error) +{ + FuSteelseriesFizzGen2 *device = FU_STEELSERIES_FIZZ_GEN2(self); + g_autoptr(FuStructSteelseriesSerial2Req) st_req = fu_struct_steelseries_serial2_req_new(); + g_autoptr(GByteArray) buf_res = NULL; + + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) + return NULL; + buf_res = fu_steelseries_device_response(FU_STEELSERIES_DEVICE(self), error); + if (buf_res == NULL) + return NULL; + + if (device->protocol_revision > 2) + return fu_steelseries_fizz_gen2_get_serial3(self, tunnel, buf_res, error); + /* fallback to initial variant */ + return fu_steelseries_fizz_gen2_get_serial2(self, tunnel, buf_res, error); +} + static gboolean fu_steelseries_fizz_gen2_is_updatable(FuSteelseriesFizzImpl *self, FuDevice *device, GError **error) { @@ -256,13 +341,14 @@ if (!fu_device_emit_request(device, request, NULL, error)) return FALSE; - /* FIXME: return commented as soon as we have support of simultaneous connections */ - // fu_device_set_remove_delay(device, FU_DEVICE_REMOVE_DELAY_USER_REPLUG); /* 40 sec */ - // fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); - // fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + /* + * FIXME: return commented as soon as we have support of simultaneous connections: + * fu_device_set_remove_delay(device, FU_DEVICE_REMOVE_DELAY_USER_REPLUG); + * fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); + * fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + */ /* success */ - // return TRUE; g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -273,10 +359,6 @@ static gboolean fu_steelseries_fizz_gen2_probe(FuDevice *device, GError **error) { - /* in bootloader mode */ - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) - fu_steelseries_device_set_iface_idx_offset(FU_STEELSERIES_DEVICE(device), 0x00); - /* FuUsbDevice->probe */ return FU_DEVICE_CLASS(fu_steelseries_fizz_gen2_parent_class)->probe(device, error); } @@ -305,11 +387,11 @@ FuSteelseriesFizzGen2 *self = FU_STEELSERIES_FIZZ_GEN2(device); guint64 tmp = 0; - if (g_strcmp0(key, "SteelSeriesFizzInterface") == 0) { - if (!fu_strtoull(value, &tmp, 0, G_MAXUINT8, FU_INTEGER_BASE_AUTO, error)) + if (g_strcmp0(key, "SteelSeriesFizzProtocolRevision") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT16, FU_INTEGER_BASE_AUTO, error)) return FALSE; - fu_steelseries_device_set_iface_idx_offset(FU_STEELSERIES_DEVICE(self), tmp); + self->protocol_revision = (guint)tmp; return TRUE; } @@ -329,5 +411,6 @@ static void fu_steelseries_fizz_gen2_init(FuSteelseriesFizzGen2 *self) { - fu_steelseries_device_set_iface_idx_offset(FU_STEELSERIES_DEVICE(self), 0x05); + /* Set the default protocol version */ + self->protocol_revision = 2; } diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz-hid.c fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-hid.c --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz-hid.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-hid.c 2026-02-26 11:36:18.000000000 +0000 @@ -47,7 +47,7 @@ return FALSE; fu_dump_raw(G_LOG_DOMAIN, "write", buf, sizeof(buf)); if (!fu_udev_device_pwrite(FU_UDEV_DEVICE(device), 0, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to write report: "); + g_prefix_error_literal(error, "failed to write report: "); return FALSE; } @@ -113,16 +113,19 @@ fu_steelseries_fizz_hid_ensure_version(FuSteelseriesFizzHid *self, GError **error) { g_autofree gchar *version = NULL; - g_autoptr(GByteArray) st_buf = NULL; + g_autoptr(GByteArray) buf = NULL; g_autoptr(FuStructSteelseriesFizzHidGetVersionReq) st = fu_struct_steelseries_fizz_hid_get_version_req_new(); - st_buf = fu_steelseries_fizz_hid_command(self, st, error); - if (st_buf == NULL) + buf = fu_steelseries_fizz_hid_command(self, st->buf, error); + if (buf == NULL) return FALSE; - version = fu_strsafe((const gchar *)st_buf->data, st_buf->len); + version = fu_strsafe((const gchar *)buf->data, buf->len); if (version == NULL) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "unable to read version"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "unable to read version"); return FALSE; } fu_device_set_version(FU_DEVICE(self), version); diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz-hid.h fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-hid.h --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz-hid.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-hid.h 2026-02-26 11:36:18.000000000 +0000 @@ -13,4 +13,4 @@ fu_steelseries_fizz_hid, FU, STEELSERIES_FIZZ_HID, - FuHidDevice) + FuUdevDevice) diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz-tunnel.c fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-tunnel.c --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz-tunnel.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz-tunnel.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,23 +19,21 @@ G_DEFINE_TYPE(FuSteelseriesFizzTunnel, fu_steelseries_fizz_tunnel, FU_TYPE_DEVICE) static gboolean -fu_steelseries_fizz_tunnel_ping(FuDevice *device, gboolean *reached, GError **error) +fu_steelseries_fizz_tunnel_ping(FuSteelseriesFizzTunnel *self, gboolean *reached, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy; FuSteelseriesFizzConnectionStatus status = FU_STEELSERIES_FIZZ_CONNECTION_STATUS_CONNECTED; guint8 level; g_autoptr(GError) error_local = NULL; g_autofree gchar *version = NULL; - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) return FALSE; - } - if (!fu_steelseries_fizz_impl_get_connection_status(FU_STEELSERIES_FIZZ_IMPL(proxy), &status, error)) { - g_prefix_error(error, "failed to get connection status: "); + g_prefix_error_literal(error, "failed to get connection status: "); return FALSE; } g_debug("FuSteelseriesFizzConnection: %s", @@ -63,7 +61,7 @@ return TRUE; } g_debug("BatteryLevel: 0x%02x", level); - fu_device_set_battery_level(device, level); + fu_device_set_battery_level(FU_DEVICE(self), level); /* re-read version after reconnect/update */ version = @@ -72,10 +70,10 @@ *reached = FALSE; g_prefix_error(error, "unable to read version from device %s: ", - fu_device_get_id(device)); + fu_device_get_id(FU_DEVICE(self))); return FALSE; } - fu_device_set_version(device, version); /* nocheck:set-version */ + fu_device_set_version(FU_DEVICE(self), version); /* nocheck:set-version */ /* success */ return TRUE; @@ -86,26 +84,30 @@ gpointer user_data, GError **error) { - FuDevice *parent = fu_device_get_parent(device); + FuSteelseriesFizzTunnel *self = FU_STEELSERIES_FIZZ_TUNNEL(device); + FuDevice *parent = fu_device_get_parent(FU_DEVICE(self), error); FuSteelseriesFizzConnectionStatus status; gboolean reached; if (!fu_steelseries_fizz_get_connection_status(FU_STEELSERIES_FIZZ(parent), &status, error)) { - g_prefix_error(error, "failed to get connection status: "); + g_prefix_error_literal(error, "failed to get connection status: "); return FALSE; } g_debug("FuSteelseriesFizzConnection: %s", fu_steelseries_fizz_connection_status_to_string(status)); if (status == FU_STEELSERIES_FIZZ_CONNECTION_STATUS_NOT_CONNECTED) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "device is unreachable"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "device is unreachable"); return FALSE; } /* ping */ - if (!fu_steelseries_fizz_tunnel_ping(device, &reached, error)) { - g_prefix_error(error, "failed to ping on reconnect: "); + if (!fu_steelseries_fizz_tunnel_ping(self, &reached, error)) { + g_prefix_error_literal(error, "failed to ping on reconnect: "); return FALSE; } @@ -114,9 +116,11 @@ } static gboolean -fu_steelseries_fizz_tunnel_wait_for_reconnect(FuDevice *device, guint delay, GError **error) +fu_steelseries_fizz_tunnel_wait_for_reconnect(FuSteelseriesFizzTunnel *self, + guint delay, + GError **error) { - return fu_device_retry_full(device, + return fu_device_retry_full(FU_DEVICE(self), fu_steelseries_fizz_tunnel_wait_for_reconnect_cb, delay / 1000, 1000, @@ -127,12 +131,9 @@ static gboolean fu_steelseries_fizz_tunnel_detach(FuDevice *device, FuProgress *progress, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); - - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + FuDevice *proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) return FALSE; - } if (!fu_steelseries_fizz_impl_is_updatable(FU_STEELSERIES_FIZZ_IMPL(proxy), device, error)) return FALSE; @@ -156,8 +157,9 @@ static gboolean fu_steelseries_fizz_tunnel_attach(FuDevice *device, FuProgress *progress, GError **error) { - FuDevice *parent = fu_device_get_parent(device); - guint remove_delay = fu_device_get_remove_delay(device); + FuSteelseriesFizzTunnel *self = FU_STEELSERIES_FIZZ_TUNNEL(device); + FuDevice *parent; + guint remove_delay = fu_device_get_remove_delay(FU_DEVICE(self)); g_autoptr(GError) error_local = NULL; fu_progress_set_id(progress, G_STRLOC); @@ -165,20 +167,25 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 67, "sleep"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 33, NULL); + parent = fu_device_get_parent(FU_DEVICE(self), error); + if (parent == NULL) + return FALSE; if (!fu_steelseries_fizz_reset(FU_STEELSERIES_FIZZ(parent), TRUE, FU_STEELSERIES_FIZZ_RESET_MODE_NORMAL, &error_local)) - g_warning("failed to reset: %s", error_local->message); + g_debug("failed to reset: %s", error_local->message); fu_progress_step_done(progress); /* wait for receiver to reset the connection status to 0 */ - fu_device_sleep_full(device, 2000, fu_progress_get_child(progress)); /* ms */ + fu_device_sleep_full(FU_DEVICE(self), 2000, fu_progress_get_child(progress)); /* ms */ remove_delay -= 2000; fu_progress_step_done(progress); - if (!fu_steelseries_fizz_tunnel_wait_for_reconnect(device, remove_delay, error)) { - g_prefix_error(error, "device %s did not come back: ", fu_device_get_id(device)); + if (!fu_steelseries_fizz_tunnel_wait_for_reconnect(self, remove_delay, error)) { + g_prefix_error(error, + "device %s did not come back: ", + fu_device_get_id(FU_DEVICE(self))); return FALSE; } fu_progress_step_done(progress); @@ -190,15 +197,13 @@ static gboolean fu_steelseries_fizz_tunnel_probe(FuDevice *device, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy; guint16 release; - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); - return FALSE; - } - /* set the version if the release has been set */ + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; release = fu_usb_device_get_release(FU_USB_DEVICE(proxy)); if (release != 0x0 && fu_device_get_version_format(device) == FWUPD_VERSION_FORMAT_UNKNOWN) { @@ -237,18 +242,14 @@ static gboolean fu_steelseries_fizz_tunnel_setup(FuDevice *device, GError **error) { - FuDevice *proxy = fu_device_get_proxy(device); + FuSteelseriesFizzTunnel *self = FU_STEELSERIES_FIZZ_TUNNEL(device); + FuDevice *proxy; gboolean reached; g_autofree gchar *serial = NULL; g_autoptr(GError) error_local = NULL; - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); - return FALSE; - } - /* ping */ - if (!fu_steelseries_fizz_tunnel_ping(device, &reached, &error_local)) { + if (!fu_steelseries_fizz_tunnel_ping(self, &reached, &error_local)) { g_debug("ignoring error for %s on ping: %s", fu_device_get_id(device), error_local->message); @@ -257,8 +258,11 @@ return TRUE; } + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; serial = fu_steelseries_fizz_impl_get_serial(FU_STEELSERIES_FIZZ_IMPL(proxy), - FALSE, + TRUE, &error_local); if (serial != NULL) { fu_device_set_serial(device, serial); @@ -280,8 +284,9 @@ static gboolean fu_steelseries_fizz_tunnel_poll(FuDevice *device, GError **error) { - FuDevice *parent = fu_device_get_parent(device); - FuDevice *proxy = fu_device_get_proxy(device); + FuSteelseriesFizzTunnel *self = FU_STEELSERIES_FIZZ_TUNNEL(device); + FuDevice *parent; + FuDevice *proxy; guint32 calculated_crc; guint32 stored_crc; gboolean reached; @@ -290,6 +295,9 @@ g_autoptr(GError) error_local = NULL; g_autoptr(FuDeviceLocker) locker = NULL; + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; if (!fu_steelseries_fizz_impl_get_fs_id(FU_STEELSERIES_FIZZ_IMPL(proxy), FALSE, &fs, error)) return FALSE; if (!fu_steelseries_fizz_impl_get_file_id(FU_STEELSERIES_FIZZ_IMPL(proxy), @@ -299,12 +307,15 @@ return FALSE; /* open */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; - if (!fu_steelseries_fizz_tunnel_ping(device, &reached, error)) { - g_prefix_error(error, "failed to ping: "); + if (!fu_steelseries_fizz_tunnel_ping(self, &reached, error)) { + g_prefix_error_literal(error, "failed to ping: "); return FALSE; } @@ -351,11 +362,14 @@ FwupdInstallFlags flags, GError **error) { - FuDevice *parent = fu_device_get_parent(device); - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *parent; + FuDevice *proxy; guint8 fs = 0; guint8 id = 0; + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; if (!fu_steelseries_fizz_impl_get_fs_id(FU_STEELSERIES_FIZZ_IMPL(proxy), FALSE, &fs, error)) return FALSE; if (!fu_steelseries_fizz_impl_get_file_id(FU_STEELSERIES_FIZZ_IMPL(proxy), @@ -367,6 +381,9 @@ fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, NULL); + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; if (!fu_steelseries_fizz_write_firmware_fs(FU_STEELSERIES_FIZZ(parent), TRUE, fs, @@ -385,12 +402,15 @@ static FuFirmware * fu_steelseries_fizz_tunnel_read_firmware(FuDevice *device, FuProgress *progress, GError **error) { - FuDevice *parent = fu_device_get_parent(device); - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *parent; + FuDevice *proxy; guint8 fs = 0; guint8 id = 0; g_autoptr(FuFirmware) firmware = NULL; + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return NULL; if (!fu_steelseries_fizz_impl_get_fs_id(FU_STEELSERIES_FIZZ_IMPL(proxy), FALSE, &fs, error)) return NULL; if (!fu_steelseries_fizz_impl_get_file_id(FU_STEELSERIES_FIZZ_IMPL(proxy), @@ -402,6 +422,9 @@ fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 100, NULL); + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; firmware = fu_steelseries_fizz_read_firmware_fs(FU_STEELSERIES_FIZZ(parent), TRUE, fs, @@ -418,7 +441,7 @@ } static void -fu_steelseries_fizz_tunnel_set_progress(FuDevice *self, FuProgress *progress) +fu_steelseries_fizz_tunnel_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -469,6 +492,7 @@ fu_device_set_poll_interval(FU_DEVICE(self), FU_STEELSERIES_FIZZ_TUNNEL_POLL_INTERVAL); fu_device_set_battery_threshold(FU_DEVICE(self), 20); fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_STEELSERIES_FIRMWARE); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_STEELSERIES_FIZZ_IMPL); } FuSteelseriesFizzTunnel * diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz.c fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz.c --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz.c 2026-02-26 11:36:18.000000000 +0000 @@ -67,11 +67,9 @@ static gboolean fu_steelseries_fizz_request(FuSteelseriesFizz *self, GByteArray *buf, GError **error) { - FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self)); - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) return FALSE; - } fu_dump_raw(G_LOG_DOMAIN, fu_steelseries_fizz_cmd_to_string(buf->data[0]), buf->data, @@ -82,11 +80,9 @@ static GByteArray * fu_steelseries_fizz_response(FuSteelseriesFizz *self, GError **error) { - FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self)); - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) return NULL; - } return fu_steelseries_fizz_impl_response(FU_STEELSERIES_FIZZ_IMPL(proxy), error); } @@ -170,7 +166,7 @@ fu_chunk_get_data_sz(chk), error)) return FALSE; - buf_res = fu_steelseries_fizz_request_response(self, st_req, error); + buf_res = fu_steelseries_fizz_request_response(self, st_req->buf, error); if (buf_res == NULL) return FALSE; fu_progress_step_done(progress); @@ -198,7 +194,7 @@ fu_struct_steelseries_fizz_erase_file_req_set_cmd(st_req, cmd); fu_struct_steelseries_fizz_erase_file_req_set_filesystem(st_req, fs); fu_struct_steelseries_fizz_erase_file_req_set_id(st_req, id); - buf_res = fu_steelseries_fizz_request_response(self, st_req, error); + buf_res = fu_steelseries_fizz_request_response(self, st_req->buf, error); return buf_res != NULL; } @@ -217,7 +213,7 @@ st_req = fu_struct_steelseries_fizz_reset_req_new(); fu_struct_steelseries_fizz_reset_req_set_cmd(st_req, cmd); fu_struct_steelseries_fizz_reset_req_set_mode(st_req, mode); - return fu_steelseries_fizz_request(self, st_req, error); + return fu_steelseries_fizz_request(self, st_req->buf, error); } gboolean @@ -241,7 +237,7 @@ fu_struct_steelseries_fizz_file_crc32_req_set_cmd(st_req, cmd); fu_struct_steelseries_fizz_file_crc32_req_set_filesystem(st_req, fs); fu_struct_steelseries_fizz_file_crc32_req_set_id(st_req, id); - buf_res = fu_steelseries_fizz_request_response(self, st_req, error); + buf_res = fu_steelseries_fizz_request_response(self, st_req->buf, error); if (buf_res == NULL) return FALSE; st_res = fu_struct_steelseries_fizz_file_crc32_res_parse(buf_res->data, @@ -295,7 +291,7 @@ fu_struct_steelseries_fizz_read_access_file_req_set_offset( st_req, fu_chunk_get_address(chk)); - buf_res = fu_steelseries_fizz_request_response(self, st_req, error); + buf_res = fu_steelseries_fizz_request_response(self, st_req->buf, error); if (buf_res == NULL) return FALSE; st_res = fu_struct_steelseries_fizz_read_access_file_res_parse(buf_res->data, @@ -325,11 +321,9 @@ static gboolean fu_steelseries_fizz_get_paired_status(FuSteelseriesFizz *self, guint8 *status, GError **error) { - FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self)); - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) return FALSE; - } return fu_steelseries_fizz_impl_get_paired_status(FU_STEELSERIES_FIZZ_IMPL(proxy), status, error); @@ -340,11 +334,9 @@ FuSteelseriesFizzConnectionStatus *status, GError **error) { - FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self)); - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) return FALSE; - } return fu_steelseries_fizz_impl_get_connection_status(FU_STEELSERIES_FIZZ_IMPL(proxy), status, error); @@ -355,12 +347,6 @@ { guint8 status; - FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self)); - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); - return FALSE; - } - /* not a USB receiver */ if (!fu_device_has_private_flag(FU_DEVICE(self), FU_STEELSERIES_DEVICE_FLAG_IS_RECEIVER)) return TRUE; @@ -370,15 +356,19 @@ return TRUE; if (!fu_steelseries_fizz_get_paired_status(self, &status, error)) { - g_prefix_error(error, "failed to get paired status: "); + g_prefix_error_literal(error, "failed to get paired status: "); return FALSE; } if (status != 0) { + FuDevice *proxy; g_autoptr(FuSteelseriesFizzTunnel) paired_device = fu_steelseries_fizz_tunnel_new(self); - fu_device_set_proxy(FU_DEVICE(paired_device), FU_DEVICE(proxy)); + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + fu_device_set_proxy(FU_DEVICE(paired_device), proxy); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(paired_device)); } @@ -429,7 +419,7 @@ FALSE, FU_STEELSERIES_FIZZ_RESET_MODE_NORMAL, &error_local)) - g_warning("failed to reset: %s", error_local->message); + g_debug("failed to reset: %s", error_local->message); fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); @@ -442,16 +432,11 @@ fu_steelseries_fizz_setup(FuDevice *device, GError **error) { FuSteelseriesFizz *self = FU_STEELSERIES_FIZZ(device); + FuDevice *proxy; g_autofree gchar *version = NULL; g_autofree gchar *serial = NULL; g_autoptr(GError) error_local = NULL; - FuDevice *proxy = fu_device_get_proxy(device); - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); - return FALSE; - } - /* in bootloader mode */ if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) return TRUE; @@ -461,14 +446,19 @@ return FALSE; } + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) + return FALSE; version = fu_steelseries_fizz_impl_get_version(FU_STEELSERIES_FIZZ_IMPL(proxy), FALSE, error); if (version == NULL) { - g_prefix_error(error, "failed to get version: "); /* nocheck:set-version */ + g_prefix_error_literal(error, "failed to get version: "); /* nocheck:set-version */ return FALSE; } fu_device_set_version(device, version); + /* read serial only for headset, since it is used to discover the same device attached + * directly and via receiver in the same time */ if (!fu_device_has_private_flag(device, FU_STEELSERIES_DEVICE_FLAG_IS_RECEIVER)) { /* direct connection */ serial = fu_steelseries_fizz_impl_get_serial(FU_STEELSERIES_FIZZ_IMPL(proxy), @@ -564,7 +554,7 @@ FuSteelseriesFizz *self = FU_STEELSERIES_FIZZ(device); guint8 fs; gboolean is_receiver = FALSE; - FuDevice *proxy = fu_device_get_proxy(device); + FuDevice *proxy; guint8 id = 0; g_autoptr(FuDeviceLocker) locker = NULL; @@ -572,11 +562,9 @@ if (locker == NULL) return FALSE; - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) return FALSE; - } - is_receiver = fu_device_has_private_flag(device, FU_STEELSERIES_DEVICE_FLAG_IS_RECEIVER); if (!fu_steelseries_fizz_impl_get_fs_id(FU_STEELSERIES_FIZZ_IMPL(proxy), is_receiver, @@ -639,7 +627,7 @@ fu_dump_raw(G_LOG_DOMAIN, "Firmware", buf, size); blob = g_bytes_new_take(g_steal_pointer(&buf), size); - if (!fu_firmware_parse_bytes(firmware, blob, 0x0, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + if (!fu_firmware_parse_bytes(firmware, blob, 0x0, FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return NULL; /* success */ @@ -650,10 +638,10 @@ fu_steelseries_fizz_read_firmware(FuDevice *device, FuProgress *progress, GError **error) { FuSteelseriesFizz *self = FU_STEELSERIES_FIZZ(device); + FuDevice *proxy; guint8 fs = 0; guint8 id = 0; gboolean is_receiver; - FuDevice *proxy = fu_device_get_proxy(device); g_autoptr(FuFirmware) firmware = NULL; g_autoptr(FuDeviceLocker) locker = NULL; @@ -661,10 +649,9 @@ if (locker == NULL) return NULL; - if (proxy == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no proxy"); + proxy = fu_device_get_proxy(device, error); + if (proxy == NULL) return NULL; - } fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 100, NULL); @@ -697,7 +684,7 @@ } static void -fu_steelseries_fizz_set_progress(FuDevice *self, FuProgress *progress) +fu_steelseries_fizz_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -736,5 +723,6 @@ fu_device_add_protocol(FU_DEVICE(self), "com.steelseries.fizz"); fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_USER_REPLUG); /* 40 s */ fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_STEELSERIES_FIRMWARE); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_STEELSERIES_FIZZ_IMPL); fu_device_set_priority(FU_DEVICE(self), 10); /* better than tunneled device */ } diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz.h fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz.h --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz.h 2026-02-26 11:36:18.000000000 +0000 @@ -14,9 +14,6 @@ #define FU_TYPE_STEELSERIES_FIZZ (fu_steelseries_fizz_get_type()) G_DECLARE_FINAL_TYPE(FuSteelseriesFizz, fu_steelseries_fizz, FU, STEELSERIES_FIZZ, FuUsbDevice) -FuSteelseriesFizz * -fu_steelseries_fizz_new(FuDevice *self); - #define FU_STEELSERIES_FIZZ_BATTERY_LEVEL_CHARGING_BIT 0x80U #define FU_STEELSERIES_FIZZ_BATTERY_LEVEL_STATUS_BITS 0x7fU diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz.rs fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz.rs --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-fizz.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-fizz.rs 2026-02-26 11:36:18.000000000 +0000 @@ -228,11 +228,20 @@ #[derive(Parse)] #[repr(C, packed)] -struct FuStructSteelseriesVersion2Res { +struct FuStructSteelseriesVersion2Res2 { reserved: u8, - version_receiver: [char; 12], + version1: [char; 12], _version_unknown: [char; 12], - version_device: [char; 12], + version2: [char; 12], +} + +// Starting from protocol revision v.3 +#[derive(Parse)] +#[repr(C, packed)] +struct FuStructSteelseriesVersion2Res3 { + reserved: u8, + version1: [char; 12], + version2: [char; 12], } // gen2 only @@ -272,7 +281,16 @@ #[derive(Parse)] #[repr(C, packed)] -struct FuStructSteelseriesSerial2Res { +struct FuStructSteelseriesSerial2Res2 { + reserved: u8, + serial: [char; 0x13], +} + +// Starting from protocol revision v.3 +#[derive(Parse)] +#[repr(C, packed)] +struct FuStructSteelseriesSerial2Res3 { reserved: u8, - serial: [char; 0x12], + serial1: [char; 0x13], + serial2: [char; 0x13], } diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-gamepad.c fwupd-2.0.20/plugins/steelseries/fu-steelseries-gamepad.c --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-gamepad.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-gamepad.c 2026-02-26 11:36:18.000000000 +0000 @@ -31,8 +31,8 @@ /* magic is needed for newer gamepad */ fu_struct_steelseries_gamepad_erase_req_set_magic3(st_req, 0x02); } - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) { - g_prefix_error(error, "unable erase flash block: "); + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) { + g_prefix_error_literal(error, "unable erase flash block: "); return FALSE; } @@ -56,7 +56,7 @@ return TRUE; /* get version of FW and bootloader */ - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) return FALSE; buf_res = fu_steelseries_device_response(FU_STEELSERIES_DEVICE(self), error); if (buf_res == NULL) @@ -93,7 +93,7 @@ return TRUE; /* switch to runtime mode */ - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, &error_local)) + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, &error_local)) g_debug("ignoring error on reset: %s", error_local->message); fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); @@ -113,7 +113,7 @@ return TRUE; /* switch to bootloader mode */ - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, &error_local)) + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, &error_local)) g_debug("ignoring error on reset: %s", error_local->message); /* controller will be renumbered after switching to bootloader mode */ @@ -140,12 +140,12 @@ error)) return FALSE; chunk_checksum = - fu_sum16(st_req->data + FU_STRUCT_STEELSERIES_GAMEPAD_WRITE_CHUNK_REQ_OFFSET_DATA, + fu_sum16(st_req->buf->data + FU_STRUCT_STEELSERIES_GAMEPAD_WRITE_CHUNK_REQ_OFFSET_DATA, FU_STRUCT_STEELSERIES_GAMEPAD_WRITE_CHUNK_REQ_SIZE_DATA); fu_struct_steelseries_gamepad_write_chunk_req_set_checksum(st_req, chunk_checksum); *checksum += (guint32)chunk_checksum; - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) { + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) { g_prefix_error(error, "unable to flash block %u: ", fu_chunk_get_idx(chunk)); return FALSE; } @@ -188,8 +188,8 @@ g_autoptr(GByteArray) buf_res = NULL; fu_struct_steelseries_gamepad_write_checksum_req_set_checksum(st_req, checksum); - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) { - g_prefix_error(error, "unable to write checksum: "); + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) { + g_prefix_error_literal(error, "unable to write checksum: "); return FALSE; } buf_res = fu_steelseries_device_response(FU_STEELSERIES_DEVICE(self), error); @@ -202,7 +202,7 @@ 0x0, error); if (st_res == NULL) { - g_prefix_error(error, "controller is unable to validate checksum: "); + g_prefix_error_literal(error, "controller is unable to validate checksum: "); return FALSE; } @@ -263,7 +263,7 @@ } static void -fu_steelseries_gamepad_set_progress(FuDevice *self, FuProgress *progress) +fu_steelseries_gamepad_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -294,8 +294,6 @@ static void fu_steelseries_gamepad_init(FuSteelseriesGamepad *self) { - fu_steelseries_device_set_iface_idx_offset(FU_STEELSERIES_DEVICE(self), -1); - fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_BCD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-mouse.c fwupd-2.0.20/plugins/steelseries/fu-steelseries-mouse.c --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-mouse.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-mouse.c 2026-02-26 11:36:18.000000000 +0000 @@ -22,14 +22,13 @@ fu_steelseries_mouse_setup(FuDevice *device, GError **error) { gsize actual_len = 0; - guint8 data[32]; + guint8 data[32] = {0}; g_autofree gchar *version = NULL; /* FuUsbDevice->setup */ if (!FU_DEVICE_CLASS(fu_steelseries_mouse_parent_class)->setup(device, error)) return FALSE; - memset(data, 0x00, sizeof(data)); data[0] = 0x16; if (!fu_usb_device_control_transfer(FU_USB_DEVICE(device), FU_USB_DIRECTION_HOST_TO_DEVICE, @@ -44,7 +43,7 @@ FU_STEELSERIES_TRANSACTION_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to do control transfer: "); + g_prefix_error_literal(error, "failed to do control transfer: "); return FALSE; } if (actual_len != 32) { @@ -63,7 +62,7 @@ FU_STEELSERIES_TRANSACTION_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to do EP1 transfer: "); + g_prefix_error_literal(error, "failed to do EP1 transfer: "); return FALSE; } if (actual_len != 32) { diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-plugin.c fwupd-2.0.20/plugins/steelseries/fu-steelseries-plugin.c --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -26,6 +26,7 @@ static void fu_steelseries_plugin_init(FuSteelseriesPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void @@ -33,7 +34,8 @@ { FuPlugin *plugin = FU_PLUGIN(obj); FuContext *ctx = fu_plugin_get_context(plugin); - fu_context_add_quirk_key(ctx, "SteelSeriesFizzInterface"); + fu_context_add_quirk_key(ctx, "SteelSeriesCmdInterface"); + fu_context_add_quirk_key(ctx, "SteelSeriesFizzProtocolRevision"); fu_plugin_add_device_gtype(plugin, FU_TYPE_STEELSERIES_FIZZ); fu_plugin_add_device_gtype(plugin, FU_TYPE_STEELSERIES_FIZZ_GEN1); fu_plugin_add_device_gtype(plugin, FU_TYPE_STEELSERIES_FIZZ_GEN2); diff -Nru fwupd-2.0.8/plugins/steelseries/fu-steelseries-sonic.c fwupd-2.0.20/plugins/steelseries/fu-steelseries-sonic.c --- fwupd-2.0.8/plugins/steelseries/fu-steelseries-sonic.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/fu-steelseries-sonic.c 2026-02-26 11:36:18.000000000 +0000 @@ -42,7 +42,7 @@ g_autoptr(FuStructSteelseriesSonicWirelessStatusRes) st_res = NULL; g_autoptr(GByteArray) buf_res = NULL; - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) return FALSE; buf_res = fu_steelseries_device_response(FU_STEELSERIES_DEVICE(self), error); if (buf_res == NULL) @@ -67,7 +67,7 @@ g_autoptr(FuStructSteelseriesSonicBatteryRes) st_res = NULL; g_autoptr(GByteArray) buf_res = NULL; - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) return FALSE; buf_res = fu_steelseries_device_response(FU_STEELSERIES_DEVICE(self), error); if (buf_res == NULL) @@ -101,7 +101,7 @@ FU_STEELSERIES_SONIC_READ_FROM_RAM_OPCODE[chip]); fu_struct_steelseries_sonic_read_from_ram_req_set_offset(st_req, fu_chunk_get_address(chk)); fu_struct_steelseries_sonic_read_from_ram_req_set_size(st_req, fu_chunk_get_data_sz(chk)); - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) return FALSE; buf_res = fu_steelseries_device_response(FU_STEELSERIES_DEVICE(self), error); @@ -170,7 +170,7 @@ fu_struct_steelseries_sonic_read_from_flash_req_set_offset(st_req, fu_chunk_get_address(chk)); fu_struct_steelseries_sonic_read_from_flash_req_set_size(st_req, fu_chunk_get_data_sz(chk)); - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) return FALSE; /* timeout to give some time to read from flash to ram */ @@ -236,7 +236,7 @@ fu_chunk_get_data_sz(chk), error)) return FALSE; - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) return FALSE; /* timeout to give some time to write to ram */ @@ -306,7 +306,7 @@ fu_struct_steelseries_sonic_write_to_flash_req_set_offset(st_req, fu_chunk_get_address(chk)); fu_struct_steelseries_sonic_write_to_flash_req_set_size(st_req, fu_chunk_get_data_sz(chk)); - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) return FALSE; /* timeout to give some time to write from ram to flash */ @@ -367,7 +367,7 @@ fu_struct_steelseries_sonic_erase_req_set_opcode(st_req, FU_STEELSERIES_SONIC_ERASE_OPCODE[chip]); fu_struct_steelseries_sonic_erase_req_set_chipid(st_req, FU_STEELSERIES_SONIC_CHIP[chip]); - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) return FALSE; /* timeout to give some time to erase flash */ @@ -394,7 +394,7 @@ fu_struct_steelseries_sonic_restart_req_set_opcode( st_req, FU_STEELSERIES_SONIC_RESTART_OPCODE[chip]); - if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req, error)) + if (!fu_steelseries_device_request(FU_STEELSERIES_DEVICE(self), st_req->buf, error)) return FALSE; /* timeout to give some time to restart chip */ @@ -412,12 +412,15 @@ FuSteelseriesSonicWirelessStatus *wl_status = (FuSteelseriesSonicWirelessStatus *)user_data; if (!fu_steelseries_sonic_wireless_status(self, wl_status, error)) { - g_prefix_error(error, "failed to get wireless status: "); + g_prefix_error_literal(error, "failed to get wireless status: "); return FALSE; } g_debug("WirelessStatus: %s", fu_steelseries_sonic_wireless_status_to_string(*wl_status)); if (*wl_status != FU_STEELSERIES_SONIC_WIRELESS_STATUS_CONNECTED) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "device is unreachable"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "device is unreachable"); return FALSE; } @@ -437,7 +440,7 @@ g_autofree gchar *msg = NULL; if (!fu_steelseries_sonic_wireless_status(self, &wl_status, error)) { - g_prefix_error(error, "failed to get wireless status: "); + g_prefix_error_literal(error, "failed to get wireless status: "); return FALSE; } g_debug("WirelessStatus: %s", fu_steelseries_sonic_wireless_status_to_string(wl_status)); @@ -532,7 +535,7 @@ error)) return FALSE; if (!fu_steelseries_sonic_ensure_battery_state(self, error)) { - g_prefix_error(error, "failed to get battery state: "); + g_prefix_error_literal(error, "failed to get battery state: "); return FALSE; } @@ -697,7 +700,8 @@ if (firmware_nordic == NULL) return NULL; fu_firmware_set_id(firmware_nordic, FU_STEELSERIES_SONIC_FIRMWARE_ID[chip]); - fu_firmware_add_image(firmware, firmware_nordic); + if (!fu_firmware_add_image(firmware, firmware_nordic, error)) + return NULL; fu_progress_step_done(progress); /* holtek */ @@ -707,7 +711,8 @@ if (firmware_holtek == NULL) return NULL; fu_firmware_set_id(firmware_holtek, FU_STEELSERIES_SONIC_FIRMWARE_ID[chip]); - fu_firmware_add_image(firmware, firmware_holtek); + if (!fu_firmware_add_image(firmware, firmware_holtek, error)) + return NULL; fu_progress_step_done(progress); /* mouse */ @@ -717,7 +722,8 @@ if (firmware_mouse == NULL) return NULL; fu_firmware_set_id(firmware_mouse, FU_STEELSERIES_SONIC_FIRMWARE_ID[chip]); - fu_firmware_add_image(firmware, firmware_mouse); + if (!fu_firmware_add_image(firmware, firmware_mouse, error)) + return NULL; fu_progress_step_done(progress); /* success */ @@ -801,8 +807,11 @@ return TRUE; } +/* nocheck:name -- should probably split out into new GType somehow */ static gboolean -fu_steelseries_sonic_parse_firmware(FuFirmware *firmware, FwupdInstallFlags flags, GError **error) +fu_steelseries_sonic_parse_firmware(FuFirmware *firmware, + FuFirmwareParseFlags flags, + GError **error) { guint32 checksum_tmp; guint32 checksum; @@ -824,7 +833,7 @@ g_bytes_get_size(blob) - sizeof(checksum_tmp)); checksum_tmp = ~checksum_tmp; if (checksum_tmp != checksum) { - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -849,7 +858,7 @@ fu_steelseries_sonic_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) firmware_nordic = NULL; @@ -896,7 +905,7 @@ } static void -fu_steelseries_sonic_set_progress(FuDevice *self, FuProgress *progress) +fu_steelseries_sonic_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -922,8 +931,6 @@ static void fu_steelseries_sonic_init(FuSteelseriesSonic *self) { - fu_steelseries_device_set_iface_idx_offset(FU_STEELSERIES_DEVICE(self), -1); - fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_BCD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); diff -Nru fwupd-2.0.8/plugins/steelseries/meson.build fwupd-2.0.20/plugins/steelseries/meson.build --- fwupd-2.0.8/plugins/steelseries/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -30,6 +30,7 @@ device_tests += files( 'tests/steelseries-aerox-3-wireless.json', + 'tests/steelseries-nova3p.json', 'tests/steelseries-nova5.json', 'tests/steelseries-rival-3-wireless.json', 'tests/steelseries-stratus-duo.json', diff -Nru fwupd-2.0.8/plugins/steelseries/steelseries.quirk fwupd-2.0.20/plugins/steelseries/steelseries.quirk --- fwupd-2.0.8/plugins/steelseries/steelseries.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/steelseries.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -6,6 +6,7 @@ Icon = usb-receiver CounterpartGuid = USB\VID_1038&PID_1432 Flags = is-receiver +SteelSeriesCmdInterface = 2 [USB\VID_1038&PID_1432] Plugin = steelseries @@ -22,6 +23,7 @@ Name = Stratus Duo Gamepad Icon = input-gaming CounterpartGuid = USB\VID_1038&PID_1433 +SteelSeriesCmdInterface = 2 [USB\VID_1038&PID_1433] Plugin = steelseries @@ -57,6 +59,7 @@ Plugin = steelseries GType = FuSteelseriesSonic Icon = input-mouse +SteelSeriesCmdInterface = 3 # Aerox 3 Wireless [HIDRAW\VEN_0111&DEV_183A] @@ -124,7 +127,7 @@ Name = Arctis Nova 5 Headset Icon = usb-headset FirmwareSize = 0x14B000 -SteelSeriesFizzInterface = 0 +SteelSeriesCmdInterface = 0 Flags = ~usable-during-update,detach-bootloader InstallDuration = 87 CounterpartGuid = USB\VID_1038&PID_2231,STEELSERIES\VID_1038&PID_2232&PROTOCOL_FIZZ_TUNNEL @@ -159,6 +162,7 @@ FirmwareSize = 0x14B000 Flags = is-receiver,detach-bootloader InstallDuration = 165 +SteelSeriesCmdInterface = 3 [USB\VID_1038&PID_2233] Plugin = steelseries @@ -170,3 +174,64 @@ CounterpartGuid = USB\VID_1038&PID_2232 Flags = is-bootloader,is-receiver,~usable-during-update InstallDuration = 165 + +# Arctis Nova 3P +[USB\VID_1038&PID_2267] +Plugin = steelseries +GType = FuSteelseriesFizz +ProxyGType = FuSteelseriesFizzGen2 +Name = Arctis Nova 3P Headset +Icon = usb-headset +FirmwareSize = 0x14B000 +Flags = ~usable-during-update,detach-bootloader +InstallDuration = 87 +CounterpartGuid = USB\VID_1038&PID_2268,STEELSERIES\VID_1038&PID_2269&PROTOCOL_FIZZ_TUNNEL +SteelSeriesCmdInterface = 0 +SteelSeriesFizzProtocolRevision = 3 + +[USB\VID_1038&PID_2268] +Plugin = steelseries +GType = FuSteelseriesFizz +ProxyGType = FuSteelseriesFizzGen2 +Name = Arctis Nova 3P Headset bootloader +Icon = usb-headset +FirmwareSize = 0x14B000 +CounterpartGuid = USB\VID_1038&PID_2267,STEELSERIES\VID_1038&PID_2269&PROTOCOL_FIZZ_TUNNEL +Flags = is-bootloader,~usable-during-update +InstallDuration = 87 +SteelSeriesFizzProtocolRevision = 3 + +[STEELSERIES\VID_1038&PID_2269&PROTOCOL_FIZZ_TUNNEL] +Plugin = steelseries +GType = FuSteelseriesFizzTunnel +Name = Arctis Nova 3P Headset via USB Receiver +Icon = usb-headset +Flags = ~usable-during-update,detach-bootloader +FirmwareSize = 0x14B000 +CounterpartGuid = USB\VID_1038&PID_2267,USB\VID_1038&PID_2268 +SteelSeriesFizzProtocolRevision = 3 + +[USB\VID_1038&PID_2269] +Plugin = steelseries +GType = FuSteelseriesFizz +ProxyGType = FuSteelseriesFizzGen2 +Name = Arctis Nova 3P USB Receiver +Icon = usb-receiver +CounterpartGuid = USB\VID_1038&PID_226A +FirmwareSize = 0x14B000 +Flags = is-receiver,detach-bootloader +InstallDuration = 165 +SteelSeriesCmdInterface = 3 +SteelSeriesFizzProtocolRevision = 3 + +[USB\VID_1038&PID_226A] +Plugin = steelseries +GType = FuSteelseriesFizz +ProxyGType = FuSteelseriesFizzGen2 +Name = Arctis Nova 3P USB Receiver bootloader +Icon = usb-receiver +CounterpartGuid = USB\VID_1038&PID_2269 +FirmwareSize = 0x14B000 +Flags = is-bootloader,is-receiver,~usable-during-update +InstallDuration = 165 +SteelSeriesFizzProtocolRevision = 3 diff -Nru fwupd-2.0.8/plugins/steelseries/tests/steelseries-aerox-3-wireless.json fwupd-2.0.20/plugins/steelseries/tests/steelseries-aerox-3-wireless.json --- fwupd-2.0.8/plugins/steelseries/tests/steelseries-aerox-3-wireless.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/tests/steelseries-aerox-3-wireless.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/41e25df1ee09f3c05a4799e4fc325115bd5ba9490309c76c545f3e3a88c1c9b9-aerox-3-wireless-dongle.cab", + "url": "41e25df1ee09f3c05a4799e4fc325115bd5ba9490309c76c545f3e3a88c1c9b9-aerox-3-wireless-dongle.cab", "components": [ { "version": "1.4.2", @@ -15,7 +15,7 @@ ] }, { - "url": "https://fwupd.org/downloads/de803999ea8f8c71268bb8ed771c230907b78e9ca8435212f977bfa859cc5f2b-aerox-3-wireless-mouse.cab", + "url": "de803999ea8f8c71268bb8ed771c230907b78e9ca8435212f977bfa859cc5f2b-aerox-3-wireless-mouse.cab", "components": [ { "version": "1.10.9", @@ -28,8 +28,8 @@ ] }, { - "url": "https://fwupd.org/downloads/596d1032e7c916822ffae84a48d5f84d641808be761723536c0d82bb83ac077f-aerox-3-wireless-dongle.cab", - "emulation-url": "https://fwupd.org/downloads/ce35aed86ad2bab08931a07c29524a2f173f10ab7f1c244083a607d64331eefa-aerox-3-wireless-dongle_1.11.4_USB-C.emulation.zip", + "url": "596d1032e7c916822ffae84a48d5f84d641808be761723536c0d82bb83ac077f-aerox-3-wireless-dongle.cab", + "emulation-url": "ce35aed86ad2bab08931a07c29524a2f173f10ab7f1c244083a607d64331eefa-aerox-3-wireless-dongle_1.11.4_USB-C.emulation.zip", "components": [ { "version": "1.11.4", @@ -41,8 +41,8 @@ ] }, { - "url": "https://fwupd.org/downloads/f1453976fe7ea27522524872327ad5aab6fba0c962df719ac05253439d7d72a9-aerox-3-wireless-mouse.cab", - "emulation-url": "https://fwupd.org/downloads/01958299662917005d9b499074d9624207e8762d7ed1d826b72e962bf4d36895-aerox-3-wireless-mouse_1.11.4_USB-C.emulation.zip", + "url": "f1453976fe7ea27522524872327ad5aab6fba0c962df719ac05253439d7d72a9-aerox-3-wireless-mouse.cab", + "emulation-url": "01958299662917005d9b499074d9624207e8762d7ed1d826b72e962bf4d36895-aerox-3-wireless-mouse_1.11.4_USB-C.emulation.zip", "components": [ { "version": "1.11.4", diff -Nru fwupd-2.0.8/plugins/steelseries/tests/steelseries-nova3p.json fwupd-2.0.20/plugins/steelseries/tests/steelseries-nova3p.json --- fwupd-2.0.8/plugins/steelseries/tests/steelseries-nova3p.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/tests/steelseries-nova3p.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +{ + "name": "SteelSeries Arctis Nova 3P Headset", + "interactive": false, + "steps": [ + { + "url": "eaa4a69ada771d6f63faaf3e5a73d30b04fda92acea8156f2351644005fb9d08-SteelSeries_Nova_3P_Headset_1.0.0.cab", + "emulation-url": "20bfb20058c54431b4e4cb1f1fa2fd93b18f6f950f6aade37155a498fc19254f-SteelSeries_Nova_3P_Headset_1.0.0.zip", + "components": [ + { + "version": "1.0.0", + "guids": [ + "8de6b474-c01f-50ec-b44f-892069a95e58" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/steelseries/tests/steelseries-nova5.json fwupd-2.0.20/plugins/steelseries/tests/steelseries-nova5.json --- fwupd-2.0.8/plugins/steelseries/tests/steelseries-nova5.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/tests/steelseries-nova5.json 2026-02-26 11:36:18.000000000 +0000 @@ -1,10 +1,10 @@ { - "name": "SteelSeries Artix Nova5 Headset", + "name": "SteelSeries Arctis Nova 5 Headset", "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/fa73c8393027e7231776c917486b2e3f417e412e3b1c0cea0be90a57df600e08-SteelSeries_Nova_5_Headset_1.6.0.cab", - "emulation-url": "https://fwupd.org/downloads/272e4725290ff7d64419cfbe57e97021695c9014fb03e9bd6087db99a7e09707-SteelSeries_Nova_5_Headset_1.6.0.zip", + "url": "fa73c8393027e7231776c917486b2e3f417e412e3b1c0cea0be90a57df600e08-SteelSeries_Nova_5_Headset_1.6.0.cab", + "emulation-url": "272e4725290ff7d64419cfbe57e97021695c9014fb03e9bd6087db99a7e09707-SteelSeries_Nova_5_Headset_1.6.0.zip", "components": [ { "version": "1.6.0", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/6317a70eef744dabe4d70931962da42fd419bdebe5436dcf939a889782fe33e1-SteelSeries_Nova_5_Headset_1.7.0.cab", - "emulation-url": "https://fwupd.org/downloads/46850811d0c25e739d8016da2a5a83fd64b57c6303c9cbbca25312a603ba1321-SteelSeries_Nova_5_Headset_1.7.0.zip", + "url": "6317a70eef744dabe4d70931962da42fd419bdebe5436dcf939a889782fe33e1-SteelSeries_Nova_5_Headset_1.7.0.cab", + "emulation-url": "46850811d0c25e739d8016da2a5a83fd64b57c6303c9cbbca25312a603ba1321-SteelSeries_Nova_5_Headset_1.7.0.zip", "components": [ { "version": "1.7.0", diff -Nru fwupd-2.0.8/plugins/steelseries/tests/steelseries-rival-3-wireless.json fwupd-2.0.20/plugins/steelseries/tests/steelseries-rival-3-wireless.json --- fwupd-2.0.8/plugins/steelseries/tests/steelseries-rival-3-wireless.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/tests/steelseries-rival-3-wireless.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": true, "steps": [ { - "url": "https://fwupd.org/downloads/0e227c83714c7c37ac8053164d420d1ca2b85d68b65ba60d51aa0a6b03ee4087-SteelSeries_Rival_3_Wireless_1.4.cab", - "emulation-url": "https://fwupd.org/downloads/5a2f36255139825c58aca8f09e183e6239f227a81626596518c139b6551112b1-SteelSeries_Rival_3_Wireless_1.4.emulation.zip", + "url": "0e227c83714c7c37ac8053164d420d1ca2b85d68b65ba60d51aa0a6b03ee4087-SteelSeries_Rival_3_Wireless_1.4.cab", + "emulation-url": "5a2f36255139825c58aca8f09e183e6239f227a81626596518c139b6551112b1-SteelSeries_Rival_3_Wireless_1.4.emulation.zip", "components": [ { "version": "1.4", diff -Nru fwupd-2.0.8/plugins/steelseries/tests/steelseries-stratus-duo-rx.json fwupd-2.0.20/plugins/steelseries/tests/steelseries-stratus-duo-rx.json --- fwupd-2.0.8/plugins/steelseries/tests/steelseries-stratus-duo-rx.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/tests/steelseries-stratus-duo-rx.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": true, "steps": [ { - "url": "https://fwupd.org/downloads/10521cfeed9086a597892469fbff79ab5d9b83d3b062f606a8ab6bf34664f6dd-steelseries-stratusduorx-1.75.cab", - "emulation-url": "https://fwupd.org/downloads/bec561d97746ae0251fca3e26355423cd2771e4c3addecab04daee851d3d877d-steelseries-stratusduorx-1.75.emulation.zip", + "url": "10521cfeed9086a597892469fbff79ab5d9b83d3b062f606a8ab6bf34664f6dd-steelseries-stratusduorx-1.75.cab", + "emulation-url": "bec561d97746ae0251fca3e26355423cd2771e4c3addecab04daee851d3d877d-steelseries-stratusduorx-1.75.emulation.zip", "components": [ { "version": "1.75", diff -Nru fwupd-2.0.8/plugins/steelseries/tests/steelseries-stratus-duo.json fwupd-2.0.20/plugins/steelseries/tests/steelseries-stratus-duo.json --- fwupd-2.0.8/plugins/steelseries/tests/steelseries-stratus-duo.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/steelseries/tests/steelseries-stratus-duo.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": true, "steps": [ { - "url": "https://fwupd.org/downloads/ecc54ffe68eebb0e5d77604683df698e7c15911421eee960d2876c175a2aa164-steelseries-stratusduo-1.75.cab", - "emulation-url": "https://fwupd.org/downloads/4da8d30dfe2343273162121a99b5ce31039e0f9e0590e15582821d32f64cf7c5-steelseries-stratusduo-1.75.emulation.zip", + "url": "ecc54ffe68eebb0e5d77604683df698e7c15911421eee960d2876c175a2aa164-steelseries-stratusduo-1.75.cab", + "emulation-url": "4da8d30dfe2343273162121a99b5ce31039e0f9e0590e15582821d32f64cf7c5-steelseries-stratusduo-1.75.emulation.zip", "components": [ { "version": "1.75", diff -Nru fwupd-2.0.8/plugins/synaptics-cape/README.md fwupd-2.0.20/plugins/synaptics-cape/README.md --- fwupd-2.0.8/plugins/synaptics-cape/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-cape/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -51,10 +51,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.7.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Simon Ho: @CNXT-Simon diff -Nru fwupd-2.0.8/plugins/synaptics-cape/fu-synaptics-cape-device.c fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape-device.c --- fwupd-2.0.8/plugins/synaptics-cape/fu-synaptics-cape-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -69,8 +69,8 @@ { return fu_hid_device_get_report(FU_HID_DEVICE(self), FU_SYNAPTICS_CAPE_CMD_HID_REPORT_DEFAULT_REPORT_ID, - st_report->data, - st_report->len, + st_report->buf->data, + st_report->buf->len, FU_SYNAPTICS_CAPE_DEVICE_USB_CMD_READ_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error); @@ -94,7 +94,7 @@ FU_SYNAPTICS_CAPE_DEVICE_USB_CMD_INTR_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to get report over interrupt ep: "); + g_prefix_error_literal(error, "failed to get report over interrupt ep: "); return FALSE; } @@ -104,142 +104,36 @@ return TRUE; } -/* dump CAPE command error if any */ static gboolean -fu_synaptics_cape_device_rc_set_error(const FuSynapticsCapeMsg *rsp, GError **error) +fu_synaptics_cape_device_rc_set_error(gint16 status, GError **error) { - gint16 value; - - g_return_val_if_fail(rsp != NULL, FALSE); - g_return_val_if_fail(error == NULL || *error == NULL, FALSE); - - value = (gint16)fu_synaptics_cape_msg_get_data_len(rsp); - if (value >= 0) - return TRUE; - switch (value) { - case FU_SYNAPTICS_CAPE_ERROR_GENERIC_FAILURE: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "generic failure"); - break; - case FU_SYNAPTICS_CAPE_ERROR_ALREADY_EXISTS: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "already exists"); - break; - case FU_SYNAPTICS_CAPE_ERROR_NULL_APP_POINTER: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "null app pointer"); - break; - case FU_SYNAPTICS_CAPE_ERROR_NULL_MODULE_POINTER: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "null module pointer"); - break; - case FU_SYNAPTICS_CAPE_ERROR_NULL_POINTER: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "null pointer"); - break; - case FU_SYNAPTICS_CAPE_ERROR_BAD_APP_ID: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "bad app id"); - break; - case FU_SYNAPTICS_CAPE_ERROR_MODULE_TYPE_HAS_NO_API: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "has no api"); - break; - case FU_SYNAPTICS_CAPE_ERROR_BAD_MAGIC_NUMBER: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "bad magic number"); - break; - case FU_SYNAPTICS_CAPE_ERROR_CMD_MODE_UNSUPPORTED: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "mode unsupported"); - break; - case FU_SYNAPTICS_CAPE_ERROR_EAGAIN: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT, "query timeout"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_FAIL: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "command failed"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_WRITE_FAIL: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "writing to flash failed"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_READ_FAIL: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_READ, - "reading from flash failed"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_CRC_ERROR: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "firmware data has been corrupted"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_USB_ID_NOT_MATCH: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "vendor and device IDs do not match"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_VERSION_DOWNGRADE: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_VERSION_NEWER, - "update is older than current version"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_HEADER_CORRUPTION: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "firmware header data has been corrupted"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_IMAGE_CORRUPTION: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "firmware payload data has been corrupted"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_ALREADY_ACTIVE: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to active new firmward"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_NOT_READY: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_BUSY, - "firmware update is not ready"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_SIGN_TRANSFER_CORRUPTION: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "signature data has been corrupted"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_DIGITAL_SIGNATURE_VERIFICATION_FAILED: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_AUTH_FAILED, - "digital signature is invalid"); - break; - case FU_SYNAPTICS_CAPE_ERROR_SFU_TASK_NOT_RUNNING: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "firmware update task is not running"); - break; - default: - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unknown error %d", value); - } - - /* success */ - return FALSE; + const gchar *msg = fu_synaptics_cape_error_to_string(status); + const FuErrorMapEntry entries[] = { + {FU_SYNAPTICS_CAPE_ERROR_GENERIC_FAILURE, FWUPD_ERROR_INTERNAL, msg}, + {FU_SYNAPTICS_CAPE_ERROR_ALREADY_EXISTS, FWUPD_ERROR_INTERNAL, msg}, + {FU_SYNAPTICS_CAPE_ERROR_NULL_APP_POINTER, FWUPD_ERROR_INVALID_DATA, msg}, + {FU_SYNAPTICS_CAPE_ERROR_NULL_MODULE_POINTER, FWUPD_ERROR_INVALID_DATA, msg}, + {FU_SYNAPTICS_CAPE_ERROR_NULL_POINTER, FWUPD_ERROR_INVALID_DATA, msg}, + {FU_SYNAPTICS_CAPE_ERROR_BAD_APP_ID, FWUPD_ERROR_INVALID_DATA, msg}, + {FU_SYNAPTICS_CAPE_ERROR_MODULE_TYPE_HAS_NO_API, FWUPD_ERROR_INTERNAL, msg}, + {FU_SYNAPTICS_CAPE_ERROR_BAD_MAGIC_NUMBER, FWUPD_ERROR_INVALID_DATA, msg}, + {FU_SYNAPTICS_CAPE_ERROR_CMD_MODE_UNSUPPORTED, FWUPD_ERROR_NOT_SUPPORTED, msg}, + {FU_SYNAPTICS_CAPE_ERROR_EAGAIN, FWUPD_ERROR_TIMED_OUT, msg}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_FAIL, FWUPD_ERROR_INVALID_DATA, "command failed"}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_WRITE_FAIL, FWUPD_ERROR_WRITE, msg}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_READ_FAIL, FWUPD_ERROR_READ, msg}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_CRC_ERROR, FWUPD_ERROR_INVALID_DATA, msg}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_USB_ID_NOT_MATCH, FWUPD_ERROR_INTERNAL, msg}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_VERSION_DOWNGRADE, FWUPD_ERROR_VERSION_NEWER, msg}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_HEADER_CORRUPTION, FWUPD_ERROR_INVALID_DATA, msg}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_IMAGE_CORRUPTION, FWUPD_ERROR_INVALID_DATA, msg}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_ALREADY_ACTIVE, FWUPD_ERROR_INTERNAL, msg}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_NOT_READY, FWUPD_ERROR_BUSY, msg}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_SIGN_TRANSFER_CORRUPTION, FWUPD_ERROR_INVALID_DATA, msg}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_SIGNATURE_VERIFICATION, FWUPD_ERROR_AUTH_FAILED, msg}, + {FU_SYNAPTICS_CAPE_ERROR_SFU_TASK_NOT_RUNNING, FWUPD_ERROR_NOT_SUPPORTED, msg}, + }; + return fu_error_map_entry_to_gerror(status, entries, G_N_ELEMENTS(entries), error); } /* sends a FuSynapticsCapeMsg structure command to device to get the response in the same structure @@ -250,6 +144,7 @@ guint delay_ms, GError **error) { + gint16 value; guint elapsed_ms = 0; g_autoptr(FuSynapticsCapeCmdHidReport) st_report = fu_synaptics_cape_cmd_hid_report_new(); g_autoptr(FuSynapticsCapeMsg) st_msg_res = NULL; @@ -269,7 +164,7 @@ return NULL; if (!fu_synaptics_cape_device_set_report(self, st_report, error)) { - g_prefix_error(error, "failed to send: "); + g_prefix_error_literal(error, "failed to send: "); return NULL; } @@ -292,7 +187,7 @@ (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND) || g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL))) { g_debug("ignoring: %s", error_local->message); - return g_byte_array_ref(st_msg_req); + return fu_synaptics_cape_msg_ref(st_msg_req); } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), @@ -316,7 +211,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL))) { g_debug("ignoring: %s", error_local->message); - g_byte_array_ref(st_msg_req); + return fu_synaptics_cape_msg_ref(st_msg_req); } g_propagate_prefixed_error(error, g_steal_pointer(&error_local), @@ -334,14 +229,17 @@ st_msg_res = fu_synaptics_cape_cmd_hid_report_get_msg(st_report); if ((fu_synaptics_cape_msg_get_cmd_id(st_msg_res) & FU_SYNAPTICS_CAPE_CMD_IS_REPLY) == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "firmware don't respond to command"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware don't respond to command"); return NULL; } - if (!fu_synaptics_cape_device_rc_set_error(st_msg_res, error)) - return NULL; + value = (gint16)fu_synaptics_cape_msg_get_data_len(st_msg_res); + if (value < 0) { + if (!fu_synaptics_cape_device_rc_set_error(value, error)) + return NULL; + } /* success */ return g_steal_pointer(&st_msg_res); @@ -394,7 +292,7 @@ 0, 0, error)) { - g_prefix_error(error, "reset command is not supported: "); + g_prefix_error_literal(error, "reset command is not supported: "); return FALSE; } @@ -486,12 +384,12 @@ return FALSE; if (!fu_synaptics_cape_device_setup_version(self, error)) { - g_prefix_error(error, "failed to get firmware version info: "); + g_prefix_error_literal(error, "failed to get firmware version info: "); return FALSE; } if (!fu_synaptics_cape_device_setup_active_partition(self, error)) { - g_prefix_error(error, "failed to get active partition info: "); + g_prefix_error_literal(error, "failed to get active partition info: "); return FALSE; } @@ -503,7 +401,7 @@ fu_synaptics_cape_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSynapticsCapeDevice *self = FU_SYNAPTICS_CAPE_DEVICE(device); @@ -535,7 +433,7 @@ return NULL; /* verify if correct device */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) == 0) { const guint16 vid = fu_synaptics_cape_firmware_get_vid(FU_SYNAPTICS_CAPE_FIRMWARE(firmware)); const guint16 pid = @@ -691,7 +589,7 @@ if (fw_header == NULL) return FALSE; if (!fu_synaptics_cape_device_write_firmware_header(self, fw_header, error)) { - g_prefix_error(error, "update header failed: "); + g_prefix_error_literal(error, "update header failed: "); return FALSE; } fu_progress_step_done(progress); @@ -704,7 +602,7 @@ stream, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "update image failed: "); + g_prefix_error_literal(error, "update image failed: "); return FALSE; } fu_progress_step_done(progress); @@ -717,7 +615,7 @@ 0, 0, error)) { - g_prefix_error(error, "failed to verify firmware: "); + g_prefix_error_literal(error, "failed to verify firmware: "); return FALSE; } fu_progress_step_done(progress); @@ -733,7 +631,7 @@ } static void -fu_synaptics_cape_device_set_progress(FuDevice *self, FuProgress *progress) +fu_synaptics_cape_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -752,7 +650,7 @@ static void fu_synaptics_cape_device_init(FuSynapticsCapeDevice *self) { - fu_device_add_icon(FU_DEVICE(self), "audio-card"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_AUDIO_CARD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); diff -Nru fwupd-2.0.8/plugins/synaptics-cape/fu-synaptics-cape-hid-firmware.c fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape-hid-firmware.c --- fwupd-2.0.8/plugins/synaptics-cape/fu-synaptics-cape-hid-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape-hid-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -22,14 +22,14 @@ static gboolean fu_synaptics_cape_hid_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSynapticsCapeHidFirmware *self = FU_SYNAPTICS_CAPE_HID_FIRMWARE(firmware); gsize streamsz = 0; g_autofree gchar *version_str = NULL; g_autoptr(FuFirmware) img_hdr = fu_firmware_new(); - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructSynapticsCapeHidHdr) st = NULL; g_autoptr(GInputStream) stream_hdr = NULL; g_autoptr(GInputStream) stream_body = NULL; @@ -69,10 +69,12 @@ if (!fu_firmware_parse_stream(img_hdr, stream_hdr, 0x0, flags, error)) return FALSE; fu_firmware_set_id(img_hdr, FU_FIRMWARE_ID_HEADER); - fu_firmware_add_image(firmware, img_hdr); + if (!fu_firmware_add_image(firmware, img_hdr, error)) + return FALSE; /* body */ - stream_body = fu_partial_input_stream_new(stream, st->len, streamsz - st->len, error); + stream_body = + fu_partial_input_stream_new(stream, st->buf->len, streamsz - st->buf->len, error); if (stream_body == NULL) return FALSE; if (!fu_firmware_set_stream(firmware, stream_body, error)) @@ -86,30 +88,30 @@ { FuSynapticsCapeHidFirmware *self = FU_SYNAPTICS_CAPE_HID_FIRMWARE(firmware); guint64 ver = fu_firmware_get_version_raw(firmware); - g_autoptr(GByteArray) buf = fu_struct_synaptics_cape_hid_hdr_new(); + g_autoptr(FuStructSynapticsCapeHidHdr) st_hdr = fu_struct_synaptics_cape_hid_hdr_new(); g_autoptr(GBytes) payload = NULL; /* pack */ fu_struct_synaptics_cape_hid_hdr_set_vid( - buf, + st_hdr, fu_synaptics_cape_firmware_get_vid(FU_SYNAPTICS_CAPE_FIRMWARE(self))); fu_struct_synaptics_cape_hid_hdr_set_pid( - buf, + st_hdr, fu_synaptics_cape_firmware_get_pid(FU_SYNAPTICS_CAPE_FIRMWARE(self))); - fu_struct_synaptics_cape_hid_hdr_set_crc(buf, 0xFFFF); - fu_struct_synaptics_cape_hid_hdr_set_ver_w(buf, ver >> 0); - fu_struct_synaptics_cape_hid_hdr_set_ver_x(buf, ver >> 16); - fu_struct_synaptics_cape_hid_hdr_set_ver_y(buf, ver >> 32); - fu_struct_synaptics_cape_hid_hdr_set_ver_z(buf, ver >> 48); + fu_struct_synaptics_cape_hid_hdr_set_crc(st_hdr, 0xFFFF); + fu_struct_synaptics_cape_hid_hdr_set_ver_w(st_hdr, ver >> 0); + fu_struct_synaptics_cape_hid_hdr_set_ver_x(st_hdr, ver >> 16); + fu_struct_synaptics_cape_hid_hdr_set_ver_y(st_hdr, ver >> 32); + fu_struct_synaptics_cape_hid_hdr_set_ver_z(st_hdr, ver >> 48); /* payload */ payload = fu_firmware_get_bytes_with_patches(firmware, error); if (payload == NULL) return NULL; - fu_byte_array_append_bytes(buf, payload); - fu_byte_array_align_up(buf, FU_FIRMWARE_ALIGNMENT_4, 0xFF); + fu_byte_array_append_bytes(st_hdr->buf, payload); + fu_byte_array_align_up(st_hdr->buf, FU_FIRMWARE_ALIGNMENT_4, 0xFF); - return g_steal_pointer(&buf); + return g_steal_pointer(&st_hdr->buf); } static void diff -Nru fwupd-2.0.8/plugins/synaptics-cape/fu-synaptics-cape-plugin.c fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape-plugin.c --- fwupd-2.0.8/plugins/synaptics-cape/fu-synaptics-cape-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -20,12 +20,14 @@ static void fu_synaptics_cape_plugin_init(FuSynapticsCapePlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void fu_synaptics_cape_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_SYNAPTICS_CAPE_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_SYNAPTICS_CAPE_HID_FIRMWARE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_SYNAPTICS_CAPE_SNGL_FIRMWARE); diff -Nru fwupd-2.0.8/plugins/synaptics-cape/fu-synaptics-cape-sngl-firmware.c fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape-sngl-firmware.c --- fwupd-2.0.8/plugins/synaptics-cape/fu-synaptics-cape-sngl-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape-sngl-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -22,13 +22,13 @@ static gboolean fu_synaptics_cape_sngl_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSynapticsCapeSnglFirmware *self = FU_SYNAPTICS_CAPE_SNGL_FIRMWARE(firmware); gsize streamsz = 0; guint16 num_fw_file; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructSynapticsCapeSnglHdr) st = NULL; g_autofree gchar *version_str = NULL; /* sanity check */ @@ -62,7 +62,7 @@ } /* check CRC */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint32 crc_calc = 0xFFFFFFFF; g_autoptr(GInputStream) stream_tmp = NULL; @@ -111,18 +111,18 @@ fu_synaptics_cape_sngl_firmware_write(FuFirmware *firmware, GError **error) { FuSynapticsCapeSnglFirmware *self = FU_SYNAPTICS_CAPE_SNGL_FIRMWARE(firmware); - g_autoptr(GByteArray) buf = fu_struct_synaptics_cape_sngl_hdr_new(); + g_autoptr(FuStructSynapticsCapeSnglHdr) st = fu_struct_synaptics_cape_sngl_hdr_new(); /* pack */ fu_struct_synaptics_cape_sngl_hdr_set_vid( - buf, + st, fu_synaptics_cape_firmware_get_vid(FU_SYNAPTICS_CAPE_FIRMWARE(self))); fu_struct_synaptics_cape_sngl_hdr_set_pid( - buf, + st, fu_synaptics_cape_firmware_get_pid(FU_SYNAPTICS_CAPE_FIRMWARE(self))); /* success */ - return g_steal_pointer(&buf); + return g_steal_pointer(&st->buf); } static void diff -Nru fwupd-2.0.8/plugins/synaptics-cape/fu-synaptics-cape.rs fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape.rs --- fwupd-2.0.8/plugins/synaptics-cape/fu-synaptics-cape.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-cape/fu-synaptics-cape.rs 2026-02-26 11:36:18.000000000 +0000 @@ -11,6 +11,7 @@ GetVersion = 0x103, } +#[derive(ToString)] enum FuSynapticsCapeError { Eagain = -11, SfuFail = -200, @@ -24,7 +25,7 @@ SfuAlreadyActive = -208, SfuNotReady = -209, SfuSignTransferCorruption = -210, - SfuDigitalSignatureVerificationFailed = -211, + SfuSignatureVerification = -211, SfuTaskNotRunning = -212, GenericFailure = -1025, AlreadyExists = -1026, diff -Nru fwupd-2.0.8/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c fwupd-2.0.20/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c --- fwupd-2.0.8/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -295,7 +295,7 @@ guint8 buf[FU_STRUCT_SYNAPTICS_CXAUDIO_STRING_HEADER_SIZE] = {0}; guint8 header_length; g_autofree gchar *str = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructSynapticsCxaudioStringHeader) st = NULL; /* read header */ if (!fu_synaptics_cxaudio_device_operation(self, @@ -315,7 +315,7 @@ if (st == NULL) return NULL; header_length = fu_struct_synaptics_cxaudio_string_header_get_length(st); - if (header_length < st->len) { + if (header_length < st->buf->len) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -324,7 +324,7 @@ } /* allocate buffer + NUL terminator */ - str = g_malloc0(header_length - st->len + 1); + str = g_malloc0(header_length - st->buf->len + 1); if (!fu_synaptics_cxaudio_device_operation(self, FU_SYNAPTICS_CXAUDIO_OPERATION_READ, FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, @@ -351,7 +351,7 @@ sizeof(tmp), FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, error)) { - g_prefix_error(error, "failed to read EEPROM patch validation byte: "); + g_prefix_error_literal(error, "failed to read EEPROM patch validation byte: "); return FALSE; } if (tmp == FU_SYNAPTICS_CXAUDIO_SIGNATURE_PATCH_BYTE) { @@ -366,7 +366,7 @@ sizeof(tmp), FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, error)) { - g_prefix_error(error, "failed to read EEPROM patch validation byte: "); + g_prefix_error_literal(error, "failed to read EEPROM patch validation byte: "); return FALSE; } if (tmp == FU_SYNAPTICS_CXAUDIO_SIGNATURE_PATCH_BYTE) { @@ -396,8 +396,8 @@ g_autofree gchar *summary = NULL; g_autofree gchar *version_fw = NULL; g_autofree gchar *version_patch = NULL; - g_autoptr(GByteArray) st_inf = NULL; - g_autoptr(GByteArray) st_sig = NULL; + g_autoptr(FuStructSynapticsCxaudioCustomInfo) st_inf = NULL; + g_autoptr(FuStructSynapticsCxaudioValiditySignature) st_sig = NULL; guint8 cinfo[FU_STRUCT_SYNAPTICS_CXAUDIO_CUSTOM_INFO_SIZE] = {0x0}; /* FuUsbDevice->setup */ @@ -413,7 +413,7 @@ sizeof(chip_id_offset), FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, error)) { - g_prefix_error(error, "failed to read ChipID: "); + g_prefix_error_literal(error, "failed to read ChipID: "); return FALSE; } self->chip_id = self->chip_id_base + chip_id_offset; @@ -443,7 +443,7 @@ sizeof(sigbuf), FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, error)) { - g_prefix_error(error, "failed to read EEPROM signature bytes: "); + g_prefix_error_literal(error, "failed to read EEPROM signature bytes: "); return FALSE; } @@ -496,7 +496,7 @@ sizeof(sigbuf), FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, error)) { - g_prefix_error(error, "failed to read EEPROM signature bytes: "); + g_prefix_error_literal(error, "failed to read EEPROM signature bytes: "); return FALSE; } self->eeprom_storage_sz = fu_memread_uint16(sigbuf, G_LITTLE_ENDIAN); @@ -515,7 +515,7 @@ sizeof(cinfo), FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, error)) { - g_prefix_error(error, "failed to read EEPROM custom info: "); + g_prefix_error_literal(error, "failed to read EEPROM custom info: "); return FALSE; } @@ -555,7 +555,7 @@ sizeof(verbuf_fw), FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, error)) { - g_prefix_error(error, "failed to read EEPROM firmware version: "); + g_prefix_error_literal(error, "failed to read EEPROM firmware version: "); return FALSE; } version_fw = g_strdup_printf("%02X.%02X.%02X.%02X", @@ -580,7 +580,7 @@ sizeof(verbuf_patch), FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, error)) { - g_prefix_error(error, "failed to read EEPROM patch version: "); + g_prefix_error_literal(error, "failed to read EEPROM patch version: "); return FALSE; } version_patch = @@ -609,7 +609,7 @@ fu_synaptics_cxaudio_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSynapticsCxaudioDevice *self = FU_SYNAPTICS_CXAUDIO_DEVICE(device); @@ -623,7 +623,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, - "device 0x%04u is incompatible with firmware 0x%04u", + "device 0x%04x is incompatible with firmware 0x%04x", self->chip_id_base, chip_id_base); return NULL; @@ -697,7 +697,7 @@ sizeof(value), FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, error)) { - g_prefix_error(error, "failed to initialize layout signature: "); + g_prefix_error_literal(error, "failed to initialize layout signature: "); return FALSE; } if (!fu_synaptics_cxaudio_device_operation( @@ -710,7 +710,7 @@ sizeof(value), FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, error)) { - g_prefix_error(error, "failed to initialize layout signature: "); + g_prefix_error_literal(error, "failed to initialize layout signature: "); return FALSE; } } @@ -747,7 +747,7 @@ * as it may have not been done by the S37 file */ if (file_kind == FU_SYNAPTICS_CXAUDIO_FILE_KIND_CX2070X_FW) { guint8 buf[FU_STRUCT_SYNAPTICS_CXAUDIO_PATCH_INFO_SIZE] = {0}; - g_autoptr(GByteArray) st_pat = NULL; + g_autoptr(FuStructSynapticsCxaudioPatchInfo) st_pat = NULL; if (!fu_synaptics_cxaudio_device_operation( self, @@ -758,7 +758,7 @@ sizeof(buf), FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, error)) { - g_prefix_error(error, "failed to read EEPROM patch info: "); + g_prefix_error_literal(error, "failed to read EEPROM patch info: "); return FALSE; } st_pat = fu_struct_synaptics_cxaudio_patch_info_parse(buf, sizeof(buf), 0x0, error); @@ -773,11 +773,12 @@ FU_SYNAPTICS_CXAUDIO_OPERATION_WRITE, FU_SYNAPTICS_CXAUDIO_MEM_KIND_EEPROM, FU_SYNAPTICS_CXAUDIO_EEPROM_PATCH_INFO_OFFSET, - st_pat->data, - st_pat->len, + st_pat->buf->data, + st_pat->buf->len, FU_SYNAPTICS_CXAUDIO_OPERATION_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write empty EEPROM patch info: "); + g_prefix_error_literal(error, + "failed to write empty EEPROM patch info: "); return FALSE; } g_debug("invalidated old FW patch for CX2070x (RAM) device"); @@ -866,7 +867,7 @@ } static void -fu_synaptics_cxaudio_device_set_progress(FuDevice *self, FuProgress *progress) +fu_synaptics_cxaudio_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -880,7 +881,7 @@ fu_synaptics_cxaudio_device_init(FuSynapticsCxaudioDevice *self) { self->sw_reset_supported = TRUE; - fu_device_add_icon(FU_DEVICE(self), "audio-card"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_AUDIO_CARD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); diff -Nru fwupd-2.0.8/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.c fwupd-2.0.20/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.c --- fwupd-2.0.8/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,7 @@ #include "fu-synaptics-cxaudio-struct.h" struct _FuSynapticsCxaudioFirmware { - FuSrecFirmwareClass parent_instance; + FuSrecFirmware parent_instance; FuSynapticsCxaudioFileKind file_kind; FuSynapticsCxaudioDeviceKind device_kind; guint8 layout_signature; @@ -154,15 +154,15 @@ static gboolean fu_synaptics_cxaudio_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags 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_autoptr(GByteArray) st = NULL; - g_autoptr(GByteArray) st_sig = NULL; - g_autoptr(GByteArray) st_pat = NULL; + g_autoptr(FuStructSynapticsCxaudioCustomInfo) st = NULL; + g_autoptr(FuStructSynapticsCxaudioValiditySignature) st_sig = NULL; + g_autoptr(FuStructSynapticsCxaudioPatchInfo) st_pat = NULL; guint8 shadow[FU_SYNAPTICS_CXAUDIO_EEPROM_SHADOW_SIZE] = {0x0}; /* copy shadow EEPROM */ diff -Nru fwupd-2.0.8/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-plugin.c fwupd-2.0.20/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-plugin.c --- fwupd-2.0.8/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-cxaudio/fu-synaptics-cxaudio-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,6 +19,7 @@ static void fu_synaptics_cxaudio_plugin_init(FuSynapticsCxaudioPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void @@ -30,6 +31,7 @@ fu_context_add_quirk_key(ctx, "CxaudioPatch1ValidAddr"); fu_context_add_quirk_key(ctx, "CxaudioPatch2ValidAddr"); fu_context_add_quirk_key(ctx, "CxaudioSoftwareReset"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_SYNAPTICS_CXAUDIO_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_SYNAPTICS_CXAUDIO_FIRMWARE); } diff -Nru fwupd-2.0.8/plugins/synaptics-cxaudio/tests/lenovo-03x7609-cxaudio.json fwupd-2.0.20/plugins/synaptics-cxaudio/tests/lenovo-03x7609-cxaudio.json --- fwupd-2.0.8/plugins/synaptics-cxaudio/tests/lenovo-03x7609-cxaudio.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-cxaudio/tests/lenovo-03x7609-cxaudio.json 2026-02-26 11:36:18.000000000 +0000 @@ -4,8 +4,8 @@ "repeat": 2, "steps": [ { - "url": "https://fwupd.org/downloads/2e0bf8aaf9c63ca11cfe3444d032277c21ec0d678e5963123a8b33e5dcd37d99-Lenovo-ThinkPad-USBCGen2Dock-Firmware-49-0E-14.cab", - "emulation-url": "https://fwupd.org/downloads/9b6a1401bbd5ab3304a50a00dfc6d17d853bfbfd63f80c0360774d0e9e46e772-Lenovo-ThinkPad-USBCGen2Dock-Firmware-49-0E-14.zip", + "url": "2e0bf8aaf9c63ca11cfe3444d032277c21ec0d678e5963123a8b33e5dcd37d99-Lenovo-ThinkPad-USBCGen2Dock-Firmware-49-0E-14.cab", + "emulation-url": "9b6a1401bbd5ab3304a50a00dfc6d17d853bfbfd63f80c0360774d0e9e46e772-Lenovo-ThinkPad-USBCGen2Dock-Firmware-49-0E-14.zip", "components": [ { "name": "cxaudio", diff -Nru fwupd-2.0.8/plugins/synaptics-mst/README.md fwupd-2.0.20/plugins/synaptics-mst/README.md --- fwupd-2.0.8/plugins/synaptics-mst/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-mst/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -140,10 +140,3 @@ gpu<--DDC/I2C-->MST MST---SPI ``` - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Apollo Ling: @ApolloLing diff -Nru fwupd-2.0.8/plugins/synaptics-mst/fu-self-test.c fwupd-2.0.20/plugins/synaptics-mst/fu-self-test.c --- fwupd-2.0.8/plugins/synaptics-mst/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-mst/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -45,7 +45,6 @@ while ((basename = g_dir_read_name(dir)) != NULL) { g_autofree gchar *fn = g_build_filename(path, basename, NULL); g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(FuProgress) progress_local = fu_progress_new(G_STRLOC); g_autoptr(FuSynapticsMstDevice) dev = NULL; g_autoptr(GError) error_local = NULL; @@ -68,7 +67,7 @@ fu_device_add_private_flag(FU_DEVICE(dev), FU_SYNAPTICS_MST_DEVICE_FLAG_IS_SOMEWHAT_EMULATED); g_debug("creating drm_dp_aux_dev object backed by %s", fn); - locker = fu_device_locker_new(dev, &error_local); + locker = fu_device_locker_new(FU_DEVICE(dev), &error_local); if (locker == NULL) { g_debug("%s", error_local->message); continue; @@ -190,7 +189,7 @@ g_assert_true(ret); csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); g_assert_no_error(error); - g_assert_cmpstr(csum1, ==, "bfcdf3e6ca6cef45543bfbb57509c92aec9a39fb"); + g_assert_cmpstr(csum1, ==, "67b8fc4661f7585a8cd6c46ef6088293d4399135"); /* ensure we can round-trip */ xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); diff -Nru fwupd-2.0.8/plugins/synaptics-mst/fu-synaptics-mst-common.c fwupd-2.0.20/plugins/synaptics-mst/fu-synaptics-mst-common.c --- fwupd-2.0.8/plugins/synaptics-mst/fu-synaptics-mst-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-mst/fu-synaptics-mst-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,6 +10,11 @@ #include "fu-synaptics-mst-common.h" +/* + * NOTE: DO NOT ALLOW ANY MORE MAGIC CONSTANTS IN THIS FILE + * nocheck:magic-inlines=550 + */ + FuSynapticsMstFamily fu_synaptics_mst_family_from_chip_id(guint16 chip_id) { diff -Nru fwupd-2.0.8/plugins/synaptics-mst/fu-synaptics-mst-device.c fwupd-2.0.20/plugins/synaptics-mst/fu-synaptics-mst-device.c --- fwupd-2.0.8/plugins/synaptics-mst/fu-synaptics-mst-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-mst/fu-synaptics-mst-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,6 +17,11 @@ #include "fu-synaptics-mst-firmware.h" #include "fu-synaptics-mst-struct.h" +/* + * NOTE: DO NOT ALLOW ANY MORE MAGIC CONSTANTS IN THIS FILE + * nocheck:magic-defines=26 + */ + #define FU_SYNAPTICS_MST_ID_CTRL_SIZE 0x1000 #define SYNAPTICS_UPDATE_ENUMERATE_TRIES 3 @@ -106,7 +111,7 @@ fu_device_set_vendor(FU_DEVICE(self), "Synaptics"); fu_device_build_vendor_id_u16(FU_DEVICE(self), "DRM_DP_AUX_DEV", 0x06CB); fu_device_set_summary(FU_DEVICE(self), "Multi-stream transport device"); - fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_VIDEO_DISPLAY); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); fu_device_register_private_flag(FU_DEVICE(self), FU_SYNAPTICS_MST_DEVICE_FLAG_IGNORE_BOARD_ID); @@ -115,6 +120,7 @@ fu_device_register_private_flag(FU_DEVICE(self), FU_SYNAPTICS_MST_DEVICE_FLAG_IS_SOMEWHAT_EMULATED); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); fu_device_add_request_flag(FU_DEVICE(self), FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE); /* this is set from ->incorporate() */ @@ -194,7 +200,7 @@ sizeof(buf), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failed to read command: "); + g_prefix_error_literal(error, "failed to read command: "); return FALSE; } if (buf[0] & 0x80) { @@ -227,7 +233,7 @@ sizeof(buf), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failed to write command: "); + g_prefix_error_literal(error, "failed to write command: "); return FALSE; } @@ -238,12 +244,12 @@ 100, /* ms */ &helper, error)) { - g_prefix_error(error, "remote command failed: "); + g_prefix_error_literal(error, "remote command failed: "); return FALSE; } if (helper.rc != FU_SYNAPTICS_MST_UPDC_RC_SUCCESS) { if (!fu_synaptics_mst_device_rc_to_error(helper.rc, error)) { - g_prefix_error(error, "remote command failed: "); + g_prefix_error_literal(error, "remote command failed: "); return FALSE; } } @@ -284,7 +290,7 @@ fu_chunk_get_data_sz(chk), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failure writing data register: "); + g_prefix_error_literal(error, "failure writing data register: "); return FALSE; } @@ -296,7 +302,7 @@ sizeof(buf2), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failure writing offset register: "); + g_prefix_error_literal(error, "failure writing offset register: "); return FALSE; } @@ -308,7 +314,7 @@ sizeof(buf2), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failure writing length register: "); + g_prefix_error_literal(error, "failure writing length register: "); return FALSE; } @@ -360,7 +366,7 @@ sizeof(buf2), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failed to write offset: "); + g_prefix_error_literal(error, "failed to write offset: "); return FALSE; } @@ -372,7 +378,7 @@ sizeof(buf2), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failed to write length: "); + g_prefix_error_literal(error, "failed to write length: "); return FALSE; } @@ -390,7 +396,7 @@ fu_chunk_get_data_sz(chk), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failed to read data: "); + g_prefix_error_literal(error, "failed to read data: "); return FALSE; } } @@ -437,7 +443,7 @@ return TRUE; if (!fu_synaptics_mst_device_disable_rc(self, error)) { - g_prefix_error(error, "failed to disable-to-enable: "); + g_prefix_error_literal(error, "failed to disable-to-enable: "); return FALSE; } if (!fu_synaptics_mst_device_rc_set_command(self, @@ -446,7 +452,7 @@ (guint8 *)sc, strlen(sc), error)) { - g_prefix_error(error, "failed to enable remote control: "); + g_prefix_error_literal(error, "failed to enable remote control: "); return FALSE; } return TRUE; @@ -473,7 +479,7 @@ cmd_datasz, FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "Failed to write command data: "); + g_prefix_error_literal(error, "failed to write command data: "); return FALSE; } } @@ -486,7 +492,7 @@ sizeof(buf2), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failed to write offset: "); + g_prefix_error_literal(error, "failed to write offset: "); return FALSE; } @@ -498,7 +504,7 @@ sizeof(buf2), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failed to write length: "); + g_prefix_error_literal(error, "failed to write length: "); return FALSE; } } @@ -515,7 +521,7 @@ bufsz, FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failed to read length: "); + g_prefix_error_literal(error, "failed to read length: "); return FALSE; } } @@ -543,7 +549,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to get flash checksum: "); + g_prefix_error_literal(error, "failed to get flash checksum: "); return FALSE; } @@ -559,7 +565,7 @@ { guint8 buf[2] = {0}; - /* Need to add Wp control ? */ + /* need to add wp control? */ fu_memwrite_uint16(buf, rc_cmd + offset, G_LITTLE_ENDIAN); if (!fu_synaptics_mst_device_rc_set_command(self, FU_SYNAPTICS_MST_UPDC_CMD_FLASH_ERASE, @@ -815,7 +821,7 @@ (guint8 *)buf, ((sizeof(buf) / sizeof(buf[0])) * 4), error)) { - g_prefix_error(error, "get active bank failed: "); + g_prefix_error_literal(error, "get active bank failed: "); return FALSE; } if ((buf[0] & BIT(7)) || (buf[0] & BIT(30))) @@ -875,7 +881,7 @@ fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), error)) { - g_prefix_error(error, "firmware write failed: "); + g_prefix_error_literal(error, "firmware write failed: "); return FALSE; } } @@ -895,7 +901,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to get flash checksum: "); + g_prefix_error_literal(error, "failed to get flash checksum: "); return FALSE; } flash_checksum = fu_memread_uint32(buf, G_LITTLE_ENDIAN); @@ -948,7 +954,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to write tag: "); + g_prefix_error_literal(error, "failed to write tag: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), 1); /* ms */ @@ -959,7 +965,7 @@ buf_verify, sizeof(buf_verify), error)) { - g_prefix_error(error, "failed to read tag: "); + g_prefix_error_literal(error, "failed to read tag: "); return FALSE; } if (memcmp(buf, buf_verify, sizeof(buf)) != 0) { @@ -1004,7 +1010,7 @@ &checksum_nul, sizeof(checksum_nul), error)) { - g_prefix_error(error, "failed to clear CRC: "); + g_prefix_error_literal(error, "failed to clear CRC: "); return FALSE; } if (!fu_synaptics_mst_device_rc_get_command( @@ -1014,7 +1020,7 @@ &checksum_tmp, sizeof(checksum_tmp), error)) { - g_prefix_error(error, "failed to read CRC from flash: "); + g_prefix_error_literal(error, "failed to read CRC from flash: "); return FALSE; } if (checksum_tmp != checksum_nul) { @@ -1106,7 +1112,7 @@ &checksum8, sizeof(checksum8), error)) { - g_prefix_error(error, "failed to read tag from flash: "); + g_prefix_error_literal(error, "failed to read tag from flash: "); return FALSE; } helper->checksum = checksum8; @@ -1133,7 +1139,7 @@ (guint8 *)buf, 4, error)) { - g_prefix_error(error, "ESM disable failed: "); + g_prefix_error_literal(error, "ESM disable failed: "); return FALSE; } @@ -1147,7 +1153,7 @@ (guint8 *)buf, ((sizeof(buf) / sizeof(buf[0])) * 4), error)) { - g_prefix_error(error, "quad query failed: "); + g_prefix_error_literal(error, "quad query failed: "); return FALSE; } @@ -1158,7 +1164,7 @@ (guint8 *)buf, 4, error)) { - g_prefix_error(error, "quad disable failed: "); + g_prefix_error_literal(error, "quad disable failed: "); return FALSE; } @@ -1169,7 +1175,7 @@ (guint8 *)buf, 4, error)) { - g_prefix_error(error, "HDCP query failed: "); + g_prefix_error_literal(error, "HDCP query failed: "); return FALSE; } @@ -1180,7 +1186,7 @@ (guint8 *)buf, 4, error)) { - g_prefix_error(error, "HDCP disable failed: "); + g_prefix_error_literal(error, "HDCP disable failed: "); return FALSE; } @@ -1250,7 +1256,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "Failed to get flash checksum: "); + g_prefix_error_literal(error, "failed to get flash checksum: "); return FALSE; } flash_checksum = fu_memread_uint32(buf, G_LITTLE_ENDIAN); @@ -1291,10 +1297,10 @@ fw_size = CARRERA_FIRMWARE_SIZE; break; default: - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Unsupported chip family"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unsupported chip family"); return FALSE; } @@ -1322,7 +1328,7 @@ NULL, 0, error)) { - g_prefix_error(error, "active firmware failed: "); + g_prefix_error_literal(error, "active firmware failed: "); return FALSE; } @@ -1350,10 +1356,10 @@ offset = 0x2020A024; break; default: - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Unsupported chip family"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unsupported chip family"); return FALSE; } /* issue the reboot command, ignore return code (triggers before returning) */ @@ -1372,7 +1378,7 @@ fu_synaptics_mst_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSynapticsMstDevice *self = FU_SYNAPTICS_MST_DEVICE(device); @@ -1384,7 +1390,7 @@ /* check firmware and board ID match */ if (!fu_firmware_parse_stream(firmware, stream, 0x0, flags, error)) return NULL; - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0 && + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) == 0 && !fu_device_has_private_flag(device, FU_SYNAPTICS_MST_DEVICE_FLAG_IGNORE_BOARD_ID)) { guint16 board_id = fu_synaptics_mst_firmware_get_board_id(FU_SYNAPTICS_MST_FIRMWARE(firmware)); @@ -1444,7 +1450,7 @@ /* enable remote control and disable on exit */ if (!fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART)) { locker = - fu_device_locker_new_full(self, + fu_device_locker_new_full(device, (FuDeviceLockerFunc)fu_synaptics_mst_device_enable_rc, (FuDeviceLockerFunc)fu_synaptics_mst_device_restart, error); @@ -1452,7 +1458,7 @@ fu_device_set_remove_delay(FU_DEVICE(self), 10000); /* a long time */ } else { locker = fu_device_locker_new_full( - self, + device, (FuDeviceLockerFunc)fu_synaptics_mst_device_enable_rc, (FuDeviceLockerFunc)fu_synaptics_mst_device_disable_rc, error); @@ -1468,21 +1474,21 @@ fw, progress, error)) { - g_prefix_error(error, "Firmware update failed: "); + g_prefix_error_literal(error, "firmware update failed: "); return FALSE; } break; case FU_SYNAPTICS_MST_FAMILY_PANAMERA: if (!fu_synaptics_mst_device_panamera_prepare_write(self, error)) { - g_prefix_error(error, "Failed to prepare for write: "); + g_prefix_error_literal(error, "failed to prepare for write: "); return FALSE; } if (!fu_synaptics_mst_device_update_esm(self, fw, progress, error)) { - g_prefix_error(error, "ESM update failed: "); + g_prefix_error_literal(error, "ESM update failed: "); return FALSE; } if (!fu_synaptics_mst_device_update_panamera_firmware(self, fw, progress, error)) { - g_prefix_error(error, "Firmware update failed: "); + g_prefix_error_literal(error, "firmware update failed: "); return FALSE; } break; @@ -1490,15 +1496,15 @@ case FU_SYNAPTICS_MST_FAMILY_SPYDER: case FU_SYNAPTICS_MST_FAMILY_CARRERA: if (!fu_synaptics_mst_device_update_firmware(self, fw, progress, error)) { - g_prefix_error(error, "Firmware update failed: "); + g_prefix_error_literal(error, "firmware update failed: "); return FALSE; } break; default: - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Unsupported chip family"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unsupported chip family"); return FALSE; } fu_progress_step_done(progress); @@ -1567,7 +1573,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "RC command failed: "); + g_prefix_error_literal(error, "RC command failed: "); return FALSE; } if (!fu_memread_uint16_safe(buf, @@ -1592,10 +1598,10 @@ offset = (gint)ADDR_MEMORY_CUSTOMER_ID_SPYDER; break; default: - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Unsupported chip family"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unsupported chip family"); return FALSE; } @@ -1606,7 +1612,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "memory query failed: "); + g_prefix_error_literal(error, "memory query failed: "); return FALSE; } if (!fu_memread_uint16_safe(buf, @@ -1625,9 +1631,7 @@ fu_synaptics_mst_device_setup(FuDevice *device, GError **error) { FuSynapticsMstDevice *self = FU_SYNAPTICS_MST_DEVICE(device); - FuDevice *parent; const gchar *name_family; - const gchar *name_parent = NULL; guint8 buf_ver[3] = {0x0}; guint8 buf_cid[2] = {0x0}; guint8 rc_cap = 0x0; @@ -1647,7 +1651,7 @@ sizeof(rc_cap), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failed to read remote control capabilities: "); + g_prefix_error_literal(error, "failed to read remote control capabilities: "); return FALSE; } if ((rc_cap & 0x04) == 0) { @@ -1659,7 +1663,7 @@ } /* enable remote control and disable on exit */ - locker = fu_device_locker_new_full(self, + locker = fu_device_locker_new_full(device, (FuDeviceLockerFunc)fu_synaptics_mst_device_enable_rc, (FuDeviceLockerFunc)fu_synaptics_mst_device_disable_rc, &error_local); @@ -1669,9 +1673,9 @@ FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "downstream endpoint not supported"); - } else { - g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; } + g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } @@ -1682,7 +1686,7 @@ sizeof(buf_ver), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failed to read firmware version: "); + g_prefix_error_literal(error, "failed to read firmware version: "); return FALSE; } @@ -1696,7 +1700,7 @@ sizeof(buf_cid), FU_SYNAPTICS_MST_DEVICE_READ_TIMEOUT, error)) { - g_prefix_error(error, "failed to read chip id: "); + g_prefix_error_literal(error, "failed to read chip id: "); return FALSE; } self->chip_id = fu_memread_uint16(buf_cid, G_BIG_ENDIAN); @@ -1736,14 +1740,7 @@ if (!fu_synaptics_mst_device_ensure_board_id(self, error)) return FALSE; - parent = fu_device_get_parent(FU_DEVICE(self)); - if (parent != NULL) - name_parent = fu_device_get_name(parent); - 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); - } + name = g_strdup_printf("VMM%04x", self->chip_id); fu_device_set_name(FU_DEVICE(self), name); /* set up the device name and kind via quirks */ @@ -1835,7 +1832,7 @@ } static void -fu_synaptics_mst_device_set_progress(FuDevice *self, FuProgress *progress) +fu_synaptics_mst_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/synaptics-mst/fu-synaptics-mst-firmware.c fwupd-2.0.20/plugins/synaptics-mst/fu-synaptics-mst-firmware.c --- fwupd-2.0.8/plugins/synaptics-mst/fu-synaptics-mst-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-mst/fu-synaptics-mst-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,7 @@ #include "fu-synaptics-mst-firmware.h" struct _FuSynapticsMstFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; guint16 board_id; FuSynapticsMstFamily family; }; @@ -56,7 +56,7 @@ { guint16 addrs[] = {ADDR_CONFIG_TESLA, ADDR_CONFIG_CAYENNE, ADDR_CONFIG_CARRERA}; for (guint i = 0; i < G_N_ELEMENTS(addrs); i++) { - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructSynapticsFirmwareConfig) st = NULL; st = fu_struct_synaptics_firmware_config_parse_stream(stream, offset + addrs[i], error); @@ -78,7 +78,7 @@ static gboolean fu_synaptics_mst_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSynapticsMstFirmware *self = FU_SYNAPTICS_MST_FIRMWARE(firmware); @@ -140,10 +140,10 @@ addr = ADDR_CUSTOMER_ID_CARRERA; break; default: - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "unsupported chip family"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "unsupported chip family"); return NULL; } /* assumed header */ diff -Nru fwupd-2.0.8/plugins/synaptics-mst/meson.build fwupd-2.0.20/plugins/synaptics-mst/meson.build --- fwupd-2.0.8/plugins/synaptics-mst/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-mst/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginSynapticsMST"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -56,6 +57,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ cargs, @@ -64,4 +66,3 @@ ) test('synaptics-mst-self-test', e, env: env) endif -endif diff -Nru fwupd-2.0.8/plugins/synaptics-prometheus/README.md fwupd-2.0.20/plugins/synaptics-prometheus/README.md --- fwupd-2.0.8/plugins/synaptics-prometheus/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-prometheus/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -41,10 +41,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.2.9`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Vincent Huang: @synavincent diff -Nru fwupd-2.0.8/plugins/synaptics-prometheus/fu-self-test.c fwupd-2.0.20/plugins/synaptics-prometheus/fu-self-test.c --- fwupd-2.0.8/plugins/synaptics-prometheus/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-prometheus/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -39,7 +39,12 @@ g_assert_cmpint(sz, ==, 294); g_assert_cmpint(buf[0], ==, 0x01); g_assert_cmpint(buf[1], ==, 0x00); - ret = fu_firmware_parse_bytes(firmware, fw, 0x0, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + ret = fu_firmware_parse_bytes(firmware, + fw, + 0x0, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH | + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, + &error); g_assert_no_error(error); g_assert_true(ret); @@ -67,7 +72,7 @@ firmware2 = fu_synaprom_device_prepare_firmware(FU_DEVICE(device), stream, progress, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, &error); g_assert_no_error(error); g_assert_nonnull(firmware2); @@ -104,7 +109,7 @@ g_assert_true(ret); csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); g_assert_no_error(error); - g_assert_cmpstr(csum1, ==, "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"); + g_assert_cmpstr(csum1, ==, "5fa24664fb28e78cbd88970e6026d996fc051550"); /* ensure we can round-trip */ xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); diff -Nru fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom-common.c fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-common.c --- fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -23,47 +23,18 @@ gboolean fu_synaprom_error_from_status(guint16 status, GError **error) { - if (status == FU_SYNAPROM_RESULT_OK) - return TRUE; - switch (status) { - case FU_SYNAPROM_RESULT_GEN_OPERATION_CANCELED: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "cancelled"); - break; - case FU_SYNAPROM_RESULT_GEN_BAD_PARAM: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "bad parameter"); - break; - case FU_SYNAPROM_RESULT_GEN_NULL_POINTER: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "NULL pointer"); - break; - case FU_SYNAPROM_RESULT_GEN_UNEXPECTED_FORMAT: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "unexpected format"); - break; - case FU_SYNAPROM_RESULT_GEN_TIMEOUT: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT, "timed out"); - break; - case FU_SYNAPROM_RESULT_GEN_OBJECT_DOESNT_EXIST: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "object does not exist"); - break; - case FU_SYNAPROM_RESULT_GEN_ERROR: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "generic error"); - break; - case FU_SYNAPROM_RESULT_SENSOR_MALFUNCTIONED: - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "sensor malfunctioned"); - break; - case FU_SYNAPROM_RESULT_SYS_OUT_OF_MEMORY: - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "out of heap memory"); - break; - default: - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "error status: 0x%x", status); - } - return FALSE; + const gchar *msg = fu_synaprom_result_to_string(status); + const FuErrorMapEntry entries[] = { + {FU_SYNAPROM_RESULT_OK, FWUPD_ERROR_LAST, NULL}, + {FU_SYNAPROM_RESULT_GEN_OPERATION_CANCELED, FWUPD_ERROR_INTERNAL, msg}, + {FU_SYNAPROM_RESULT_GEN_BAD_PARAM, FWUPD_ERROR_INVALID_DATA, msg}, + {FU_SYNAPROM_RESULT_GEN_NULL_POINTER, FWUPD_ERROR_INVALID_DATA, msg}, + {FU_SYNAPROM_RESULT_GEN_UNEXPECTED_FORMAT, FWUPD_ERROR_INVALID_DATA, msg}, + {FU_SYNAPROM_RESULT_GEN_TIMEOUT, FWUPD_ERROR_TIMED_OUT, msg}, + {FU_SYNAPROM_RESULT_GEN_OBJECT_DOESNT_EXIST, FWUPD_ERROR_NOT_FOUND, msg}, + {FU_SYNAPROM_RESULT_GEN_ERROR, FWUPD_ERROR_INTERNAL, msg}, + {FU_SYNAPROM_RESULT_SENSOR_MALFUNCTIONED, FWUPD_ERROR_INTERNAL, msg}, + {FU_SYNAPROM_RESULT_SYS_OUT_OF_MEMORY, FWUPD_ERROR_INTERNAL, msg}, + }; + return fu_error_map_entry_to_gerror(status, entries, G_N_ELEMENTS(entries), error); } diff -Nru fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom-config.c fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-config.c --- fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom-config.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-config.c 2026-02-26 11:36:18.000000000 +0000 @@ -31,28 +31,31 @@ static gboolean fu_synaprom_config_setup(FuDevice *device, GError **error) { - FuDevice *parent = fu_device_get_parent(device); + FuDevice *parent; FuSynapromConfig *self = FU_SYNAPROM_CONFIG(device); g_autofree gchar *configid1_str = NULL; g_autofree gchar *configid2_str = NULL; g_autofree gchar *version = NULL; g_autoptr(GByteArray) reply = NULL; - g_autoptr(GByteArray) st_request = fu_struct_synaprom_request_new(); - g_autoptr(GByteArray) st_cfg = NULL; - g_autoptr(GByteArray) st_hdr = NULL; - g_autoptr(GByteArray) st_cmd = fu_struct_synaprom_cmd_iota_find_new(); + g_autoptr(FuStructSynapromRequest) st_request = fu_struct_synaprom_request_new(); + g_autoptr(FuStructSynapromIotaConfigVersion) st_cfg = NULL; + g_autoptr(FuStructSynapromReplyIotaFindHdr) st_hdr = NULL; + g_autoptr(FuStructSynapromCmdIotaFind) st_cmd = fu_struct_synaprom_cmd_iota_find_new(); g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); /* get IOTA */ fu_struct_synaprom_cmd_iota_find_set_itype(st_cmd, FU_SYNAPROM_IOTA_ITYPE_CONFIG_VERSION); fu_struct_synaprom_cmd_iota_find_set_flags(st_cmd, FU_SYNAPROM_CMD_IOTA_FIND_FLAGS_READMAX); fu_struct_synaprom_request_set_cmd(st_request, FU_SYNAPROM_CMD_IOTA_FIND); - g_byte_array_append(st_request, st_cmd->data, st_cmd->len); + fu_byte_array_append_array(st_request->buf, st_cmd->buf); reply = fu_synaprom_reply_new(FU_STRUCT_SYNAPROM_REPLY_IOTA_FIND_HDR_SIZE + FU_SYNAPROM_MAX_IOTA_READ_SIZE); + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; if (!fu_synaprom_device_cmd_send(FU_SYNAPROM_DEVICE(parent), - st_request, + st_request->buf, reply, progress, 5000, @@ -81,7 +84,7 @@ } st_cfg = fu_struct_synaprom_iota_config_version_parse(reply->data, reply->len, - st_hdr->len, + st_hdr->buf->len, error); if (st_cfg == NULL) return FALSE; @@ -110,16 +113,22 @@ fu_synaprom_config_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSynapromConfig *self = FU_SYNAPROM_CONFIG(device); - FuDevice *parent = fu_device_get_parent(device); - g_autoptr(GByteArray) st_hdr = NULL; + FuDevice *parent = fu_device_get_parent(device, error); + g_autoptr(FuStructSynapromCfgHdr) st_hdr = NULL; g_autoptr(GInputStream) stream_hdr = NULL; g_autoptr(FuFirmware) firmware = fu_synaprom_firmware_new(); g_autoptr(FuFirmware) img_hdr = NULL; + /* sanity check */ + if (parent == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no parent"); + return NULL; + } + if (fu_synaprom_device_get_product_type(FU_SYNAPROM_DEVICE(parent)) == FU_SYNAPROM_PRODUCT_TYPE_TRITON) { if (!fu_synaprom_firmware_set_signature_size(FU_SYNAPROM_FIRMWARE(firmware), @@ -140,11 +149,11 @@ return NULL; st_hdr = fu_struct_synaprom_cfg_hdr_parse_stream(stream_hdr, 0x0, error); if (st_hdr == NULL) { - g_prefix_error(error, "CFG metadata is invalid: "); + g_prefix_error_literal(error, "CFG metadata is invalid: "); return NULL; } if (fu_struct_synaprom_cfg_hdr_get_product(st_hdr) != FU_SYNAPROM_PRODUCT_PROMETHEUS) { - if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) { + if (flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) { g_warning("CFG metadata not compatible, " "got 0x%02x expected 0x%02x", fu_struct_synaprom_cfg_hdr_get_product(st_hdr), @@ -162,7 +171,7 @@ } if (fu_struct_synaprom_cfg_hdr_get_id1(st_hdr) != self->configid1 || fu_struct_synaprom_cfg_hdr_get_id2(st_hdr) != self->configid2) { - if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) { + if (flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) { g_warning("CFG version not compatible, " "got %u:%u expected %u:%u", fu_struct_synaprom_cfg_hdr_get_id1(st_hdr), @@ -194,7 +203,7 @@ FwupdInstallFlags flags, GError **error) { - FuDevice *parent = fu_device_get_parent(device); + FuDevice *parent; g_autoptr(GBytes) fw = NULL; /* get default image */ @@ -203,6 +212,9 @@ return FALSE; /* I assume the CFG/MFW difference is detected in the device...*/ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; return fu_synaprom_device_write_fw(FU_SYNAPROM_DEVICE(parent), fw, progress, error); } @@ -214,18 +226,19 @@ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PLAIN); fu_device_set_logical_id(FU_DEVICE(self), "cfg"); - fu_device_set_name(FU_DEVICE(self), "Prometheus IOTA Config"); + fu_device_set_name(FU_DEVICE(self), "IOTA Config"); fu_device_set_summary(FU_DEVICE(self), "Fingerprint reader config"); - fu_device_add_icon(FU_DEVICE(self), "auth-fingerprint"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_AUTH_FINGERPRINT); } static void fu_synaprom_config_constructed(GObject *obj) { FuSynapromConfig *self = FU_SYNAPROM_CONFIG(obj); - FuDevice *parent = fu_device_get_parent(FU_DEVICE(self)); + FuDevice *parent = fu_device_get_parent(FU_DEVICE(self), NULL); /* append the firmware kind to the generated GUID */ if (parent != NULL) { @@ -242,14 +255,18 @@ static gboolean fu_synaprom_config_attach(FuDevice *device, FuProgress *progress, GError **error) { - FuDevice *parent = fu_device_get_parent(device); + FuDevice *parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; return fu_device_attach_full(parent, progress, error); } static gboolean fu_synaprom_config_detach(FuDevice *device, FuProgress *progress, GError **error) { - FuDevice *parent = fu_device_get_parent(device); + FuDevice *parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; return fu_device_detach_full(parent, progress, error); } diff -Nru fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom-device.c fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-device.c --- fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -67,7 +67,7 @@ timeout_ms, NULL, error)) { - g_prefix_error(error, "failed to request: "); + g_prefix_error_literal(error, "failed to request: "); return FALSE; } if (actual_len < request->len) { @@ -89,7 +89,7 @@ timeout_ms, NULL, error)) { - g_prefix_error(error, "failed to reply: "); + g_prefix_error_literal(error, "failed to reply: "); return FALSE; } fu_dump_full(G_LOG_DOMAIN, @@ -173,8 +173,8 @@ /* get version */ fu_struct_synaprom_request_set_cmd(st_request, FU_SYNAPROM_CMD_GET_VERSION); reply = fu_synaprom_reply_new(FU_STRUCT_SYNAPROM_REPLY_GET_VERSION_SIZE); - if (!fu_synaprom_device_cmd_send(self, st_request, reply, progress, 250, error)) { - g_prefix_error(error, "failed to get version: "); + if (!fu_synaprom_device_cmd_send(self, st_request->buf, reply, progress, 250, error)) { + g_prefix_error_literal(error, "failed to get version: "); return FALSE; } st_reply = fu_struct_synaprom_reply_get_version_parse(reply->data, reply->len, 0x0, error); @@ -232,7 +232,7 @@ fu_synaprom_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint32 product_id; @@ -251,7 +251,7 @@ product_id = fu_synaprom_firmware_get_product_id(FU_SYNAPROM_FIRMWARE(firmware)); if (product_id != FU_SYNAPROM_PRODUCT_PROMETHEUS && product_id != FU_SYNAPROM_PRODUCT_TRITON) { - if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) { + if (flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) { g_warning("MFW metadata not compatible, " "got 0x%02x expected 0x%02x or 0x%02x", product_id, @@ -290,10 +290,10 @@ /* patch */ fu_struct_synaprom_request_set_cmd(st_request, FU_SYNAPROM_CMD_BOOTLDR_PATCH); - g_byte_array_append(st_request, chunk->data, chunk->len); + g_byte_array_append(st_request->buf, chunk->data, chunk->len); reply = fu_synaprom_reply_new(FU_STRUCT_SYNAPROM_REPLY_GENERIC_SIZE); if (!fu_synaprom_device_cmd_send(self, - st_request, + st_request->buf, reply, fu_progress_get_child(progress), 20000, @@ -419,7 +419,7 @@ return FALSE; } if (!fu_usb_device_reset(FU_USB_DEVICE(device), error)) { - g_prefix_error(error, "failed to force-reset device: "); + g_prefix_error_literal(error, "failed to force-reset device: "); return FALSE; } fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); @@ -463,7 +463,7 @@ } fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_RESTART); if (!fu_usb_device_reset(FU_USB_DEVICE(device), error)) { - g_prefix_error(error, "failed to force-reset device: "); + g_prefix_error_literal(error, "failed to force-reset device: "); return FALSE; } fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); @@ -471,7 +471,7 @@ } static void -fu_synaprom_device_set_progress(FuDevice *self, FuProgress *progress) +fu_synaprom_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -494,7 +494,7 @@ fu_device_set_name(FU_DEVICE(self), "Prometheus"); fu_device_set_summary(FU_DEVICE(self), "Fingerprint reader"); fu_device_set_vendor(FU_DEVICE(self), "Synaptics"); - fu_device_add_icon(FU_DEVICE(self), "auth-fingerprint"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_AUTH_FINGERPRINT); fu_usb_device_add_interface(FU_USB_DEVICE(self), 0x0); } diff -Nru fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom-device.h fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-device.h --- fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -37,7 +37,7 @@ fu_synaprom_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error); guint32 diff -Nru fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom-firmware.c fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-firmware.c --- fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -50,7 +50,7 @@ static gboolean fu_synaprom_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware); @@ -74,7 +74,7 @@ guint32 tag; g_autoptr(FuFirmware) img = fu_firmware_new(); g_autoptr(FuFirmware) img_old = NULL; - g_autoptr(GByteArray) st_hdr = NULL; + g_autoptr(FuStructSynapromHdr) st_hdr = NULL; g_autoptr(GInputStream) partial_stream = NULL; /* verify item header */ @@ -110,7 +110,7 @@ tag); return FALSE; } - offset += st_hdr->len; + offset += st_hdr->buf->len; partial_stream = fu_partial_input_stream_new(stream, offset, hdrsz, error); if (partial_stream == NULL) return FALSE; @@ -122,13 +122,13 @@ hdrsz); fu_firmware_set_idx(img, tag); fu_firmware_set_id(img, fu_synaprom_firmware_tag_to_string(tag)); - if (!fu_firmware_add_image_full(firmware, img, error)) + if (!fu_firmware_add_image(firmware, img, error)) return FALSE; /* metadata */ if (tag == FU_SYNAPROM_FIRMWARE_TAG_MFW_UPDATE_HEADER) { g_autofree gchar *version = NULL; - g_autoptr(GByteArray) st_mfw = NULL; + g_autoptr(FuStructSynapromMfwHdr) st_mfw = NULL; st_mfw = fu_struct_synaprom_mfw_hdr_parse_stream(stream, offset, error); if (st_mfw == NULL) return FALSE; @@ -150,16 +150,16 @@ { FuSynapromFirmware *self = FU_SYNAPROM_FIRMWARE(firmware); g_autoptr(GByteArray) buf = g_byte_array_new(); - g_autoptr(GByteArray) st_hdr = fu_struct_synaprom_hdr_new(); - g_autoptr(GByteArray) st_mfw = fu_struct_synaprom_mfw_hdr_new(); + g_autoptr(FuStructSynapromHdr) st_hdr = fu_struct_synaprom_hdr_new(); + g_autoptr(FuStructSynapromMfwHdr) st_mfw = fu_struct_synaprom_mfw_hdr_new(); g_autoptr(GBytes) payload = NULL; /* add header */ fu_struct_synaprom_hdr_set_tag(st_hdr, FU_SYNAPROM_FIRMWARE_TAG_MFW_UPDATE_HEADER); - fu_struct_synaprom_hdr_set_bufsz(st_hdr, st_mfw->len); - g_byte_array_append(buf, st_hdr->data, st_hdr->len); + fu_struct_synaprom_hdr_set_bufsz(st_hdr, st_mfw->buf->len); + g_byte_array_append(buf, st_hdr->buf->data, st_hdr->buf->len); fu_struct_synaprom_mfw_hdr_set_product(st_mfw, self->product_id); - g_byte_array_append(buf, st_mfw->data, st_mfw->len); + fu_byte_array_append_array(buf, st_mfw->buf); /* add payload */ payload = fu_firmware_get_bytes_with_patches(firmware, error); @@ -167,7 +167,7 @@ return NULL; fu_struct_synaprom_hdr_set_tag(st_hdr, FU_SYNAPROM_FIRMWARE_TAG_MFW_UPDATE_PAYLOAD); fu_struct_synaprom_hdr_set_bufsz(st_hdr, g_bytes_get_size(payload)); - g_byte_array_append(buf, st_hdr->data, st_hdr->len); + g_byte_array_append(buf, st_hdr->buf->data, st_hdr->buf->len); fu_byte_array_append_bytes(buf, payload); /* add signature */ diff -Nru fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom-plugin.c fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-plugin.c --- fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -33,6 +33,7 @@ fu_synaprom_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_set_device_gtype_default(plugin, FU_TYPE_SYNAPROM_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_SYNAPROM_CONFIG); /* for coverage */ fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_SYNAPROM_FIRMWARE); diff -Nru fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom.rs fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom.rs --- fwupd-2.0.8/plugins/synaptics-prometheus/fu-synaprom.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-prometheus/fu-synaprom.rs 2026-02-26 11:36:18.000000000 +0000 @@ -55,6 +55,7 @@ device_type: u8, } +#[derive(ToString)] enum FuSynapromResult { Ok = 0, GenOperationCanceled = 103, diff -Nru fwupd-2.0.8/plugins/synaptics-prometheus/meson.build fwupd-2.0.20/plugins/synaptics-prometheus/meson.build --- fwupd-2.0.8/plugins/synaptics-prometheus/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-prometheus/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -39,6 +39,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ cargs, diff -Nru fwupd-2.0.8/plugins/synaptics-prometheus/synaptics-prometheus.quirk fwupd-2.0.20/plugins/synaptics-prometheus/synaptics-prometheus.quirk --- fwupd-2.0.8/plugins/synaptics-prometheus/synaptics-prometheus.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-prometheus/synaptics-prometheus.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,10 @@ Plugin = synaptics_prometheus InstallDuration = 2 +[USB\VID_06CB&PID_00C6] +Plugin = synaptics_prometheus +InstallDuration = 2 + [USB\VID_06CB&PID_00F9] Plugin = synaptics_prometheus InstallDuration = 2 diff -Nru fwupd-2.0.8/plugins/synaptics-prometheus/tests/synaptics-prometheus.json fwupd-2.0.20/plugins/synaptics-prometheus/tests/synaptics-prometheus.json --- fwupd-2.0.8/plugins/synaptics-prometheus/tests/synaptics-prometheus.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-prometheus/tests/synaptics-prometheus.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/7c260a13ea6df444f7a1fa8fa2bf431876a8c7203b6aeac371564aad30756ff1-Synaptics-Prometheus-10.01.3121519.cab", - "emulation-url": "https://fwupd.org/downloads/73877ed7c8c15dac6adf04db32bd5e9f1e702229cfdb266fe98855ee53929cd1-Synaptics-Prometheus-10.01.3121519.zip", + "url": "7c260a13ea6df444f7a1fa8fa2bf431876a8c7203b6aeac371564aad30756ff1-Synaptics-Prometheus-10.01.3121519.cab", + "emulation-url": "73877ed7c8c15dac6adf04db32bd5e9f1e702229cfdb266fe98855ee53929cd1-Synaptics-Prometheus-10.01.3121519.zip", "components": [ { "version": "10.01.3121519", diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/README.md fwupd-2.0.20/plugins/synaptics-rmi/README.md --- fwupd-2.0.8/plugins/synaptics-rmi/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -42,11 +42,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.3.3`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* David Chiu: @blueue -* Vincent Huang: @vhuag diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-common.c fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-common.c --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -99,21 +99,6 @@ } gboolean -fu_synaptics_rmi_device_writeln(const gchar *fn, const gchar *buf, GError **error) -{ - g_autoptr(FuIOChannel) io = NULL; - io = fu_io_channel_new_file(fn, FU_IO_CHANNEL_OPEN_FLAG_WRITE, error); - if (io == NULL) - return FALSE; - return fu_io_channel_write_raw(io, - (const guint8 *)buf, - strlen(buf), - 1000, - FU_IO_CHANNEL_FLAG_NONE, - error); -} - -gboolean fu_synaptics_rmi_verify_sha256_signature(GBytes *payload, GBytes *pubkey, GBytes *signature, diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-common.h fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-common.h --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -33,8 +33,6 @@ guint interrupt_count, GError **error); gboolean -fu_synaptics_rmi_device_writeln(const gchar *fn, const gchar *buf, GError **error); -gboolean fu_synaptics_rmi_verify_sha256_signature(GBytes *payload, GBytes *pubkey, GBytes *signature, diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-device.c fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-device.c --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -42,6 +42,7 @@ #define RMI_F01_CMD_DEVICE_RESET 1 #define RMI_F01_DEFAULT_RESET_DELAY_MS 100 +#define RMI_F01_BLV10_RESET_DELAY_MS 5000 typedef struct { FuSynapticsRmiFlash flash; @@ -52,6 +53,7 @@ guint16 sig_size; /* 0x0 for non-secure update */ guint8 max_page; gboolean in_iep_mode; + guint16 previous_sbl_version; } FuSynapticsRmiDevicePrivate; G_DEFINE_TYPE_WITH_PRIVATE(FuSynapticsRmiDevice, fu_synaptics_rmi_device, FU_TYPE_UDEV_DEVICE) @@ -79,6 +81,9 @@ fwupd_codec_string_append_hex(str, idt, "FlashConfigLength", flash->config_length); fwupd_codec_string_append_hex(str, idt, "PayloadLength", flash->payload_length); fwupd_codec_string_append_hex(str, idt, "BuildID", flash->build_id); + fwupd_codec_string_append_hex(str, idt, "HasSBL", flash->has_sbl); + fwupd_codec_string_append_hex(str, idt, "SBLVersion", flash->sbl_version); + fwupd_codec_string_append_hex(str, idt, "HasSecurity", flash->has_security); } static void @@ -90,6 +95,7 @@ fwupd_codec_string_append_hex(str, idt, "InIepMode", priv->in_iep_mode); fwupd_codec_string_append_hex(str, idt, "MaxPage", priv->max_page); fwupd_codec_string_append_hex(str, idt, "SigSize", priv->sig_size); + fwupd_codec_string_append_hex(str, idt, "PreviousSBLVersion", priv->previous_sbl_version); if (priv->f34 != NULL) { fwupd_codec_string_append_hex(str, idt, "BlVer", priv->f34->function_version + 0x5); } @@ -191,7 +197,9 @@ { FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); g_autoptr(GByteArray) req = g_byte_array_new(); - + priv->f01 = fu_synaptics_rmi_device_get_function(self, 0x01, error); + if (priv->f01 == NULL) + return FALSE; fu_byte_array_append_uint8(req, RMI_F01_CMD_DEVICE_RESET); if (!fu_synaptics_rmi_device_write(self, priv->f01->command_base, @@ -199,11 +207,16 @@ FU_SYNAPTICS_RMI_DEVICE_FLAG_ALLOW_FAILURE, error)) return FALSE; - fu_device_sleep(FU_DEVICE(self), RMI_F01_DEFAULT_RESET_DELAY_MS); + if (priv->flash.bootloader_id[1] >= 10) { + fu_device_sleep(FU_DEVICE(self), RMI_F01_BLV10_RESET_DELAY_MS); + g_info("wait %d ms for BLV10+ reset", RMI_F01_BLV10_RESET_DELAY_MS); + } else { + fu_device_sleep(FU_DEVICE(self), RMI_F01_DEFAULT_RESET_DELAY_MS); + } return TRUE; } -static gboolean +gboolean fu_synaptics_rmi_device_scan_pdt(FuSynapticsRmiDevice *self, GError **error) { FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); @@ -387,7 +400,7 @@ f01_basic = fu_synaptics_rmi_device_read(self, addr, RMI_DEVICE_F01_BASIC_QUERY_LEN, error); if (f01_basic == NULL) { - g_prefix_error(error, "failed to read the basic query: "); + g_prefix_error_literal(error, "failed to read the basic query: "); return FALSE; } has_lts = (f01_basic->data[1] & RMI_DEVICE_F01_QRY1_HAS_LTS) > 0; @@ -398,11 +411,11 @@ addr += 11; f01_product_id = fu_synaptics_rmi_device_read(self, addr, RMI_PRODUCT_ID_LENGTH, error); if (f01_product_id == NULL) { - g_prefix_error(error, "failed to read the product id: "); + g_prefix_error_literal(error, "failed to read the product id: "); return FALSE; } if (!fu_synaptics_rmi_device_query_product_sub_id(self, &product_sub_id, error)) { - g_prefix_error(error, "failed to query product sub id: "); + g_prefix_error_literal(error, "failed to query product sub id: "); return FALSE; } if (product_sub_id == 0) { @@ -437,7 +450,7 @@ g_autoptr(GByteArray) f01_tmp = NULL; f01_tmp = fu_synaptics_rmi_device_read(self, addr++, 1, error); if (f01_tmp == NULL) { - g_prefix_error(error, "failed to read query 42: "); + g_prefix_error_literal(error, "failed to read query 42: "); return FALSE; } has_dds4_queries = (f01_tmp->data[0] & RMI_DEVICE_F01_QRY42_DS4_QUERIES) > 0; @@ -446,14 +459,14 @@ g_autoptr(GByteArray) f01_tmp = NULL; f01_tmp = fu_synaptics_rmi_device_read(self, addr++, 1, error); if (f01_tmp == NULL) { - g_prefix_error(error, "failed to read DS4 query length: "); + g_prefix_error_literal(error, "failed to read DS4 query length: "); return FALSE; } ds4_query_length = f01_tmp->data[0]; } f01_ds4 = fu_synaptics_rmi_device_read(self, addr, 0x1, error); if (f01_ds4 == NULL) { - g_prefix_error(error, "failed to read F01 Query43: "); + g_prefix_error_literal(error, "failed to read F01 Query43: "); return FALSE; } has_package_id_query = (f01_ds4->data[0] & RMI_DEVICE_F01_QRY43_01_PACKAGE_ID) > 0; @@ -466,7 +479,7 @@ guint8 buf32[4] = {0x0}; f01_tmp = fu_synaptics_rmi_device_read(self, prod_info_addr, 0x3, error); if (f01_tmp == NULL) { - g_prefix_error(error, "failed to read build ID bytes: "); + g_prefix_error_literal(error, "failed to read build ID bytes: "); return FALSE; } if (!fu_memcpy_safe(buf32, @@ -489,7 +502,7 @@ /* read build ID, typically only for PS/2 */ if (!fu_synaptics_rmi_device_query_build_id(self, &priv->flash.build_id, error)) { - g_prefix_error(error, "failed to query build id: "); + g_prefix_error_literal(error, "failed to query build id: "); return FALSE; } @@ -499,17 +512,17 @@ return FALSE; if (priv->f34->function_version == 0x0) { if (!fu_synaptics_rmi_v5_device_setup(self, error)) { - g_prefix_error(error, "failed to do v5 setup: "); + g_prefix_error_literal(error, "failed to do v5 setup: "); return FALSE; } } else if (priv->f34->function_version == 0x1) { if (!fu_synaptics_rmi_v6_device_setup(self, error)) { - g_prefix_error(error, "failed to do v6 setup: "); + g_prefix_error_literal(error, "failed to do v6 setup: "); return FALSE; } } else if (priv->f34->function_version == 0x2) { if (!fu_synaptics_rmi_v7_device_setup(self, error)) { - g_prefix_error(error, "failed to do v7 setup: "); + g_prefix_error_literal(error, "failed to do v7 setup: "); return FALSE; } } else { @@ -521,7 +534,7 @@ return FALSE; } if (!fu_synaptics_rmi_device_query_status(self, error)) { - g_prefix_error(error, "failed to read bootloader status: "); + g_prefix_error_literal(error, "failed to read bootloader status: "); return FALSE; } @@ -546,7 +559,7 @@ fu_synaptics_rmi_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); @@ -558,6 +571,13 @@ if (!fu_firmware_parse_stream(firmware, stream, 0x0, flags, error)) return NULL; + if (priv->flash.bootloader_id[1] >= 10) { + g_info("bootloader >= 10 (%d.%d), skip checking firmware size.", + priv->flash.bootloader_id[1], + priv->flash.bootloader_id[0]); + + return g_steal_pointer(&firmware); + } /* check sizes */ bytes_bin = fu_firmware_get_image_by_id_bytes(firmware, "ui", error); @@ -600,7 +620,7 @@ /* get if the last flash read completed successfully */ f34_db = fu_synaptics_rmi_device_read(self, priv->f34->data_base, 0x1, error); if (f34_db == NULL) { - g_prefix_error(error, "failed to read f34_db: "); + g_prefix_error_literal(error, "failed to read f34_db: "); return FALSE; } if ((f34_db->data[0] & 0x1f) != 0x0) { @@ -659,7 +679,7 @@ if (rmi_class->enter_iep_mode != NULL) { g_debug("enabling RMI iep_mode"); if (!rmi_class->enter_iep_mode(self, error)) { - g_prefix_error(error, "failed to enable RMI iep_mode: "); + g_prefix_error_literal(error, "failed to enable RMI iep_mode: "); return FALSE; } } @@ -670,7 +690,7 @@ gboolean fu_synaptics_rmi_device_wait_for_idle(FuSynapticsRmiDevice *self, guint timeout_ms, - RmiDeviceWaitForIdleFlags flags, + FuSynapticsRmiDeviceWaitForIdleFlags flags, GError **error) { FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); @@ -680,6 +700,11 @@ g_autoptr(GByteArray) res = NULL; g_autoptr(GError) error_local = NULL; + /* refresh */ + priv->f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (priv->f34 == NULL) + return FALSE; + /* try to get report without requesting */ if (timeout_ms > 0 && !fu_synaptics_rmi_device_wait_for_attr(self, priv->f34->interrupt_mask, @@ -691,11 +716,14 @@ "failed to wait for attr: "); return FALSE; } - } else if ((flags & RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34) == 0) { + } else if ((flags & FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34) == 0) { /* device reported idle via an event */ return TRUE; } + if (flags & FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_DETACH_DEVICE) + fu_device_sleep(FU_DEVICE(self), RMI_F34_ENABLE_SBL_WAIT_MS); + /* if for some reason we are not getting attention reports for HID devices * then we can still continue after the timeout and read F34 status * but if we have to wait for the timeout to elapse every time then this @@ -775,7 +803,7 @@ bootloader_id_req, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write bootloader_id: "); + g_prefix_error_literal(error, "failed to write bootloader_id: "); return FALSE; } @@ -796,7 +824,7 @@ interrupt_disable_req, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to disable interrupts: "); + g_prefix_error_literal(error, "failed to disable interrupts: "); return FALSE; } return TRUE; @@ -812,14 +840,14 @@ FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); if (priv->f34->function_version == 0x0 || priv->f34->function_version == 0x1) { - return fu_synaptics_rmi_v5_device_write_firmware(device, + return fu_synaptics_rmi_v5_device_write_firmware(self, firmware, progress, flags, error); } if (priv->f34->function_version == 0x2) { - return fu_synaptics_rmi_v7_device_write_firmware(device, + return fu_synaptics_rmi_v7_device_write_firmware(self, firmware, progress, flags, @@ -855,6 +883,18 @@ } static void +fu_synaptics_rmi_device_replace(FuDevice *device, FuDevice *donor) +{ + FuSynapticsRmiDevice *old_self = FU_SYNAPTICS_RMI_DEVICE(donor); + FuSynapticsRmiDevicePrivate *old_priv = GET_PRIVATE(old_self); + + FuSynapticsRmiDevice *new_self = FU_SYNAPTICS_RMI_DEVICE(device); + FuSynapticsRmiDevicePrivate *new_priv = GET_PRIVATE(new_self); + + new_priv->previous_sbl_version = old_priv->previous_sbl_version; +} + +static void fu_synaptics_rmi_device_class_init(FuSynapticsRmiDeviceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS(klass); @@ -864,4 +904,19 @@ device_class->prepare_firmware = fu_synaptics_rmi_device_prepare_firmware; device_class->setup = fu_synaptics_rmi_device_setup; device_class->write_firmware = fu_synaptics_rmi_device_write_firmware; + device_class->replace = fu_synaptics_rmi_device_replace; +} + +void +fu_synaptics_rmi_device_set_previous_sbl_version(FuSynapticsRmiDevice *self, guint16 sbl_version) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + priv->previous_sbl_version = sbl_version; +} + +guint16 +fu_synaptics_rmi_device_get_previous_sbl_version(FuSynapticsRmiDevice *self) +{ + FuSynapticsRmiDevicePrivate *priv = GET_PRIVATE(self); + return priv->previous_sbl_version; } diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-device.h fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-device.h --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -21,7 +21,7 @@ FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE = 0, FU_SYNAPTICS_RMI_DEVICE_FLAG_ALLOW_FAILURE = 1 << 0, FU_SYNAPTICS_RMI_DEVICE_FLAG_FORCE = 1 << 1, -} FuSynapticsRmiDeviceFlags; +} G_GNUC_FLAG_ENUM FuSynapticsRmiDeviceFlags; struct _FuSynapticsRmiDeviceClass { FuUdevDeviceClass parent_class; @@ -61,6 +61,9 @@ guint8 bootloader_id[2]; guint8 status_addr; gboolean has_pubkey; + gboolean has_sbl; + guint16 sbl_version; + gboolean has_security; } FuSynapticsRmiFlash; #define RMI_F34_HAS_NEW_REG_MAP (1 << 0) @@ -71,6 +74,7 @@ #define RMI_F34_ENABLE_WAIT_MS 300 /* ms */ #define RMI_F34_IDLE_WAIT_MS 500 /* ms */ +#define RMI_F34_ENABLE_SBL_WAIT_MS 3000 /* ms */ #define RMI_DEVICE_PAGE_SELECT_REGISTER 0xff #define RMI_DEVICE_BUS_SELECT_REGISTER 0xfe @@ -78,9 +82,10 @@ #define RMI_KEY_SIZE_2K 0x100 typedef enum { - RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE = 0, - RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34 = (1 << 0), -} RmiDeviceWaitForIdleFlags; + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE = 0, + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34 = (1 << 0), + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_DETACH_DEVICE = (1 << 1), +} FuSynapticsRmiDeviceWaitForIdleFlags; void fu_synaptics_rmi_device_set_iepmode(FuSynapticsRmiDevice *self, gboolean iepmode); @@ -111,7 +116,7 @@ gboolean fu_synaptics_rmi_device_wait_for_idle(FuSynapticsRmiDevice *self, guint timeout_ms, - RmiDeviceWaitForIdleFlags flags, + FuSynapticsRmiDeviceWaitForIdleFlags flags, GError **error); gboolean fu_synaptics_rmi_device_disable_sleep(FuSynapticsRmiDevice *self, GError **error); @@ -135,3 +140,9 @@ GError **error); gboolean fu_synaptics_rmi_device_write_bus_select(FuSynapticsRmiDevice *self, guint8 bus, GError **error); +gboolean +fu_synaptics_rmi_device_scan_pdt(FuSynapticsRmiDevice *self, GError **error); +void +fu_synaptics_rmi_device_set_previous_sbl_version(FuSynapticsRmiDevice *self, guint16 sbl_version); +guint16 +fu_synaptics_rmi_device_get_previous_sbl_version(FuSynapticsRmiDevice *self); diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,15 +15,14 @@ #include "fu-synaptics-rmi-struct.h" typedef enum { - RMI_FIRMWARE_KIND_UNKNOWN = 0x00, - RMI_FIRMWARE_KIND_0X = 0x01, - RMI_FIRMWARE_KIND_10 = 0x10, - RMI_FIRMWARE_KIND_LAST, -} RmiFirmwareKind; + FU_SYNAPTICS_RMI_FIRMWARE_KIND_UNKNOWN = 0x00, + FU_SYNAPTICS_RMI_FIRMWARE_KIND_0X = 0x01, + FU_SYNAPTICS_RMI_FIRMWARE_KIND_10 = 0x10, +} FuSynapticsRmiFirmwareKind; struct _FuSynapticsRmiFirmware { FuFirmware parent_instance; - RmiFirmwareKind kind; + FuSynapticsRmiFirmwareKind kind; guint32 checksum; guint8 io; guint8 bootloader_version; @@ -42,11 +41,21 @@ #define RMI_IMG_MAX_CONTAINERS 1024 static gboolean -fu_synaptics_rmi_firmware_add_image(FuFirmware *firmware, +fu_synaptics_rmi_firmware_parse_sbl_container_v10(FuSynapticsRmiFirmware *self, + GInputStream *stream, + const guint8 *buf, + gsize bufsz, + guint32 start_offset, + FuFirmwareParseFlags flags, + GError **error); + +static gboolean +fu_synaptics_rmi_firmware_add_image(FuSynapticsRmiFirmware *self, const gchar *id, GInputStream *stream, gsize offset, gsize bufsz, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) img = fu_firmware_new(); @@ -54,22 +63,23 @@ partial_stream = fu_partial_input_stream_new(stream, offset, bufsz, error); if (partial_stream == NULL) return FALSE; - if (!fu_firmware_parse_stream(img, partial_stream, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error)) return FALSE; fu_firmware_set_id(img, id); - return fu_firmware_add_image_full(firmware, img, error); + return fu_firmware_add_image(FU_FIRMWARE(self), img, error); } static gboolean -fu_synaptics_rmi_firmware_add_image_v10(FuFirmware *firmware, +fu_synaptics_rmi_firmware_add_image_v10(FuSynapticsRmiFirmware *self, const gchar *id, GInputStream *stream, gsize offset, gsize bufsz, gsize sig_sz, + FuFirmwareParseFlags flags, GError **error) { - if (!fu_synaptics_rmi_firmware_add_image(firmware, id, stream, offset, bufsz, error)) + if (!fu_synaptics_rmi_firmware_add_image(self, id, stream, offset, bufsz, flags, error)) return FALSE; if (sig_sz != 0) { g_autoptr(GInputStream) partial_stream = NULL; @@ -79,15 +89,12 @@ partial_stream = fu_partial_input_stream_new(stream, offset + bufsz, sig_sz, error); if (partial_stream == NULL) return FALSE; - if (!fu_firmware_parse_stream(img, - partial_stream, - 0x0, - FWUPD_INSTALL_FLAG_NONE, - error)) + if (!fu_firmware_parse_stream(img, partial_stream, 0x0, flags, error)) return FALSE; sig_id = g_strdup_printf("%s-signature", id); fu_firmware_set_id(img, sig_id); - fu_firmware_add_image(firmware, img); + if (!fu_firmware_add_image(FU_FIRMWARE(self), img, error)) + return FALSE; } return TRUE; } @@ -112,9 +119,11 @@ } static gboolean -fu_synaptics_rmi_firmware_parse_v10(FuFirmware *firmware, GInputStream *stream, GError **error) +fu_synaptics_rmi_firmware_parse_v10(FuSynapticsRmiFirmware *self, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) { - FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); guint16 container_id; guint32 cntrs_len; guint32 offset; @@ -123,7 +132,7 @@ gsize bufsz = 0; const guint8 *buf; guint32 signature_size; - g_autoptr(GByteArray) st_dsc = NULL; + g_autoptr(FuStructRmiContainerDescriptor) st_dsc = NULL; g_autoptr(GBytes) fw = NULL; /* maybe stream later */ @@ -142,10 +151,10 @@ g_debug("v10 RmiContainerDescriptor at 0x%x", cntr_addr); st_dsc = fu_struct_rmi_container_descriptor_parse_stream(stream, cntr_addr, error); if (st_dsc == NULL) { - g_prefix_error(error, "RmiContainerDescriptor invalid: "); + g_prefix_error_literal(error, "RmiContainerDescriptor invalid: "); return FALSE; } - if (bufsz < sizeof(guint32) + st_dsc->len) { + if (bufsz < sizeof(guint32) + st_dsc->buf->len) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, @@ -163,7 +172,7 @@ return FALSE; } offset = fu_struct_rmi_container_descriptor_get_content_address(st_dsc); - if (offset > bufsz - sizeof(guint32) - st_dsc->len) { + if (offset > bufsz - sizeof(guint32) - st_dsc->buf->len) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, @@ -188,7 +197,7 @@ guint32 content_addr; guint32 addr; guint32 length; - g_autoptr(GByteArray) st_dsc2 = NULL; + g_autoptr(FuStructRmiContainerDescriptor) st_dsc2 = NULL; if (!fu_memread_uint32_safe(buf, bufsz, offset, &addr, G_LITTLE_ENDIAN, error)) return FALSE; @@ -233,66 +242,80 @@ &self->bootloader_version, error)) return FALSE; + if (!fu_synaptics_rmi_firmware_parse_sbl_container_v10(self, + stream, + buf, + bufsz, + offset, + flags, + error)) + return FALSE; break; case FU_RMI_CONTAINER_ID_UI: case FU_RMI_CONTAINER_ID_CORE_CODE: - if (!fu_synaptics_rmi_firmware_add_image_v10(firmware, + if (!fu_synaptics_rmi_firmware_add_image_v10(self, "ui", stream, content_addr, length, signature_size, + flags, error)) return FALSE; break; case FU_RMI_CONTAINER_ID_FLASH_CONFIG: - if (!fu_synaptics_rmi_firmware_add_image_v10(firmware, + if (!fu_synaptics_rmi_firmware_add_image_v10(self, "flash-config", stream, content_addr, length, signature_size, + flags, error)) return FALSE; break; case FU_RMI_CONTAINER_ID_UI_CONFIG: case FU_RMI_CONTAINER_ID_CORE_CONFIG: - if (!fu_synaptics_rmi_firmware_add_image_v10(firmware, + if (!fu_synaptics_rmi_firmware_add_image_v10(self, "config", stream, content_addr, length, signature_size, + flags, error)) return FALSE; break; case FU_RMI_CONTAINER_ID_FIXED_LOCATION_DATA: - if (!fu_synaptics_rmi_firmware_add_image_v10(firmware, + if (!fu_synaptics_rmi_firmware_add_image_v10(self, "fixed-location-data", stream, content_addr, length, signature_size, + flags, error)) return FALSE; break; case FU_RMI_CONTAINER_ID_EXTERNAL_TOUCH_AFE_CONFIG: - if (!fu_synaptics_rmi_firmware_add_image_v10(firmware, + if (!fu_synaptics_rmi_firmware_add_image_v10(self, "afe-config", stream, content_addr, length, signature_size, + flags, error)) return FALSE; break; case FU_RMI_CONTAINER_ID_DISPLAY_CONFIG: - if (!fu_synaptics_rmi_firmware_add_image_v10(firmware, + if (!fu_synaptics_rmi_firmware_add_image_v10(self, "display-config", stream, content_addr, length, signature_size, + flags, error)) return FALSE; break; @@ -348,12 +371,14 @@ } static gboolean -fu_synaptics_rmi_firmware_parse_v0x(FuFirmware *firmware, GInputStream *stream, GError **error) +fu_synaptics_rmi_firmware_parse_v0x(FuSynapticsRmiFirmware *self, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) { - FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); guint32 cfg_sz; guint32 img_sz; - g_autoptr(GByteArray) st_img = NULL; + g_autoptr(FuStructRmiImg) st_img = NULL; /* main firmware */ st_img = fu_struct_rmi_img_parse_stream(stream, 0x0, error); @@ -364,19 +389,21 @@ /* payload, then signature appended */ if (self->sig_size > 0) { guint32 sig_offset = img_sz - self->sig_size; - if (!fu_synaptics_rmi_firmware_add_image(firmware, + if (!fu_synaptics_rmi_firmware_add_image(self, "sig", stream, RMI_IMG_FW_OFFSET + sig_offset, self->sig_size, + flags, error)) return FALSE; } - if (!fu_synaptics_rmi_firmware_add_image(firmware, + if (!fu_synaptics_rmi_firmware_add_image(self, "ui", stream, RMI_IMG_FW_OFFSET, img_sz, + flags, error)) return FALSE; } @@ -384,11 +411,12 @@ /* config */ cfg_sz = fu_struct_rmi_img_get_config_size(st_img); if (cfg_sz > 0) { - if (!fu_synaptics_rmi_firmware_add_image(firmware, + if (!fu_synaptics_rmi_firmware_add_image(self, "config", stream, RMI_IMG_FW_OFFSET + img_sz, cfg_sz, + flags, error)) return FALSE; } @@ -398,13 +426,13 @@ static gboolean fu_synaptics_rmi_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); gsize bufsz = 0; const guint8 *buf; - g_autoptr(GByteArray) st_img = NULL; + g_autoptr(FuStructRmiImg) st_img = NULL; g_autoptr(GBytes) fw = NULL; /* maybe stream later */ @@ -434,7 +462,7 @@ /* verify checksum */ self->checksum = fu_struct_rmi_img_get_checksum(st_img); - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint32 checksum_calculated = fu_synaptics_rmi_generate_checksum(buf + 4, bufsz - 4); if (self->checksum != checksum_calculated) { @@ -469,15 +497,15 @@ case 6: if ((self->io & 0x10) >> 1) self->sig_size = fu_struct_rmi_img_get_signature_size(st_img); - if (!fu_synaptics_rmi_firmware_parse_v0x(firmware, stream, error)) + if (!fu_synaptics_rmi_firmware_parse_v0x(self, stream, flags, error)) return FALSE; - self->kind = RMI_FIRMWARE_KIND_0X; + self->kind = FU_SYNAPTICS_RMI_FIRMWARE_KIND_0X; break; case 16: case 17: - if (!fu_synaptics_rmi_firmware_parse_v10(firmware, stream, error)) + if (!fu_synaptics_rmi_firmware_parse_v10(self, stream, flags, error)) return FALSE; - self->kind = RMI_FIRMWARE_KIND_10; + self->kind = FU_SYNAPTICS_RMI_FIRMWARE_KIND_10; break; default: g_set_error(error, @@ -499,18 +527,17 @@ } static GByteArray * -fu_synaptics_rmi_firmware_write_v0x(FuFirmware *firmware, GError **error) +fu_synaptics_rmi_firmware_write_v0x(FuSynapticsRmiFirmware *self, GError **error) { - FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); gsize bufsz = 0; guint32 csum; g_autoptr(FuFirmware) img = NULL; g_autoptr(GByteArray) buf = g_byte_array_new(); - g_autoptr(GByteArray) st_img = fu_struct_rmi_img_new(); + g_autoptr(FuStructRmiImg) st_img = fu_struct_rmi_img_new(); g_autoptr(GBytes) buf_blob = NULL; /* default image */ - img = fu_firmware_get_image_by_id(firmware, "ui", error); + img = fu_firmware_get_image_by_id(FU_FIRMWARE(self), "ui", error); if (img == NULL) return NULL; buf_blob = fu_firmware_write(img, error); @@ -527,7 +554,7 @@ fu_struct_rmi_img_set_product_info(st_img, 0x1234); fu_struct_rmi_img_set_image_size(st_img, bufsz); fu_struct_rmi_img_set_config_size(st_img, bufsz); - g_byte_array_append(buf, st_img->data, st_img->len); + fu_byte_array_append_array(buf, st_img->buf); fu_byte_array_set_size(buf, RMI_IMG_FW_OFFSET + 0x4 + bufsz, 0x00); fu_memwrite_uint32(buf->data + RMI_IMG_FW_OFFSET, 0xDEAD, G_LITTLE_ENDIAN); /* img */ fu_memwrite_uint32(buf->data + RMI_IMG_FW_OFFSET + bufsz, @@ -543,33 +570,35 @@ } static GByteArray * -fu_synaptics_rmi_firmware_write_v10(FuFirmware *firmware, GError **error) +fu_synaptics_rmi_firmware_write_v10(FuSynapticsRmiFirmware *self, GError **error) { - FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); gsize bufsz; guint32 csum; g_autoptr(FuFirmware) img = NULL; g_autoptr(GByteArray) buf = g_byte_array_new(); - g_autoptr(GByteArray) desc_hdr = fu_struct_rmi_container_descriptor_new(); - g_autoptr(GByteArray) desc = fu_struct_rmi_container_descriptor_new(); + g_autoptr(FuStructRmiContainerDescriptor) st_dsc2 = + fu_struct_rmi_container_descriptor_new(); + g_autoptr(FuStructRmiContainerDescriptor) st_dsc = fu_struct_rmi_container_descriptor_new(); g_autoptr(GBytes) buf_blob = NULL; /* header | desc_hdr | offset_table | desc | flash_config | * \0x0 \0x20 \0x24 \0x44 |0x48 */ guint32 offset_table[] = {/* offset to first descriptor */ - GUINT32_TO_LE(RMI_IMG_FW_OFFSET + 0x24)}; /* nocheck:blocked */ - fu_struct_rmi_container_descriptor_set_container_id(desc, FU_RMI_CONTAINER_ID_FLASH_CONFIG); - fu_struct_rmi_container_descriptor_set_content_address(desc, RMI_IMG_FW_OFFSET + 0x44); + GUINT32_TO_LE(RMI_IMG_FW_OFFSET + 0x24), + /* nocheck:blocked */}; + fu_struct_rmi_container_descriptor_set_container_id(st_dsc, + FU_RMI_CONTAINER_ID_FLASH_CONFIG); + fu_struct_rmi_container_descriptor_set_content_address(st_dsc, RMI_IMG_FW_OFFSET + 0x44); /* default image */ - img = fu_firmware_get_image_by_id(firmware, "ui", error); + img = fu_firmware_get_image_by_id(FU_FIRMWARE(self), "ui", error); if (img == NULL) return NULL; buf_blob = fu_firmware_write(img, error); if (buf_blob == NULL) return NULL; bufsz = g_bytes_get_size(buf_blob); - fu_struct_rmi_container_descriptor_set_content_length(desc, bufsz); + fu_struct_rmi_container_descriptor_set_content_length(st_dsc, bufsz); /* create empty block */ fu_byte_array_set_size(buf, RMI_IMG_FW_OFFSET + 0x48, 0x00); @@ -587,6 +616,7 @@ error)) return NULL; } + /* nocheck:memread */ fu_memwrite_uint32(buf->data + FU_STRUCT_RMI_IMG_OFFSET_FW_BUILD_ID, 0x1234, G_LITTLE_ENDIAN); @@ -605,21 +635,20 @@ G_LITTLE_ENDIAN); /* hierarchical section */ - fu_struct_rmi_container_descriptor_set_container_id(desc_hdr, - FU_RMI_CONTAINER_ID_TOP_LEVEL); - fu_struct_rmi_container_descriptor_set_content_length(desc_hdr, 0x1 * 4); /* bytes */ - fu_struct_rmi_container_descriptor_set_content_address(desc_hdr, + fu_struct_rmi_container_descriptor_set_container_id(st_dsc2, FU_RMI_CONTAINER_ID_TOP_LEVEL); + fu_struct_rmi_container_descriptor_set_content_length(st_dsc2, 0x1 * 4); /* bytes */ + fu_struct_rmi_container_descriptor_set_content_address(st_dsc2, RMI_IMG_FW_OFFSET + 0x20); /* offset to table */ memcpy(buf->data + RMI_IMG_FW_OFFSET + 0x00, /* nocheck:blocked */ - desc_hdr->data, - desc_hdr->len); + st_dsc2->buf->data, + st_dsc2->buf->len); memcpy(buf->data + RMI_IMG_FW_OFFSET + 0x20, /* nocheck:blocked */ offset_table, sizeof(offset_table)); memcpy(buf->data + RMI_IMG_FW_OFFSET + 0x24, /* nocheck:blocked */ - desc->data, - desc->len); + st_dsc->buf->data, + st_dsc->buf->len); fu_memwrite_uint32(buf->data + RMI_IMG_FW_OFFSET + 0x44, 0xfeed, G_LITTLE_ENDIAN); /* flash_config */ @@ -671,10 +700,10 @@ FuSynapticsRmiFirmware *self = FU_SYNAPTICS_RMI_FIRMWARE(firmware); /* two supported container formats */ - if (self->kind == RMI_FIRMWARE_KIND_0X) - return fu_synaptics_rmi_firmware_write_v0x(firmware, error); - if (self->kind == RMI_FIRMWARE_KIND_10) - return fu_synaptics_rmi_firmware_write_v10(firmware, error); + if (self->kind == FU_SYNAPTICS_RMI_FIRMWARE_KIND_0X) + return fu_synaptics_rmi_firmware_write_v0x(self, error); + if (self->kind == FU_SYNAPTICS_RMI_FIRMWARE_KIND_10) + return fu_synaptics_rmi_firmware_write_v10(self, error); /* not supported */ g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "kind not supported"); @@ -713,3 +742,92 @@ { return FU_FIRMWARE(g_object_new(FU_TYPE_SYNAPTICS_RMI_FIRMWARE, NULL)); } + +static gboolean +fu_synaptics_rmi_firmware_parse_sbl_container_v10(FuSynapticsRmiFirmware *self, + GInputStream *stream, + const guint8 *buf, + gsize bufsz, + guint32 start_offset, + FuFirmwareParseFlags flags, + GError **error) +{ + guint32 cntr_addr; + guint32 offset; + guint32 cntrs_len; + g_autoptr(FuStructRmiContainerDescriptor) st_dsc = NULL; + + if (!fu_memread_uint32_safe(buf, bufsz, start_offset, &cntr_addr, G_LITTLE_ENDIAN, error)) + return FALSE; + + st_dsc = fu_struct_rmi_container_descriptor_parse_stream(stream, cntr_addr, error); + if (st_dsc == NULL) { + g_prefix_error_literal(error, "RmiContainerDescriptor invalid: "); + return FALSE; + } + + offset = fu_struct_rmi_container_descriptor_get_content_address(st_dsc); + cntrs_len = fu_struct_rmi_container_descriptor_get_content_length(st_dsc) / 4; + if (cntrs_len > RMI_IMG_MAX_CONTAINERS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "too many containers in file [%u], maximum is %u", + cntrs_len, + (guint)RMI_IMG_MAX_CONTAINERS); + return FALSE; + } + for (guint32 i = 0; i < cntrs_len; i++) { + guint32 addr_offset = offset + i * 4; + guint16 container_id; + guint32 addr; + guint32 length; + guint32 content_addr; + guint32 signature_size; + g_autoptr(FuStructRmiContainerDescriptor) st_dsc2 = NULL; + + if (!fu_memread_uint32_safe(buf, bufsz, addr_offset, &addr, G_LITTLE_ENDIAN, error)) + return FALSE; + + st_dsc2 = fu_struct_rmi_container_descriptor_parse_stream(stream, addr, error); + if (st_dsc2 == NULL) { + g_prefix_error_literal(error, "RmiContainerDescriptor invalid: "); + return FALSE; + } + + container_id = fu_struct_rmi_container_descriptor_get_container_id(st_dsc2); + content_addr = fu_struct_rmi_container_descriptor_get_content_address(st_dsc2); + length = fu_struct_rmi_container_descriptor_get_content_length(st_dsc2); + signature_size = fu_struct_rmi_container_descriptor_get_signature_size(st_dsc2); + + g_debug("RmiContainerDescriptor 0x%02x @ 0x%x (len 0x%x) sig_size 0x%x", + container_id, + content_addr, + length, + signature_size); + if (length == 0) { + g_debug("no SBL image found"); + continue; + } + + if (container_id == FU_RMI_CONTAINER_ID_BL_IMAGE) { + if (signature_size != 0) + g_debug("SBL signature size : 0x%x", signature_size); + + if (!fu_synaptics_rmi_firmware_add_image_v10(self, + "sbl", + stream, + content_addr, + length, + signature_size, + flags, + error)) { + g_prefix_error_literal(error, "failed to add SBL image: "); + return FALSE; + } + } + } + + /* success */ + return TRUE; +} diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.c fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.c --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -41,6 +41,8 @@ #define HID_RMI4_ATTN_INTERRUPT_SOURCES 1 #define HID_RMI4_ATTN_DATA 2 +#define HID_RMI4_REPORT_ID_SIZE 1 /* Size of the report ID field */ +#define HID_RMI4_DATA_LENGTH_SIZE 1 /* Size of the data length field */ /* * This bit disables whatever sleep mode may be selected by the sleep_mode * field and forces the device to run at full power without sleeping. @@ -65,7 +67,6 @@ GError **error) { FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(rmi_device); - FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); g_autoptr(GByteArray) buf = g_byte_array_new(); g_autoptr(GByteArray) req = g_byte_array_new(); @@ -91,24 +92,26 @@ /* request */ for (guint j = req->len; j < 21; j++) fu_byte_array_append_uint8(req, 0x0); + fu_dump_full(G_LOG_DOMAIN, "ReportWrite", req->data, req->len, 80, FU_DUMP_FLAGS_NONE); - if (!fu_io_channel_write_byte_array(io_channel, - req, - RMI_DEVICE_DEFAULT_TIMEOUT, - FU_IO_CHANNEL_FLAG_SINGLE_SHOT | - FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, - error)) + if (!fu_udev_device_write_byte_array(FU_UDEV_DEVICE(self), + req, + RMI_DEVICE_DEFAULT_TIMEOUT, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT | + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, + error)) return NULL; /* keep reading responses until we get enough data */ while (buf->len < req_sz) { guint8 input_count_sz = 0; g_autoptr(GByteArray) res = NULL; - res = fu_io_channel_read_byte_array(io_channel, - req_sz, - RMI_DEVICE_DEFAULT_TIMEOUT, - FU_IO_CHANNEL_FLAG_SINGLE_SHOT, - error); + res = fu_udev_device_read_byte_array(FU_UDEV_DEVICE(self), + req_sz + HID_RMI4_REPORT_ID_SIZE + + HID_RMI4_DATA_LENGTH_SIZE, + RMI_DEVICE_DEFAULT_TIMEOUT, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT, + error); if (res == NULL) return NULL; if (res->len == 0) { @@ -155,7 +158,13 @@ (guint)input_count_sz + HID_RMI4_READ_INPUT_DATA); return NULL; } - g_byte_array_append(buf, res->data + HID_RMI4_READ_INPUT_DATA, input_count_sz); + if (!fu_byte_array_append_safe(buf, + res->data, + res->len, + HID_RMI4_READ_INPUT_DATA, /* offset */ + input_count_sz, + error)) + return NULL; } fu_dump_full(G_LOG_DOMAIN, "DeviceRead", buf->data, buf->len, 80, FU_DUMP_FLAGS_NONE); @@ -179,7 +188,6 @@ GError **error) { FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(rmi_device); - FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); guint8 len = 0x0; g_autoptr(GByteArray) buf = g_byte_array_new(); @@ -213,12 +221,12 @@ fu_byte_array_append_uint8(buf, 0x0); fu_dump_full(G_LOG_DOMAIN, "DeviceWrite", buf->data, buf->len, 80, FU_DUMP_FLAGS_NONE); - return fu_io_channel_write_byte_array(io_channel, - buf, - RMI_DEVICE_DEFAULT_TIMEOUT, - FU_IO_CHANNEL_FLAG_SINGLE_SHOT | - FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, - error); + return fu_udev_device_write_byte_array(FU_UDEV_DEVICE(self), + buf, + RMI_DEVICE_DEFAULT_TIMEOUT, + FU_IO_CHANNEL_FLAG_SINGLE_SHOT | + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, + error); } static gboolean @@ -228,7 +236,6 @@ GError **error) { FuSynapticsRmiHidDevice *self = FU_SYNAPTICS_RMI_HID_DEVICE(rmi_device); - FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); g_autoptr(GTimer) timer = g_timer_new(); /* wait for event from hardware */ @@ -237,11 +244,11 @@ g_autoptr(GError) error_local = NULL; /* read from fd */ - res = fu_io_channel_read_byte_array(io_channel, - HID_RMI4_ATTN_INTERRUPT_SOURCES + 1, - timeout_ms, - FU_IO_CHANNEL_FLAG_NONE, - &error_local); + res = fu_udev_device_read_byte_array(FU_UDEV_DEVICE(self), + HID_RMI4_ATTN_INTERRUPT_SOURCES + 1, + timeout_ms, + FU_IO_CHANNEL_FLAG_NONE, + &error_local); if (res == NULL) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT)) break; @@ -346,14 +353,8 @@ static gboolean fu_synaptics_rmi_hid_device_rebind_driver(FuSynapticsRmiDevice *self, GError **error) { - const gchar *hid_id; - const gchar *driver; - const gchar *subsystem; - g_autofree gchar *fn_rebind = NULL; - g_autofree gchar *fn_unbind = NULL; g_autoptr(FuDevice) parent_hid = NULL; g_autoptr(FuUdevDevice) parent_phys = NULL; - g_auto(GStrv) hid_strs = NULL; /* get actual HID node */ parent_hid = fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), "hid", error); @@ -375,31 +376,35 @@ fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(parent_hid))); return FALSE; } - - /* find the physical ID to use for the rebind */ - hid_strs = g_strsplit(fu_udev_device_get_sysfs_path(parent_phys), "/", -1); - hid_id = hid_strs[g_strv_length(hid_strs) - 1]; - if (hid_id == NULL) { + if (!fu_device_probe(FU_DEVICE(parent_phys), error)) { + g_prefix_error_literal(error, "failed to probe parent: "); + return FALSE; + } + if (fu_udev_device_get_subsystem(parent_phys) == NULL) { g_set_error(error, FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "no HID_PHYS in %s", - fu_udev_device_get_sysfs_path(parent_phys)); + FWUPD_ERROR_NOT_SUPPORTED, + "no parent subsystem for %s", + fu_device_get_id_display(FU_DEVICE(parent_phys))); + return FALSE; + } + if (fu_udev_device_get_driver(parent_phys) == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no parent driver for %s", + fu_device_get_id_display(FU_DEVICE(parent_phys))); return FALSE; } - - g_debug("HID_PHYS: %s", hid_id); - - driver = fu_udev_device_get_driver(parent_phys); - subsystem = fu_udev_device_get_subsystem(parent_phys); - fn_rebind = g_build_filename("/sys/bus/", subsystem, "drivers", driver, "bind", NULL); - fn_unbind = g_build_filename("/sys/bus/", subsystem, "drivers", driver, "unbind", NULL); /* unbind hidraw, then bind it again to get a replug */ fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - if (!fu_synaptics_rmi_device_writeln(fn_unbind, hid_id, error)) + if (!fu_device_unbind_driver(FU_DEVICE(parent_phys), error)) return FALSE; - if (!fu_synaptics_rmi_device_writeln(fn_rebind, hid_id, error)) + if (!fu_device_bind_driver(FU_DEVICE(parent_phys), + fu_udev_device_get_subsystem(parent_phys), + fu_udev_device_get_driver(parent_phys), + error)) return FALSE; /* success */ @@ -416,10 +421,10 @@ if (f34 == NULL) return FALSE; if (f34->function_version == 0x0 || f34->function_version == 0x1) { - if (!fu_synaptics_rmi_v5_device_detach(device, progress, error)) + if (!fu_synaptics_rmi_v5_device_detach(self, progress, error)) return FALSE; } else if (f34->function_version == 0x2) { - if (!fu_synaptics_rmi_v7_device_detach(device, progress, error)) + if (!fu_synaptics_rmi_v7_device_detach(self, progress, error)) return FALSE; } else { g_set_error(error, @@ -478,7 +483,7 @@ return FALSE; f01_control0 = fu_synaptics_rmi_device_read(rmi_device, f01->control_base, 0x1, error); if (f01_control0 == NULL) { - g_prefix_error(error, "failed to write get f01_control0: "); + g_prefix_error_literal(error, "failed to write get f01_control0: "); return FALSE; } f01_control0->data[0] |= RMI_F01_CRTL0_NOSLEEP_BIT; @@ -489,7 +494,7 @@ f01_control0, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write f01_control0: "); + g_prefix_error_literal(error, "failed to write f01_control0: "); return FALSE; } @@ -504,12 +509,10 @@ f34 = fu_synaptics_rmi_device_get_function(rmi_device, 0x34, error); if (f34 == NULL) return FALSE; - if (f34->function_version == 0x0 || f34->function_version == 0x1) { + if (f34->function_version == 0x0 || f34->function_version == 0x1) return fu_synaptics_rmi_v5_device_query_status(rmi_device, error); - } - if (f34->function_version == 0x2) { + if (f34->function_version == 0x2) return fu_synaptics_rmi_v7_device_query_status(rmi_device, error); - } g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -519,7 +522,7 @@ } static void -fu_synaptics_rmi_hid_device_set_progress(FuDevice *self, FuProgress *progress) +fu_synaptics_rmi_hid_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -535,6 +538,8 @@ fu_device_set_name(FU_DEVICE(self), "Touchpad"); fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); fu_synaptics_rmi_device_set_max_page(FU_SYNAPTICS_RMI_DEVICE(self), 0xff); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); } static void diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-ps2-device.c fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-ps2-device.c --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-ps2-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-ps2-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -23,16 +23,15 @@ static gboolean fu_synaptics_rmi_ps2_device_read_ack(FuSynapticsRmiPs2Device *self, guint8 *pbuf, GError **error) { - FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); for (guint i = 0; i < 60; i++) { g_autoptr(GError) error_local = NULL; - if (!fu_io_channel_read_raw(io_channel, - pbuf, - 0x1, - NULL, - 10, - FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, - &error_local)) { + if (!fu_udev_device_read(FU_UDEV_DEVICE(self), + pbuf, + 0x1, + NULL, + 10, + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, + &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT)) { g_warning("read timed out: %u", i); fu_device_sleep(FU_DEVICE(self), 1); /* ms */ @@ -43,7 +42,7 @@ } return TRUE; } - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT, "read timed out"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT, "read timed out"); return FALSE; } @@ -54,15 +53,14 @@ guint timeout, GError **error) { - FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); g_return_val_if_fail(timeout > 0, FALSE); - return fu_io_channel_read_raw(io_channel, - pbuf, - 0x1, - NULL, - timeout, - FU_IO_CHANNEL_FLAG_NONE, - error); + return fu_udev_device_read(FU_UDEV_DEVICE(self), + pbuf, + 0x1, + NULL, + timeout, + FU_IO_CHANNEL_FLAG_NONE, + error); } /* write a single byte to the touchpad and the read the acknowledge */ @@ -73,20 +71,19 @@ FuSynapticsRmiDeviceFlags flags, GError **error) { - FuIOChannel *io_channel = fu_udev_device_get_io_channel(FU_UDEV_DEVICE(self)); gboolean do_write = TRUE; g_return_val_if_fail(timeout > 0, FALSE); for (guint i = 0;; i++) { guint8 res = 0; g_autoptr(GError) error_local = NULL; if (do_write) { - if (!fu_io_channel_write_raw(io_channel, - &buf, - sizeof(buf), - timeout, - FU_IO_CHANNEL_FLAG_FLUSH_INPUT | - FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, - error)) + if (!fu_udev_device_write(FU_UDEV_DEVICE(self), + &buf, + sizeof(buf), + timeout, + FU_IO_CHANNEL_FLAG_FLUSH_INPUT | + FU_IO_CHANNEL_FLAG_USE_BLOCKING_IO, + error)) return FALSE; } do_write = FALSE; @@ -154,7 +151,7 @@ return FALSE; } for (gint i = 3; i >= 0; --i) { - guint8 ucTwoBitArg = (arg >> (i * 2)) & 0x3; + guint8 uc_two_bit = (arg >> (i * 2)) & 0x3; if (!fu_synaptics_rmi_ps2_device_write_byte(self, FU_RMI_EDP_COMMAND_AUX_SET_RESOLUTION, 50, @@ -163,7 +160,7 @@ return FALSE; } if (!fu_synaptics_rmi_ps2_device_write_byte(self, - ucTwoBitArg, + uc_two_bit, 50, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) @@ -204,7 +201,7 @@ break; } if (success == FALSE) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed"); return FALSE; } @@ -212,7 +209,7 @@ for (gint i = 0; i < 3; ++i) { guint8 tmp = 0x0; if (!fu_synaptics_rmi_ps2_device_read_byte(self, &tmp, 10, error)) { - g_prefix_error(error, "failed to read byte: "); + g_prefix_error_literal(error, "failed to read byte: "); return FALSE; } *buf = ((*buf) << 8) | tmp; @@ -273,11 +270,11 @@ 10, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write IBMReadSecondaryID(0xE1): "); + g_prefix_error_literal(error, "failed to write IBMReadSecondaryID(0xE1): "); return FALSE; } if (!fu_synaptics_rmi_ps2_device_read_byte(self, &buf, 10, error)) { - g_prefix_error(error, "failed to receive IBMReadSecondaryID: "); + g_prefix_error_literal(error, "failed to receive IBMReadSecondaryID: "); return FALSE; } if (buf == FU_RMI_STICK_DEVICE_TYPE_JYT_SYNA || buf == FU_RMI_STICK_DEVICE_TYPE_SYNAPTICS) @@ -299,14 +296,14 @@ FU_RMI_STATUS_REQUEST_IDENTIFY_SYNAPTICS, &buf, error)) { - g_prefix_error(error, "failed to request IdentifySynaptics: "); + g_prefix_error_literal(error, "failed to request IdentifySynaptics: "); return FALSE; } g_debug("identify Synaptics response = 0x%x", buf); esdr = (buf & 0xFF00) >> 8; if (!fu_synaptics_rmi_ps2_device_detect_synaptics_styk(self, &is_synaptics_styk, error)) { - g_prefix_error(error, "failed to detect Synaptics styk: "); + g_prefix_error_literal(error, "failed to detect Synaptics styk: "); return FALSE; } fu_synaptics_rmi_device_set_iepmode(rmi_device, FALSE); @@ -318,7 +315,7 @@ FU_RMI_STATUS_REQUEST_READ_EXTRA_CAPABILITIES2, build_id, error)) { - g_prefix_error(error, "failed to read extraCapabilities2: "); + g_prefix_error_literal(error, "failed to read extraCapabilities2: "); return FALSE; } } @@ -336,8 +333,9 @@ FU_RMI_STATUS_REQUEST_READ_CAPABILITIES, &buf, error)) { - g_prefix_error(error, - "failed to status_request_sequence read esrReadCapabilities: "); + g_prefix_error_literal( + error, + "failed to status_request_sequence read esrReadCapabilities: "); return FALSE; } *sub_id = (buf >> 8) & 0xFF; @@ -355,7 +353,7 @@ 50, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to disable stream mode: "); + g_prefix_error_literal(error, "failed to disable stream mode: "); return FALSE; } @@ -365,7 +363,7 @@ FU_RMI_EDP_COMMAND_AUX_FULL_RMI_BACK_DOOR, FALSE, error)) { - g_prefix_error(error, "failed to enter RMI mode: "); + g_prefix_error_literal(error, "failed to enter RMI mode: "); return FALSE; } @@ -393,7 +391,7 @@ timeout, flags, error)) { - g_prefix_error(error, "failed to edpAuxSetScaling2To1: "); + g_prefix_error_literal(error, "failed to edpAuxSetScaling2To1: "); return FALSE; } if (!fu_synaptics_rmi_ps2_device_write_byte(self, @@ -401,11 +399,11 @@ timeout, flags, error)) { - g_prefix_error(error, "failed to edpAuxSetSampleRate: "); + g_prefix_error_literal(error, "failed to edpAuxSetSampleRate: "); return FALSE; } if (!fu_synaptics_rmi_ps2_device_write_byte(self, addr, timeout, flags, error)) { - g_prefix_error(error, "failed to write address: "); + g_prefix_error_literal(error, "failed to write address: "); return FALSE; } for (guint8 i = 0; i < buflen; i++) { @@ -462,7 +460,8 @@ 50, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write command in Read RMI register: "); + g_prefix_error_literal(error, + "failed to write command in Read RMI register: "); return FALSE; } if (!fu_synaptics_rmi_ps2_device_read_byte(self, buf, 10, &error_local)) { @@ -520,7 +519,8 @@ 50, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write command in Read RMI Packet Register: "); + g_prefix_error_literal(error, + "failed to write command in Read RMI Packet Register: "); return NULL; } for (guint i = 0; i < req_sz; ++i) { @@ -544,12 +544,10 @@ f34 = fu_synaptics_rmi_device_get_function(rmi_device, 0x34, error); if (f34 == NULL) return FALSE; - if (f34->function_version == 0x0 || f34->function_version == 0x1) { + if (f34->function_version == 0x0 || f34->function_version == 0x1) return fu_synaptics_rmi_v5_device_query_status(rmi_device, error); - } - if (f34->function_version == 0x2) { + if (f34->function_version == 0x2) return fu_synaptics_rmi_v7_device_query_status(rmi_device, error); - } g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -586,7 +584,7 @@ g_autofree gchar *dump = NULL; if (!fu_synaptics_rmi_device_set_page(rmi_device, addr >> 8, error)) { - g_prefix_error(error, "failed to set RMI page: "); + g_prefix_error_literal(error, "failed to set RMI page: "); return NULL; } @@ -635,7 +633,7 @@ g_autofree gchar *dump = g_strdup_printf("R %x", addr); if (!fu_synaptics_rmi_device_set_page(rmi_device, addr >> 8, error)) { - g_prefix_error(error, "failed to set RMI page: "); + g_prefix_error_literal(error, "failed to set RMI page: "); return NULL; } @@ -660,7 +658,7 @@ g_autofree gchar *str = g_strdup_printf("W %x", addr); if (!fu_synaptics_rmi_device_set_page(rmi_device, addr >> 8, error)) { - g_prefix_error(error, "failed to set RMI page: "); + g_prefix_error_literal(error, "failed to set RMI page: "); return FALSE; } if (!fu_synaptics_rmi_ps2_device_write_rmi_register(self, @@ -732,21 +730,21 @@ 600, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to reset: "); + g_prefix_error_literal(error, "failed to reset: "); return FALSE; } /* read the 0xAA 0x00 announcing the touchpad is ready */ if (!fu_synaptics_rmi_ps2_device_read_byte(self, &buf[0], 500, error) || !fu_synaptics_rmi_ps2_device_read_byte(self, &buf[1], 500, error)) { - g_prefix_error(error, "failed to read 0xAA00: "); + g_prefix_error_literal(error, "failed to read 0xAA00: "); return FALSE; } if (buf[0] != 0xAA || buf[1] != 0x00) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "failed to read 0xAA00, got 0x%02X%02X: ", + "failed to read 0xAA00, got 0x%02X%02X", buf[0], buf[1]); return FALSE; @@ -758,7 +756,7 @@ 50, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to disable stream mode: "); + g_prefix_error_literal(error, "failed to disable stream mode: "); return FALSE; } } @@ -785,7 +783,7 @@ "serio_raw", FU_SYNAPTICS_RMI_DEVICE_BIND_TIMEOUT, error)) { - g_prefix_error(error, "failed to write to drvctl: "); + g_prefix_error_literal(error, "failed to write to drvctl: "); return FALSE; } @@ -801,10 +799,10 @@ if (f34 == NULL) return FALSE; if (f34->function_version == 0x0 || f34->function_version == 0x1) { - if (!fu_synaptics_rmi_v5_device_detach(device, progress, error)) + if (!fu_synaptics_rmi_v5_device_detach(self, progress, error)) return FALSE; } else if (f34->function_version == 0x2) { - if (!fu_synaptics_rmi_v7_device_detach(device, progress, error)) + if (!fu_synaptics_rmi_v7_device_detach(self, progress, error)) return FALSE; } else { g_set_error(error, @@ -822,7 +820,7 @@ return FALSE; if (!fu_synaptics_rmi_ps2_device_query_status(self, error)) { - g_prefix_error(error, "failed to query status after detach: "); + g_prefix_error_literal(error, "failed to query status after detach: "); return FALSE; } @@ -862,7 +860,7 @@ error)) return FALSE; if (!fu_synaptics_rmi_device_reset(rmi_device, error)) { - g_prefix_error(error, "failed to reset device: "); + g_prefix_error_literal(error, "failed to reset device: "); return FALSE; } fu_device_sleep_full(device, 5000, progress); /* ms */ @@ -873,7 +871,7 @@ "psmouse", FU_SYNAPTICS_RMI_DEVICE_BIND_TIMEOUT, error)) { - g_prefix_error(error, "failed to write to drvctl: "); + g_prefix_error_literal(error, "failed to write to drvctl: "); return FALSE; } diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.c fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.c --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,14 +25,13 @@ #define RMI_F34_ERASE_WAIT_MS (5 * 1000) /* ms */ gboolean -fu_synaptics_rmi_v5_device_detach(FuDevice *device, FuProgress *progress, GError **error) +fu_synaptics_rmi_v5_device_detach(FuSynapticsRmiDevice *self, FuProgress *progress, GError **error) { - FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); g_autoptr(GByteArray) enable_req = g_byte_array_new(); /* sanity check */ - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { g_debug("already in bootloader mode, skipping"); return TRUE; } @@ -41,7 +40,7 @@ if (!fu_synaptics_rmi_device_disable_irqs(self, error)) return FALSE; if (!fu_synaptics_rmi_device_write_bus_select(self, 0, error)) { - g_prefix_error(error, "failed to write bus select: "); + g_prefix_error_literal(error, "failed to write bus select: "); return FALSE; } @@ -54,11 +53,11 @@ enable_req, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to enable programming: "); + g_prefix_error_literal(error, "failed to enable programming: "); return FALSE; } - fu_device_sleep(device, RMI_F34_ENABLE_WAIT_MS); + fu_device_sleep(FU_DEVICE(self), RMI_F34_ENABLE_WAIT_MS); return TRUE; } @@ -81,7 +80,7 @@ erase_cmd, FU_SYNAPTICS_RMI_DEVICE_FLAG_ALLOW_FAILURE, error)) { - g_prefix_error(error, "failed to erase core config: "); + g_prefix_error_literal(error, "failed to erase core config: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), RMI_F34_ERASE_WAIT_MS); @@ -90,11 +89,12 @@ FU_SYNAPTICS_RMI_DEVICE_FLAG_FORCE, error)) return FALSE; - if (!fu_synaptics_rmi_device_wait_for_idle(self, - RMI_F34_ERASE_WAIT_MS, - RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34, - error)) { - g_prefix_error(error, "failed to wait for idle for erase: "); + if (!fu_synaptics_rmi_device_wait_for_idle( + self, + RMI_F34_ERASE_WAIT_MS, + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34, + error)) { + g_prefix_error_literal(error, "failed to wait for idle for erase: "); return FALSE; } return TRUE; @@ -122,7 +122,7 @@ } if (!fu_synaptics_rmi_device_wait_for_idle(self, RMI_F34_IDLE_WAIT_MS, - RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, error)) { g_prefix_error(error, "failed to wait for idle @0x%x: ", address); return FALSE; @@ -131,12 +131,11 @@ } static gboolean -fu_synaptics_rmi_v5_device_secure_check(FuDevice *device, +fu_synaptics_rmi_v5_device_secure_check(FuSynapticsRmiDevice *self, GBytes *payload, GBytes *signature, GError **error) { - FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); FuSynapticsRmiFunction *f34; guint16 rsa_pubkey_len = fu_synaptics_rmi_device_get_sig_size(self) / 8; guint16 rsa_block_cnt = rsa_pubkey_len / 3; @@ -156,7 +155,7 @@ for (guint retries = 0;; retries++) { /* need read another register to reset the offset of packet register */ if (!fu_synaptics_rmi_v5_device_query_status(self, error)) { - g_prefix_error(error, "failed to read status: "); + g_prefix_error_literal(error, "failed to read status: "); return FALSE; } if (!fu_synaptics_rmi_device_enter_iep_mode(self, @@ -196,7 +195,7 @@ error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, - "RSA public key length not matched %u: after %u retries: ", + "RSA public key length not matched %u: after %u retries", pubkey_buf->len, retries); return FALSE; @@ -220,7 +219,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "RSA public key length did not match: %u != %u: ", + "RSA public key length did not match: %u != %u", rsa_pubkey_len, pubkey_buf->len); return FALSE; @@ -230,13 +229,12 @@ } gboolean -fu_synaptics_rmi_v5_device_write_firmware(FuDevice *device, +fu_synaptics_rmi_v5_device_write_firmware(FuSynapticsRmiDevice *self, FuFirmware *firmware, FuProgress *progress, FwupdInstallFlags flags, GError **error) { - FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); FuSynapticsRmiFunction *f34; FuSynapticsRmiFirmware *rmi_firmware = FU_SYNAPTICS_RMI_FIRMWARE(firmware); @@ -261,7 +259,7 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 10, "cfg-image"); /* we should be in bootloader mode now, but check anyway */ - if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (!fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -274,11 +272,12 @@ return FALSE; /* check is idle */ - if (!fu_synaptics_rmi_device_wait_for_idle(self, - 0, - RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34, - error)) { - g_prefix_error(error, "not idle: "); + if (!fu_synaptics_rmi_device_wait_for_idle( + self, + 0, + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34, + error)) { + g_prefix_error_literal(error, "not idle: "); return FALSE; } if (fu_synaptics_rmi_firmware_get_sig_size(rmi_firmware) == 0 && @@ -317,24 +316,24 @@ return FALSE; signature_bin = fu_firmware_get_image_by_id_bytes(firmware, "sig", NULL); if (signature_bin != NULL) { - if (!fu_synaptics_rmi_v5_device_secure_check(device, + if (!fu_synaptics_rmi_v5_device_secure_check(self, firmware_bin, signature_bin, error)) { - g_prefix_error(error, "secure check failed: "); + g_prefix_error_literal(error, "secure check failed: "); return FALSE; } } /* disable powersaving */ if (!fu_synaptics_rmi_device_disable_sleep(self, error)) { - g_prefix_error(error, "failed to disable sleep: "); + g_prefix_error_literal(error, "failed to disable sleep: "); return FALSE; } /* unlock again */ if (!fu_synaptics_rmi_device_write_bootloader_id(self, error)) { - g_prefix_error(error, "failed to unlock again: "); + g_prefix_error_literal(error, "failed to unlock again: "); return FALSE; } fu_progress_step_done(progress); @@ -342,7 +341,7 @@ /* erase all */ fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_ERASE); if (!fu_synaptics_rmi_v5_device_erase_all(self, error)) { - g_prefix_error(error, "failed to erase all: "); + g_prefix_error_literal(error, "failed to erase all: "); return FALSE; } fu_progress_step_done(progress); @@ -355,7 +354,7 @@ req_addr, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write 1st address zero: "); + g_prefix_error_literal(error, "failed to write 1st address zero: "); return FALSE; } @@ -406,7 +405,7 @@ req_addr, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write 1st address zero: "); + g_prefix_error_literal(error, "failed to write 1st address zero: "); return FALSE; } fu_progress_set_id(progress_child, G_STRLOC); @@ -428,7 +427,7 @@ } fu_progress_step_done(progress_child); } - fu_device_sleep(device, 1000); /* ms */ + fu_device_sleep(FU_DEVICE(self), 1000); /* ms */ } fu_progress_step_done(progress); @@ -443,7 +442,7 @@ req_addr, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to 2nd write address zero: "); + g_prefix_error_literal(error, "failed to 2nd write address zero: "); return FALSE; } for (guint i = 0; i < fu_chunk_array_length(chunks_cfg); i++) { @@ -489,7 +488,7 @@ /* get bootloader ID */ f34_data0 = fu_synaptics_rmi_device_read(self, f34->query_base, 0x2, error); if (f34_data0 == NULL) { - g_prefix_error(error, "failed to read bootloader ID: "); + g_prefix_error_literal(error, "failed to read bootloader ID: "); return FALSE; } flash->bootloader_id[0] = f34_data0->data[0]; @@ -505,7 +504,7 @@ buf_flash_properties2 = fu_synaptics_rmi_device_read(self, f34->query_base + 0x9, 1, error); if (buf_flash_properties2 == NULL) { - g_prefix_error(error, "failed to read Flash Properties 2: "); + g_prefix_error_literal(error, "failed to read Flash Properties 2: "); return FALSE; } if (!fu_memread_uint8_safe(buf_flash_properties2->data, @@ -513,7 +512,7 @@ 0x0, /* offset */ &flash_properties2, error)) { - g_prefix_error(error, "failed to parse Flash Properties 2: "); + g_prefix_error_literal(error, "failed to parse Flash Properties 2: "); return FALSE; } if (flash_properties2 & 0x01) { @@ -524,7 +523,7 @@ 2, error); if (buf_rsa_key == NULL) { - g_prefix_error(error, "failed to read RSA key length: "); + g_prefix_error_literal(error, "failed to read RSA key length: "); return FALSE; } if (!fu_memread_uint16_safe(buf_rsa_key->data, @@ -533,7 +532,7 @@ &sig_size, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to parse RSA key length: "); + g_prefix_error_literal(error, "failed to parse RSA key length: "); return FALSE; } fu_synaptics_rmi_device_set_sig_size(self, sig_size); @@ -580,7 +579,7 @@ return FALSE; f01_db = fu_synaptics_rmi_device_read(self, f01->data_base, 0x1, error); if (f01_db == NULL) { - g_prefix_error(error, "failed to read the f01 data base: "); + g_prefix_error_literal(error, "failed to read the f01 data base: "); return FALSE; } if (f01_db->data[0] & 0x40) { diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.h fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.h --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v5-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,9 +9,9 @@ #include "fu-synaptics-rmi-device.h" gboolean -fu_synaptics_rmi_v5_device_detach(FuDevice *device, FuProgress *progress, GError **error); +fu_synaptics_rmi_v5_device_detach(FuSynapticsRmiDevice *self, FuProgress *progress, GError **error); gboolean -fu_synaptics_rmi_v5_device_write_firmware(FuDevice *device, +fu_synaptics_rmi_v5_device_write_firmware(FuSynapticsRmiDevice *self, FuFirmware *firmware, FuProgress *progress, FwupdInstallFlags flags, diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-v6-device.c fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v6-device.c --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-v6-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v6-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -29,7 +29,7 @@ /* get bootloader ID */ f34_data0 = fu_synaptics_rmi_device_read(self, f34->query_base, 0x2, error); if (f34_data0 == NULL) { - g_prefix_error(error, "failed to read bootloader ID: "); + g_prefix_error_literal(error, "failed to read bootloader ID: "); return FALSE; } if (!fu_memread_uint8_safe(f34_data0->data, diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.c fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.c --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,88 +13,105 @@ #define RMI_F34_ERASE_WAIT_MS 10000 /* ms */ -typedef enum { - RMI_FLASH_CMD_IDLE = 0x00, - RMI_FLASH_CMD_ENTER_BL, - RMI_FLASH_CMD_READ, - RMI_FLASH_CMD_WRITE, - RMI_FLASH_CMD_ERASE, - RMI_FLASH_CMD_ERASE_AP, - RMI_FLASH_CMD_SENSOR_ID, - RMI_FLASH_CMD_SIGNATURE, -} RmiFlashCommand; +static gboolean +fu_synaptics_rmi_v7_device_enter_sbl(FuSynapticsRmiDevice *self, GError **error); gboolean -fu_synaptics_rmi_v7_device_detach(FuDevice *device, FuProgress *progress, GError **error) +fu_synaptics_rmi_v7_device_detach(FuSynapticsRmiDevice *self, FuProgress *progress, GError **error) { - FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); - g_autoptr(GByteArray) enable_req = g_byte_array_new(); FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); FuSynapticsRmiFunction *f34; + g_autoptr(FuStructSynapticsRmiV7EnterBl) st = NULL; - /* f34 */ - f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); - if (f34 == NULL) - return FALSE; + fu_synaptics_rmi_device_set_previous_sbl_version(self, 0); /* disable interrupts */ if (!fu_synaptics_rmi_device_disable_irqs(self, error)) return FALSE; + /* enter SBL */ + if (flash->has_sbl) { + FuSynapticsRmiFunction *f01; + g_autoptr(GByteArray) f01_basic = NULL; + + if (!fu_synaptics_rmi_v7_device_enter_sbl(self, error)) { + g_prefix_error_literal(error, "failed to enter SBL mode: "); + return FALSE; + } + + f01 = fu_synaptics_rmi_device_get_function(self, 0x01, error); + if (f01 == NULL) { + g_prefix_error_literal(error, "f01 not found: "); + return FALSE; + } + f01_basic = fu_synaptics_rmi_device_read(self, f01->query_base, 11, error); + if (f01_basic == NULL) { + g_prefix_error_literal(error, "failed to read the basic query: "); + return FALSE; + } + fu_synaptics_rmi_device_set_previous_sbl_version(self, + f01_basic->data[2] << 8 | + f01_basic->data[3]); + g_debug("SBL version: %d.%d", + fu_synaptics_rmi_device_get_previous_sbl_version(self) >> 8, + fu_synaptics_rmi_device_get_previous_sbl_version(self) & 0xff); + } + /* enter BL */ - fu_byte_array_append_uint8(enable_req, FU_RMI_PARTITION_ID_BOOTLOADER); - fu_byte_array_append_uint32(enable_req, 0x0, G_LITTLE_ENDIAN); - fu_byte_array_append_uint8(enable_req, RMI_FLASH_CMD_ENTER_BL); - fu_byte_array_append_uint8(enable_req, flash->bootloader_id[0]); - fu_byte_array_append_uint8(enable_req, flash->bootloader_id[1]); + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + st = fu_struct_synaptics_rmi_v7_enter_bl_new(); + fu_struct_synaptics_rmi_v7_enter_bl_set_bootloader_id0(st, flash->bootloader_id[0]); + fu_struct_synaptics_rmi_v7_enter_bl_set_bootloader_id1(st, flash->bootloader_id[1]); if (!fu_synaptics_rmi_device_write(self, f34->data_base + 1, - enable_req, + st->buf, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to enable programming: "); + g_prefix_error_literal(error, "failed to enable programming: "); return FALSE; } /* wait for idle */ - if (!fu_synaptics_rmi_device_wait_for_idle(self, - RMI_F34_ENABLE_WAIT_MS, - RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, - error)) + if (!fu_synaptics_rmi_device_wait_for_idle( + self, + RMI_F34_ENABLE_WAIT_MS, + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE | + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_DETACH_DEVICE, + error)) return FALSE; if (!fu_synaptics_rmi_device_poll_wait(self, error)) return FALSE; - fu_device_sleep(device, RMI_F34_ENABLE_WAIT_MS); + fu_device_sleep(FU_DEVICE(self), RMI_F34_ENABLE_WAIT_MS); return TRUE; } static gboolean fu_synaptics_rmi_v7_device_erase_partition(FuSynapticsRmiDevice *self, - guint8 partition_id, + FuRmiPartitionId partition_id, GError **error) { FuSynapticsRmiFunction *f34; FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); - g_autoptr(GByteArray) erase_cmd = g_byte_array_new(); + g_autoptr(FuStructSynapticsRmiV7Erase) st = fu_struct_synaptics_rmi_v7_erase_new(); /* f34 */ f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); if (f34 == NULL) return FALSE; - fu_byte_array_append_uint8(erase_cmd, partition_id); - fu_byte_array_append_uint32(erase_cmd, 0x0, G_LITTLE_ENDIAN); - fu_byte_array_append_uint8(erase_cmd, RMI_FLASH_CMD_ERASE); - fu_byte_array_append_uint8(erase_cmd, flash->bootloader_id[0]); - fu_byte_array_append_uint8(erase_cmd, flash->bootloader_id[1]); + fu_struct_synaptics_rmi_v7_erase_set_partition_id(st, partition_id); + fu_struct_synaptics_rmi_v7_erase_set_bootloader_id0(st, flash->bootloader_id[0]); + fu_struct_synaptics_rmi_v7_erase_set_bootloader_id1(st, flash->bootloader_id[1]); fu_device_sleep(FU_DEVICE(self), 1000); /* ms */ if (!fu_synaptics_rmi_device_write(self, f34->data_base + 1, - erase_cmd, + st->buf, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to unlock erasing: "); + g_prefix_error_literal(error, "failed to unlock erasing: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), 100); /* ms */ @@ -102,14 +119,14 @@ /* wait for ATTN */ if (!fu_synaptics_rmi_device_wait_for_idle(self, RMI_F34_ERASE_WAIT_MS, - RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to wait for idle: "); + g_prefix_error_literal(error, "failed to wait for idle: "); return FALSE; } if (!fu_synaptics_rmi_device_poll_wait(self, error)) { - g_prefix_error(error, "failed to get flash success: "); + g_prefix_error_literal(error, "failed to get flash success: "); return FALSE; } return TRUE; @@ -120,67 +137,63 @@ { FuSynapticsRmiFunction *f34; FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); - g_autoptr(GByteArray) erase_cmd = g_byte_array_new(); + g_autoptr(FuStructSynapticsRmiV7EraseCoreCode) st = + fu_struct_synaptics_rmi_v7_erase_core_code_new(); /* f34 */ f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); if (f34 == NULL) return FALSE; - fu_byte_array_append_uint8(erase_cmd, FU_RMI_PARTITION_ID_CORE_CODE); - fu_byte_array_append_uint32(erase_cmd, 0x0, G_LITTLE_ENDIAN); - if (flash->bootloader_id[1] >= 8) { - /* For bootloader v8 */ - fu_byte_array_append_uint8(erase_cmd, RMI_FLASH_CMD_ERASE_AP); - } else { - /* For bootloader v7 */ - fu_byte_array_append_uint8(erase_cmd, RMI_FLASH_CMD_ERASE); + if (flash->bootloader_id[1] < 8) { + fu_struct_synaptics_rmi_v7_erase_core_code_set_cmd( + st, + FU_SYNAPTICS_RMI_FLASH_CMD_ERASE); } - fu_byte_array_append_uint8(erase_cmd, flash->bootloader_id[0]); - fu_byte_array_append_uint8(erase_cmd, flash->bootloader_id[1]); + fu_struct_synaptics_rmi_v7_erase_core_code_set_bootloader_id0(st, flash->bootloader_id[0]); + fu_struct_synaptics_rmi_v7_erase_core_code_set_bootloader_id1(st, flash->bootloader_id[1]); + /* for BL8 device, we need hold 1 seconds after querying F34 status to * avoid not get attention by following giving erase command */ if (flash->bootloader_id[1] >= 8) fu_device_sleep(FU_DEVICE(self), 1000); /* ms */ if (!fu_synaptics_rmi_device_write(self, f34->data_base + 1, - erase_cmd, + st->buf, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to unlock erasing: "); + g_prefix_error_literal(error, "failed to unlock erasing: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), 100); /* ms */ if (flash->bootloader_id[1] >= 8) { /* wait for ATTN */ - if (!fu_synaptics_rmi_device_wait_for_idle(self, - RMI_F34_ERASE_WAIT_MS, - RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, - error)) { - g_prefix_error(error, "failed to wait for idle: "); + if (!fu_synaptics_rmi_device_wait_for_idle( + self, + RMI_F34_ERASE_WAIT_MS, + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to wait for idle: "); return FALSE; } } if (!fu_synaptics_rmi_device_poll_wait(self, error)) { - g_prefix_error(error, "failed to get flash success: "); + g_prefix_error_literal(error, "failed to get flash success: "); return FALSE; } /* for BL7, we need erase config partition */ if (flash->bootloader_id[1] == 7) { - g_autoptr(GByteArray) erase_config_cmd = g_byte_array_new(); - - fu_byte_array_append_uint8(erase_config_cmd, FU_RMI_PARTITION_ID_CORE_CONFIG); - fu_byte_array_append_uint32(erase_config_cmd, 0x0, G_LITTLE_ENDIAN); - fu_byte_array_append_uint8(erase_config_cmd, RMI_FLASH_CMD_ERASE); + g_autoptr(FuStructSynapticsRmiV7EraseCoreConfig) st_cfg = + fu_struct_synaptics_rmi_v7_erase_core_config_new(); fu_device_sleep(FU_DEVICE(self), 100); /* ms */ if (!fu_synaptics_rmi_device_write(self, f34->data_base + 1, - erase_config_cmd, + st_cfg->buf, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to erase core config: "); + g_prefix_error_literal(error, "failed to erase core config: "); return FALSE; } @@ -189,13 +202,13 @@ if (!fu_synaptics_rmi_device_wait_for_idle( self, RMI_F34_ERASE_WAIT_MS, - RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34, + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_REFRESH_F34, error)) { - g_prefix_error(error, "failed to wait for idle: "); + g_prefix_error_literal(error, "failed to wait for idle: "); return FALSE; } if (!fu_synaptics_rmi_device_poll_wait(self, error)) { - g_prefix_error(error, "failed to get flash success: "); + g_prefix_error_literal(error, "failed to get flash success: "); return FALSE; } } @@ -241,11 +254,13 @@ /* wait for idle */ if (!fu_synaptics_rmi_device_wait_for_idle(self, RMI_F34_IDLE_WAIT_MS, - RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, error)) { g_prefix_error(error, "failed to wait for idle @0x%x: ", address); return FALSE; } + if (!fu_synaptics_rmi_device_poll_wait(self, error)) + return FALSE; /* success */ return TRUE; @@ -260,6 +275,7 @@ { FuSynapticsRmiFunction *f34; FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + g_autofree gchar *signature = NULL; g_autoptr(GByteArray) req_offset = g_byte_array_new(); g_autoptr(FuChunkArray) chunks = NULL; g_autoptr(GBytes) bytes = NULL; @@ -269,9 +285,9 @@ if (f34 == NULL) return FALSE; - /*check if signature exists */ - bytes = - fu_firmware_get_image_by_id_bytes(firmware, g_strdup_printf("%s-signature", id), NULL); + /* check if signature exists */ + signature = g_strdup_printf("%s-signature", id); + bytes = fu_firmware_get_image_by_id_bytes(firmware, signature, NULL); if (bytes == NULL) { return TRUE; } @@ -285,7 +301,7 @@ req_offset, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write offset: "); + g_prefix_error_literal(error, "failed to write offset: "); return FALSE; } @@ -313,16 +329,16 @@ req_trans_sz, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write transfer length: "); + g_prefix_error_literal(error, "failed to write transfer length: "); return FALSE; } - fu_byte_array_append_uint8(req_cmd, RMI_FLASH_CMD_SIGNATURE); + fu_byte_array_append_uint8(req_cmd, FU_SYNAPTICS_RMI_FLASH_CMD_SIGNATURE); if (!fu_synaptics_rmi_device_write(self, f34->data_base + 0x4, req_cmd, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write signature command: "); + g_prefix_error_literal(error, "failed to write signature command: "); return FALSE; } chk_blob = fu_chunk_get_bytes(chk); @@ -363,7 +379,7 @@ req_partition_id, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write flash partition: "); + g_prefix_error_literal(error, "failed to write flash partition: "); return FALSE; } fu_byte_array_append_uint16(req_offset, 0x0, G_LITTLE_ENDIAN); @@ -372,7 +388,7 @@ req_offset, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write offset: "); + g_prefix_error_literal(error, "failed to write offset: "); return FALSE; } @@ -403,16 +419,16 @@ req_trans_sz, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write transfer length: "); + g_prefix_error_literal(error, "failed to write transfer length: "); return FALSE; } - fu_byte_array_append_uint8(req_cmd, RMI_FLASH_CMD_WRITE); + fu_byte_array_append_uint8(req_cmd, FU_SYNAPTICS_RMI_FLASH_CMD_WRITE); if (!fu_synaptics_rmi_device_write(self, f34->data_base + 0x4, req_cmd, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to flash command: "); + g_prefix_error_literal(error, "failed to flash command: "); return FALSE; } chk_blob = fu_chunk_get_bytes(chk); @@ -458,7 +474,7 @@ req_partition_id, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write flash partition id: "); + g_prefix_error_literal(error, "failed to write flash partition id: "); return NULL; } fu_byte_array_append_uint16(req_addr_zero, 0x0, G_LITTLE_ENDIAN); @@ -467,7 +483,7 @@ req_addr_zero, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write flash config address: "); + g_prefix_error_literal(error, "failed to write flash config address: "); return NULL; } @@ -480,29 +496,29 @@ req_transfer_length, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to set transfer length: "); + g_prefix_error_literal(error, "failed to set transfer length: "); return NULL; } /* set command to read */ - fu_byte_array_append_uint8(req_cmd, RMI_FLASH_CMD_READ); + fu_byte_array_append_uint8(req_cmd, FU_SYNAPTICS_RMI_FLASH_CMD_READ); if (!fu_synaptics_rmi_device_write(self, f34->data_base + 0x4, req_cmd, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write command to read: "); + g_prefix_error_literal(error, "failed to write command to read: "); return NULL; } if (!fu_synaptics_rmi_device_poll_wait(self, error)) { - g_prefix_error(error, "failed to wait: "); + g_prefix_error_literal(error, "failed to wait: "); return NULL; } /* read back entire buffer in blocks */ res = fu_synaptics_rmi_device_read(self, f34->data_base + 0x5, (guint32)key_size, error); if (res == NULL) { - g_prefix_error(error, "failed to read: "); + g_prefix_error_literal(error, "failed to read: "); return NULL; } @@ -527,7 +543,7 @@ pubkey = fu_synaptics_rmi_v7_device_get_pubkey(self, error); if (pubkey == NULL) { - g_prefix_error(error, "get pubkey failed: "); + g_prefix_error_literal(error, "get pubkey failed: "); return FALSE; } @@ -560,16 +576,103 @@ return TRUE; } +static gboolean +fu_synaptics_rmi_v7_device_write_sbl(FuSynapticsRmiDevice *self, + FuFirmware *firmware, + FuProgress *progress, + GError **error) +{ + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + FuSynapticsRmiFunction *f34; + gboolean need_update_sbl = FALSE; + guint16 f34_sblmsl = 0; + guint16 previous_sbl_version = fu_synaptics_rmi_device_get_previous_sbl_version(self); + g_autoptr(GBytes) bytes_sbl = NULL; + + /* no SBL image */ + bytes_sbl = fu_firmware_get_image_by_id_bytes(firmware, "sbl", NULL); + if (bytes_sbl == NULL) + return TRUE; + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + if (flash->has_sbl) { + g_autoptr(GByteArray) f34_query = NULL; + + f34_query = + fu_synaptics_rmi_device_read(self, + f34->query_base + (flash->has_security ? 10 : 8), + 2, + error); + if (f34_query == NULL) { + g_prefix_error_literal(error, "failed to read the F34 query: "); + return FALSE; + } + + if (!fu_memread_uint16_safe(f34_query->data, + 2, + 0, + &f34_sblmsl, + G_LITTLE_ENDIAN, + error)) { + g_prefix_error_literal(error, "failed to parse the previous SBL version: "); + return FALSE; + } + + if (f34_sblmsl > previous_sbl_version) { + g_debug("updating SBL from version %d.%d to %d.%d", + previous_sbl_version >> 8, + previous_sbl_version & 0xff, + f34_sblmsl >> 8, + f34_sblmsl & 0xff); + need_update_sbl = TRUE; + } + } else { + g_debug("updating SBL for the first time to version %d.%d", + f34_sblmsl >> 8, + f34_sblmsl & 0xff); + need_update_sbl = TRUE; + } + + if (need_update_sbl) { + /* need update SBL */ + g_debug("erasing SBL partition"); + if (!fu_synaptics_rmi_v7_device_erase_partition(self, + FU_RMI_PARTITION_ID_BOOTLOADER, + error)) + return FALSE; + if (!fu_synaptics_rmi_v7_device_write_partition(self, + firmware, + "sbl", + FU_RMI_PARTITION_ID_BOOTLOADER, + bytes_sbl, + progress, + error)) + return FALSE; + } else { + g_debug("skipping SBL update"); + } + + if (!fu_synaptics_rmi_v7_device_enter_sbl(self, error)) { + g_prefix_error_literal(error, "failed to enter SBL mode: "); + return FALSE; + } + + /* success */ + return TRUE; +} + gboolean -fu_synaptics_rmi_v7_device_write_firmware(FuDevice *device, +fu_synaptics_rmi_v7_device_write_firmware(FuSynapticsRmiDevice *self, FuFirmware *firmware, FuProgress *progress, FwupdInstallFlags flags, GError **error) { - FuSynapticsRmiDevice *self = FU_SYNAPTICS_RMI_DEVICE(device); FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); - FuSynapticsRmiFunction *f34; g_autoptr(GBytes) bytes_bin = NULL; g_autoptr(GBytes) bytes_cfg = NULL; g_autoptr(GBytes) bytes_flashcfg = NULL; @@ -579,7 +682,21 @@ /* progress */ fu_progress_set_id(progress, G_STRLOC); - if (flash->bootloader_id[1] > 8) { + if (flash->bootloader_id[1] >= 10 && flash->bootloader_id[0] >= 1) { + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "disable-sleep"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 0, "verify-signature"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "fixed-location-data"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 4, "flash-config"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 4, "sbl"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 9, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 81, "core-code"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "core-config"); + fu_progress_add_step(progress, + FWUPD_STATUS_DEVICE_WRITE, + 0, + "external-touch-afe-config"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 0, "display-config"); + } else if (flash->bootloader_id[1] > 8) { fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "disable-sleep"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_READ, 0, "verify-signature"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 1, "fixed-location-data"); @@ -621,7 +738,7 @@ } /* we should be in bootloader mode now, but check anyway */ - if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + if (!fu_device_has_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -629,11 +746,6 @@ return FALSE; } - /* f34 */ - f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); - if (f34 == NULL) - return FALSE; - /* get both images */ bytes_bin = fu_firmware_get_image_by_id_bytes(firmware, "ui", error); if (bytes_bin == NULL) @@ -691,9 +803,19 @@ fu_progress_step_done(progress); } + /* check whether it need to update SBL for BL > v10.1 */ + if (flash->bootloader_id[1] >= 10 && flash->bootloader_id[0] >= 1) { + if (!fu_synaptics_rmi_v7_device_write_sbl(self, + firmware, + fu_progress_get_child(progress), + error)) + return FALSE; + fu_progress_step_done(progress); + } + /* erase all */ if (!fu_synaptics_rmi_v7_device_erase_all(self, error)) { - g_prefix_error(error, "failed to erase all: "); + g_prefix_error_literal(error, "failed to erase all: "); return FALSE; } fu_progress_step_done(progress); @@ -788,7 +910,7 @@ req_partition_id, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write flash partition id: "); + g_prefix_error_literal(error, "failed to write flash partition id: "); return FALSE; } fu_byte_array_append_uint16(req_addr_zero, 0x0, G_LITTLE_ENDIAN); @@ -797,7 +919,7 @@ req_addr_zero, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write flash config address: "); + g_prefix_error_literal(error, "failed to write flash config address: "); return FALSE; } @@ -808,22 +930,22 @@ req_transfer_length, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to set transfer length: "); + g_prefix_error_literal(error, "failed to set transfer length: "); return FALSE; } /* set command to read */ - fu_byte_array_append_uint8(req_cmd, RMI_FLASH_CMD_READ); + fu_byte_array_append_uint8(req_cmd, FU_SYNAPTICS_RMI_FLASH_CMD_READ); if (!fu_synaptics_rmi_device_write(self, f34->data_base + 0x4, req_cmd, FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to write command to read: "); + g_prefix_error_literal(error, "failed to write command to read: "); return FALSE; } if (!fu_synaptics_rmi_device_poll_wait(self, error)) { - g_prefix_error(error, "failed to wait: "); + g_prefix_error_literal(error, "failed to wait: "); return FALSE; } @@ -834,7 +956,7 @@ (guint32)flash->block_size * (guint32)flash->config_length, error); if (res == NULL) { - g_prefix_error(error, "failed to read: "); + g_prefix_error_literal(error, "failed to read: "); return FALSE; } @@ -847,7 +969,7 @@ /* parse the config length */ for (guint i = 0x2; i < res->len; i += partition_size) { guint16 partition_id; - g_autoptr(GByteArray) st_prt = NULL; + g_autoptr(FuStructRmiPartitionTbl) st_prt = NULL; st_prt = fu_struct_rmi_partition_tbl_parse(res->data, res->len, i, error); if (st_prt == NULL) return FALSE; @@ -883,6 +1005,7 @@ FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); FuSynapticsRmiFunction *f34; guint8 offset; + g_autoptr(FuStructSynapticsRmiV7F34x) st_f34x = NULL; g_autoptr(GByteArray) f34_data0 = NULL; g_autoptr(GByteArray) f34_dataX = NULL; @@ -893,53 +1016,29 @@ f34_data0 = fu_synaptics_rmi_device_read(self, f34->query_base, 1, error); if (f34_data0 == NULL) { - g_prefix_error(error, "failed to read bootloader ID: "); + g_prefix_error_literal(error, "failed to read bootloader ID: "); return FALSE; } + flash->has_security = (f34_data0->data[0] & 0x40) ? TRUE : FALSE; offset = (f34_data0->data[0] & 0b00000111) + 1; f34_dataX = fu_synaptics_rmi_device_read(self, f34->query_base + offset, 21, error); if (f34_dataX == NULL) return FALSE; - if (!fu_memread_uint8_safe(f34_dataX->data, - f34_dataX->len, - 0x0, - &flash->bootloader_id[0], - error)) - return FALSE; - if (!fu_memread_uint8_safe(f34_dataX->data, - f34_dataX->len, - 0x1, - &flash->bootloader_id[1], - error)) - return FALSE; - if (!fu_memread_uint16_safe(f34_dataX->data, - f34_dataX->len, - 0x07, - &flash->block_size, - G_LITTLE_ENDIAN, - error)) - return FALSE; - if (!fu_memread_uint16_safe(f34_dataX->data, - f34_dataX->len, - 0x0d, - &flash->config_length, - G_LITTLE_ENDIAN, - error)) - return FALSE; - if (!fu_memread_uint16_safe(f34_dataX->data, - f34_dataX->len, - 0x0f, - &flash->payload_length, - G_LITTLE_ENDIAN, - error)) - return FALSE; - if (!fu_memread_uint32_safe(f34_dataX->data, - f34_dataX->len, - 0x02, - &flash->build_id, - G_LITTLE_ENDIAN, - error)) - return FALSE; + + st_f34x = + fu_struct_synaptics_rmi_v7_f34x_parse(f34_dataX->data, f34_dataX->len, 0x0, error); + if (st_f34x == NULL) + return FALSE; + flash->bootloader_id[0] = fu_struct_synaptics_rmi_v7_f34x_get_bootloader_id0(st_f34x); + flash->bootloader_id[1] = fu_struct_synaptics_rmi_v7_f34x_get_bootloader_id1(st_f34x); + flash->build_id = fu_struct_synaptics_rmi_v7_f34x_get_build_id(st_f34x); + flash->block_size = fu_struct_synaptics_rmi_v7_f34x_get_block_size(st_f34x); + flash->config_length = fu_struct_synaptics_rmi_v7_f34x_get_config_length(st_f34x); + flash->payload_length = fu_struct_synaptics_rmi_v7_f34x_get_payload_length(st_f34x); + + flash->has_sbl = (fu_struct_synaptics_rmi_v7_f34x_get_supported_partitions(st_f34x) >> + FU_RMI_PARTITION_ID_BOOTLOADER) & + 0x0001; /* sanity check */ if ((guint32)flash->block_size * (guint32)flash->config_length > G_MAXUINT16) { @@ -953,6 +1052,8 @@ } /* read flash config */ + if (flash->bootloader_id[1] >= 10) + return TRUE; return fu_synaptics_rmi_v7_device_read_flash_config(self, error); } @@ -969,7 +1070,7 @@ return FALSE; f34_data = fu_synaptics_rmi_device_read(self, f34->data_base, 0x1, error); if (f34_data == NULL) { - g_prefix_error(error, "failed to read the f01 data base: "); + g_prefix_error_literal(error, "failed to read the f01 data base: "); return FALSE; } status = f34_data->data[0]; @@ -1049,4 +1150,55 @@ return FALSE; } return TRUE; +} + +static gboolean +fu_synaptics_rmi_v7_device_enter_sbl(FuSynapticsRmiDevice *self, GError **error) +{ + FuSynapticsRmiFlash *flash = fu_synaptics_rmi_device_get_flash(self); + FuSynapticsRmiFunction *f34; + g_autoptr(FuStructSynapticsRmiV7EnterSbl) st = fu_struct_synaptics_rmi_v7_enter_sbl_new(); + + /* f34 */ + f34 = fu_synaptics_rmi_device_get_function(self, 0x34, error); + if (f34 == NULL) + return FALSE; + + /* disable interrupts */ + if (!fu_synaptics_rmi_device_disable_irqs(self, error)) + return FALSE; + + /* enter BL */ + fu_struct_synaptics_rmi_v7_enter_sbl_set_bootloader_id0(st, flash->bootloader_id[0]); + fu_struct_synaptics_rmi_v7_enter_sbl_set_bootloader_id1(st, flash->bootloader_id[1]); + if (!fu_synaptics_rmi_device_write(self, + f34->data_base + 1, + st->buf, + FU_SYNAPTICS_RMI_DEVICE_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to enable programming: "); + return FALSE; + } + + /* wait for idle */ + if (!fu_synaptics_rmi_device_wait_for_idle(self, + RMI_F34_ENABLE_WAIT_MS, + FU_SYNAPTICS_RMI_DEVICE_WAIT_FOR_IDLE_FLAG_NONE, + error)) + return FALSE; + if (!fu_synaptics_rmi_device_poll_wait(self, error)) + return FALSE; + fu_device_sleep(FU_DEVICE(self), RMI_F34_ENABLE_SBL_WAIT_MS); + + /* re-scan PDT after idle */ + if (!fu_synaptics_rmi_device_scan_pdt(self, error)) { + g_prefix_error_literal(error, "failed to scan PDT: "); + return FALSE; + } + + if (!fu_synaptics_rmi_v7_device_setup(self, error)) { + g_prefix_error_literal(error, "failed to do v7 setup: "); + return FALSE; + } + return TRUE; } diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.h fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.h --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi-v7-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,9 +9,9 @@ #include "fu-synaptics-rmi-device.h" gboolean -fu_synaptics_rmi_v7_device_detach(FuDevice *device, FuProgress *progress, GError **error); +fu_synaptics_rmi_v7_device_detach(FuSynapticsRmiDevice *self, FuProgress *progress, GError **error); gboolean -fu_synaptics_rmi_v7_device_write_firmware(FuDevice *device, +fu_synaptics_rmi_v7_device_write_firmware(FuSynapticsRmiDevice *self, FuFirmware *firmware, FuProgress *progress, FwupdInstallFlags flags, diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi.rs fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi.rs --- fwupd-2.0.8/plugins/synaptics-rmi/fu-synaptics-rmi.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/fu-synaptics-rmi.rs 2026-02-26 11:36:18.000000000 +0000 @@ -2,7 +2,7 @@ // SPDX-License-Identifier: LGPL-2.1-or-later #[derive(ToString)] -#[repr(u16le)] +#[repr(u8)] enum FuRmiPartitionId { None = 0x00, Bootloader = 0x01, @@ -24,7 +24,7 @@ #[derive(Parse)] #[repr(C, packed)] struct FuStructRmiPartitionTbl { - partition_id: FuRmiPartitionId, + partition_id: u16le, // but actually a FuRmiPartitionId... partition_len: u16le, partition_addr: u16le, partition_prop: u16le, @@ -170,3 +170,78 @@ Synaptics = 6, Unknown = 0xFFFFFFFF, } + +#[repr(u8)] +enum FuSynapticsRmiFlashCmd { + Idle, + EnterBl, + Read, + Write, + Erase, + EraseAp, + SensorId, + Signature, +} + +#[derive(Parse)] +#[repr(C, packed)] +struct FuStructSynapticsRmiV7F34x { + bootloader_id0: u8, + bootloader_id1: u8, + build_id: u32le, + reserved: u8, + block_size: u16le, + reserved: [u8; 4], + config_length: u16le, + payload_length: u16le, + supported_partitions: u16le, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructSynapticsRmiV7EnterSbl { + partition_id: FuRmiPartitionId == Bootloader, + reserved: u32le, + cmd: FuSynapticsRmiFlashCmd == EnterBl, + bootloader_id0: u8, + bootloader_id1: u8, + unknown: u8 == 0x1, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructSynapticsRmiV7EnterBl { + partition_id: FuRmiPartitionId == Bootloader, + reserved: u32le, + cmd: FuSynapticsRmiFlashCmd == EnterBl, + bootloader_id0: u8, + bootloader_id1: u8, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructSynapticsRmiV7Erase { + partition_id: FuRmiPartitionId, + reserved: u32le, + cmd: FuSynapticsRmiFlashCmd == Erase, + bootloader_id0: u8, + bootloader_id1: u8, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructSynapticsRmiV7EraseCoreCode { + partition_id: FuRmiPartitionId == CoreCode, + reserved: u32le, + cmd: FuSynapticsRmiFlashCmd = EraseAp, + bootloader_id0: u8, + bootloader_id1: u8, +} + +#[derive(New, Default)] +#[repr(C, packed)] +struct FuStructSynapticsRmiV7EraseCoreConfig { + partition_id: FuRmiPartitionId == CoreConfig, + reserved: u32le, + cmd: FuSynapticsRmiFlashCmd == Erase, +} diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/meson.build fwupd-2.0.20/plugins/synaptics-rmi/meson.build --- fwupd-2.0.8/plugins/synaptics-rmi/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if gnutls.found() and host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() +gnutls.found() or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginSynapticsRmi"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -25,6 +27,10 @@ ) plugin_builtins += plugin_builtin_synaptics_rmi +device_tests += files( + 'tests/synaptics-rmi-tm4066.json', +) + if get_option('tests') install_data(['tests/synaptics-rmi-0x.builder.xml','tests/synaptics-rmi-10.builder.xml'], install_dir: join_paths(installed_test_datadir, 'tests')) @@ -44,6 +50,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', @@ -51,4 +58,3 @@ ) test('synaptics-rmi-self-test', e, env: env) endif -endif diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/synaptics-rmi.quirk fwupd-2.0.20/plugins/synaptics-rmi/synaptics-rmi.quirk --- fwupd-2.0.8/plugins/synaptics-rmi/synaptics-rmi.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/synaptics-rmi.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -2,7 +2,6 @@ [HIDRAW\VEN_06CB&DEV_2819] Plugin = synaptics_rmi GType = FuSynapticsRmiHidDevice -Vendor = Synaptics Flags = only-supported # LENOVO X1 Nano @@ -15,3 +14,20 @@ Plugin = synaptics_rmi GType = FuSynapticsRmiPs2Device InstallDuration = 236 + +# ALSO +[HIDRAW\VEN_06CB&DEV_96E7] +Plugin = synaptics_rmi +GType = FuSynapticsRmiHidDevice +Vendor = Synaptics + +[HIDRAW\VEN_06CB&DEV_D014] +Plugin = synaptics_rmi +GType = FuSynapticsRmiHidDevice +Vendor = Synaptics + +# HP Lapaz14N +[HIDRAW\VEN_06CB&DEV_CFE2] +Plugin = synaptics_rmi +GType = FuSynapticsRmiHidDevice +Vendor = Synaptics diff -Nru fwupd-2.0.8/plugins/synaptics-rmi/tests/synaptics-rmi-tm4066.json fwupd-2.0.20/plugins/synaptics-rmi/tests/synaptics-rmi-tm4066.json --- fwupd-2.0.8/plugins/synaptics-rmi/tests/synaptics-rmi-tm4066.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-rmi/tests/synaptics-rmi-tm4066.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +{ + "name": "Synaptics RMI (TM4066)", + "interactive": false, + "steps": [ + { + "url": "edc2518b2cbc707ac4099db425084ee4bb47a657aa20519ded8e221df450d14c-Synaptics-RMI-touchpad.4442899.cab", + "emulation-url": "6b7370eba5569643909af12831d48caa03806a273ce6edcf6eba85537571a1e6-synaptics_HapticsPad.zip", + "components": [ + { + "version": "1.2.4442899", + "guids": [ + "cee8e2d1-bb2a-54bf-9a9f-3d38359dc76a" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/synaptics-vmm9/README.md fwupd-2.0.20/plugins/synaptics-vmm9/README.md --- fwupd-2.0.8/plugins/synaptics-vmm9/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-vmm9/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -54,10 +54,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.9.20`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Apollo Ling: @ApolloLing diff -Nru fwupd-2.0.8/plugins/synaptics-vmm9/fu-synaptics-vmm9-device.c fwupd-2.0.20/plugins/synaptics-vmm9/fu-synaptics-vmm9-device.c --- fwupd-2.0.8/plugins/synaptics-vmm9/fu-synaptics-vmm9-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-vmm9/fu-synaptics-vmm9-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -27,18 +27,6 @@ #define FU_SYNAPTICS_VMM9_CTRL_BUSY_MASK 0x80 #define FU_SYNAPTICS_VMM9_BUSY_POLL 10 /* ms */ -#define FU_SYNAPTICS_VMM9_MEM_OFFSET_CHIP_SERIAL 0x20200D3C /* 0x4 bytes, %02x */ -#define FU_SYNAPTICS_VMM9_MEM_OFFSET_RC_TRIGGER 0x2020A024 /* write 0xF5000000 to reset */ -#define FU_SYNAPTICS_VMM9_MEM_OFFSET_MCU_BOOTLOADER_STS 0x2020A030 /* bootloader status */ -#define FU_SYNAPTICS_VMM9_MEM_OFFSET_MCU_FW_VERSION 0x2020A038 /* 0x4 bytes, maj.min.mic.? */ -#define FU_SYNAPTICS_VMM9_MEM_OFFSET_FIRMWARE_BUILD 0x2020A084 /* 0x4 bytes, be */ -#define FU_SYNAPTICS_VMM9_MEM_OFFSET_RC_COMMAND 0x2020B000 -#define FU_SYNAPTICS_VMM9_MEM_OFFSET_RC_OFFSET 0x2020B004 -#define FU_SYNAPTICS_VMM9_MEM_OFFSET_RC_LENGTH 0x2020B008 -#define FU_SYNAPTICS_VMM9_MEM_OFFSET_RC_DATA 0x2020B010 /* until 0x2020B02C */ -#define FU_SYNAPTICS_VMM9_MEM_OFFSET_FIRMWARE_NAME 0x90000230 /* 0xF bytes, ASCII */ -#define FU_SYNAPTICS_VMM9_MEM_OFFSET_BOARD_ID 0x9000014E /* 0x2 bytes, customer.hardware */ - static void fu_synaptics_vmm9_device_to_string(FuDevice *device, guint idt, GString *str) { @@ -53,7 +41,7 @@ FU_SYNAPTICS_VMM9_COMMAND_FLAG_FULL_BUFFER = 1 << 0, FU_SYNAPTICS_VMM9_COMMAND_FLAG_NO_REPLY = 1 << 1, FU_SYNAPTICS_VMM9_COMMAND_FLAG_IGNORE_REPLY = 1 << 2, -} FuSynapticsVmm9DeviceCommandFlags; +} G_GNUC_FLAG_ENUM FuSynapticsVmm9DeviceCommandFlags; typedef struct { guint8 *buf; @@ -61,7 +49,7 @@ } FuSynapticsVmm9DeviceCommandHelper; static gboolean -fu_synaptics_vmm9_device_command_cb(FuDevice *self, gpointer user_data, GError **error) +fu_synaptics_vmm9_device_command_cb(FuDevice *device, gpointer user_data, GError **error) { FuSynapticsVmm9DeviceCommandHelper *helper = (FuSynapticsVmm9DeviceCommandHelper *)user_data; @@ -70,22 +58,32 @@ g_autoptr(FuStructHidPayload) st_payload = NULL; /* get, and parse */ - if (!fu_hid_device_get_report(FU_HID_DEVICE(self), + if (!fu_hid_device_get_report(FU_HID_DEVICE(device), FU_STRUCT_HID_GET_COMMAND_DEFAULT_ID, buf, sizeof(buf), FU_SYNAPTICS_VMM9_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to send packet: "); + g_prefix_error_literal(error, "failed to send packet: "); return FALSE; } st = fu_struct_hid_get_command_parse(buf, sizeof(buf), 0x0, error); if (st == NULL) return FALSE; - /* sanity check */ + /* check if RC mode is already enabled */ st_payload = fu_struct_hid_get_command_get_payload(st); + if (fu_struct_hid_payload_get_sts(st_payload) == FU_SYNAPTICS_VMM9_RC_STS_INVALID && + fu_struct_hid_payload_get_ctrl(st_payload) == FU_SYNAPTICS_VMM9_RC_CTRL_ENABLE_RC) { + g_debug( + "RC already enabled, sts is %s [0x%x]", + fu_synaptics_vmm9_rc_sts_to_string(fu_struct_hid_payload_get_sts(st_payload)), + fu_struct_hid_payload_get_sts(st_payload)); + return TRUE; + } + + /* sanity check */ if (fu_struct_hid_payload_get_sts(st_payload) != FU_SYNAPTICS_VMM9_RC_STS_SUCCESS) { g_set_error( error, @@ -153,28 +151,32 @@ fu_struct_hid_set_command_set_size(st, FU_STRUCT_HID_PAYLOAD_OFFSET_FIFO + src_bufsz); if (!fu_struct_hid_set_command_set_payload(st, st_payload, error)) return FALSE; - checksum = 0x100 - fu_sum8(st->data + 1, st->len - 1); + checksum = 0x100 - fu_sum8(st->buf->data + 1, st->buf->len - 1); if (flags & FU_SYNAPTICS_VMM9_COMMAND_FLAG_FULL_BUFFER) { fu_struct_hid_set_command_set_checksum(st, checksum); } else { goffset offset_checksum = FU_STRUCT_HID_SET_COMMAND_OFFSET_PAYLOAD + FU_STRUCT_HID_PAYLOAD_OFFSET_FIFO + src_bufsz; - if (!fu_memwrite_uint8_safe(st->data, st->len, offset_checksum, checksum, error)) + if (!fu_memwrite_uint8_safe(st->buf->data, + st->buf->len, + offset_checksum, + checksum, + error)) return FALSE; } - fu_byte_array_set_size(st, FU_SYNAPTICS_VMM9_DEVICE_REPORT_SIZE, 0x0); + fu_byte_array_set_size(st->buf, FU_SYNAPTICS_VMM9_DEVICE_REPORT_SIZE, 0x0); /* set */ str = fu_struct_hid_set_command_to_string(st); g_debug("%s", str); if (!fu_hid_device_set_report(FU_HID_DEVICE(self), FU_STRUCT_HID_SET_COMMAND_DEFAULT_ID, - st->data, - st->len, + st->buf->data, + st->buf->len, FU_SYNAPTICS_VMM9_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to send packet: "); + g_prefix_error_literal(error, "failed to send packet: "); return FALSE; } @@ -327,9 +329,10 @@ 0, FU_SYNAPTICS_VMM9_COMMAND_FLAG_NO_REPLY, error)) { - g_prefix_error(error, "failed to DISABLE_RC before ENABLE_RC: "); + g_prefix_error_literal(error, "failed to DISABLE_RC before ENABLE_RC: "); return FALSE; } + fu_device_sleep(device, 10); if (!fu_synaptics_vmm9_device_command(self, FU_SYNAPTICS_VMM9_RC_CTRL_ENABLE_RC, 0x0, /* offset */ @@ -339,7 +342,7 @@ 0, FU_SYNAPTICS_VMM9_COMMAND_FLAG_FULL_BUFFER, error)) { - g_prefix_error(error, "failed to ENABLE_RC: "); + g_prefix_error_literal(error, "failed to ENABLE_RC: "); return FALSE; } @@ -362,7 +365,7 @@ 0x0, FU_SYNAPTICS_VMM9_COMMAND_FLAG_NONE, error)) { - g_prefix_error(error, "failed to DISABLE_RC: "); + g_prefix_error_literal(error, "failed to DISABLE_RC: "); return FALSE; } @@ -378,7 +381,7 @@ fu_synaptics_vmm9_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSynapticsVmm9Device *self = FU_SYNAPTICS_VMM9_DEVICE(device); @@ -396,7 +399,7 @@ return NULL; /* verify this firmware is for this hardware */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID) == 0) { if (self->board_id != fu_synaptics_vmm9_firmware_get_board_id(FU_SYNAPTICS_VMM9_FIRMWARE(firmware))) { g_set_error(error, @@ -452,7 +455,7 @@ FU_SYNAPTICS_VMM9_COMMAND_FLAG_NONE, error)) { g_prefix_error(error, - "failed at page %u, @0x%x", + "failed at page %u, @0x%x: ", fu_chunk_get_idx(chk), (guint)fu_chunk_get_address(chk)); return FALSE; @@ -479,7 +482,7 @@ 0, FU_SYNAPTICS_VMM9_COMMAND_FLAG_NONE, error)) { - g_prefix_error(error, "failed to erase: "); + g_prefix_error_literal(error, "failed to erase: "); return FALSE; } return TRUE; @@ -512,7 +515,7 @@ FU_SYNAPTICS_VMM9_COMMAND_FLAG_NONE, error)) { g_prefix_error(error, - "failed at chunk %u, @0x%x", + "failed at chunk %u, @0x%x: ", fu_chunk_get_idx(chk), (guint)fu_chunk_get_address(chk)); return NULL; @@ -524,7 +527,7 @@ /* parse */ fw = g_bytes_new_take(g_steal_pointer(&buf), bufsz); - if (!fu_firmware_parse_bytes(firmware, fw, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_bytes(firmware, fw, 0x0, FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, error)) return NULL; /* success */ @@ -551,7 +554,7 @@ /* erase the storage bank */ if (!fu_synaptics_vmm9_device_erase(self, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to erase: "); + g_prefix_error_literal(error, "failed to erase: "); return FALSE; } fu_progress_step_done(progress); @@ -575,7 +578,7 @@ chunks, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write: "); + g_prefix_error_literal(error, "failed to write: "); return FALSE; } fu_device_sleep(device, 10); @@ -591,7 +594,7 @@ 0, FU_SYNAPTICS_VMM9_COMMAND_FLAG_NONE, error)) { - g_prefix_error(error, "failed to activate: "); + g_prefix_error_literal(error, "failed to activate: "); return FALSE; } fu_progress_step_done(progress); @@ -599,7 +602,7 @@ /* if device reboot is not required */ if (fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_SKIPS_RESTART)) { fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INSTALL_SKIP_VERSION_CHECK); - g_debug("skipped device reboot intentionally."); + g_debug("skipped device reboot intentionally"); return TRUE; } @@ -626,7 +629,7 @@ FU_SYNAPTICS_VMM9_COMMAND_FLAG_FULL_BUFFER | FU_SYNAPTICS_VMM9_COMMAND_FLAG_IGNORE_REPLY, error)) { - g_prefix_error(error, "failed to reboot: "); + g_prefix_error_literal(error, "failed to reboot: "); return FALSE; } } @@ -637,7 +640,7 @@ } static void -fu_synaptics_vmm9_device_set_progress(FuDevice *self, FuProgress *progress) +fu_synaptics_vmm9_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/synaptics-vmm9/fu-synaptics-vmm9-firmware.c fwupd-2.0.20/plugins/synaptics-vmm9/fu-synaptics-vmm9-firmware.c --- fwupd-2.0.8/plugins/synaptics-vmm9/fu-synaptics-vmm9-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-vmm9/fu-synaptics-vmm9-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -45,11 +45,11 @@ static gboolean fu_synaptics_vmm9_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuSynapticsVmm9Firmware *self = FU_SYNAPTICS_VMM9_FIRMWARE(firmware); - g_autoptr(GByteArray) st_hdr = NULL; + g_autoptr(FuStructSynapticsVmm9) st_hdr = NULL; guint8 version_major = 0; guint8 version_minor = 0; guint16 version_micro = 0; diff -Nru fwupd-2.0.8/plugins/synaptics-vmm9/fu-synaptics-vmm9-plugin.c fwupd-2.0.20/plugins/synaptics-vmm9/fu-synaptics-vmm9-plugin.c --- fwupd-2.0.8/plugins/synaptics-vmm9/fu-synaptics-vmm9-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-vmm9/fu-synaptics-vmm9-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,12 +19,14 @@ static void fu_synaptics_vmm9_plugin_init(FuSynapticsVmm9Plugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void fu_synaptics_vmm9_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_SYNAPTICS_VMM9_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_SYNAPTICS_VMM9_FIRMWARE); } diff -Nru fwupd-2.0.8/plugins/synaptics-vmm9/fu-synaptics-vmm9.rs fwupd-2.0.20/plugins/synaptics-vmm9/fu-synaptics-vmm9.rs --- fwupd-2.0.8/plugins/synaptics-vmm9/fu-synaptics-vmm9.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-vmm9/fu-synaptics-vmm9.rs 2026-02-26 11:36:18.000000000 +0000 @@ -33,6 +33,20 @@ MemoryReadBusy = 0x80|0x31, } +enum FuSynapticsVmm9MemOffset { + ChipSerial = 0x20200D3C, // 0x4 bytes, %02x + RcTrigger = 0x2020A024, // write 0xF5000000 to reset + McuBootloaderSts = 0x2020A030, // bootloader status + McuFwVersion = 0x2020A038, // 0x4 bytes, maj.min.mic.? + FirmwareBuild = 0x2020A084, // 0x4 bytes, be + RcCommand = 0x2020B000, + RcOffset = 0x2020B004, + RcLength = 0x2020B008, + RcData = 0x2020B010, // until 0x2020B02C + FirmwareName = 0x90000230, // 0xF bytes, ASCII + BoardId = 0x9000014E, // 0x2 bytes, customer.hardware +} + #[derive(ToString)] #[repr(u8)] enum FuSynapticsVmm9RcSts { diff -Nru fwupd-2.0.8/plugins/synaptics-vmm9/tests/synaptics-vmm9430evb.json fwupd-2.0.20/plugins/synaptics-vmm9/tests/synaptics-vmm9430evb.json --- fwupd-2.0.8/plugins/synaptics-vmm9/tests/synaptics-vmm9430evb.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/synaptics-vmm9/tests/synaptics-vmm9430evb.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/85caf8d1913b9d6ec29b8c9d3db8609c5376b1ac9eaf3a109d1867493bc283ef-Synaptics-Carrera_EVB-9.02.008.cab", - "emulation-url": "https://fwupd.org/downloads/7d8faaa922e0a46e83bfed30ce04170f9f76ae2c6966080152a26ec183f4a527-Synaptics-Carrera_EVB-9.02.008.zip", + "url": "85caf8d1913b9d6ec29b8c9d3db8609c5376b1ac9eaf3a109d1867493bc283ef-Synaptics-Carrera_EVB-9.02.008.cab", + "emulation-url": "7d8faaa922e0a46e83bfed30ce04170f9f76ae2c6966080152a26ec183f4a527-Synaptics-Carrera_EVB-9.02.008.zip", "components": [ { "version": "9.02.008", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/54925eceadd0dfca3d203230041c45623e8f99b2ff20d7aee4a55a67a94e5369-Synaptics-Carrera_EVB-9.02.009.cab", - "emulation-url": "https://fwupd.org/downloads/4023ba7cb2a6cb370116eefe75a8d07db95ff6f0157044d005fc38a004f1781c-Synaptics-Carrera_EVB-9.02.009.zip", + "url": "54925eceadd0dfca3d203230041c45623e8f99b2ff20d7aee4a55a67a94e5369-Synaptics-Carrera_EVB-9.02.009.cab", + "emulation-url": "4023ba7cb2a6cb370116eefe75a8d07db95ff6f0157044d005fc38a004f1781c-Synaptics-Carrera_EVB-9.02.009.zip", "components": [ { "version": "9.02.009", diff -Nru fwupd-2.0.8/plugins/system76-launch/README.md fwupd-2.0.20/plugins/system76-launch/README.md --- fwupd-2.0.8/plugins/system76-launch/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/system76-launch/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -37,10 +37,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.5.6`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Jeremy Soller: @jackpot51 diff -Nru fwupd-2.0.8/plugins/system76-launch/fu-system76-launch-device.c fwupd-2.0.20/plugins/system76-launch/fu-system76-launch-device.c --- fwupd-2.0.8/plugins/system76-launch/fu-system76-launch-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/system76-launch/fu-system76-launch-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -42,7 +42,7 @@ SYSTEM76_LAUNCH_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to read response: "); + g_prefix_error_literal(error, "failed to read response: "); return FALSE; } if (actual_len < helper->len) { @@ -59,14 +59,17 @@ } static gboolean -fu_system76_launch_device_command(FuDevice *device, guint8 *data, gsize len, GError **error) +fu_system76_launch_device_command(FuSystem76LaunchDevice *self, + guint8 *data, + gsize len, + GError **error) { const guint8 ep_out = 0x03; gsize actual_len = 0; FuSystem76LaunchDeviceHelper helper = {.data = data, .len = len}; /* send command */ - if (!fu_usb_device_interrupt_transfer(FU_USB_DEVICE(device), + if (!fu_usb_device_interrupt_transfer(FU_USB_DEVICE(self), ep_out, data, len, @@ -74,7 +77,7 @@ SYSTEM76_LAUNCH_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to send command: "); + g_prefix_error_literal(error, "failed to send command: "); return FALSE; } if (actual_len < len) { @@ -85,18 +88,23 @@ actual_len); return FALSE; } - return fu_device_retry(device, fu_system76_launch_device_response_cb, 5, &helper, error); + return fu_device_retry(FU_DEVICE(self), + fu_system76_launch_device_response_cb, + 5, + &helper, + error); } static gboolean fu_system76_launch_device_version_cb(FuDevice *device, gpointer user_data, GError **error) { + FuSystem76LaunchDevice *self = FU_SYSTEM76_LAUNCH_DEVICE(device); guint8 data[32] = {SYSTEM76_LAUNCH_CMD_VERSION, 0}; g_autofree gchar *version = NULL; /* execute version command */ - if (!fu_system76_launch_device_command(device, data, sizeof(data), error)) { - g_prefix_error(error, "failed to execute version command: "); + if (!fu_system76_launch_device_command(self, data, sizeof(data), error)) { + g_prefix_error_literal(error, "failed to execute version command: "); return FALSE; } @@ -123,13 +131,13 @@ } static gboolean -fu_system76_launch_device_reset(FuDevice *device, guint8 *rc, GError **error) +fu_system76_launch_device_reset(FuSystem76LaunchDevice *self, guint8 *rc, GError **error) { guint8 data[32] = {SYSTEM76_LAUNCH_CMD_RESET, 0}; /* execute reset command */ - if (!fu_system76_launch_device_command(device, data, sizeof(data), error)) { - g_prefix_error(error, "failed to execute reset command: "); + if (!fu_system76_launch_device_command(self, data, sizeof(data), error)) { + g_prefix_error_literal(error, "failed to execute reset command: "); return FALSE; } @@ -138,7 +146,7 @@ } static gboolean -fu_system76_launch_device_security_set(FuDevice *device, +fu_system76_launch_device_security_set(FuSystem76LaunchDevice *self, FuSystem76LaunchSecurityState state, guint8 *rc, GError **error) @@ -146,8 +154,8 @@ guint8 data[32] = {SYSTEM76_LAUNCH_CMD_SECURITY_SET, 0, state, 0}; /* execute security set command */ - if (!fu_system76_launch_device_command(device, data, sizeof(data), error)) { - g_prefix_error(error, "failed to execute security set command: "); + if (!fu_system76_launch_device_command(self, data, sizeof(data), error)) { + g_prefix_error_literal(error, "failed to execute security set command: "); return FALSE; } @@ -158,12 +166,13 @@ static gboolean fu_system76_launch_device_detach(FuDevice *device, FuProgress *progress, GError **error) { + FuSystem76LaunchDevice *self = FU_SYSTEM76_LAUNCH_DEVICE(device); guint8 rc = 0x0; g_autoptr(FwupdRequest) request = fwupd_request_new(); g_autoptr(GTimer) timer = g_timer_new(); /* prompt for unlock if reset was blocked */ - if (!fu_system76_launch_device_reset(device, &rc, error)) + if (!fu_system76_launch_device_reset(self, &rc, error)) return FALSE; /* unlikely, but already unlocked */ @@ -174,7 +183,7 @@ /* notify device of desire to unlock */ if (!fu_system76_launch_device_security_set( - device, + self, FU_SYSTEM76_LAUNCH_SECURITY_STATE_PREPARE_UNLOCK, &rc, error)) @@ -213,7 +222,7 @@ /* poll for the user-unlock */ do { fu_device_sleep(device, 1000); /* ms */ - if (!fu_system76_launch_device_reset(device, &rc, error)) + if (!fu_system76_launch_device_reset(self, &rc, error)) return FALSE; } while (rc != 0 && g_timer_elapsed(timer, NULL) * 1000.f < FU_DEVICE_REMOVE_DELAY_USER_REPLUG); @@ -231,7 +240,7 @@ } static void -fu_system76_launch_device_set_progress(FuDevice *self, FuProgress *progress) +fu_system76_launch_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/system76-launch/fu-system76-launch-plugin.c fwupd-2.0.20/plugins/system76-launch/fu-system76-launch-plugin.c --- fwupd-2.0.8/plugins/system76-launch/fu-system76-launch-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/system76-launch/fu-system76-launch-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,6 +25,7 @@ fu_system76_launch_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_SYSTEM76_LAUNCH_DEVICE); } diff -Nru fwupd-2.0.8/plugins/telink-dfu/README.md fwupd-2.0.20/plugins/telink-dfu/README.md --- fwupd-2.0.8/plugins/telink-dfu/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/telink-dfu/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -54,10 +54,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.0`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Liang-Jiazhi: @liangjiazhi-telink diff -Nru fwupd-2.0.8/plugins/telink-dfu/fu-telink-dfu-archive.c fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu-archive.c --- fwupd-2.0.8/plugins/telink-dfu/fu-telink-dfu-archive.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu-archive.c 2026-02-26 11:36:18.000000000 +0000 @@ -22,7 +22,7 @@ FuArchive *archive, JsonObject *obj, guint i, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { struct { @@ -101,7 +101,8 @@ guint image_addr = json_object_get_int_member(obj, "load_address"); fu_firmware_set_addr(image, image_addr); } - fu_firmware_add_image(FU_FIRMWARE(self), image); + if (!fu_firmware_add_image(FU_FIRMWARE(self), image, error)) + return FALSE; if (!json_object_has_member(obj, "image_version")) { g_set_error_literal(error, @@ -123,7 +124,7 @@ static gboolean fu_telink_dfu_archive_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuTelinkDfuArchive *self = FU_TELINK_DFU_ARCHIVE(firmware); @@ -149,7 +150,7 @@ g_bytes_get_data(manifest, NULL), g_bytes_get_size(manifest), error)) { - g_prefix_error(error, "manifest not in JSON format: "); + g_prefix_error_literal(error, "manifest not in JSON format: "); return FALSE; } json_root_node = json_parser_get_root(parser); diff -Nru fwupd-2.0.8/plugins/telink-dfu/fu-telink-dfu-ble-device.c fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu-ble-device.c --- fwupd-2.0.8/plugins/telink-dfu/fu-telink-dfu-ble-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu-ble-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -35,7 +35,7 @@ } fu_struct_telink_dfu_ble_pkt_set_crc( st_pkt, - ~fu_crc16(FU_CRC_KIND_B16_USB, st_pkt->data, st_pkt->len - 2)); + ~fu_crc16(FU_CRC_KIND_B16_USB, st_pkt->buf->data, st_pkt->buf->len - 2)); return g_steal_pointer(&st_pkt); } @@ -64,7 +64,7 @@ return FALSE; if (!fu_bluez_device_write(FU_BLUEZ_DEVICE(self), FU_TELINK_DFU_BLE_DEVICE_UUID_OTA, - st_pkt, + st_pkt->buf, error)) return FALSE; fu_device_sleep(FU_DEVICE(self), 5); @@ -89,7 +89,7 @@ return FALSE; if (!fu_bluez_device_write(FU_BLUEZ_DEVICE(self), FU_TELINK_DFU_BLE_DEVICE_UUID_OTA, - st_pkt, + st_pkt->buf, error)) return FALSE; @@ -109,14 +109,14 @@ fu_struct_telink_dfu_end_check_set_pkt_index(st_end_check, pkt_index); fu_struct_telink_dfu_end_check_set_inverted_pkt_index(st_end_check, ~pkt_index); st_pkt = fu_telink_dfu_ble_device_create_packet(FU_TELINK_DFU_CMD_OTA_END, - st_end_check->data, - st_end_check->len, + st_end_check->buf->data, + st_end_check->buf->len, error); if (st_pkt == NULL) return FALSE; if (!fu_bluez_device_write(FU_BLUEZ_DEVICE(self), FU_TELINK_DFU_BLE_DEVICE_UUID_OTA, - st_pkt, + st_pkt->buf, error)) return FALSE; @@ -149,7 +149,7 @@ return FALSE; if (!fu_bluez_device_write(FU_BLUEZ_DEVICE(self), FU_TELINK_DFU_BLE_DEVICE_UUID_OTA, - st_pkt, + st_pkt->buf, error)) return FALSE; fu_device_sleep(FU_DEVICE(self), 5); @@ -207,7 +207,7 @@ } static void -fu_telink_dfu_ble_device_set_progress(FuDevice *self, FuProgress *progress) +fu_telink_dfu_ble_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/telink-dfu/fu-telink-dfu-hid-device.c fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu-hid-device.c --- fwupd-2.0.8/plugins/telink-dfu/fu-telink-dfu-hid-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu-hid-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -43,7 +43,8 @@ { guint16 ota_data_len; g_autoptr(FuStructTelinkDfuHidPkt) st_pkt = fu_struct_telink_dfu_hid_pkt_new(); - g_autoptr(FuStructTelinkDfuHidPkt) st_payload = fu_struct_telink_dfu_hid_pkt_payload_new(); + g_autoptr(FuStructTelinkDfuHidPktPayload) st_payload = + fu_struct_telink_dfu_hid_pkt_payload_new(); switch (cmd) { case FU_TELINK_DFU_CMD_OTA_FW_VERSION: @@ -74,7 +75,7 @@ /* exclude the ota_cmd field */ fu_struct_telink_dfu_hid_pkt_payload_set_crc( st_payload, - ~fu_crc16(FU_CRC_KIND_B16_USB, st_payload->data, st_payload->len - 2)); + ~fu_crc16(FU_CRC_KIND_B16_USB, st_payload->buf->data, st_payload->buf->len - 2)); fu_struct_telink_dfu_hid_pkt_set_ota_data_len(st_pkt, ota_data_len); if (!fu_struct_telink_dfu_hid_pkt_set_payload(st_pkt, st_payload, error)) return NULL; @@ -83,10 +84,7 @@ } static gboolean -fu_telink_dfu_hid_device_write(FuTelinkDfuHidDevice *self, - const guint8 *buf, - gsize bufsz, - GError **error) +fu_telink_dfu_hid_device_write(FuTelinkDfuHidDevice *self, GByteArray *buf, GError **error) { FuHidDeviceFlags set_report_flag = FU_HID_DEVICE_FLAG_NONE; guint8 buf_mut[FU_TELINK_DFU_HID_DEVICE_OTA_LENGTH] = {0}; @@ -94,7 +92,14 @@ if (self->windows_hid_tool_ver >= FU_TELINK_DEVICE_WINDOWS_TOOL_VERSION(5, 2)) set_report_flag = FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER; - if (!fu_memcpy_safe(buf_mut, sizeof(buf_mut), 0x0, buf, bufsz, 0x0, bufsz, error)) + if (!fu_memcpy_safe(buf_mut, + sizeof(buf_mut), + 0x0, + buf->data, + buf->len, + 0x0, + buf->len, + error)) return FALSE; return fu_hid_device_set_report(FU_HID_DEVICE(self), FU_TELINK_DFU_HID_DEVICE_REPORT_ID, @@ -169,21 +174,18 @@ st_payload, error)) return FALSE; - if (!fu_telink_dfu_hid_device_write(self, - st_long_pkt->data, - st_long_pkt->len, - error)) + if (!fu_telink_dfu_hid_device_write(self, st_long_pkt->buf, error)) return FALSE; } else { /* should not reach here */ - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "wrong payload index"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "wrong payload index"); return FALSE; } } else { - if (!fu_telink_dfu_hid_device_write(self, st_pkt->data, st_pkt->len, error)) + if (!fu_telink_dfu_hid_device_write(self, st_pkt->buf, error)) return FALSE; fu_device_sleep(FU_DEVICE(self), 20); } @@ -194,10 +196,7 @@ if (self->windows_hid_tool_ver >= FU_TELINK_DEVICE_WINDOWS_TOOL_VERSION(5, 2) && payload_index != 2) { - if (!fu_telink_dfu_hid_device_write(self, - st_long_pkt->data, - st_long_pkt->len, - error)) + if (!fu_telink_dfu_hid_device_write(self, st_long_pkt->buf, error)) return FALSE; } @@ -227,13 +226,10 @@ st_payload, error)) return FALSE; - if (!fu_telink_dfu_hid_device_write(self, - st_long_pkt->data, - st_long_pkt->len, - error)) + if (!fu_telink_dfu_hid_device_write(self, st_long_pkt->buf, error)) return FALSE; } else { - if (!fu_telink_dfu_hid_device_write(self, st_pkt->data, st_pkt->len, error)) + if (!fu_telink_dfu_hid_device_write(self, st_pkt->buf, error)) return FALSE; } @@ -256,8 +252,8 @@ else fu_struct_telink_dfu_end_check_set_inverted_pkt_index(st_end_check, ~pkt_index); st_pkt = fu_telink_dfu_hid_device_create_packet(FU_TELINK_DFU_CMD_OTA_END, - st_end_check->data, - st_end_check->len, + st_end_check->buf->data, + st_end_check->buf->len, error); if (st_pkt == NULL) return FALSE; @@ -272,13 +268,10 @@ st_payload, error)) return FALSE; - if (!fu_telink_dfu_hid_device_write(self, - st_long_pkt->data, - st_long_pkt->len, - error)) + if (!fu_telink_dfu_hid_device_write(self, st_long_pkt->buf, error)) return FALSE; } else { - if (!fu_telink_dfu_hid_device_write(self, st_pkt->data, st_pkt->len, error)) + if (!fu_telink_dfu_hid_device_write(self, st_pkt->buf, error)) return FALSE; } @@ -354,7 +347,7 @@ } static void -fu_telink_dfu_hid_device_set_progress(FuDevice *self, FuProgress *progress) +fu_telink_dfu_hid_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/telink-dfu/fu-telink-dfu-plugin.c fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu-plugin.c --- fwupd-2.0.8/plugins/telink-dfu/fu-telink-dfu-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -20,6 +20,7 @@ static void fu_telink_dfu_plugin_init(FuTelinkDfuPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void diff -Nru fwupd-2.0.8/plugins/telink-dfu/fu-telink-dfu.rs fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu.rs --- fwupd-2.0.8/plugins/telink-dfu/fu-telink-dfu.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/telink-dfu/fu-telink-dfu.rs 2026-02-26 11:36:18.000000000 +0000 @@ -23,7 +23,7 @@ #[repr(C, packed)] struct FuStructTelinkDfuBlePkt { preamble: u16le, - payload: [u8; 16] = 0xFF, + payload: [u8; 16] = [0xFF; 16], crc: u16le, } @@ -31,7 +31,7 @@ #[repr(C, packed)] struct FuStructTelinkDfuHidPktPayload { ota_cmd: u16le = 0xFFFF, - ota_data: [u8; 16] = 0xFF, + ota_data: [u8; 16] = [0xFF; 16], crc: u16le = 0xFFFF, } diff -Nru fwupd-2.0.8/plugins/telink-dfu/tests/telink-ryder-dongle.json fwupd-2.0.20/plugins/telink-dfu/tests/telink-ryder-dongle.json --- fwupd-2.0.8/plugins/telink-dfu/tests/telink-ryder-dongle.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/telink-dfu/tests/telink-ryder-dongle.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/35ee97796aae0990cb403ffb0d6bf123e75c67ed9a3d096045580d1f69197271-primax-ryder_dongle-v1.25.cab", - "emulation-url": "https://fwupd.org/downloads/5e9f7e8686c1a9bd7735745daf2c9d0fd1413cd1e7d3ccb651487218be77cf18-ryder-dongle-v1.25-emulation.zip", + "url": "35ee97796aae0990cb403ffb0d6bf123e75c67ed9a3d096045580d1f69197271-primax-ryder_dongle-v1.25.cab", + "emulation-url": "5e9f7e8686c1a9bd7735745daf2c9d0fd1413cd1e7d3ccb651487218be77cf18-ryder-dongle-v1.25-emulation.zip", "components": [ { "version": "1.25", diff -Nru fwupd-2.0.8/plugins/test/fu-test-device.c fwupd-2.0.20/plugins/test/fu-test-device.c --- fwupd-2.0.8/plugins/test/fu-test-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/test/fu-test-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,69 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-test-device.h" + +struct _FuTestDevice { + FuDevice parent_instance; +}; + +G_DEFINE_TYPE(FuTestDevice, fu_test_device, FU_TYPE_DEVICE) + +static void +fu_test_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 1, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 3, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 33, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 3, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 61, "reload"); +} + +static gboolean +fu_test_device_probe(FuDevice *device, GError **error) +{ + fu_device_add_instance_id(device, "b585990a-003e-5270-89d5-3705a17f9a43"); + return TRUE; +} + +static void +fu_test_device_init(FuTestDevice *self) +{ + fu_device_set_id(FU_DEVICE(self), "FakeDevice"); + fu_device_set_name(FU_DEVICE(self), "Integrated_Webcam(TM)"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_WEB_CAMERA); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_IGNORE_SYSTEM_POWER); + fu_device_add_request_flag(FU_DEVICE(self), FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE); + fu_device_add_protocol(FU_DEVICE(self), "com.acme.test"); + fu_device_set_summary(FU_DEVICE(self), "Fake webcam"); + fu_device_set_vendor(FU_DEVICE(self), "ACME Corp."); + fu_device_build_vendor_id_u16(FU_DEVICE(self), "USB", 0x046D); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); + fu_device_set_version_bootloader(FU_DEVICE(self), "0.1.2"); + fu_device_set_version(FU_DEVICE(self), "1.2.2"); + fu_device_set_version_lowest(FU_DEVICE(self), "1.2.0"); +} + +static void +fu_test_device_class_init(FuTestDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->set_progress = fu_test_device_set_progress; + device_class->probe = fu_test_device_probe; +} + +FuDevice * +fu_test_device_new(FuContext *ctx) +{ + return g_object_new(FU_TYPE_TEST_DEVICE, "context", ctx, NULL); +} diff -Nru fwupd-2.0.8/plugins/test/fu-test-device.h fwupd-2.0.20/plugins/test/fu-test-device.h --- fwupd-2.0.8/plugins/test/fu-test-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/test/fu-test-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,15 @@ +/* + * Copyright 2025 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_TEST_DEVICE (fu_test_device_get_type()) +G_DECLARE_FINAL_TYPE(FuTestDevice, fu_test_device, FU, TEST_DEVICE, FuDevice) + +FuDevice * +fu_test_device_new(FuContext *ctx) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/test/fu-test-plugin.c fwupd-2.0.20/plugins/test/fu-test-plugin.c --- fwupd-2.0.8/plugins/test/fu-test-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/test/fu-test-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,6 +6,7 @@ #include "config.h" +#include "fu-test-device.h" #include "fu-test-plugin.h" struct _FuTestPlugin { @@ -18,34 +19,17 @@ fu_test_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) { FuContext *ctx = fu_plugin_get_context(plugin); - g_autoptr(FuDevice) device = NULL; - device = fu_device_new(ctx); - fu_device_set_id(device, "FakeDevice"); - fu_device_add_instance_id(device, "b585990a-003e-5270-89d5-3705a17f9a43"); - fu_device_set_name(device, "Integrated_Webcam(TM)"); - fu_device_add_icon(device, "preferences-desktop-keyboard"); - 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_CAN_VERIFY_IMAGE); - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); - fu_device_add_request_flag(device, FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE); - fu_device_add_protocol(device, "com.acme.test"); - fu_device_set_summary(device, "Fake webcam"); - fu_device_set_vendor(device, "ACME Corp."); - fu_device_build_vendor_id_u16(device, "USB", 0x046D); - fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); - fu_device_set_version_bootloader(device, "0.1.2"); - fu_device_set_version(device, "1.2.2"); - fu_device_set_version_lowest(device, "1.2.0"); + g_autoptr(FuDevice) device = fu_test_device_new(ctx); + if (!fu_device_setup(device, error)) + return FALSE; if (fu_plugin_get_config_value_boolean(plugin, "RegistrationSupported")) { fu_plugin_device_register(plugin, device); if (fu_device_get_metadata(device, "BestDevice") == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "Device not set by another plugin"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "Device not set by another plugin"); return FALSE; } } @@ -68,6 +52,7 @@ fu_device_add_flag(child1, FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(child1, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_private_flag(child1, FU_DEVICE_PRIVATE_FLAG_INSTALL_PARENT_FIRST); + fu_device_add_private_flag(child1, FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); fu_plugin_device_add(plugin, child1); child2 = fu_device_new(ctx); @@ -83,6 +68,7 @@ fu_device_add_flag(child2, FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(child2, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_private_flag(child2, FU_DEVICE_PRIVATE_FLAG_INSTALL_PARENT_FIRST); + fu_device_add_private_flag(child2, FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); fu_plugin_device_add(plugin, child2); } @@ -160,11 +146,9 @@ static gchar * fu_test_plugin_get_version(GBytes *blob_fw) { - gsize bufsz = 0; - const gchar *buf = g_bytes_get_data(blob_fw, &bufsz); guint64 val = 0; g_autoptr(GError) error_local = NULL; - g_autofree gchar *str_safe = fu_strsafe(buf, bufsz); + g_autofree gchar *str_safe = fu_strsafe_bytes(blob_fw, G_MAXSIZE); if (str_safe == NULL) return NULL; @@ -210,7 +194,7 @@ 10000, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "failed to parse DecompressDelay: "); + g_prefix_error_literal(error, "failed to parse DecompressDelay: "); return FALSE; } } @@ -240,7 +224,7 @@ 10000, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "failed to parse RequestDelay: "); + g_prefix_error_literal(error, "failed to parse RequestDelay: "); return FALSE; } } @@ -256,7 +240,7 @@ 10000, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "failed to parse WriteDelay: "); + g_prefix_error_literal(error, "failed to parse WriteDelay: "); return FALSE; } } @@ -273,7 +257,7 @@ 10000, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "failed to parse VerifyDelay: "); + g_prefix_error_literal(error, "failed to parse VerifyDelay: "); return FALSE; } } @@ -303,8 +287,16 @@ } else { g_autofree gchar *ver = NULL; g_autoptr(GBytes) blob_fw = NULL; + g_autoptr(GInputStream) stream = NULL; - blob_fw = fu_firmware_get_bytes(firmware, error); + /* live update */ + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + + stream = fu_firmware_get_stream(firmware, error); + if (stream == NULL) + return FALSE; + blob_fw = fu_input_stream_read_bytes(stream, 0x0, 9, NULL, error); if (blob_fw == NULL) return FALSE; ver = fu_test_plugin_get_version(blob_fw); diff -Nru fwupd-2.0.8/plugins/test/meson.build fwupd-2.0.20/plugins/test/meson.build --- fwupd-2.0.8/plugins/test/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/test/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -7,6 +7,7 @@ plugin_builtins += static_library('fu_plugin_test', sources: [ + 'fu-test-device.c', 'fu-test-plugin.c', ], include_directories: plugin_incdirs, diff -Nru fwupd-2.0.8/plugins/thelio-io/README.md fwupd-2.0.20/plugins/thelio-io/README.md --- fwupd-2.0.8/plugins/thelio-io/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thelio-io/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -37,10 +37,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.3.2`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Jeremy Soller: @jackpot51 diff -Nru fwupd-2.0.8/plugins/thelio-io/fu-thelio-io-device.c fwupd-2.0.20/plugins/thelio-io/fu-thelio-io-device.c --- fwupd-2.0.8/plugins/thelio-io/fu-thelio-io-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thelio-io/fu-thelio-io-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -59,37 +59,14 @@ static gboolean fu_thelio_io_device_detach(FuDevice *device, FuProgress *progress, GError **error) { - const gchar *devpath; - g_autofree gchar *fn = NULL; - g_autoptr(FuIOChannel) io_channel = NULL; - const guint8 buf[] = {'1', '\n'}; - - devpath = fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(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, FU_IO_CHANNEL_OPEN_FLAG_WRITE, error); - if (io_channel == NULL) - return FALSE; - if (!fu_io_channel_write_raw(io_channel, - buf, - sizeof(buf), - 500, - FU_IO_CHANNEL_FLAG_SINGLE_SHOT, - error)) + if (!fu_udev_device_write_sysfs(FU_UDEV_DEVICE(device), "bootloader", "1\n", 500, error)) return FALSE; fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); return TRUE; } static void -fu_thelio_io_device_set_progress(FuDevice *self, FuProgress *progress) +fu_thelio_io_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); diff -Nru fwupd-2.0.8/plugins/thelio-io/fu-thelio-io-plugin.c fwupd-2.0.20/plugins/thelio-io/fu-thelio-io-plugin.c --- fwupd-2.0.8/plugins/thelio-io/fu-thelio-io-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thelio-io/fu-thelio-io-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -25,6 +25,7 @@ fu_thelio_io_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_THELIO_IO_DEVICE); } diff -Nru fwupd-2.0.8/plugins/thelio-io/meson.build fwupd-2.0.20/plugins/thelio-io/meson.build --- fwupd-2.0.8/plugins/thelio-io/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thelio-io/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginThelioIo"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -13,4 +14,3 @@ c_args: cargs, dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/thunderbolt/README.md fwupd-2.0.20/plugins/thunderbolt/README.md --- fwupd-2.0.8/plugins/thunderbolt/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thunderbolt/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -59,6 +59,10 @@ ### USB4 Retimer +> [!IMPORTANT] +> The retimer devices will only appear when using `fwupd` (the daemon) and not +> `fwupdtool` (the standalone tool) as they need to be enabled and then added after a few seconds. + To update the retimer userspace need to set the port offline (`echo 1 > offline`), and then rescan it (`echo 1 > rescan`). This will cause the retimer to be visible to the OS, it will be added to sysfs and thus a fwupd diff -Nru fwupd-2.0.8/plugins/thunderbolt/fu-self-test.c fwupd-2.0.20/plugins/thunderbolt/fu-self-test.c --- fwupd-2.0.8/plugins/thunderbolt/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thunderbolt/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -55,9 +55,9 @@ return path; } -typedef struct MockDevice MockDevice; +typedef struct FuThunderboltMockDevice FuThunderboltMockDevice; -struct MockDevice { +struct FuThunderboltMockDevice { const char *name; /* sysfs: device_name */ const char *id; /* sysfs: device */ const char *nvm_version; @@ -67,18 +67,18 @@ int domain_id; - struct MockDevice *children; + struct FuThunderboltMockDevice *children; /* optionally filled out */ const char *uuid; }; -typedef struct MockTree MockTree; +typedef struct FuThunderboltMockTree FuThunderboltMockTree; -struct MockTree { - const MockDevice *device; +struct FuThunderboltMockTree { + const FuThunderboltMockDevice *device; - MockTree *parent; + FuThunderboltMockTree *parent; GPtrArray *children; gchar *sysfs_parent; @@ -97,10 +97,10 @@ FuDevice *fu_device; }; -static MockTree * -mock_tree_new(MockTree *parent, MockDevice *device, int *id) +static FuThunderboltMockTree * +mock_tree_new(FuThunderboltMockTree *parent, FuThunderboltMockDevice *device, int *id) { - MockTree *node = g_slice_new0(MockTree); + FuThunderboltMockTree *node = g_slice_new0(FuThunderboltMockTree); int current_id = (*id)++; node->device = device; @@ -118,10 +118,10 @@ } static void -mock_tree_free(MockTree *tree) +mock_tree_free(FuThunderboltMockTree *tree) { for (guint i = 0; i < tree->children->len; i++) { - MockTree *child = g_ptr_array_index(tree->children, i); + FuThunderboltMockTree *child = g_ptr_array_index(tree->children, i); mock_tree_free(child); } @@ -155,22 +155,22 @@ g_free(tree->nvm_non_active); g_free(tree->path); g_free(tree->sysfs_parent); - g_slice_free(MockTree, tree); + g_slice_free(FuThunderboltMockTree, tree); } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(MockTree, mock_tree_free); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuThunderboltMockTree, mock_tree_free); #pragma clang diagnostic pop static GPtrArray * -mock_tree_init_children(MockTree *node, int *id) +mock_tree_init_children(FuThunderboltMockTree *node, int *id) { GPtrArray *children = g_ptr_array_new(); - MockDevice *iter; + FuThunderboltMockDevice *iter; for (iter = node->device->children; iter && iter->name; iter++) { - MockTree *child = mock_tree_new(node, iter, id); + FuThunderboltMockTree *child = mock_tree_new(node, iter, id); g_ptr_array_add(children, child); child->children = mock_tree_init_children(child, id); } @@ -178,10 +178,10 @@ return children; } -static MockTree * -mock_tree_init(MockDevice *device) +static FuThunderboltMockTree * +mock_tree_init(FuThunderboltMockDevice *device) { - MockTree *tree; + FuThunderboltMockTree *tree; int devices = 0; tree = mock_tree_new(NULL, device, &devices); @@ -191,7 +191,7 @@ } static void -mock_tree_dump(const MockTree *node, int level) +mock_tree_dump(const FuThunderboltMockTree *node, int level) { if (node->path) { g_debug("%*s * %s [%s] at %s", @@ -212,13 +212,13 @@ } for (guint i = 0; i < node->children->len; i++) { - const MockTree *child = g_ptr_array_index(node->children, i); + const FuThunderboltMockTree *child = g_ptr_array_index(node->children, i); mock_tree_dump(child, level + 2); } } static void -mock_tree_firmware_verify(const MockTree *node, GBytes *data) +mock_tree_firmware_verify(const FuThunderboltMockTree *node, GBytes *data) { g_autoptr(GFile) nvm_device = NULL; g_autoptr(GFile) nvm = NULL; @@ -261,17 +261,20 @@ g_assert_cmpstr(sum_data, ==, sum_disk); } -typedef gboolean (*MockTreePredicate)(const MockTree *node, gpointer data); +typedef gboolean (*FuThunderboltMockTreePredicate)(const FuThunderboltMockTree *node, + gpointer data); -static const MockTree * -mock_tree_contains(const MockTree *node, MockTreePredicate predicate, gpointer data) +static const FuThunderboltMockTree * +mock_tree_contains(const FuThunderboltMockTree *node, + FuThunderboltMockTreePredicate predicate, + gpointer data) { if (predicate(node, data)) return node; for (guint i = 0; i < node->children->len; i++) { - const MockTree *child; - const MockTree *match; + const FuThunderboltMockTree *child; + const FuThunderboltMockTree *match; child = g_ptr_array_index(node->children, i); match = mock_tree_contains(child, predicate, data); @@ -283,13 +286,15 @@ } static gboolean -mock_tree_all(const MockTree *node, MockTreePredicate predicate, gpointer data) +mock_tree_all(const FuThunderboltMockTree *node, + FuThunderboltMockTreePredicate predicate, + gpointer data) { if (!predicate(node, data)) return FALSE; for (guint i = 0; i < node->children->len; i++) { - const MockTree *child; + const FuThunderboltMockTree *child; child = g_ptr_array_index(node->children, i); if (!mock_tree_all(child, predicate, data)) @@ -300,20 +305,20 @@ } static gboolean -mock_tree_compare_uuid(const MockTree *node, gpointer data) +mock_tree_compare_uuid(const FuThunderboltMockTree *node, gpointer data) { const gchar *uuid = (const gchar *)data; return g_str_equal(node->uuid, uuid); } -static const MockTree * -mock_tree_find_uuid(const MockTree *root, const char *uuid) +static const FuThunderboltMockTree * +mock_tree_find_uuid(const FuThunderboltMockTree *root, const char *uuid) { return mock_tree_contains(root, mock_tree_compare_uuid, (gpointer)uuid); } static gboolean -mock_tree_node_have_fu_device(const MockTree *node, gpointer data) +mock_tree_node_have_fu_device(const FuThunderboltMockTree *node, gpointer data) { return node->fu_device != NULL; } @@ -361,8 +366,8 @@ static gboolean mock_tree_attach_device(gpointer user_data) { - MockTree *tree = (MockTree *)user_data; - const MockDevice *dev = tree->device; + FuThunderboltMockTree *tree = (FuThunderboltMockTree *)user_data; + const FuThunderboltMockDevice *dev = tree->device; g_autofree gchar *idstr = NULL; g_autofree gchar *authenticate = NULL; @@ -409,7 +414,7 @@ write_controller_fw(tree->nvm_active); for (guint i = 0; i < tree->children->len; i++) { - MockTree *child; + FuThunderboltMockTree *child; child = g_ptr_array_index(tree->children, i); @@ -422,15 +427,15 @@ return FALSE; } -typedef struct SyncContext { - MockTree *tree; +typedef struct { + FuThunderboltMockTree *tree; GMainLoop *loop; -} SyncContext; +} FuThunderboltSyncContext; static gboolean on_sync_timeout(gpointer user_data) { - SyncContext *ctx = (SyncContext *)user_data; + FuThunderboltSyncContext *ctx = (FuThunderboltSyncContext *)user_data; g_main_loop_quit(ctx->loop); return FALSE; } @@ -438,12 +443,12 @@ static void sync_device_added(FuPlugin *plugin, FuDevice *device, gpointer user_data) { - SyncContext *ctx = (SyncContext *)user_data; - MockTree *tree = ctx->tree; + FuThunderboltSyncContext *ctx = (FuThunderboltSyncContext *)user_data; + FuThunderboltMockTree *tree = ctx->tree; const gchar *uuid = fu_device_get_physical_id(device); - MockTree *target; + FuThunderboltMockTree *target; - target = (MockTree *)mock_tree_find_uuid(tree, uuid); + target = (FuThunderboltMockTree *)mock_tree_find_uuid(tree, uuid); if (target == NULL) { g_critical("Got device that could not be matched: %s", uuid); @@ -459,12 +464,12 @@ static void sync_device_removed(FuPlugin *plugin, FuDevice *device, gpointer user_data) { - SyncContext *ctx = (SyncContext *)user_data; - MockTree *tree = ctx->tree; + FuThunderboltSyncContext *ctx = (FuThunderboltSyncContext *)user_data; + FuThunderboltMockTree *tree = ctx->tree; const gchar *uuid = fu_device_get_physical_id(device); - MockTree *target; + FuThunderboltMockTree *target; - target = (MockTree *)mock_tree_find_uuid(tree, uuid); + target = (FuThunderboltMockTree *)mock_tree_find_uuid(tree, uuid); if (target == NULL) { g_warning("Got device that could not be matched: %s", uuid); @@ -480,12 +485,12 @@ } static void -mock_tree_sync(MockTree *root, FuPlugin *plugin, int timeout_ms) +mock_tree_sync(FuThunderboltMockTree *root, FuPlugin *plugin, int timeout_ms) { g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); gulong id_add; gulong id_del; - SyncContext ctx = { + FuThunderboltSyncContext ctx = { .tree = root, .loop = mainloop, }; @@ -509,24 +514,23 @@ g_signal_handler_disconnect(plugin, id_del); } -typedef struct AttachContext { +typedef struct { /* in */ - MockTree *tree; + FuThunderboltMockTree *tree; GMainLoop *loop; /* out */ gboolean complete; - -} AttachContext; +} FuThunderboltAttachContext; static void mock_tree_plugin_device_added(FuPlugin *plugin, FuDevice *device, gpointer user_data) { - AttachContext *ctx = (AttachContext *)user_data; - MockTree *tree = ctx->tree; + FuThunderboltAttachContext *ctx = (FuThunderboltAttachContext *)user_data; + FuThunderboltMockTree *tree = ctx->tree; const gchar *uuid = fu_device_get_physical_id(device); - MockTree *target; + FuThunderboltMockTree *target; - target = (MockTree *)mock_tree_find_uuid(tree, uuid); + target = (FuThunderboltMockTree *)mock_tree_find_uuid(tree, uuid); if (target == NULL) { g_warning("Got device that could not be matched: %s", uuid); @@ -542,11 +546,11 @@ } static gboolean -mock_tree_settle(MockTree *root, FuPlugin *plugin) +mock_tree_settle(FuThunderboltMockTree *root, FuPlugin *plugin) { g_autoptr(GMainLoop) mainloop = g_main_loop_new(NULL, FALSE); gulong id; - AttachContext ctx = { + FuThunderboltAttachContext ctx = { .tree = root, .loop = mainloop, }; @@ -563,7 +567,7 @@ } static gboolean -mock_tree_attach(MockTree *root, UMockdevTestbed *bed, FuPlugin *plugin) +mock_tree_attach(FuThunderboltMockTree *root, UMockdevTestbed *bed, FuPlugin *plugin) { root->bed = g_object_ref(bed); root->sysfs_parent = udev_mock_add_usb4_port(bed, 1); @@ -575,9 +579,9 @@ } /* the unused parameter makes the function signature compatible - * with 'MockTreePredicate' */ + * with 'FuThunderboltMockTreePredicate' */ static gboolean -mock_tree_node_is_detached(const MockTree *node, gpointer unused) +mock_tree_node_is_detached(const FuThunderboltMockTree *node, gpointer unused) { gboolean ret = node->path == NULL; @@ -597,7 +601,7 @@ } static void -mock_tree_detach(MockTree *node) +mock_tree_detach(FuThunderboltMockTree *node) { UMockdevTestbed *bed; @@ -605,7 +609,7 @@ return; for (guint i = 0; i < node->children->len; i++) { - MockTree *child = g_ptr_array_index(node->children, i); + FuThunderboltMockTree *child = g_ptr_array_index(node->children, i); mock_tree_detach(child); g_free(child->sysfs_parent); child->sysfs_parent = NULL; @@ -633,7 +637,7 @@ node->bed = NULL; } -typedef enum FuThunderboltTestUpdateResult { +typedef enum { UPDATE_SUCCESS = 0, /* nvm_authenticate will report error condition */ UPDATE_FAIL_DEVICE_INTERNAL = 1, @@ -641,7 +645,7 @@ UPDATE_FAIL_DEVICE_NOSHOW = 2 } FuThunderboltTestUpdateResult; -typedef struct UpdateContext { +typedef struct FuThunderboltUpdateContext { GFileMonitor *monitor; FuThunderboltTestUpdateResult result; @@ -650,12 +654,12 @@ UMockdevTestbed *bed; FuPlugin *plugin; - MockTree *node; + FuThunderboltMockTree *node; gchar *version; -} UpdateContext; +} FuThunderboltUpdateContext; static void -update_context_free(UpdateContext *ctx) +update_context_free(FuThunderboltUpdateContext *ctx) { if (ctx == NULL) return; @@ -672,16 +676,16 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(UpdateContext, update_context_free); +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuThunderboltUpdateContext, update_context_free); #pragma clang diagnostic pop static gboolean reattach_tree(gpointer user_data) { - UpdateContext *ctx = (UpdateContext *)user_data; - MockTree *node = ctx->node; + FuThunderboltUpdateContext *ctx = (FuThunderboltUpdateContext *)user_data; + FuThunderboltMockTree *node = ctx->node; - g_debug("Mock update done, reattaching tree..."); + g_debug("mock update done, reattaching tree…"); node->bed = g_object_ref(ctx->bed); g_timeout_add(node->device->delay_ms, mock_tree_attach_device, node); @@ -696,13 +700,13 @@ GFileMonitorEvent event_type, gpointer user_data) { - UpdateContext *ctx = (UpdateContext *)user_data; + FuThunderboltUpdateContext *ctx = (FuThunderboltUpdateContext *)user_data; gboolean ok; gsize len; g_autofree gchar *data = NULL; g_autoptr(GError) error = NULL; - g_debug("Got update trigger"); + g_debug("got update trigger"); ok = g_file_monitor_cancel(monitor); g_assert_true(ok); @@ -716,7 +720,7 @@ /* verify the firmware is correct */ mock_tree_firmware_verify(ctx->node, ctx->data); - g_debug("Removing tree below and including: %s", ctx->node->path); + g_debug("removing tree below and including: %s", ctx->node->path); mock_tree_detach(ctx->node); ctx->node->nvm_authenticate = (guint)ctx->result; @@ -727,7 +731,7 @@ ctx->node->nvm_version = g_strdup(ctx->version); } - g_debug("Simulating update to '%s' with result: 0x%x", + g_debug("simulating update to '%s' with result: 0x%x", ctx->version, ctx->node->nvm_authenticate); @@ -737,24 +741,24 @@ return; } - g_debug("Device tree reattachment in %3.2f seconds", ctx->timeout / 1000.0); + g_debug("device tree reattachment in %3.2f seconds", ctx->timeout / 1000.0); g_timeout_add(ctx->timeout, reattach_tree, ctx); } -static UpdateContext * -mock_tree_prepare_for_update(MockTree *node, +static FuThunderboltUpdateContext * +mock_tree_prepare_for_update(FuThunderboltMockTree *node, FuPlugin *plugin, const char *version, GBytes *fw_data, guint timeout_ms) { - UpdateContext *ctx; + FuThunderboltUpdateContext *ctx; g_autoptr(GFile) dir = NULL; g_autoptr(GFile) f = NULL; g_autoptr(GError) error = NULL; GFileMonitor *monitor; - ctx = g_new0(UpdateContext, 1); + ctx = g_new0(FuThunderboltUpdateContext, 1); dir = g_file_new_for_path(node->path); f = g_file_get_child(dir, "nvm_authenticate"); @@ -775,7 +779,7 @@ return ctx; } -static MockDevice root_one = { +const FuThunderboltMockDevice root_one = { .name = "Laptop", .id = "0x23", @@ -783,22 +787,22 @@ .nvm_parsed_version = "20.02", .children = - (MockDevice[]){ + (FuThunderboltMockDevice[]){ { .name = "Thunderbolt Cable", .id = "0x24", .nvm_version = "20.0", .nvm_parsed_version = "20.00", - .children = (MockDevice[]){{ - .name = "Thunderbolt Dock", - .id = "0x25", - .nvm_version = "10.0", - .nvm_parsed_version = "10.00", - }, - { - NULL, - } + .children = (FuThunderboltMockDevice[]){{ + .name = "Thunderbolt Dock", + .id = "0x25", + .nvm_version = "10.0", + .nvm_parsed_version = "10.00", + }, + { + NULL, + } }, }, @@ -808,16 +812,16 @@ .nvm_version = "23.0", .nvm_parsed_version = "23.00", - .children = (MockDevice[]){{ - .name = "Thunderbolt SSD", - .id = "0x26", - - .nvm_version = "5.0", - .nvm_parsed_version = "05.00", - }, - { - NULL, - }}, + .children = (FuThunderboltMockDevice[]){{ + .name = "Thunderbolt SSD", + .id = "0x26", + + .nvm_version = "5.0", + .nvm_parsed_version = "05.00", + }, + { + NULL, + }}, }, { NULL, @@ -826,14 +830,14 @@ }; -typedef struct TestParam { +typedef struct FuThunderboltTestParam { gboolean initialize_tree; gboolean attach_and_coldplug; const char *firmware_file; -} TestParam; +} FuThunderboltTestParam; -typedef enum FuThunderboltTestFlags { +typedef enum { TEST_INITIALIZE_TREE = 1 << 0, TEST_ATTACH = 1 << 1, TEST_PREPARE_FIRMWARE = 1 << 2, @@ -844,26 +848,26 @@ #define TEST_INIT_FULL (GUINT_TO_POINTER(TEST_PREPARE_ALL)) #define TEST_INIT_NONE (GUINT_TO_POINTER(0)) -typedef struct ThunderboltTest { +typedef struct FuThunderboltTest { UMockdevTestbed *bed; FuPlugin *plugin; FuContext *ctx; GUdevClient *udev_client; - /* if TestParam::initialize_tree */ - MockTree *tree; + /* if FuThunderboltTestParam::initialize_tree */ + FuThunderboltMockTree *tree; - /* if TestParam::firmware_file is nonnull */ + /* if FuThunderboltTestParam::firmware_file is nonnull */ GBytes *fw_data; GInputStream *fw_stream; -} ThunderboltTest; +} FuThunderboltTest; static void fu_thunderbolt_gudev_uevent_cb(GUdevClient *gudev_client, const gchar *action, GUdevDevice *udev_device, - ThunderboltTest *tt) + FuThunderboltTest *tt) { if (g_strcmp0(action, "add") == 0) { g_autoptr(FuUdevDevice) device = NULL; @@ -890,14 +894,15 @@ if (g_strcmp0(action, "change") == 0) { const gchar *uuid = g_udev_device_get_sysfs_attr(udev_device, "unique_id"); /* nocheck:blocked */ - MockTree *target = (MockTree *)mock_tree_find_uuid(tt->tree, uuid); + FuThunderboltMockTree *target = + (FuThunderboltMockTree *)mock_tree_find_uuid(tt->tree, uuid); g_assert_nonnull(target); fu_udev_device_emit_changed(FU_UDEV_DEVICE(target->fu_device)); return; } } static void -test_set_up(ThunderboltTest *tt, gconstpointer params) +test_set_up(FuThunderboltTest *tt, gconstpointer params) { FuThunderboltTestFlags flags = GPOINTER_TO_UINT(params); gboolean ret; @@ -970,7 +975,7 @@ } static void -test_tear_down(ThunderboltTest *tt, gconstpointer user_data) +test_tear_down(FuThunderboltTest *tt, gconstpointer user_data) { g_object_unref(tt->plugin); g_object_unref(tt->ctx); @@ -987,15 +992,15 @@ } static gboolean -test_tree_uuids(const MockTree *node, gpointer data) +test_tree_uuids(const FuThunderboltMockTree *node, gpointer data) { - const MockTree *root = (MockTree *)data; + const FuThunderboltMockTree *root = (FuThunderboltMockTree *)data; const gchar *uuid = node->uuid; - const MockTree *found; + const FuThunderboltMockTree *found; g_assert_nonnull(uuid); - g_debug("Looking for %s", uuid); + g_debug("looking for %s", uuid); found = mock_tree_find_uuid(root, uuid); g_assert_nonnull(node); @@ -1007,11 +1012,11 @@ } static void -test_tree(ThunderboltTest *tt, gconstpointer user_data) +test_tree(FuThunderboltTest *tt, gconstpointer user_data) { - const MockTree *found; + const FuThunderboltMockTree *found; gboolean ret; - g_autoptr(MockTree) tree = NULL; + g_autoptr(FuThunderboltMockTree) tree = NULL; tree = mock_tree_init(&root_one); g_assert_nonnull(tree); @@ -1032,7 +1037,7 @@ } static void -test_image_validation(ThunderboltTest *tt, gconstpointer user_data) +test_image_validation(FuThunderboltTest *tt, gconstpointer user_data) { gboolean ret; g_autofree gchar *ctl_path = NULL; @@ -1071,7 +1076,7 @@ ret = fu_firmware_parse_bytes(firmware_bad, bad_data, 0x0, - FWUPD_INSTALL_FLAG_NO_SEARCH, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, &error); g_assert_false(ret); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_READ); @@ -1088,10 +1093,10 @@ } static void -test_change_uevent(ThunderboltTest *tt, gconstpointer user_data) +test_change_uevent(FuThunderboltTest *tt, gconstpointer user_data) { FuPlugin *plugin = tt->plugin; - MockTree *tree = tt->tree; + FuThunderboltMockTree *tree = tt->tree; gboolean ret; const gchar *version_after; @@ -1116,15 +1121,15 @@ } static void -test_update_working(ThunderboltTest *tt, gconstpointer user_data) +test_update_working(FuThunderboltTest *tt, gconstpointer user_data) { FuPlugin *plugin = tt->plugin; - MockTree *tree = tt->tree; + FuThunderboltMockTree *tree = tt->tree; GBytes *fw_data = tt->fw_data; gboolean ret; const gchar *version_after; g_autoptr(GError) error = NULL; - g_autoptr(UpdateContext) up_ctx = NULL; + g_autoptr(FuThunderboltUpdateContext) up_ctx = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); /* test sanity check */ @@ -1139,7 +1144,7 @@ tree->fu_device, tt->fw_stream, progress, - FWUPD_INSTALL_FLAG_NO_SEARCH, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, &error); g_assert_no_error(error); g_assert_true(ret); @@ -1169,10 +1174,10 @@ } static void -test_update_wd19(ThunderboltTest *tt, gconstpointer user_data) +test_update_wd19(FuThunderboltTest *tt, gconstpointer user_data) { FuPlugin *plugin = tt->plugin; - MockTree *tree = tt->tree; + FuThunderboltMockTree *tree = tt->tree; GBytes *fw_data = tt->fw_data; gboolean ret; const gchar *version_before; @@ -1193,7 +1198,7 @@ tree->fu_device, tt->fw_stream, progress, - FWUPD_INSTALL_FLAG_NO_SEARCH, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, &error); g_assert_no_error(error); g_assert_true(ret); @@ -1206,15 +1211,15 @@ } static void -test_update_fail(ThunderboltTest *tt, gconstpointer user_data) +test_update_fail(FuThunderboltTest *tt, gconstpointer user_data) { FuPlugin *plugin = tt->plugin; - MockTree *tree = tt->tree; + FuThunderboltMockTree *tree = tt->tree; GBytes *fw_data = tt->fw_data; gboolean ret; const gchar *version_after; g_autoptr(GError) error = NULL; - g_autoptr(UpdateContext) up_ctx = NULL; + g_autoptr(FuThunderboltUpdateContext) up_ctx = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); /* test sanity check */ @@ -1231,7 +1236,7 @@ tree->fu_device, tt->fw_stream, progress, - FWUPD_INSTALL_FLAG_NO_SEARCH, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, &error); g_assert_no_error(error); g_assert_true(ret); @@ -1260,14 +1265,14 @@ } static void -test_update_fail_nowshow(ThunderboltTest *tt, gconstpointer user_data) +test_update_fail_nowshow(FuThunderboltTest *tt, gconstpointer user_data) { FuPlugin *plugin = tt->plugin; - MockTree *tree = tt->tree; + FuThunderboltMockTree *tree = tt->tree; GBytes *fw_data = tt->fw_data; gboolean ret; g_autoptr(GError) error = NULL; - g_autoptr(UpdateContext) up_ctx = NULL; + g_autoptr(FuThunderboltUpdateContext) up_ctx = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); /* test sanity check */ @@ -1284,7 +1289,7 @@ tree->fu_device, tt->fw_stream, progress, - FWUPD_INSTALL_FLAG_NO_SEARCH, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, &error); g_assert_no_error(error); g_assert_true(ret); @@ -1307,48 +1312,48 @@ (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); g_test_add("/thunderbolt/basic", - ThunderboltTest, + FuThunderboltTest, NULL, test_set_up, test_tree, test_tear_down); g_test_add("/thunderbolt/image-validation", - ThunderboltTest, + FuThunderboltTest, TEST_INIT_NONE, test_set_up, test_image_validation, test_tear_down); g_test_add("/thunderbolt/change-uevent", - ThunderboltTest, + FuThunderboltTest, GUINT_TO_POINTER(TEST_INITIALIZE_TREE | TEST_ATTACH), test_set_up, test_change_uevent, test_tear_down); g_test_add("/thunderbolt/update{working}", - ThunderboltTest, + FuThunderboltTest, TEST_INIT_FULL, test_set_up, test_update_working, test_tear_down); g_test_add("/thunderbolt/update{failing}", - ThunderboltTest, + FuThunderboltTest, TEST_INIT_FULL, test_set_up, test_update_fail, test_tear_down); g_test_add("/thunderbolt/update{failing-noshow}", - ThunderboltTest, + FuThunderboltTest, TEST_INIT_FULL, test_set_up, test_update_fail_nowshow, test_tear_down); g_test_add("/thunderbolt/update{delayed_activation}", - ThunderboltTest, + FuThunderboltTest, TEST_INIT_FULL, test_set_up, test_update_wd19, diff -Nru fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-common.c fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-common.c --- fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -16,10 +16,12 @@ { const gchar *sysfs_path = fu_udev_device_get_sysfs_path(device); for (guint i = 0; i < 9; i++) { + gboolean attribute_exists = FALSE; g_autofree gchar *path = g_strdup_printf("usb4_port%u/%s", i, attribute); g_autofree gchar *fn = g_build_filename(sysfs_path, path, NULL); - g_autoptr(GFile) file = g_file_new_for_path(fn); - if (g_file_query_exists(file, NULL)) + if (!fu_device_query_file_exists(FU_DEVICE(device), fn, &attribute_exists, error)) + return NULL; + if (attribute_exists) return g_steal_pointer(&path); } g_set_error(error, @@ -46,7 +48,7 @@ "1", FU_THUNDERBOLT_DEVICE_WRITE_TIMEOUT, error)) { - g_prefix_error(error, "setting usb4 port offline failed: "); + g_prefix_error_literal(error, "setting usb4 port offline failed: "); return FALSE; } return TRUE; @@ -68,7 +70,7 @@ "1", FU_THUNDERBOLT_DEVICE_WRITE_TIMEOUT, error)) { - g_prefix_error(error, "rescan on port failed: "); + g_prefix_error_literal(error, "rescan on port failed: "); return FALSE; } return TRUE; @@ -91,7 +93,7 @@ "0", FU_THUNDERBOLT_DEVICE_WRITE_TIMEOUT, error)) { - g_prefix_error(error, "setting port online failed: "); + g_prefix_error_literal(error, "setting port online failed: "); return FALSE; } return TRUE; diff -Nru fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-controller.c fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-controller.c --- fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-controller.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-controller.c 2026-02-26 11:36:18.000000000 +0000 @@ -120,10 +120,9 @@ fu_thunderbolt_controller_read_status_block(FuThunderboltController *self, GError **error) { gsize nr_chunks; + g_autofree gchar *nvmem = NULL; g_autoptr(GBytes) blob = NULL; - g_autoptr(GFile) nvmem = NULL; g_autoptr(GInputStream) istr = NULL; - g_autoptr(GInputStream) istr_partial = NULL; g_autoptr(FuFirmware) firmware = NULL; nvmem = fu_thunderbolt_device_find_nvmem(FU_THUNDERBOLT_DEVICE(self), TRUE, error); @@ -132,16 +131,17 @@ /* read just enough bytes to read the status byte */ nr_chunks = (FU_TBT_OFFSET_NATIVE + FU_TBT_CHUNK_SZ - 1) / FU_TBT_CHUNK_SZ; - istr = G_INPUT_STREAM(g_file_read(nvmem, NULL, error)); - if (istr == NULL) - return FALSE; - blob = fu_input_stream_read_bytes(istr, 0x0, nr_chunks * FU_TBT_CHUNK_SZ, NULL, error); + blob = fu_device_get_contents_bytes(FU_DEVICE(self), + nvmem, + nr_chunks * FU_TBT_CHUNK_SZ, + NULL, + error); if (blob == NULL) return FALSE; - istr_partial = g_memory_input_stream_new_from_bytes(blob); - firmware = fu_firmware_new_from_gtypes(istr_partial, + istr = g_memory_input_stream_new_from_bytes(blob); + firmware = fu_firmware_new_from_gtypes(istr, 0x0, - FWUPD_INSTALL_FLAG_NO_SEARCH, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error, FU_TYPE_INTEL_THUNDERBOLT_NVM, FU_TYPE_FIRMWARE, @@ -159,7 +159,7 @@ fu_thunderbolt_controller_can_update(FuThunderboltController *self) { g_autoptr(GError) nvmem_error = NULL; - g_autoptr(GFile) non_active_nvmem = NULL; + g_autofree gchar *non_active_nvmem = NULL; non_active_nvmem = fu_thunderbolt_device_find_nvmem(FU_THUNDERBOLT_DEVICE(self), FALSE, &nvmem_error); @@ -188,6 +188,14 @@ static gboolean fu_thunderbolt_controller_setup_usb4(FuThunderboltController *self, GError **error) { + FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); + + /* it takes 5 seconds (!) before we can re-online the thunderbolt controller */ + if (fu_context_has_flag(ctx, FU_CONTEXT_FLAG_NO_IDLE_SOURCES)) { + g_message("skipping usb4 setup as port needs to be onlined"); + return TRUE; + } + if (!fu_thunderbolt_udev_set_port_offline(FU_UDEV_DEVICE(self), error)) return FALSE; if (!fu_thunderbolt_udev_rescan_port(FU_UDEV_DEVICE(self), error)) @@ -195,7 +203,11 @@ if (self->host_online_timer_id > 0) g_source_remove(self->host_online_timer_id); self->host_online_timer_id = - g_timeout_add_seconds(5, fu_thunderbolt_controller_set_port_online_cb, self); + g_timeout_add_seconds_full(G_PRIORITY_DEFAULT, + 5, /* seconds */ + fu_thunderbolt_controller_set_port_online_cb, + g_object_ref(self), + g_object_unref); return TRUE; } @@ -255,7 +267,8 @@ "device_name", FU_UDEV_DEVICE_ATTR_READ_TIMEOUT_DEFAULT, NULL); - fu_device_set_name(device, attr_device_name); + if (attr_device_name != NULL) + fu_device_set_name(device, attr_device_name); } /* set the controller name */ @@ -310,10 +323,10 @@ return FALSE; } else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "updates are distributed as part of the platform"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "updates are distributed as part of the platform"); return FALSE; } fu_device_add_instance_id(device, device_id); @@ -343,6 +356,8 @@ /* set up signed payload attribute */ if (self->controller_kind == FU_THUNDERBOLT_CONTROLLER_KIND_HOST && self->gen >= 3) fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); + else if (self->gen == 3) + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); /* success */ return TRUE; diff -Nru fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-device.c fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-device.c --- fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -37,38 +37,22 @@ priv->retries = retries; } -GFile * +gchar * fu_thunderbolt_device_find_nvmem(FuThunderboltDevice *self, gboolean active, GError **error) { const gchar *nvmem_dir = active ? "nvm_active" : "nvm_non_active"; - const gchar *name; const gchar *devpath = fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(self)); - g_autoptr(GDir) d = NULL; + g_autoptr(GPtrArray) basenames = NULL; - if (G_UNLIKELY(devpath == NULL)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Could not determine sysfs path for device"); - return NULL; - } - - d = g_dir_open(devpath, 0, error); - if (d == NULL) + basenames = fu_udev_device_list_sysfs(FU_UDEV_DEVICE(self), error); + if (basenames == NULL) return NULL; - - while ((name = g_dir_read_name(d)) != NULL) { - if (g_str_has_prefix(name, nvmem_dir)) { - g_autoptr(GFile) parent = g_file_new_for_path(devpath); - g_autoptr(GFile) nvm_dir = g_file_get_child(parent, name); - return g_file_get_child(nvm_dir, "nvmem"); - } + for (guint i = 0; i < basenames->len; i++) { + const gchar *name = g_ptr_array_index(basenames, i); + if (g_str_has_prefix(name, nvmem_dir)) + return g_build_filename(devpath, name, "nvmem", NULL); } - - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Could not find non-volatile memory location"); + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "could not find %s", nvmem_dir); return NULL; } @@ -91,11 +75,13 @@ "missing authorized attribute"); return FALSE; } - - if (!g_file_get_contents(safe_path, &attribute, NULL, error)) + attribute = fu_device_get_contents(FU_DEVICE(self), safe_path, 0x100, NULL, error); + if (attribute == NULL) { + g_prefix_error(error, "failed to read %s: ", safe_path); return FALSE; + } if (!fu_strtoull(attribute, &status, 0, G_MAXUINT64, FU_INTEGER_BASE_16, error)) { - g_prefix_error(error, "failed to read authorized: "); + g_prefix_error_literal(error, "failed to read authorized: "); return FALSE; } if (status == 1 || status == 2) @@ -111,15 +97,17 @@ { FuThunderboltDevicePrivate *priv = GET_PRIVATE(self); const gchar *devpath = fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(self)); + gboolean exists = FALSE; guint64 version_major = 0; guint64 version_minor = 0; g_auto(GStrv) split = NULL; g_autofree gchar *version_raw = NULL; g_autofree gchar *version = NULL; - /* read directly from file to prevent udev caching */ g_autofree gchar *safe_path = g_build_path("/", devpath, "nvm_version", NULL); - if (!g_file_test(safe_path, G_FILE_TEST_EXISTS)) { + if (!fu_device_query_file_exists(FU_DEVICE(self), safe_path, &exists, error)) + return FALSE; + if (!exists) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -131,13 +119,16 @@ g_autoptr(GError) error_local = NULL; /* glib can't return a properly mapped -ENODATA but the * kernel only returns -ENODATA or -EAGAIN */ - if (g_file_get_contents(safe_path, &version_raw, NULL, &error_local)) + version_raw = + fu_device_get_contents(FU_DEVICE(self), safe_path, 0x100, NULL, &error_local); + if (version_raw != NULL) break; g_debug("attempt %u: failed to read NVM version", i); - fu_device_sleep(FU_DEVICE(self), TBT_NVM_RETRY_TIMEOUT); - /* safe mode probably */ - if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT)) { + g_debug("timeout maybe means safe mode?"); break; + } + fu_device_sleep(FU_DEVICE(self), TBT_NVM_RETRY_TIMEOUT); } if (version_raw == NULL) { @@ -190,13 +181,10 @@ } static gboolean -fu_thunderbolt_device_authenticate(FuDevice *device, GError **error) +fu_thunderbolt_device_authenticate(FuThunderboltDevice *self, GError **error) { - FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE(device); FuThunderboltDevicePrivate *priv = GET_PRIVATE(self); - FuUdevDevice *udev = FU_UDEV_DEVICE(device); - - return fu_udev_device_write_sysfs(udev, + return fu_udev_device_write_sysfs(FU_UDEV_DEVICE(self), priv->auth_method, "1", FU_THUNDERBOLT_DEVICE_WRITE_TIMEOUT, @@ -204,13 +192,10 @@ } static gboolean -fu_thunderbolt_device_flush_update(FuDevice *device, GError **error) +fu_thunderbolt_device_flush_update(FuThunderboltDevice *self, GError **error) { - FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE(device); FuThunderboltDevicePrivate *priv = GET_PRIVATE(self); - FuUdevDevice *udev = FU_UDEV_DEVICE(device); - - return fu_udev_device_write_sysfs(udev, + return fu_udev_device_write_sysfs(FU_UDEV_DEVICE(self), priv->auth_method, "2", FU_THUNDERBOLT_DEVICE_WRITE_TIMEOUT, @@ -236,7 +221,7 @@ G_MAXUINT64, FU_INTEGER_BASE_16, error)) { - g_prefix_error(error, "failed to read nvm_authenticate: "); + g_prefix_error_literal(error, "failed to read nvm_authenticate: "); return FALSE; } @@ -267,65 +252,24 @@ } static gboolean -fu_thunderbolt_device_write_stream(GOutputStream *ostream, - GBytes *bytes, - FuProgress *progress, - GError **error) -{ - gsize bufsz = g_bytes_get_size(bytes); - gsize total_written = 0; - - do { - gssize wrote; - g_autoptr(GBytes) fw_data = NULL; - fw_data = fu_bytes_new_offset(bytes, total_written, bufsz - total_written, error); - if (fw_data == NULL) - return FALSE; - wrote = g_output_stream_write_bytes(ostream, fw_data, NULL, error); - if (wrote < 0) - return FALSE; - total_written += wrote; - fu_progress_set_percentage_full(progress, total_written, bufsz); - } while (total_written < bufsz); - if (total_written != bufsz) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_WRITE, - "only wrote 0x%x of 0x%x", - (guint)total_written, - (guint)bufsz); - return FALSE; - } - - /* success */ - return TRUE; -} - -static gboolean fu_thunderbolt_device_write_data(FuThunderboltDevice *self, GBytes *blob_fw, FuProgress *progress, GError **error) { - g_autoptr(GFile) nvmem = NULL; - g_autoptr(GOutputStream) ostream = NULL; + g_autofree gchar *nvmem = NULL; nvmem = fu_thunderbolt_device_find_nvmem(self, FALSE, error); if (nvmem == NULL) return FALSE; - ostream = (GOutputStream *)g_file_append_to(nvmem, G_FILE_CREATE_NONE, NULL, error); - if (ostream == NULL) - return FALSE; - if (!fu_thunderbolt_device_write_stream(ostream, blob_fw, progress, error)) - return FALSE; - return g_output_stream_close(ostream, NULL, error); + return fu_device_set_contents_bytes(FU_DEVICE(self), nvmem, blob_fw, progress, error); } static FuFirmware * fu_thunderbolt_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuThunderboltDevice *self = FU_THUNDERBOLT_DEVICE(device); @@ -344,17 +288,20 @@ /* get current NVMEM */ if (fu_firmware_has_flag(firmware, FU_FIRMWARE_FLAG_HAS_CHECK_COMPATIBLE)) { + g_autofree gchar *nvmem = NULL; g_autoptr(FuFirmware) firmware_old = NULL; - g_autoptr(GFile) nvmem = NULL; + g_autoptr(GBytes) controller_blob = NULL; g_autoptr(GInputStream) controller_fw = NULL; fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); nvmem = fu_thunderbolt_device_find_nvmem(self, TRUE, error); if (nvmem == NULL) return NULL; - controller_fw = G_INPUT_STREAM(g_file_read(nvmem, NULL, error)); - if (controller_fw == NULL) + controller_blob = + fu_device_get_contents_bytes(device, nvmem, G_MAXSIZE, progress, error); + if (controller_blob == NULL) return NULL; + controller_fw = g_memory_input_stream_new_from_bytes(controller_blob); firmware_old = fu_firmware_new_from_gtypes(controller_fw, 0x0, flags, @@ -397,7 +344,7 @@ /* flush the image if supported by kernel and/or device */ if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE)) { - if (!fu_thunderbolt_device_flush_update(device, error)) + if (!fu_thunderbolt_device_flush_update(self, error)) return FALSE; fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); } @@ -410,8 +357,8 @@ } /* authenticate (possibly on unplug if device supports it) */ - if (!fu_thunderbolt_device_authenticate(FU_DEVICE(self), error)) { - g_prefix_error(error, "could not start thunderbolt device upgrade: "); + if (!fu_thunderbolt_device_authenticate(self, error)) { + g_prefix_error_literal(error, "could not start thunderbolt device upgrade: "); return FALSE; } @@ -443,12 +390,12 @@ } static void -fu_thunderbolt_device_set_progress(FuDevice *self, FuProgress *progress) +fu_thunderbolt_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 17, "prepare-fw"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 83, "write"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); } @@ -459,8 +406,9 @@ FuThunderboltDevicePrivate *priv = GET_PRIVATE(self); priv->auth_method = "nvm_authenticate"; priv->retries = 50; - fu_device_add_icon(FU_DEVICE(self), "thunderbolt"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_THUNDERBOLT); fu_device_add_protocol(FU_DEVICE(self), "com.intel.thunderbolt"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); } static void diff -Nru fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-device.h fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-device.h --- fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -22,7 +22,7 @@ gboolean fu_thunderbolt_device_get_version(FuThunderboltDevice *self, GError **error); -GFile * +gchar * fu_thunderbolt_device_find_nvmem(FuThunderboltDevice *self, gboolean active, GError **error); gboolean fu_thunderbolt_device_check_authorized(FuThunderboltDevice *self, GError **error); diff -Nru fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-plugin.c fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-plugin.c --- fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -42,7 +42,7 @@ if (g_strcmp0(fu_device_get_plugin(device), "thunderbolt") != 0) return; - /* Operating system will handle finishing updates later */ + /* operating system will handle finishing updates later */ if (fu_plugin_get_config_value_boolean(plugin, "DelayedActivation") && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE)) { g_info("turning on delayed activation for %s", fu_device_get_name(device)); @@ -66,7 +66,9 @@ if ((g_strcmp0(fu_device_get_plugin(dev), "thunderbolt") == 0) && fu_device_has_private_flag(dev, FU_THUNDERBOLT_DEVICE_FLAG_FORCE_ENUMERATION) && fu_device_has_private_flag(dev, FU_DEVICE_PRIVATE_FLAG_NO_AUTO_REMOVE)) { - return fu_thunderbolt_retimer_set_parent_port_offline(dev, error); + return fu_thunderbolt_retimer_offline_parent_port( + FU_THUNDERBOLT_RETIMER(dev), + error); } } return TRUE; @@ -81,7 +83,9 @@ fu_device_has_private_flag(dev, FU_THUNDERBOLT_DEVICE_FLAG_FORCE_ENUMERATION) && fu_device_has_private_flag(dev, FU_DEVICE_PRIVATE_FLAG_NO_AUTO_REMOVE)) { fu_device_sleep(dev, FU_THUNDERBOLT_RETIMER_CLEANUP_DELAY); - return fu_thunderbolt_retimer_set_parent_port_online(dev, error); + return fu_thunderbolt_retimer_online_parent_port( + FU_THUNDERBOLT_RETIMER(dev), + error); } } return TRUE; diff -Nru fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-retimer.c fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-retimer.c --- fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-retimer.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-retimer.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,10 +18,10 @@ G_DEFINE_TYPE(FuThunderboltRetimer, fu_thunderbolt_retimer, FU_TYPE_THUNDERBOLT_DEVICE) gboolean -fu_thunderbolt_retimer_set_parent_port_offline(FuDevice *device, GError **error) +fu_thunderbolt_retimer_offline_parent_port(FuThunderboltRetimer *self, GError **error) { g_autoptr(FuDevice) parent = - fu_device_get_backend_parent_with_subsystem(device, + fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), "thunderbolt:thunderbolt_domain", error); if (parent == NULL) @@ -32,10 +32,10 @@ } gboolean -fu_thunderbolt_retimer_set_parent_port_online(FuDevice *device, GError **error) +fu_thunderbolt_retimer_online_parent_port(FuThunderboltRetimer *self, GError **error) { g_autoptr(FuDevice) parent = - fu_device_get_backend_parent_with_subsystem(device, + fu_device_get_backend_parent_with_subsystem(FU_DEVICE(self), "thunderbolt:thunderbolt_domain", error); if (parent == NULL) @@ -72,20 +72,22 @@ static gboolean fu_thunderbolt_retimer_attach(FuDevice *device, FuProgress *progress, GError **error) { + FuThunderboltRetimer *self = FU_THUNDERBOLT_RETIMER(device); + /* FuThunderboltDevice->attach for nvm_authenticate */ if (!FU_DEVICE_CLASS(fu_thunderbolt_retimer_parent_class)->attach(device, progress, error)) return FALSE; /* online */ fu_device_sleep(device, FU_THUNDERBOLT_RETIMER_CLEANUP_DELAY); - if (!fu_thunderbolt_retimer_set_parent_port_online(device, error)) + if (!fu_thunderbolt_retimer_online_parent_port(self, error)) return FALSE; /* retimer gets removed, which we ignore, due to no-auto-remove */ fu_device_sleep(device, 1000); /* get the new retimer firmware version by rescanning */ - if (!fu_thunderbolt_retimer_set_parent_port_offline(device, error)) + if (!fu_thunderbolt_retimer_offline_parent_port(self, error)) return FALSE; /* wait for it to re-appear */ diff -Nru fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-retimer.h fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-retimer.h --- fwupd-2.0.8/plugins/thunderbolt/fu-thunderbolt-retimer.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thunderbolt/fu-thunderbolt-retimer.h 2026-02-26 11:36:18.000000000 +0000 @@ -22,7 +22,7 @@ #define FU_THUNDERBOLT_RETIMER_CLEANUP_DELAY 5000 /* ms */ gboolean -fu_thunderbolt_retimer_set_parent_port_offline(FuDevice *device, GError **error); +fu_thunderbolt_retimer_offline_parent_port(FuThunderboltRetimer *self, GError **error); gboolean -fu_thunderbolt_retimer_set_parent_port_online(FuDevice *device, GError **error); +fu_thunderbolt_retimer_online_parent_port(FuThunderboltRetimer *self, GError **error); diff -Nru fwupd-2.0.8/plugins/thunderbolt/meson.build fwupd-2.0.20/plugins/thunderbolt/meson.build --- fwupd-2.0.8/plugins/thunderbolt/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thunderbolt/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ -if host_machine.system() == 'linux' and (host_cpu == 'x86' or host_cpu == 'x86_64') +host_machine.system() == 'linux' or subdir_done() +host_cpu in ['x86', 'x86_64'] or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginThunderbolt"'] plugin_quirks += files('thunderbolt.quirk') @@ -22,5 +24,5 @@ device_tests += files( 'tests/caldigit-ts4-tbt.json', + 'tests/lenovo-x280-tbt.json', ) -endif diff -Nru fwupd-2.0.8/plugins/thunderbolt/tests/caldigit-ts4-tbt.json fwupd-2.0.20/plugins/thunderbolt/tests/caldigit-ts4-tbt.json --- fwupd-2.0.8/plugins/thunderbolt/tests/caldigit-ts4-tbt.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/thunderbolt/tests/caldigit-ts4-tbt.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/78f0eb25da781a88a2a9a1191a32b6c7f8c8a61664a42dcb6b156a1b87d525e4-CalDigit-TS4_TBT39.cab", + "url": "78f0eb25da781a88a2a9a1191a32b6c7f8c8a61664a42dcb6b156a1b87d525e4-CalDigit-TS4_TBT39.cab", "components": [ { "version": "39.82", diff -Nru fwupd-2.0.8/plugins/thunderbolt/tests/lenovo-x280-tbt.json fwupd-2.0.20/plugins/thunderbolt/tests/lenovo-x280-tbt.json --- fwupd-2.0.8/plugins/thunderbolt/tests/lenovo-x280-tbt.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/thunderbolt/tests/lenovo-x280-tbt.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +{ + "name": "Lenovo X280 (ThunderBolt)", + "interactive": false, + "steps": [ + { + "url": "fea39e6c1005983b03dfa4ea683f4d749b6696ff2279f6f5f754584ce29c8792-Lenovo-ThinkPad-X280-Thunderbolt-Firmware-N20TF19W-Secured.cab", + "emulation-url": "fa12821bbf51500e1e5f9a4eeaedc55ac1cf755ffa0fcc29aa68d1c3179b61fe-emulation.zip", + "components": [ + { + "version": "20.00", + "guids": [ + "4808eca4-fd4a-50e6-9e8d-bfd813f063da" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/ti-tps6598x/fu-ti-tps6598x-common.h fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-common.h --- fwupd-2.0.8/plugins/ti-tps6598x/fu-ti-tps6598x-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,26 +9,6 @@ #include -/* registers */ -#define TI_TPS6598X_REGISTER_TBT_VID 0x00 /* ro, 4 bytes -- Intel assigned */ -#define TI_TPS6598X_REGISTER_TBT_DID 0x01 /* ro, 4 bytes -- Intel assigned */ -#define TI_TPS6598X_REGISTER_PROTO_VER 0x02 /* ro, 4 bytes */ -#define TI_TPS6598X_REGISTER_MODE 0x03 /* ro, 4 bytes */ -#define TI_TPS6598X_REGISTER_TYPE 0x04 /* ro, 4 bytes */ -#define TI_TPS6598X_REGISTER_UID 0x05 /* ro, 16 bytes */ -#define TI_TPS6598X_REGISTER_OUID 0x06 /* ro, 8 bytes */ -#define TI_TPS6598X_REGISTER_CMD1 0x08 /* ro, 4CC */ -#define TI_TPS6598X_REGISTER_DATA1 0x09 /* rw, 64 bytes */ -#define TI_TPS6598X_REGISTER_VERSION 0x0F /* rw, 4 bytes */ -#define TI_TPS6598X_REGISTER_CMD2 0x10 /* ro, 4CC */ -#define TI_TPS6598X_REGISTER_DATA2 0x11 /* rw, 64 bytes */ -#define TI_TPS6598X_REGISTER_CMD3 0x1E /* ro, variable */ -#define TI_TPS6598X_REGISTER_DATA3 0x1F /* ro, variable */ -#define TI_TPS6598X_REGISTER_OTP_CONFIG 0x2D /* ro, 12 bytes */ -#define TI_TPS6598X_REGISTER_BUILD_IDENTIFIER 0x2E /* ro, 64 bytes */ -#define TI_TPS6598X_REGISTER_DEVICE_INFO 0x2F /* ro, 47 bytes */ -#define TI_TPS6598X_REGISTER_TX_IDENTITY 0x47 /* rw, 49 bytes */ - #define FU_TI_TPS6598X_PD_MAX 2 /* devices */ gboolean diff -Nru fwupd-2.0.8/plugins/ti-tps6598x/fu-ti-tps6598x-device.c fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-device.c --- fwupd-2.0.8/plugins/ti-tps6598x/fu-ti-tps6598x-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -63,7 +63,7 @@ TI_TPS6598X_DEVICE_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to contact device: "); + g_prefix_error_literal(error, "failed to contact device: "); return NULL; } fu_dump_raw(G_LOG_DOMAIN, title, buf->data, buf->len); @@ -144,7 +144,7 @@ TI_TPS6598X_DEVICE_USB_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to contact device: "); + g_prefix_error_literal(error, "failed to contact device: "); return FALSE; } if (actual_length != fu_chunk_get_data_sz(chk)) { @@ -167,11 +167,11 @@ fu_ti_tps6598x_device_read_data(FuTiTps6598xDevice *self, gsize bufsz, GError **error) { g_autoptr(GByteArray) buf = - fu_ti_tps6598x_device_usbep_read(self, TI_TPS6598X_REGISTER_DATA3, bufsz, error); + fu_ti_tps6598x_device_usbep_read(self, FU_TI_TPS6598X_REGISTER_DATA3, bufsz, error); if (buf == NULL) { g_prefix_error(error, "failed to read data at 0x%x: ", - (guint)TI_TPS6598X_REGISTER_DATA3); + (guint)FU_TI_TPS6598X_REGISTER_DATA3); return NULL; } return g_steal_pointer(&buf); @@ -181,10 +181,10 @@ static gboolean fu_ti_tps6598x_device_write_data(FuTiTps6598xDevice *self, GByteArray *buf, GError **error) { - if (!fu_ti_tps6598x_device_usbep_write(self, TI_TPS6598X_REGISTER_DATA3, buf, error)) { + if (!fu_ti_tps6598x_device_usbep_write(self, FU_TI_TPS6598X_REGISTER_DATA3, buf, error)) { g_prefix_error(error, "failed to write data at 0x%x: ", - (guint)TI_TPS6598X_REGISTER_DATA3); + (guint)FU_TI_TPS6598X_REGISTER_DATA3); return FALSE; } return TRUE; @@ -212,7 +212,7 @@ } for (guint i = 0; i < 4; i++) fu_byte_array_append_uint8(buf, cmd[i]); - return fu_ti_tps6598x_device_usbep_write(self, TI_TPS6598X_REGISTER_CMD3, buf, error); + return fu_ti_tps6598x_device_usbep_write(self, FU_TI_TPS6598X_REGISTER_CMD3, buf, error); } static gboolean @@ -228,7 +228,7 @@ g_autoptr(GByteArray) buf = NULL; /* 4 bytes of data and the first byte is length */ - buf = fu_ti_tps6598x_device_usbep_read(self, TI_TPS6598X_REGISTER_CMD3, 4, error); + buf = fu_ti_tps6598x_device_usbep_read(self, FU_TI_TPS6598X_REGISTER_CMD3, 4, error); if (buf == NULL) return FALSE; @@ -424,7 +424,7 @@ g_autoptr(GByteArray) buf = NULL; /* get bcdVersion */ - buf = fu_ti_tps6598x_device_usbep_read(self, TI_TPS6598X_REGISTER_VERSION, 4, error); + buf = fu_ti_tps6598x_device_usbep_read(self, FU_TI_TPS6598X_REGISTER_VERSION, 4, error); if (buf == NULL) return FALSE; str = g_strdup_printf("%X.%X.%X", buf->data[2], buf->data[1], buf->data[0]); @@ -438,7 +438,7 @@ g_autofree gchar *str = NULL; g_autoptr(GByteArray) buf = NULL; - buf = fu_ti_tps6598x_device_usbep_read(self, TI_TPS6598X_REGISTER_MODE, 4, error); + buf = fu_ti_tps6598x_device_usbep_read(self, FU_TI_TPS6598X_REGISTER_MODE, 4, error); if (buf == NULL) return FALSE; @@ -468,7 +468,7 @@ fu_ti_tps6598x_device_ensure_uid(FuTiTps6598xDevice *self, GError **error) { g_autoptr(GByteArray) buf = - fu_ti_tps6598x_device_usbep_read(self, TI_TPS6598X_REGISTER_UID, 16, error); + fu_ti_tps6598x_device_usbep_read(self, FU_TI_TPS6598X_REGISTER_UID, 16, error); if (buf == NULL) return FALSE; g_free(self->uid); @@ -480,7 +480,7 @@ fu_ti_tps6598x_device_ensure_ouid(FuTiTps6598xDevice *self, GError **error) { g_autoptr(GByteArray) buf = - fu_ti_tps6598x_device_usbep_read(self, TI_TPS6598X_REGISTER_OUID, 8, error); + fu_ti_tps6598x_device_usbep_read(self, FU_TI_TPS6598X_REGISTER_OUID, 8, error); if (buf == NULL) return FALSE; g_free(self->ouid); @@ -495,10 +495,10 @@ /* there are two devices with the same VID:PID -- ignore the non-vendor one */ if (fu_usb_device_get_class(FU_USB_DEVICE(self)) != FU_USB_CLASS_VENDOR_SPECIFIC) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "non-vendor specific interface ignored"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "non-vendor specific interface ignored"); return FALSE; } @@ -508,19 +508,19 @@ /* get hardware details */ if (!fu_ti_tps6598x_device_ensure_version(self, error)) { - g_prefix_error(error, "failed to read version: "); + g_prefix_error_literal(error, "failed to read version: "); return FALSE; } if (!fu_ti_tps6598x_device_ensure_mode(self, error)) { - g_prefix_error(error, "failed to read mode: "); + g_prefix_error_literal(error, "failed to read mode: "); return FALSE; } if (!fu_ti_tps6598x_device_ensure_uid(self, error)) { - g_prefix_error(error, "failed to read UID: "); + g_prefix_error_literal(error, "failed to read UID: "); return FALSE; } if (!fu_ti_tps6598x_device_ensure_ouid(self, error)) { - g_prefix_error(error, "failed to read oUID: "); + g_prefix_error_literal(error, "failed to read oUID: "); return FALSE; } @@ -688,7 +688,7 @@ chunks_payload, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write SFWd: "); + g_prefix_error_literal(error, "failed to write SFWd: "); return FALSE; } fu_progress_step_done(progress); @@ -708,7 +708,7 @@ chunks_sig, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write SFWs with signature: "); + g_prefix_error_literal(error, "failed to write SFWs with signature: "); return FALSE; } fu_progress_step_done(progress); @@ -728,7 +728,7 @@ chunks_pubkey, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write SFWs with pubkey: "); + g_prefix_error_literal(error, "failed to write SFWs with pubkey: "); return FALSE; } fu_progress_step_done(progress); @@ -738,7 +738,7 @@ } static void -fu_ti_tps6598x_device_set_progress(FuDevice *self, FuProgress *progress) +fu_ti_tps6598x_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/ti-tps6598x/fu-ti-tps6598x-firmware.c fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-firmware.c --- fwupd-2.0.8/plugins/ti-tps6598x/fu-ti-tps6598x-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -30,7 +30,7 @@ static gboolean fu_ti_tps6598x_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint8 verbuf[3] = {0x0}; @@ -57,7 +57,8 @@ if (!fu_firmware_parse_stream(img_pubkey, stream_pubkey, 0x0, flags, error)) return FALSE; fu_firmware_set_id(img_pubkey, "pubkey"); - fu_firmware_add_image(firmware, img_pubkey); + if (!fu_firmware_add_image(firmware, img_pubkey, error)) + return FALSE; offset += FU_TI_TPS6598X_FIRMWARE_PUBKEY_SIZE; /* RSA signature */ @@ -68,7 +69,8 @@ if (!fu_firmware_parse_stream(img_sig, stream_sig, 0x0, flags, error)) return FALSE; fu_firmware_set_id(img_sig, FU_FIRMWARE_ID_SIGNATURE); - fu_firmware_add_image(firmware, img_sig); + if (!fu_firmware_add_image(firmware, img_sig, error)) + return FALSE; offset += FU_TI_TPS6598X_FIRMWARE_PUBKEY_SIZE; /* payload */ @@ -88,10 +90,9 @@ version_str = g_strdup_printf("%X.%X.%X", verbuf[2], verbuf[1], verbuf[0]); fu_firmware_set_version(img_payload, version_str); fu_firmware_set_id(img_payload, FU_FIRMWARE_ID_PAYLOAD); - fu_firmware_add_image(firmware, img_payload); /* success */ - return TRUE; + return fu_firmware_add_image(firmware, img_payload, error); } static GByteArray * diff -Nru fwupd-2.0.8/plugins/ti-tps6598x/fu-ti-tps6598x-pd-device.c fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-pd-device.c --- fwupd-2.0.8/plugins/ti-tps6598x/fu-ti-tps6598x-pd-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-pd-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,6 +10,7 @@ #include "fu-ti-tps6598x-device.h" #include "fu-ti-tps6598x-firmware.h" #include "fu-ti-tps6598x-pd-device.h" +#include "fu-ti-tps6598x-struct.h" struct _FuTiTps6598xPdDevice { FuDevice parent_instance; @@ -33,13 +34,16 @@ static gboolean fu_ti_tps6598x_pd_device_ensure_version(FuTiTps6598xPdDevice *self, GError **error) { - FuTiTps6598xDevice *proxy = FU_TI_TPS6598X_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuTiTps6598xDevice *proxy; g_autoptr(GByteArray) buf = NULL; g_autofree gchar *str = NULL; + proxy = FU_TI_TPS6598X_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; buf = fu_ti_tps6598x_device_read_target_register(proxy, self->target, - TI_TPS6598X_REGISTER_VERSION, + FU_TI_TPS6598X_REGISTER_VERSION, 4, error); if (buf == NULL) @@ -56,13 +60,16 @@ static gboolean fu_ti_tps6598x_pd_device_ensure_tx_identity(FuTiTps6598xPdDevice *self, GError **error) { - FuTiTps6598xDevice *proxy = FU_TI_TPS6598X_DEVICE(fu_device_get_proxy(FU_DEVICE(self))); + FuTiTps6598xDevice *proxy; guint16 val = 0; g_autoptr(GByteArray) buf = NULL; + proxy = FU_TI_TPS6598X_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; buf = fu_ti_tps6598x_device_read_target_register(proxy, self->target, - TI_TPS6598X_REGISTER_TX_IDENTITY, + FU_TI_TPS6598X_REGISTER_TX_IDENTITY, 47, error); if (buf == NULL) @@ -112,11 +119,14 @@ fu_ti_tps6598x_pd_device_report_metadata_pre(FuDevice *device, GHashTable *metadata) { FuTiTps6598xPdDevice *self = FU_TI_TPS6598X_PD_DEVICE(device); - FuTiTps6598xDevice *proxy = FU_TI_TPS6598X_DEVICE(fu_device_get_proxy(device)); + FuTiTps6598xDevice *proxy; /* this is too slow to do for each update... */ if (g_getenv("FWUPD_TI_TPS6598X_VERBOSE") == NULL) return; + proxy = FU_TI_TPS6598X_DEVICE(fu_device_get_proxy(device, NULL)); + if (proxy == NULL) + return; for (guint i = 0; i < 0x80; i++) { g_autoptr(GByteArray) buf = NULL; g_autoptr(GError) error_local = NULL; @@ -145,7 +155,9 @@ static gboolean fu_ti_tps6598x_pd_device_attach(FuDevice *device, FuProgress *progress, GError **error) { - FuTiTps6598xDevice *proxy = FU_TI_TPS6598X_DEVICE(fu_device_get_proxy(device)); + FuTiTps6598xDevice *proxy = FU_TI_TPS6598X_DEVICE(fu_device_get_proxy(device, error)); + if (proxy == NULL) + return FALSE; return fu_device_attach_full(FU_DEVICE(proxy), progress, error); } @@ -156,7 +168,9 @@ FwupdInstallFlags flags, GError **error) { - FuTiTps6598xDevice *proxy = FU_TI_TPS6598X_DEVICE(fu_device_get_proxy(device)); + FuTiTps6598xDevice *proxy = FU_TI_TPS6598X_DEVICE(fu_device_get_proxy(device, error)); + if (proxy == NULL) + return FALSE; return fu_ti_tps6598x_device_write_firmware(FU_DEVICE(proxy), firmware, progress, @@ -165,7 +179,7 @@ } static void -fu_ti_tps6598x_pd_device_set_progress(FuDevice *self, FuProgress *progress) +fu_ti_tps6598x_pd_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -185,6 +199,7 @@ fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_VENDOR); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN); fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_TI_TPS6598X_FIRMWARE); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_TI_TPS6598X_DEVICE); fu_device_set_remove_delay(FU_DEVICE(self), 30000); } diff -Nru fwupd-2.0.8/plugins/ti-tps6598x/fu-ti-tps6598x-plugin.c fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-plugin.c --- fwupd-2.0.8/plugins/ti-tps6598x/fu-ti-tps6598x-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -27,6 +27,7 @@ fu_ti_tps6598x_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_set_device_gtype_default(plugin, FU_TYPE_TI_TPS6598X_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_TI_TPS6598X_PD_DEVICE); /* coverage */ fu_plugin_add_firmware_gtype(plugin, "ti-tps6598x", FU_TYPE_TI_TPS6598X_FIRMWARE); diff -Nru fwupd-2.0.8/plugins/ti-tps6598x/fu-ti-tps6598x.rs fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x.rs --- fwupd-2.0.8/plugins/ti-tps6598x/fu-ti-tps6598x.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ti-tps6598x/fu-ti-tps6598x.rs 2026-02-26 11:36:18.000000000 +0000 @@ -46,3 +46,24 @@ FailCrcBusy = 0xE, FailUnknownError = 0xF, } + +enum FuTiTps6598xRegister { + TbtVid = 0x00, // ro, 4 bytes -- Intel assigned + TbtDid = 0x01, // ro, 4 bytes -- Intel assigned + ProtoVer = 0x02, // ro, 4 bytes + Mode = 0x03, // ro, 4 bytes + Type = 0x04, // ro, 4 bytes + Uid = 0x05, // ro, 16 bytes + Ouid = 0x06, // ro, 8 bytes + Cmd1 = 0x08, // ro, 4CC + Data1 = 0x09, // rw, 64 bytes + Version = 0x0F, // rw, 4 bytes + Cmd2 = 0x10, // ro, 4CC + Data2 = 0x11, // rw, 64 bytes + Cmd3 = 0x1E, // ro, variable + Data3 = 0x1F, // ro, variable + Otp_config = 0x2D, // ro, 12 bytes + BuildIdentifier = 0x2E, // ro, 64 bytes + DeviceInfo = 0x2F, // ro, 47 bytes + TxIdentity = 0x47, // rw, 49 bytes +} diff -Nru fwupd-2.0.8/plugins/ti-tps6598x/tests/caldigit-ts4.json fwupd-2.0.20/plugins/ti-tps6598x/tests/caldigit-ts4.json --- fwupd-2.0.8/plugins/ti-tps6598x/tests/caldigit-ts4.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/ti-tps6598x/tests/caldigit-ts4.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/e07ae12eefb6dc1b9985b5759a017a74160a587361db2e4e63c4032d1af5d7d2-CalDigit-TS4-combind-050322-secure-Cus-22.cab", - "emulation-url": "https://fwupd.org/downloads/e755191ff3c7d26777273262eff0be266758ebe5b79685cd44d14d491ab6107a-CalDigit-TS4-combind-050322-secure-Cus-22.zip", + "url": "e07ae12eefb6dc1b9985b5759a017a74160a587361db2e4e63c4032d1af5d7d2-CalDigit-TS4-combind-050322-secure-Cus-22.cab", + "emulation-url": "e755191ff3c7d26777273262eff0be266758ebe5b79685cd44d14d491ab6107a-CalDigit-TS4-combind-050322-secure-Cus-22.zip", "components": [ { "version": "F907.14.10", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/d685ab1fa73a63a797172d498370d8717a6d80c70fff527be051baeaf8ac2bb8-CalDigit-TS4-combind-121022-secure-Cus-37.cab", - "emulation-url": "https://fwupd.org/downloads/21c81548a6126f806966497613bc6e33ddd5b3dc045e7cc83b27abd64ba6afd8-CalDigit-TS4-combind-121022-secure-Cus-37.zip", + "url": "d685ab1fa73a63a797172d498370d8717a6d80c70fff527be051baeaf8ac2bb8-CalDigit-TS4-combind-121022-secure-Cus-37.cab", + "emulation-url": "21c81548a6126f806966497613bc6e33ddd5b3dc045e7cc83b27abd64ba6afd8-CalDigit-TS4-combind-121022-secure-Cus-37.zip", "components": [ { "version": "F907.14.13", diff -Nru fwupd-2.0.8/plugins/tpm/README.md fwupd-2.0.20/plugins/tpm/README.md --- fwupd-2.0.8/plugins/tpm/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/tpm/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -47,9 +47,10 @@ If a vendor wanted to use the plugin code provided here they would still need to tell us how to set up the correct `keyHandle` for `FieldUpgradeStart` and how to handle authorization. -We do not know of any vendor that does TPM updates using the standardized API, and so the code -provided here is more of a *this is how it should be implemented* rather than with any expectation -it is actually going to just work. +> [!WARNING] +> We do not know of any vendor that does TPM updates using the standardized API, and so the code +> provided here is more of a *this is how it should be implemented* rather than with any expectation +> it is actually going to just work. ## Vendor ID Security diff -Nru fwupd-2.0.8/plugins/tpm/fu-self-test.c fwupd-2.0.20/plugins/tpm/fu-self-test.c --- fwupd-2.0.8/plugins/tpm/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/tpm/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -30,6 +30,12 @@ g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) pcr0s = NULL; g_autoptr(GPtrArray) pcrXs = NULL; + const gchar *tpm_server_running = g_getenv("TPM2TOOLS_TCTI"); + + if (tpm_server_running != NULL) { + g_test_skip("Skipping TPM1.2 tests when simulator running"); + return; + } /* do not save silo */ ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); @@ -41,6 +47,9 @@ ret = fu_plugin_runner_startup(plugin, progress, &error); g_assert_no_error(error); g_assert_true(ret); + ret = fu_plugin_runner_coldplug(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); /* get the v1.2 device */ devices = fu_plugin_get_devices(plugin); @@ -72,7 +81,7 @@ &error); g_assert_no_error(error); g_assert_nonnull(attr1); - /* Some PCRs are empty, but PCRs 0-7 are set (tests/tpm0/pcrs) */ + /* some PCRs are empty, but PCRs 0-7 are set (tests/tpm0/pcrs) */ g_assert_cmpint(fwupd_security_attr_get_result(attr1), ==, FWUPD_SECURITY_ATTR_RESULT_VALID); @@ -82,43 +91,49 @@ fu_tpm_device_2_0_func(void) { gboolean ret; + FuTpmV2Device *device; + GPtrArray *devices; + const gchar *tpm_server_running = g_getenv("TPM2TOOLS_TCTI"); g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuPlugin) plugin = NULL; g_autoptr(FuDeviceLocker) locker = NULL; - g_autoptr(FuTpmDevice) device = fu_tpm_v2_device_new(ctx); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); 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"); - (void)g_setenv("FWUPD_FORCE_TPM2", "1", TRUE); -#ifdef HAVE_GETUID - 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"); - g_unsetenv("FWUPD_FORCE_TPM2"); - return; - } -#endif - fu_device_set_physical_id(FU_DEVICE(device), "dummy"); - locker = fu_device_locker_new(FU_DEVICE(device), &error); - if (locker == NULL && 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"); - g_unsetenv("FWUPD_FORCE_TPM2"); + if (tpm_server_running == NULL) { + g_test_skip("TPM2.0 tests require simulated TPM2.0 running"); return; } + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* load the plugin */ + plugin = fu_plugin_new_from_gtype(fu_tpm_plugin_get_type(), ctx); + ret = fu_plugin_runner_startup(plugin, progress, &error); g_assert_no_error(error); - g_assert_nonnull(locker); - ret = fu_device_setup(FU_DEVICE(device), &error); + g_assert_true(ret); + + ret = fu_plugin_runner_coldplug(plugin, progress, &error); g_assert_no_error(error); g_assert_true(ret); - pcr0s = fu_tpm_device_get_checksums(device, 0); + + /* get the v2.0 device */ + devices = fu_plugin_get_devices(plugin); + g_assert_cmpint(devices->len, ==, 1); + device = g_ptr_array_index(devices, 0); + g_assert_true(FU_IS_TPM_V2_DEVICE(device)); + + pcr0s = fu_tpm_device_get_checksums(FU_TPM_DEVICE(device), 0); g_assert_nonnull(pcr0s); g_assert_cmpint(pcr0s->len, >=, 1); - pcrXs = fu_tpm_device_get_checksums(device, 999); + pcrXs = fu_tpm_device_get_checksums(FU_TPM_DEVICE(device), 999); g_assert_nonnull(pcrXs); g_assert_cmpint(pcrXs->len, ==, 0); - g_unsetenv("FWUPD_FORCE_TPM2"); } static void @@ -203,6 +218,12 @@ g_autoptr(FuSecurityAttrs) attrs = fu_security_attrs_new(); g_autoptr(FwupdSecurityAttr) attr = NULL; g_autoptr(GError) error = NULL; + const gchar *tpm_server_running = g_getenv("TPM2TOOLS_TCTI"); + + if (tpm_server_running != NULL) { + g_test_skip("Skipping empty PCR tests when simulator running"); + return; + } /* do not save silo */ ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); @@ -219,6 +240,9 @@ ret = fu_plugin_runner_startup(plugin, progress, &error); g_assert_no_error(error); g_assert_true(ret); + ret = fu_plugin_runner_coldplug(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); /* verify HSI attr */ fu_plugin_runner_add_security_attrs(plugin, attrs); @@ -247,6 +271,7 @@ g_test_init(&argc, &argv, NULL); testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + (void)g_setenv("FWUPD_SYSFSDIR", testdatadir, TRUE); (void)g_setenv("FWUPD_SYSFSTPMDIR", testdatadir, TRUE); (void)g_setenv("FWUPD_UEFI_TEST", "1", TRUE); (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); diff -Nru fwupd-2.0.8/plugins/tpm/fu-tpm-device.c fwupd-2.0.20/plugins/tpm/fu-tpm-device.c --- fwupd-2.0.8/plugins/tpm/fu-tpm-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/tpm/fu-tpm-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -105,7 +105,7 @@ fu_device_set_name(FU_DEVICE(self), "TPM"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); } static void diff -Nru fwupd-2.0.8/plugins/tpm/fu-tpm-eventlog-common.c fwupd-2.0.20/plugins/tpm/fu-tpm-eventlog-common.c --- fwupd-2.0.8/plugins/tpm/fu-tpm-eventlog-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/tpm/fu-tpm-eventlog-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -95,9 +95,9 @@ continue; /* if TXT is enabled then the first event for PCR0 should be a StartupLocality */ - if (item->kind == FU_TPM_EVENTLOG_ITEM_KIND_EV_NO_ACTION && item->pcr == 0 && + if (item->kind == FU_TPM_EVENTLOG_ITEM_KIND_NO_ACTION && item->pcr == 0 && item->blob != NULL && i == 0) { - g_autoptr(GByteArray) st_loc = NULL; + g_autoptr(FuStructTpmEfiStartupLocalityEvent) st_loc = NULL; st_loc = fu_struct_tpm_efi_startup_locality_event_parse_bytes(item->blob, 0x0, NULL); diff -Nru fwupd-2.0.8/plugins/tpm/fu-tpm-eventlog-parser.c fwupd-2.0.20/plugins/tpm/fu-tpm-eventlog-parser.c --- fwupd-2.0.8/plugins/tpm/fu-tpm-eventlog-parser.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/tpm/fu-tpm-eventlog-parser.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,12 +11,6 @@ #include "fu-tpm-eventlog-parser.h" #include "fu-tpm-struct.h" -#define FU_TPM_EVENTLOG_V1_IDX_PCR 0x00 -#define FU_TPM_EVENTLOG_V1_IDX_TYPE 0x04 -#define FU_TPM_EVENTLOG_V1_IDX_DIGEST 0x08 -#define FU_TPM_EVENTLOG_V1_IDX_EVENT_SIZE 0x1c -#define FU_TPM_EVENTLOG_V1_SIZE 0x20 - #define FU_TPM_EVENTLOG_V2_HDR_SIGNATURE "Spec ID Event03" static void @@ -76,26 +70,26 @@ /* advance over the header block */ if (!fu_memread_uint32_safe(buf, bufsz, - FU_TPM_EVENTLOG_V1_IDX_EVENT_SIZE, + FU_STRUCT_TPM_EVENT_LOG1_ITEM_OFFSET_DATASZ, &hdrsz, G_LITTLE_ENDIAN, error)) return NULL; items = g_ptr_array_new_with_free_func((GDestroyNotify)fu_tpm_eventlog_parser_item_free); - for (gsize idx = FU_TPM_EVENTLOG_V1_SIZE + hdrsz; idx < bufsz;) { + for (gsize idx = FU_STRUCT_TPM_EVENT_LOG1_ITEM_SIZE + hdrsz; idx < bufsz;) { guint32 pcr; guint32 digestcnt; guint32 datasz = 0; g_autoptr(GBytes) checksum_sha1 = NULL; g_autoptr(GBytes) checksum_sha256 = NULL; g_autoptr(GBytes) checksum_sha384 = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructTpmEventLog2) st = NULL; /* read checksum block */ st = fu_struct_tpm_event_log2_parse(buf, bufsz, idx, error); if (st == NULL) return NULL; - idx += st->len; + idx += st->buf->len; digestcnt = fu_struct_tpm_event_log2_get_digest_count(st); for (guint i = 0; i < digestcnt; i++) { guint16 alg_type = 0; @@ -216,7 +210,7 @@ 0x0, /* dst */ buf, bufsz, - FU_TPM_EVENTLOG_V1_SIZE, /* src */ + FU_STRUCT_TPM_EVENT_LOG1_ITEM_SIZE, /* src */ sizeof(sig), error)) return NULL; @@ -225,31 +219,18 @@ /* assume v1 structure */ items = g_ptr_array_new_with_free_func((GDestroyNotify)fu_tpm_eventlog_parser_item_free); - for (gsize idx = 0; idx < bufsz; idx += FU_TPM_EVENTLOG_V1_SIZE) { + for (gsize idx = 0; idx < bufsz; idx += FU_STRUCT_TPM_EVENT_LOG1_ITEM_SIZE) { guint32 datasz = 0; guint32 pcr = 0; guint32 event_type = 0; - if (!fu_memread_uint32_safe(buf, - bufsz, - idx + FU_TPM_EVENTLOG_V1_IDX_PCR, - &pcr, - G_LITTLE_ENDIAN, - error)) - return NULL; - if (!fu_memread_uint32_safe(buf, - bufsz, - idx + FU_TPM_EVENTLOG_V1_IDX_TYPE, - &event_type, - G_LITTLE_ENDIAN, - error)) - return NULL; - if (!fu_memread_uint32_safe(buf, - bufsz, - idx + FU_TPM_EVENTLOG_V1_IDX_EVENT_SIZE, - &datasz, - G_LITTLE_ENDIAN, - error)) + g_autoptr(FuStructTpmEventLog1Item) st = NULL; + + st = fu_struct_tpm_event_log1_item_parse(buf, bufsz, idx, error); + if (st == NULL) return NULL; + pcr = fu_struct_tpm_event_log1_item_get_pcr(st); + event_type = fu_struct_tpm_event_log1_item_get_event_type(st); + datasz = fu_struct_tpm_event_log1_item_get_datasz(st); if (datasz > 1024 * 1024) { g_set_error_literal(error, FWUPD_ERROR, @@ -259,24 +240,15 @@ } if (pcr == ESYS_TR_PCR0 || flags & FU_TPM_EVENTLOG_PARSER_FLAG_ALL_PCRS) { g_autoptr(FuTpmEventlogItem) item = NULL; - guint8 digest[TPM2_SHA1_DIGEST_SIZE] = {0x0}; - - /* copy hash */ - if (!fu_memcpy_safe(digest, - sizeof(digest), - 0x0, /* dst */ - buf, - bufsz, - idx + FU_TPM_EVENTLOG_V1_IDX_DIGEST, /* src */ - sizeof(digest), - error)) - return NULL; + gsize digestsz = 0; + const guint8 *digest = + fu_struct_tpm_event_log1_item_get_digest(st, &digestsz); /* build item */ item = g_new0(FuTpmEventlogItem, 1); item->pcr = pcr; item->kind = event_type; - item->checksum_sha1 = g_bytes_new(digest, sizeof(digest)); + item->checksum_sha1 = g_bytes_new(digest, digestsz); if (datasz > 0) { g_autofree guint8 *data = g_malloc0(datasz); if (!fu_memcpy_safe(data, @@ -284,7 +256,7 @@ 0x0, /* dst */ buf, bufsz, - idx + FU_TPM_EVENTLOG_V1_SIZE, /* src */ + idx + st->buf->len, /* src */ datasz, error)) return NULL; diff -Nru fwupd-2.0.8/plugins/tpm/fu-tpm-eventlog-parser.h fwupd-2.0.20/plugins/tpm/fu-tpm-eventlog-parser.h --- fwupd-2.0.8/plugins/tpm/fu-tpm-eventlog-parser.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/tpm/fu-tpm-eventlog-parser.h 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,7 @@ FU_TPM_EVENTLOG_PARSER_FLAG_NONE = 0, FU_TPM_EVENTLOG_PARSER_FLAG_ALL_PCRS = 1 << 0, FU_TPM_EVENTLOG_PARSER_FLAG_LAST -} FuTpmEventlogParserFlags; +} G_GNUC_FLAG_ENUM FuTpmEventlogParserFlags; GPtrArray * fu_tpm_eventlog_parser_new(const guint8 *buf, diff -Nru fwupd-2.0.8/plugins/tpm/fu-tpm-eventlog.c fwupd-2.0.20/plugins/tpm/fu-tpm-eventlog.c --- fwupd-2.0.8/plugins/tpm/fu-tpm-eventlog.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/tpm/fu-tpm-eventlog.c 2026-02-26 11:36:18.000000000 +0000 @@ -16,6 +16,27 @@ #include "fu-tpm-eventlog-parser.h" +typedef struct { + gint pcr; + gboolean dump; +} FuUtil; + +static FuUtil * +fu_tpm_eventlog_new(void) +{ + FuUtil *self = g_new0(FuUtil, 1); + self->pcr = -1; + return self; +} + +static void +fu_tpm_eventlog_free(FuUtil *self) +{ + g_free(self); +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtil, fu_tpm_eventlog_free) + static gint fu_tpm_eventlog_sort_cb(gconstpointer a, gconstpointer b) { @@ -29,7 +50,7 @@ } static gboolean -fu_tpm_eventlog_process(const gchar *fn, gint pcr, GError **error) +fu_tpm_eventlog_process(FuUtil *self, const gchar *fn, GError **error) { gsize bufsz = 0; g_autofree guint8 *buf = NULL; @@ -49,17 +70,23 @@ FuTpmEventlogItem *item = g_ptr_array_index(items, i); if (item->pcr > max_pcr) max_pcr = item->pcr; - if (pcr >= 0 && item->pcr != pcr) + if (self->pcr >= 0 && item->pcr != self->pcr) continue; fu_tpm_eventlog_item_to_string(item, 0, str); g_string_append(str, "\n"); + if (self->dump) { + g_autofree gchar *blobfn = + g_strdup_printf("tpm-pcr%02u-%03u.bin", item->pcr, i); + if (!fu_bytes_set_contents(blobfn, item->blob, error)) + return FALSE; + } } - if (pcr > max_pcr) { + if (self->pcr > max_pcr) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "invalid PCR specified: %d", - pcr); + self->pcr); return FALSE; } fwupd_codec_string_append(str, 0, "Reconstructed PCRs", ""); @@ -71,7 +98,7 @@ const gchar *csum = g_ptr_array_index(pcrs, j); g_autofree gchar *title = NULL; g_autofree gchar *pretty = NULL; - if (pcr >= 0 && i != (guint)pcr) + if (self->pcr >= 0 && i != (guint)self->pcr) continue; title = g_strdup_printf("PCR %x", i); pretty = fwupd_checksum_format_for_display(csum); @@ -90,26 +117,36 @@ const gchar *fn; gboolean verbose = FALSE; gboolean interactive = isatty(fileno(stdout)) != 0; - gint pcr = -1; + g_autoptr(FuUtil) self = fu_tpm_eventlog_new(); g_autoptr(GError) error = NULL; g_autoptr(GOptionContext) context = g_option_context_new(NULL); - const GOptionEntry options[] = {{"verbose", - 'v', - 0, - G_OPTION_ARG_NONE, - &verbose, - /* TRANSLATORS: command line option */ - N_("Show extra debugging information"), - NULL}, - {"pcr", - 'p', - 0, - G_OPTION_ARG_INT, - &pcr, - /* TRANSLATORS: command line option */ - N_("Only show single PCR value"), - NULL}, - {NULL}}; + const GOptionEntry options[] = { + {"verbose", + 'v', + 0, + G_OPTION_ARG_NONE, + &verbose, + /* TRANSLATORS: command line option */ + N_("Show extra debugging information"), + NULL}, + {"dump", + '\0', + 0, + G_OPTION_ARG_NONE, + &self->dump, + /* TRANSLATORS: command line option */ + N_("Dump PCR contents to a file on disk"), + NULL}, + {"pcr", + 'p', + 0, + G_OPTION_ARG_INT, + &self->pcr, + /* TRANSLATORS: command line option */ + N_("Only show single PCR value"), + NULL}, + {NULL}, + }; setlocale(LC_ALL, ""); bindtextdomain(GETTEXT_PACKAGE, FWUPD_LOCALEDIR); @@ -144,7 +181,7 @@ /* allow user to chose a local file */ fn = argc <= 1 ? "/sys/kernel/security/tpm0/binary_bios_measurements" : argv[1]; - if (!fu_tpm_eventlog_process(fn, pcr, &error)) { + if (!fu_tpm_eventlog_process(self, fn, &error)) { /* TRANSLATORS: failed to read measurements file */ g_printerr("%s: %s\n", _("Failed to parse file"), error->message); return EXIT_FAILURE; diff -Nru fwupd-2.0.8/plugins/tpm/fu-tpm-plugin.c fwupd-2.0.20/plugins/tpm/fu-tpm-plugin.c --- fwupd-2.0.8/plugins/tpm/fu-tpm-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/tpm/fu-tpm-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -24,16 +24,18 @@ fu_tpm_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) { FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); - if (self->tpm_device != NULL) + if (self->tpm_device != NULL) { fwupd_codec_string_append(str, idt, "TpmDevice", fu_device_get_id(self->tpm_device)); - if (self->bios_device != NULL) + } + if (self->bios_device != NULL) { fwupd_codec_string_append(str, idt, "BiosDevice", fu_device_get_id(self->bios_device)); + } } static void @@ -295,16 +297,15 @@ gsize bufsz = 0; g_autofree gchar *fn = NULL; g_autofree gchar *str = NULL; - g_autofree gchar *sysfsdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR); g_autofree guint8 *buf = NULL; /* do not show a warning if no TPM exists, or the kernel is too old */ - fn = g_build_filename(sysfsdir, - "kernel", - "security", - "tpm0", - "binary_bios_measurements", - NULL); + fn = fu_path_build(FU_PATH_KIND_SYSFSDIR, + "kernel", + "security", + "tpm0", + "binary_bios_measurements", + NULL); if (!g_file_test(fn, G_FILE_TEST_EXISTS)) { g_debug("no %s, so skipping", fn); return TRUE; @@ -333,28 +334,32 @@ static gboolean fu_tpm_plugin_coldplug(FuPlugin *plugin, FuProgress *progress, GError **error) { + FuContext *ctx = fu_plugin_get_context(plugin); + FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); g_autoptr(GError) error_local = NULL; + g_autofree gchar *fn_pcrs = NULL; + + /* look for TPM v2.0 via software TCTI */ + if (g_getenv("TPM2TOOLS_TCTI") != NULL) { + g_autoptr(FuDeviceLocker) locker = NULL; + + self->tpm_device = fu_tpm_v2_device_new(ctx); + fu_device_set_physical_id(FU_DEVICE(self->tpm_device), "TCTI"); + locker = fu_device_locker_new(FU_DEVICE(self->tpm_device), error); + if (locker == NULL) + return FALSE; + fu_plugin_device_add(plugin, FU_DEVICE(self->tpm_device)); + return TRUE; + } /* best effort */ if (!fu_tpm_plugin_coldplug_eventlog(plugin, &error_local)) g_warning("failed to load eventlog: %s", error_local->message); - /* success */ - return TRUE; -} - -static gboolean -fu_tpm_plugin_startup(FuPlugin *plugin, FuProgress *progress, GError **error) -{ - FuTpmPlugin *self = FU_TPM_PLUGIN(plugin); - g_autofree gchar *sysfstpmdir = NULL; - g_autofree gchar *fn_pcrs = NULL; - /* look for TPM v1.2 */ - sysfstpmdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_TPM); - fn_pcrs = g_build_filename(sysfstpmdir, "tpm0", "pcrs", NULL); - if (g_file_test(fn_pcrs, G_FILE_TEST_EXISTS) && g_getenv("FWUPD_FORCE_TPM2") == NULL) { - self->tpm_device = fu_tpm_v1_device_new(fu_plugin_get_context(plugin)); + fn_pcrs = fu_path_build(FU_PATH_KIND_SYSFSDIR_TPM, "tpm0", "pcrs", NULL); + if (g_file_test(fn_pcrs, G_FILE_TEST_EXISTS)) { + self->tpm_device = fu_tpm_v1_device_new(ctx); g_object_set(self->tpm_device, "device-file", fn_pcrs, NULL); fu_device_set_physical_id(FU_DEVICE(self->tpm_device), "tpm"); if (!fu_device_probe(FU_DEVICE(self->tpm_device), error)) @@ -378,7 +383,8 @@ /* old name */ fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_CONFLICTS, "tpm_eventlog"); - fu_plugin_add_device_udev_subsystem(plugin, "tpm"); + if (g_getenv("TPM2TOOLS_TCTI") == NULL) + fu_plugin_add_device_udev_subsystem(plugin, "tpm"); fu_plugin_set_device_gtype_default(plugin, FU_TYPE_TPM_V2_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_TPM_V1_DEVICE); /* coverage */ } @@ -405,7 +411,6 @@ object_class->finalize = fu_tpm_plugin_finalize; plugin_class->constructed = fu_tpm_plugin_constructed; plugin_class->to_string = fu_tpm_plugin_to_string; - plugin_class->startup = fu_tpm_plugin_startup; plugin_class->coldplug = fu_tpm_plugin_coldplug; plugin_class->device_added = fu_tpm_plugin_device_added; plugin_class->device_registered = fu_tpm_plugin_device_registered; diff -Nru fwupd-2.0.8/plugins/tpm/fu-tpm-v2-device.c fwupd-2.0.20/plugins/tpm/fu-tpm-v2-device.c --- fwupd-2.0.8/plugins/tpm/fu-tpm-v2-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/tpm/fu-tpm-v2-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -20,21 +20,15 @@ static gboolean fu_tpm_v2_device_probe(FuDevice *device, GError **error) { - g_autofree gchar *physical_id = NULL; - g_autofree gchar *prop_devname = NULL; - - /* we don't have anything better */ - prop_devname = fu_udev_device_read_property(FU_UDEV_DEVICE(device), "DEVNAME", error); - if (prop_devname == NULL) - return FALSE; - physical_id = g_strdup_printf("DEVNAME=%s", prop_devname); - fu_device_set_physical_id(device, physical_id); - - /* fake something as we cannot talk to tpmd */ - if (fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_IS_FAKE)) { - fu_device_add_instance_str(device, "VEN", "fwupd"); - fu_device_add_instance_str(device, "DEV", "TPM"); - return fu_device_build_instance_id(device, error, "TPM", "VEN", "DEV", NULL); + if (fu_device_get_physical_id(device) == NULL) { + g_autofree gchar *physical_id = NULL; + g_autofree gchar *prop_devname = NULL; + prop_devname = + fu_udev_device_read_property(FU_UDEV_DEVICE(device), "DEVNAME", error); + if (prop_devname == NULL) + return FALSE; + physical_id = g_strdup_printf("DEVNAME=%s", prop_devname); + fu_device_set_physical_id(device, physical_id); } /* success */ @@ -110,55 +104,6 @@ return fu_strstrip(result); } -/* taken from TCG-TPM-Vendor-ID-Registry-Version-1.01-Revision-1.00.pdf */ -static const gchar * -fu_tpm_v2_device_convert_manufacturer(const gchar *manufacturer) -{ - if (g_strcmp0(manufacturer, "AMD") == 0) - return "Advanced Micro Devices, Inc."; - if (g_strcmp0(manufacturer, "ATML") == 0) - return "Atmel"; - if (g_strcmp0(manufacturer, "BRCM") == 0) - return "Broadcom"; - if (g_strcmp0(manufacturer, "HPE") == 0) - return "HPE"; - if (g_strcmp0(manufacturer, "IBM") == 0) - return "IBM"; - if (g_strcmp0(manufacturer, "IFX") == 0) - return "Infineon"; - if (g_strcmp0(manufacturer, "INTC") == 0) - return "Intel"; - if (g_strcmp0(manufacturer, "LEN") == 0) - return "Lenovo"; - if (g_strcmp0(manufacturer, "MSFT") == 0) - return "Microsoft"; - if (g_strcmp0(manufacturer, "NSM") == 0) - return "National Semiconductor"; - if (g_strcmp0(manufacturer, "NTZ") == 0) - return "Nationz"; - if (g_strcmp0(manufacturer, "NTC") == 0) - return "Nuvoton Technology"; - if (g_strcmp0(manufacturer, "QCOM") == 0) - return "Qualcomm"; - if (g_strcmp0(manufacturer, "SMSC") == 0) - return "SMSC"; - if (g_strcmp0(manufacturer, "STM") == 0) - return "ST Microelectronics"; - if (g_strcmp0(manufacturer, "SMSN") == 0) - return "Samsung"; - if (g_strcmp0(manufacturer, "SNS") == 0) - return "Sinosun"; - if (g_strcmp0(manufacturer, "TXN") == 0) - return "Texas Instruments"; - if (g_strcmp0(manufacturer, "WEC") == 0) - return "Winbond"; - if (g_strcmp0(manufacturer, "ROCC") == 0) - return "Fuzhou Rockchip"; - if (g_strcmp0(manufacturer, "GOOG") == 0) - return "Google"; - return NULL; -} - static gboolean fu_tpm_v2_device_setup_pcrs(FuTpmV2Device *self, GError **error) { @@ -290,7 +235,6 @@ { FuTpmV2Device *self = FU_TPM_V2_DEVICE(device); TSS2_RC rc; - const gchar *tmp; guint32 tpm_type = 0; guint32 version1 = 0; guint32 version2 = 0; @@ -319,29 +263,29 @@ /* lookup guaranteed details from TPM */ family = fu_tpm_v2_device_get_string(self, TPM2_PT_FAMILY_INDICATOR, error); if (family == NULL) { - g_prefix_error(error, "failed to read TPM family: "); + g_prefix_error_literal(error, "failed to read TPM family: "); return FALSE; } fu_tpm_device_set_family(FU_TPM_DEVICE(self), family); manufacturer = fu_tpm_v2_device_get_string(self, TPM2_PT_MANUFACTURER, error); if (manufacturer == NULL) { - g_prefix_error(error, "failed to read TPM manufacturer: "); + g_prefix_error_literal(error, "failed to read TPM manufacturer: "); return FALSE; } model1 = fu_tpm_v2_device_get_string(self, TPM2_PT_VENDOR_STRING_1, error); if (model1 == NULL) { - g_prefix_error(error, "failed to read TPM vendor string: "); + g_prefix_error_literal(error, "failed to read TPM vendor string: "); return FALSE; } if (!fu_tpm_v2_device_get_uint32(self, TPM2_PT_VENDOR_TPM_TYPE, &tpm_type, error)) { - g_prefix_error(error, "failed to read TPM type: "); + g_prefix_error_literal(error, "failed to read TPM type: "); return FALSE; } /* these are not guaranteed by spec and may be NULL */ - model2 = fu_tpm_v2_device_get_string(self, TPM2_PT_VENDOR_STRING_2, error); - model3 = fu_tpm_v2_device_get_string(self, TPM2_PT_VENDOR_STRING_3, error); - model4 = fu_tpm_v2_device_get_string(self, TPM2_PT_VENDOR_STRING_4, error); + model2 = fu_tpm_v2_device_get_string(self, TPM2_PT_VENDOR_STRING_2, NULL); + model3 = fu_tpm_v2_device_get_string(self, TPM2_PT_VENDOR_STRING_3, NULL); + model4 = fu_tpm_v2_device_get_string(self, TPM2_PT_VENDOR_STRING_4, NULL); model = g_strjoin("", model1, model2, model3, model4, NULL); /* add GUIDs to daemon */ @@ -349,6 +293,12 @@ fu_device_add_instance_u16(device, "DEV", tpm_type); fu_device_add_instance_str(device, "MOD", model); fu_device_add_instance_str(device, "VER", family); + fu_device_build_instance_id_full(device, + FU_DEVICE_INSTANCE_FLAG_QUIRKS, + NULL, + "TPM", + "VEN", + NULL); fu_device_build_instance_id(device, NULL, "TPM", "VEN", "DEV", NULL); fu_device_build_instance_id(device, NULL, "TPM", "VEN", "MOD", NULL); fu_device_build_instance_id(device, NULL, "TPM", "VEN", "DEV", "VER", NULL); @@ -356,8 +306,6 @@ /* enforce vendors can only ship updates for their own hardware */ fu_device_build_vendor_id(device, "TPM", manufacturer); - tmp = fu_tpm_v2_device_convert_manufacturer(manufacturer); - fu_device_set_vendor(device, tmp != NULL ? tmp : manufacturer); /* get version */ if (!fu_tpm_v2_device_get_uint32(self, TPM2_PT_FIRMWARE_VERSION_1, &version1, error)) diff -Nru fwupd-2.0.8/plugins/tpm/fu-tpm.rs fwupd-2.0.20/plugins/tpm/fu-tpm.rs --- fwupd-2.0.8/plugins/tpm/fu-tpm.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/tpm/fu-tpm.rs 2026-02-26 11:36:18.000000000 +0000 @@ -4,34 +4,34 @@ #[derive(ToString)] #[repr(u32le)] enum FuTpmEventlogItemKind { - EV_PREBOOT_CERT = 0x00000000, - EV_POST_CODE = 0x00000001, - EV_NO_ACTION = 0x00000003, - EV_SEPARATOR = 0x00000004, - EV_ACTION = 0x00000005, - EV_EVENT_TAG = 0x00000006, - EV_S_CRTM_CONTENTS = 0x00000007, - EV_S_CRTM_VERSION = 0x00000008, - EV_CPU_MICROCODE = 0x00000009, - EV_PLATFORM_CONFIG_FLAGS = 0x0000000a, - EV_TABLE_OF_DEVICES = 0x0000000b, - EV_COMPACT_HASH = 0x0000000c, - EV_NONHOST_CODE = 0x0000000f, - EV_NONHOST_CONFIG = 0x00000010, - EV_NONHOST_INFO = 0x00000011, - EV_OMIT_BOOT_DEVICE_EVENTS = 0x00000012, - EV_EFI_EVENT_BASE = 0x80000000, - EV_EFI_VARIABLE_DRIVER_CONFIG = 0x80000001, - EV_EFI_VARIABLE_BOOT = 0x80000002, - EV_EFI_BOOT_SERVICES_APPLICATION = 0x80000003, - EV_EFI_BOOT_SERVICES_DRIVER = 0x80000004, - EV_EFI_RUNTIME_SERVICES_DRIVER = 0x80000005, - EV_EFI_GPT_EVENT = 0x80000006, - EV_EFI_ACTION = 0x80000007, - EV_EFI_PLATFORM_FIRMWARE_BLOB = 0x80000008, - EV_EFI_HANDOFF_TABLES = 0x80000009, - EV_EFI_HCRTM_EVENT = 0x80000010, - EV_EFI_VARIABLE_AUTHORITY = 0x800000e0, + PrebootCert = 0x00000000, + PostCode = 0x00000001, + NoAction = 0x00000003, + Separator = 0x00000004, + Action = 0x00000005, + EventTag = 0x00000006, + SCrtmContents = 0x00000007, + SCrtmVersion = 0x00000008, + CpuMicrocode = 0x00000009, + PlatformConfigFlags = 0x0000000a, + TableOfDevices = 0x0000000b, + CompactHash = 0x0000000c, + NonhostCode = 0x0000000f, + NonhostConfig = 0x00000010, + NonhostInfo = 0x00000011, + OmitBootDeviceEvents = 0x00000012, + EfiEventBase = 0x80000000, + EfiVariableDriverConfig = 0x80000001, + EfiVariableBoot = 0x80000002, + EfiBootServicesApplication = 0x80000003, + EfiBootServicesDriver = 0x80000004, + EfiRuntimeServicesDriver = 0x80000005, + EfiGptEvent = 0x80000006, + EfiAction = 0x80000007, + EfiPlatformFirmwareBlob = 0x80000008, + EfiHandoffTables = 0x80000009, + EfiHcrtmEvent = 0x80000010, + EfiVariableAuthority = 0x800000e0, } #[derive(Parse)] @@ -48,3 +48,12 @@ signature: [char; 16] == "StartupLocality", locality: u8, // from which TPM2_Startup() was issued -- which is the initial value of PCR0 } + +#[derive(Parse)] +#[repr(C, packed)] +struct FuStructTpmEventLog1Item { + pcr: u32le, + event_type: u32le, + digest: [u8; 20], + datasz: u32le, +} diff -Nru fwupd-2.0.8/plugins/tpm/meson.build fwupd-2.0.20/plugins/tpm/meson.build --- fwupd-2.0.8/plugins/tpm/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/tpm/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,8 @@ +hsi or subdir_done() tpm2tss_tpm = dependency('tss2-esys', version: '>= 2.0', required: false) +tpm2tss_tpm.found() or subdir_done() +host_machine.system() == 'linux' or subdir_done() -if hsi and tpm2tss_tpm.found() and host_machine.system() == 'linux' cargs = ['-DG_LOG_DOMAIN="FuPluginTpm"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -53,6 +55,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ cargs, @@ -98,5 +101,3 @@ plugin_builtin_tpm, ], ) - -endif diff -Nru fwupd-2.0.8/plugins/tpm/tpm.quirk fwupd-2.0.20/plugins/tpm/tpm.quirk --- fwupd-2.0.8/plugins/tpm/tpm.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/tpm/tpm.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -1,2 +1,60 @@ [TPM\VEN_MSFT&MOD_Pluton.TPM.A] Flags = host-cpu-child + +# taken from TCG-TPM-Vendor-ID-Registry-Family-1.2-and-2.0-Version-1.07-Revision-0.02_pub.pdf +[TPM\VEN_AMD] +Vendor = AMD +[TPM\VEN_ANT] +Vendor = ANT +[TPM\VEN_ATML] +Vendor = Atmel +[TPM\VEN_BRCM] +Vendor = Broadcom +[TPM\VEN_CSCO] +Vendor = Cisco +[TPM\VEN_FLYS] +Vendor = Flyslice +[TPM\VEN_ROCC] +Vendor = Rockchip +[TPM\VEN_GOOG] +Vendor = Google +[TPM\VEN_HPI] +Vendor = HPI +[TPM\VEN_HPE] +Vendor = HPE +[TPM\VEN_IBM] +Vendor = IBM +[TPM\VEN_IFX] +Vendor = Infineon +[TPM\VEN_INTC] +Vendor = Intel +[TPM\VEN_LEN] +Vendor = Lenovo +[TPM\VEN_MSFT] +Vendor = Microsoft +[TPM\VEN_NSM] +Vendor = National Semiconductor +[TPM\VEN_NTZ] +Vendor = Nationz +[TPM\VEN_NSG] +Vendor = NSING +[TPM\VEN_NTC] +Vendor = Nuvoton +[TPM\VEN_QCOM] +Vendor = Qualcomm +[TPM\VEN_SMSN] +Vendor = Samsung +[TPM\VEN_SECE] +Vendor = SecEdge +[TPM\VEN_SMSC] +Vendor = SMSC +[TPM\VEN_STM] +Vendor = ST Microelectronics +[TPM\VEN_SNS] +Vendor = Sinosun +[TPM\VEN_TXN] +Vendor = Texas Instruments +[TPM\VEN_WEC] +Vendor = Winbond +[TPM\VEN_SEAL] +Vendor = Wisekey diff -Nru fwupd-2.0.8/plugins/uefi-capsule/README.md fwupd-2.0.20/plugins/uefi-capsule/README.md --- fwupd-2.0.8/plugins/uefi-capsule/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -8,10 +8,21 @@ defines the software interface between an OS and platform firmware. With the UpdateCapsule boot service it can be used to update system firmware. -When this plugin is enabled, the companion UEFI binary may also be built from the [fwupd-efi](https://github.com/fwupd/fwupd-efi) project if not already present on the filesystem by using the meson option `-Defi_binary=true`. +## Companion EFI application + +When this plugin is enabled, for it to work properly a UEFI binary must be available to flash capsules that have been staged. +This can be satisfied either by the [fwupd-efi](https://github.com/fwupd/fwupd-efi) project or by a 3rd party bootloader. + +### fwupd-efi project + +The companion UEFI binary may be built from the [fwupd-efi](https://github.com/fwupd/fwupd-efi) project if not already present on the filesystem by using the meson option `-Defi_binary=true`. For this companion binary to work with secure boot, it will need to be signed by an authority trusted with shim and/or the host environment. +### 3rd party bootloader + +If a 3rd party bootloader has implemented the fwupd UEFI capsule update protocol then it can create a 1 byte EFI variable ```BootloaderSupportsFwupd``` under the GUID namespace `0abba7dc-e516-4167-bbf5-4d9d1c739416`. fwupd will discover this at runtime and avoid the use of fwupd-efi. + ## Lenovo Specific Behavior On Lenovo hardware only the boot label is set to `Linux-Firmware-Updater` rather @@ -19,11 +30,12 @@ bugs. Many users will have these old BIOS versions installed and so we use the `use-legacy-bootmgr-desc` quirk to use the safe name. -On some Lenovo hardware only one capsule is installable due to possible problems -with the UpdateCapsule coalesce operation. As soon as one UEFI device has been -scheduled for update the other UEFI devices found in the ESRT will be marked -as `updatable-hidden` rather than `updatable`. Rebooting will restore them so -they can be updated on next OS boot. +> [!NOTE] +> On some Lenovo hardware only one capsule is installable due to possible problems +> with the UpdateCapsule coalesce operation. As soon as one UEFI device has been +> scheduled for update the other UEFI devices found in the ESRT will be marked +> as `updatable-hidden` rather than `updatable`. Rebooting will restore them so +> they can be updated on next OS boot. ## Firmware Format @@ -70,8 +82,9 @@ There are more details about firmware version formats and a full list of all the different allowed values on the [LVFS](https://lvfs.readthedocs.io/en/latest/metainfo.html#version-format). -NOTE: Firmware can require either the `quad` or `triplet` string version format, but it may be more -portable to depend on the number -- which will also work if the metadata has not been refreshed yet. +> [!TIP] +> Firmware can require either the `quad` or `triplet` string version format, but it may be more +> portable to depend on the number -- which will also work if the metadata has not been refreshed yet. ## Update Behavior @@ -109,8 +122,7 @@ ## GUID Generation -These devices use the UEFI GUID as provided in the ESRT. Additionally, for the -system device the `main-system-firmware` internal flag is also added. +These devices use the UEFI GUID as provided in the ESRT. For compatibility with Windows 10, the plugin also adds GUIDs of the form `UEFI\RES_{$(esrt)}`. @@ -187,6 +199,12 @@ Since: 2.0.7 +### `Flags=no-capsule-on-disk` + +Do not use Capsule-on-Disk support even if `OsIndicationsSupported` says it should be used. + +Since: 2.0.14 + ## External Interface Access This plugin requires: diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-acpi-uefi.c fwupd-2.0.20/plugins/uefi-capsule/fu-acpi-uefi.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-acpi-uefi.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-acpi-uefi.c 2026-02-26 11:36:18.000000000 +0000 @@ -38,7 +38,7 @@ { const gchar *needle = "$QUIRK"; gsize data_offset = 0; - g_autoptr(GByteArray) st_qrk = NULL; + g_autoptr(FuStructAcpiInsydeQuirk) st_qrk = NULL; g_autoptr(GBytes) fw = NULL; fw = fu_input_stream_read_bytes(stream, 0x0, G_MAXSIZE, NULL, error); @@ -50,7 +50,7 @@ strlen(needle), &data_offset, error)) { - g_prefix_error(error, "$QUIRK not found"); + g_prefix_error_literal(error, "$QUIRK not found: "); return FALSE; } @@ -58,7 +58,7 @@ st_qrk = fu_struct_acpi_insyde_quirk_parse_stream(stream, data_offset, error); if (st_qrk == NULL) return FALSE; - if (fu_struct_acpi_insyde_quirk_get_size(st_qrk) < st_qrk->len) { + if (fu_struct_acpi_insyde_quirk_get_size(st_qrk) < st_qrk->buf->len) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_READ, @@ -73,7 +73,7 @@ static gboolean fu_acpi_uefi_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuAcpiUefi *self = FU_ACPI_UEFI(firmware); @@ -81,7 +81,7 @@ /* FuAcpiTable->parse */ if (!FU_FIRMWARE_CLASS(fu_acpi_uefi_parent_class) - ->parse(FU_FIRMWARE(self), stream, FWUPD_INSTALL_FLAG_NONE, error)) + ->parse(FU_FIRMWARE(self), stream, flags, error)) return FALSE; /* check signature and read flags */ diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-bitmap-image.c fwupd-2.0.20/plugins/uefi-capsule/fu-bitmap-image.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-bitmap-image.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-bitmap-image.c 2026-02-26 11:36:18.000000000 +0000 @@ -28,7 +28,7 @@ static gboolean fu_bitmap_image_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuBitmapImage *self = FU_BITMAP_IMAGE(firmware); @@ -37,13 +37,13 @@ st_file = fu_struct_bitmap_file_header_parse_stream(stream, 0x0, error); if (st_file == NULL) { - g_prefix_error(error, "image is corrupt: "); + g_prefix_error_literal(error, "image is corrupt: "); return FALSE; } fu_firmware_set_size(firmware, fu_struct_bitmap_file_header_get_size(st_file)); - st_info = fu_struct_bitmap_info_header_parse_stream(stream, st_file->len, error); + st_info = fu_struct_bitmap_info_header_parse_stream(stream, st_file->buf->len, error); if (st_info == NULL) { - g_prefix_error(error, "header is corrupt: "); + g_prefix_error_literal(error, "header is corrupt: "); return FALSE; } self->width = fu_struct_bitmap_info_header_get_width(st_info); diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-self-test.c fwupd-2.0.20/plugins/uefi-capsule/fu-self-test.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,10 +8,15 @@ #include "fu-bitmap-image.h" #include "fu-context-private.h" +#include "fu-efivars-private.h" +#include "fu-plugin-private.h" #include "fu-uefi-bgrt.h" #include "fu-uefi-capsule-backend.h" +#include "fu-uefi-capsule-plugin.h" #include "fu-uefi-cod-device.h" #include "fu-uefi-common.h" +#include "fu-uefi-grub-device.h" +#include "fu-uefi-nvram-device.h" #include "fu-volume-private.h" static void @@ -32,8 +37,11 @@ device = g_object_new(FU_TYPE_UEFI_CAPSULE_DEVICE, "context", ctx, NULL); fu_uefi_capsule_device_set_esp(FU_UEFI_CAPSULE_DEVICE(device), volume_esp); - firmware = - fu_device_prepare_firmware(device, stream, progress, FWUPD_INSTALL_FLAG_NONE, &error); + firmware = fu_device_prepare_firmware(device, + stream, + progress, + FU_FIRMWARE_PARSE_FLAG_NONE, + &error); g_assert_no_error(error); g_assert_nonnull(firmware); } @@ -56,8 +64,11 @@ device = g_object_new(FU_TYPE_UEFI_CAPSULE_DEVICE, "context", ctx, NULL); fu_uefi_capsule_device_set_esp(FU_UEFI_CAPSULE_DEVICE(device), volume_esp); - firmware = - fu_device_prepare_firmware(device, stream, progress, FWUPD_INSTALL_FLAG_NONE, &error); + firmware = fu_device_prepare_firmware(device, + stream, + progress, + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, + &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_null(firmware); } @@ -81,8 +92,11 @@ device = g_object_new(FU_TYPE_UEFI_CAPSULE_DEVICE, "context", ctx, NULL); fu_device_add_private_flag(device, "no-esp-backup"); fu_uefi_capsule_device_set_esp(FU_UEFI_CAPSULE_DEVICE(device), volume_esp); - firmware = - fu_device_prepare_firmware(device, stream, progress, FWUPD_INSTALL_FLAG_NONE, &error); + firmware = fu_device_prepare_firmware(device, + stream, + progress, + FU_FIRMWARE_PARSE_FLAG_NONE, + &error); g_assert_no_error(error); g_assert_nonnull(firmware); } @@ -113,8 +127,8 @@ ret = fu_uefi_get_framebuffer_size(&width, &height, &error); g_assert_no_error(error); g_assert_true(ret); - g_assert_cmpint(width, ==, 456); - g_assert_cmpint(height, ==, 789); + g_assert_cmpint(width, ==, 800); + g_assert_cmpint(height, ==, 600); } static void @@ -133,7 +147,7 @@ ret = fu_firmware_parse_stream(FU_FIRMWARE(bmp_image), stream, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -146,21 +160,19 @@ { fwupd_guid_t guid = {0x0}; gboolean ret; - guint8 timestamp[16] = {0x0}; - g_autoptr(GByteArray) buf = g_byte_array_new(); g_autoptr(GError) error = NULL; + g_autoptr(FuStructEfiCapsuleResultVariableHeader) st = + fu_struct_efi_capsule_result_variable_header_new(); - fu_byte_array_append_uint32(buf, 0x3A, G_LITTLE_ENDIAN); /* VariableTotalSize */ - fu_byte_array_append_uint32(buf, 0xFF, G_LITTLE_ENDIAN); /* Reserved */ + fu_struct_efi_capsule_result_variable_header_set_total_size( + st, + FU_STRUCT_EFI_CAPSULE_RESULT_VARIABLE_HEADER_SIZE); ret = fwupd_guid_from_string(guidstr, &guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, &error); g_assert_no_error(error); g_assert_true(ret); - g_byte_array_append(buf, guid, sizeof(guid)); /* CapsuleGuid */ - g_byte_array_append(buf, timestamp, sizeof(timestamp)); /* CapsuleProcessed */ - fu_byte_array_append_uint32(buf, - FU_UEFI_CAPSULE_DEVICE_STATUS_ERROR_PWR_EVT_BATT, - G_LITTLE_ENDIAN); /* Status */ - return g_bytes_new(buf->data, buf->len); + fu_struct_efi_capsule_result_variable_header_set_guid(st, &guid); + fu_struct_efi_capsule_result_variable_header_set_status(st, FU_EFI_STATUS_ACCESS_DENIED); + return fu_struct_efi_capsule_result_variable_header_to_bytes(st); } static void @@ -240,89 +252,683 @@ /* debug */ str = fu_device_to_string(dev); g_debug("%s", str); - g_assert_cmpint(fu_device_get_update_state(dev), ==, FWUPD_UPDATE_STATE_FAILED_TRANSIENT); + g_assert_cmpint(fu_device_get_update_state(dev), ==, FWUPD_UPDATE_STATE_FAILED); g_assert_cmpstr(fu_device_get_update_error(dev), ==, - "failed to update to 0: error-pwr-evt-batt"); + "failed to update to 0: error-auth-error"); g_assert_cmpint(fu_uefi_capsule_device_get_status(FU_UEFI_CAPSULE_DEVICE(dev)), ==, - FU_UEFI_CAPSULE_DEVICE_STATUS_ERROR_PWR_EVT_BATT); + FU_UEFI_CAPSULE_DEVICE_STATUS_ERROR_AUTH_ERROR); +} + +#ifndef EFI_OS_DIR +#define EFI_OS_DIR "systemd" +#endif + +static FuVolume * +fu_uefi_plugin_fake_esp_new(void) +{ + g_autofree gchar *tmpdir_efi = NULL; + g_autofree gchar *tmpdir = NULL; + g_autoptr(FuVolume) esp = NULL; + g_autoptr(GError) error = NULL; + + /* enough to fit the firmware */ + tmpdir = g_dir_make_tmp("fwupd-esp-XXXXXX", &error); + g_assert_no_error(error); + g_assert_nonnull(tmpdir); + esp = fu_volume_new_from_mount_path(tmpdir); + fu_volume_set_filesystem_free(esp, 10 * 1024 * 1024); + fu_volume_set_partition_kind(esp, FU_VOLUME_KIND_ESP); + fu_volume_set_partition_uuid(esp, "00000000-0000-0000-0000-000000000000"); + + /* make fu_uefi_get_esp_path_for_os() distro-neutral */ + tmpdir_efi = g_build_filename(tmpdir, "EFI", EFI_OS_DIR, NULL); + g_assert_cmpint(g_mkdir_with_parents(tmpdir_efi, 0700), ==, 0); + + /* success */ + return g_steal_pointer(&esp); } static void -fu_uefi_plugin_func(void) +fu_uefi_plugin_no_coalesce_func(void) { - FuUefiCapsuleDevice *dev; + FuUefiCapsuleDevice *dev1; + FuUefiCapsuleDevice *dev2; + GPtrArray *devices; gboolean ret; g_autoptr(FuContext) ctx = fu_context_new(); - g_autoptr(FuBackend) backend = fu_uefi_capsule_backend_new(ctx); + g_autoptr(FuPlugin) plugin = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuVolume) esp = fu_uefi_plugin_fake_esp_new(); g_autoptr(GError) error = NULL; - g_autoptr(GPtrArray) devices = NULL; #ifndef __linux__ g_test_skip("ESRT data is mocked only on Linux"); return; #endif + /* override ESP */ + fu_context_add_esp_volume(ctx, esp); + + /* set up at least one HWID */ + fu_config_set_default(fu_context_get_config(ctx), "fwupd", "Manufacturer", "fwupd"); + /* do not save silo */ ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); g_assert_no_error(error); g_assert_true(ret); - /* add each device */ - ret = fu_backend_coldplug(backend, progress, &error); + /* load dummy hwids */ + ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_LOAD_CONFIG, &error); g_assert_no_error(error); g_assert_true(ret); - devices = fu_backend_get_devices(backend); - g_assert_cmpint(devices->len, ==, 3); - /* system firmware */ - dev = g_ptr_array_index(devices, 0); - ret = fu_device_probe(FU_DEVICE(dev), &error); + /* create plugin, and ->startup then ->coldplug */ + plugin = + g_object_new(FU_TYPE_UEFI_CAPSULE_PLUGIN, "context", ctx, "name", "uefi_capsule", NULL); + ret = fu_plugin_runner_startup(plugin, progress, &error); g_assert_no_error(error); g_assert_true(ret); - g_assert_cmpint(fu_uefi_capsule_device_get_kind(dev), + ret = fu_plugin_runner_coldplug(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check each device */ + devices = fu_plugin_get_devices(plugin); + g_assert_cmpint(devices->len, ==, 2); + + /* system firmware */ + dev1 = g_ptr_array_index(devices, 0); + g_assert_cmpint(fu_uefi_capsule_device_get_kind(dev1), ==, FU_UEFI_CAPSULE_DEVICE_KIND_SYSTEM_FIRMWARE); - g_assert_cmpstr(fu_uefi_capsule_device_get_guid(dev), + g_assert_cmpstr(fu_uefi_capsule_device_get_guid(dev1), ==, "ddc0ee61-e7f0-4e7d-acc5-c070a398838e"); - g_assert_cmpint(fu_uefi_capsule_device_get_hardware_instance(dev), ==, 0x0); - g_assert_cmpint(fu_uefi_capsule_device_get_version(dev), ==, 65586); - g_assert_cmpint(fu_uefi_capsule_device_get_version_lowest(dev), ==, 65582); - g_assert_cmpint(fu_uefi_capsule_device_get_version_error(dev), ==, 18472960); - g_assert_cmpint(fu_uefi_capsule_device_get_capsule_flags(dev), ==, 0xfe); - g_assert_cmpint(fu_uefi_capsule_device_get_status(dev), + g_assert_cmpint(fu_uefi_capsule_device_get_hardware_instance(dev1), ==, 0x0); + g_assert_cmpint(fu_uefi_capsule_device_get_version(dev1), ==, 65586); + g_assert_cmpint(fu_uefi_capsule_device_get_version_lowest(dev1), ==, 65582); + g_assert_cmpint(fu_uefi_capsule_device_get_version_error(dev1), ==, 18472960); + g_assert_cmpint(fu_uefi_capsule_device_get_capsule_flags(dev1) & 0xFF, ==, 0xFE); + g_assert_cmpint(fu_uefi_capsule_device_get_status(dev1), ==, FU_UEFI_CAPSULE_DEVICE_STATUS_ERROR_UNSUCCESSFUL); + g_assert_true(fu_device_has_flag(FU_DEVICE(dev1), FWUPD_DEVICE_FLAG_UPDATABLE)); /* system firmware */ - dev = g_ptr_array_index(devices, 1); - ret = fu_device_probe(FU_DEVICE(dev), &error); - g_assert_no_error(error); - g_assert_true(ret); - g_assert_cmpint(fu_uefi_capsule_device_get_kind(dev), + dev2 = g_ptr_array_index(devices, 1); + g_assert_cmpint(fu_uefi_capsule_device_get_kind(dev2), ==, FU_UEFI_CAPSULE_DEVICE_KIND_DEVICE_FIRMWARE); - g_assert_cmpstr(fu_uefi_capsule_device_get_guid(dev), + g_assert_cmpstr(fu_uefi_capsule_device_get_guid(dev2), ==, "671d19d0-d43c-4852-98d9-1ce16f9967e4"); - g_assert_cmpint(fu_uefi_capsule_device_get_version(dev), ==, 3090287969); - g_assert_cmpint(fu_uefi_capsule_device_get_version_lowest(dev), ==, 1); - g_assert_cmpint(fu_uefi_capsule_device_get_version_error(dev), ==, 0); - g_assert_cmpint(fu_uefi_capsule_device_get_capsule_flags(dev), ==, 32784); - g_assert_cmpint(fu_uefi_capsule_device_get_status(dev), + g_assert_cmpint(fu_uefi_capsule_device_get_version(dev2), ==, 3090287969); + g_assert_cmpint(fu_uefi_capsule_device_get_version_lowest(dev2), ==, 1); + g_assert_cmpint(fu_uefi_capsule_device_get_version_error(dev2), ==, 0); + g_assert_cmpint(fu_uefi_capsule_device_get_capsule_flags(dev2) & 0xFF, ==, 0x10); + g_assert_cmpint(fu_uefi_capsule_device_get_status(dev2), ==, FU_UEFI_CAPSULE_DEVICE_STATUS_SUCCESS); + g_assert_true(fu_device_has_flag(FU_DEVICE(dev2), FWUPD_DEVICE_FLAG_UPDATABLE)); + + /* ensure the other device is not updatable when the first is updated */ + fu_device_set_update_state(FU_DEVICE(dev2), FWUPD_UPDATE_STATE_NEEDS_REBOOT); + g_assert_false(fu_device_has_flag(FU_DEVICE(dev1), FWUPD_DEVICE_FLAG_UPDATABLE)); +} + +static void +fu_uefi_plugin_setup_indications(FuContext *ctx, guint64 indications) +{ + FuEfivars *efivars = fu_context_get_efivars(ctx); + gboolean ret; + guint8 buf[8] = {0}; + g_autoptr(GError) error = NULL; + + fu_memwrite_uint64(buf, indications, G_LITTLE_ENDIAN); + ret = fu_efivars_set_data(efivars, + FU_EFIVARS_GUID_EFI_GLOBAL, + "OsIndicationsSupported", + buf, + sizeof(buf), + 0x0, + &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_uefi_plugin_no_cod_func(void) +{ + FuBackend *backend; + GType device_gtype; + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuVolume) esp = fu_uefi_plugin_fake_esp_new(); + g_autoptr(GError) error = NULL; + +#ifndef __linux__ + g_test_skip("ESRT data is mocked only on Linux"); + return; +#endif + + /* the firmware supports CoD */ + fu_uefi_plugin_setup_indications(ctx, EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED); + + /* override ESP */ + fu_context_add_esp_volume(ctx, esp); + + /* set up at least one HWID */ + fu_config_set_default(fu_context_get_config(ctx), "fwupd", "Manufacturer", "fwupd"); + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* load dummy hwids */ + ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_LOAD_CONFIG, &error); + g_assert_no_error(error); + g_assert_true(ret); - /* invalid */ - dev = g_ptr_array_index(devices, 2); - ret = fu_device_probe(FU_DEVICE(dev), &error); + /* create plugin, and ->startup then ->coldplug */ + plugin = + g_object_new(FU_TYPE_UEFI_CAPSULE_PLUGIN, "context", ctx, "name", "uefi_capsule", NULL); + ret = fu_plugin_runner_startup(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_plugin_runner_coldplug(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* backend devices should be NVRAM, not CoD */ + backend = fu_uefi_capsule_plugin_get_backend(FU_UEFI_CAPSULE_PLUGIN(plugin)); + g_assert_nonnull(backend); + g_assert_true(FU_IS_UEFI_CAPSULE_BACKEND(backend)); + device_gtype = fu_uefi_capsule_backend_get_device_gtype(FU_UEFI_CAPSULE_BACKEND(backend)); + g_assert_cmpint(device_gtype, ==, FU_TYPE_UEFI_NVRAM_DEVICE); +} + +static void +fu_uefi_plugin_no_flashes_func(void) +{ + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuFirmware) firmware = fu_firmware_new(); + g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuVolume) esp = fu_uefi_plugin_fake_esp_new(); + g_autoptr(GError) error = NULL; + + /* override ESP */ + fu_context_add_esp_volume(ctx, esp); + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* load dummy hwids */ + ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_LOAD_CONFIG, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* create plugin, and ->startup then ->coldplug */ + plugin = + g_object_new(FU_TYPE_UEFI_CAPSULE_PLUGIN, "context", ctx, "name", "uefi_capsule", NULL); + ret = fu_plugin_runner_startup(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_plugin_runner_coldplug(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* test with almost no flashes left */ + device = g_object_new(FU_TYPE_UEFI_NVRAM_DEVICE, + "context", + ctx, + "fw-class", + "cc4cbfa9-bf9d-540b-b92b-172ce31013c1", + NULL); + fu_device_add_private_flag(device, FU_UEFI_CAPSULE_DEVICE_FLAG_NO_UX_CAPSULE); + fu_device_set_flashes_left(device, 2); + ret = fu_plugin_runner_write_firmware(plugin, + device, + firmware, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_false(ret); } +static gboolean +fu_uefi_plugin_esp_file_exists(FuVolume *esp, const gchar *filename) +{ + g_autofree gchar *mount_point = fu_volume_get_mount_point(esp); + g_autofree gchar *fn = g_build_filename(mount_point, filename, NULL); + return g_file_test(fn, G_FILE_TEST_EXISTS); +} + +static void +fu_uefi_plugin_esp_rmtree(FuVolume *esp) +{ + gboolean ret; + g_autofree gchar *mount_point = fu_volume_get_mount_point(esp); + g_autoptr(GError) error = NULL; + ret = fu_path_rmtree(mount_point, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_uefi_plugin_nvram_func(void) +{ + gboolean ret; + guint16 bootnext = 0; + guint16 idx; + g_autoptr(GBytes) blob = g_bytes_new_static("GUIDGUIDGUIDGUID", 16); + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuFirmware) firmware = fu_firmware_new_from_bytes(blob); + g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuVolume) esp = fu_uefi_plugin_fake_esp_new(); + g_autoptr(GArray) bootorder = NULL; + g_autoptr(GError) error = NULL; + +#ifndef __x86_64__ + g_test_skip("NVRAM binary is mocked only for x86_64"); + return; +#endif + + /* override ESP */ + fu_context_add_esp_volume(ctx, esp); + + /* set up system so that secure boot is on */ + ret = fu_efivars_set_secure_boot(fu_context_get_efivars(ctx), TRUE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_efivars_create_boot_entry_for_volume(fu_context_get_efivars(ctx), + 0x0000, + esp, + "Fedora", + "grubx64.efi", + &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_efivars_set_boot_current(fu_context_get_efivars(ctx), 0x0000, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_efivars_build_boot_order(fu_context_get_efivars(ctx), &error, 0x0000, G_MAXUINT16); + g_assert_no_error(error); + g_assert_true(ret); + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* load dummy hwids */ + ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_LOAD_CONFIG, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* create plugin, and ->startup then ->coldplug */ + plugin = + g_object_new(FU_TYPE_UEFI_CAPSULE_PLUGIN, "context", ctx, "name", "uefi_capsule", NULL); + fu_plugin_set_config_default(plugin, "ScreenWidth", "800"); + fu_plugin_set_config_default(plugin, "ScreenHeight", "600"); + ret = fu_plugin_runner_startup(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_plugin_runner_coldplug(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* test with a dummy device that just writes the splash */ + device = g_object_new(FU_TYPE_UEFI_NVRAM_DEVICE, + "context", + ctx, + "fw-class", + "cc4cbfa9-bf9d-540b-b92b-172ce31013c1", + NULL); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_private_flag(device, FU_UEFI_CAPSULE_DEVICE_FLAG_USE_FWUPD_EFI); + fu_device_add_private_flag(device, FU_UEFI_CAPSULE_DEVICE_FLAG_USE_LEGACY_BOOTMGR_DESC); + fu_device_add_private_flag(device, FU_UEFI_CAPSULE_DEVICE_FLAG_MODIFY_BOOTORDER); + fu_uefi_capsule_device_set_esp(FU_UEFI_CAPSULE_DEVICE(device), esp); + ret = fu_device_prepare(device, progress, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_plugin_runner_write_firmware(plugin, + device, + firmware, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + +#ifdef FWUPD_UEFI_CAPSULE_SPLASH_ENABLED + /* check UX splash was created */ + g_assert_true(fu_uefi_plugin_esp_file_exists( + esp, + "EFI/" EFI_OS_DIR "/fw/fwupd-" FU_EFIVARS_GUID_UX_CAPSULE ".cap")); + g_assert_true(fu_efivars_exists(fu_context_get_efivars(ctx), + FU_EFIVARS_GUID_FWUPDATE, + "fwupd-ux-capsule")); +#endif + + /* check FW was created */ + g_assert_true(fu_uefi_plugin_esp_file_exists( + esp, + "EFI/" EFI_OS_DIR "/fw/fwupd-cc4cbfa9-bf9d-540b-b92b-172ce31013c1.cap")); +#ifdef FWUPD_UEFI_CAPSULE_SPLASH_ENABLED + g_assert_true(fu_efivars_exists(fu_context_get_efivars(ctx), + FU_EFIVARS_GUID_FWUPDATE, + "fwupd-ux-capsule")); +#endif + g_assert_true(fu_efivars_exists(fu_context_get_efivars(ctx), + FU_EFIVARS_GUID_FWUPDATE, + "fwupd-cc4cbfa9-bf9d-540b-b92b-172ce31013c1-0")); + + /* we skipped this, so emulate something */ + g_assert_no_error(error); + g_assert_true(ret); + + /* verify BootOrder */ + bootorder = fu_efivars_get_boot_order(fu_context_get_efivars(ctx), &error); + g_assert_no_error(error); + g_assert_nonnull(bootorder); + g_assert_cmpint(bootorder->len, ==, 2); + idx = g_array_index(bootorder, guint16, 0); + g_assert_cmpint(idx, ==, 0x0000); + idx = g_array_index(bootorder, guint16, 1); + g_assert_cmpint(idx, ==, 0x0001); + + /* verify BootNext */ + ret = fu_efivars_get_boot_next(fu_context_get_efivars(ctx), &bootnext, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(bootnext, ==, 0x0001); + + /* clear results */ + ret = fu_plugin_runner_clear_results(plugin, device, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* cleanup */ + ret = fu_plugin_runner_reboot_cleanup(plugin, device, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check both files and variables no longer exist */ + g_assert_false(fu_uefi_plugin_esp_file_exists( + esp, + "EFI/" EFI_OS_DIR "/fw/fwupd-" FU_EFIVARS_GUID_UX_CAPSULE ".cap")); + g_assert_false(fu_efivars_exists(fu_context_get_efivars(ctx), + FU_EFIVARS_GUID_FWUPDATE, + "fwupd-ux-capsule")); + g_assert_false(fu_uefi_plugin_esp_file_exists( + esp, + "EFI/" EFI_OS_DIR "/fw/fwupd-cc4cbfa9-bf9d-540b-b92b-172ce31013c1.cap")); + g_assert_false(fu_efivars_exists(fu_context_get_efivars(ctx), + FU_EFIVARS_GUID_FWUPDATE, + "fwupd-cc4cbfa9-bf9d-540b-b92b-172ce31013c1-0")); + + /* check BootNext was removed */ + g_assert_false( + fu_efivars_exists(fu_context_get_efivars(ctx), FU_EFIVARS_GUID_EFI_GLOBAL, "BootNext")); + + /* get results */ + ret = fu_device_get_results(device, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* cleanup */ + fu_uefi_plugin_esp_rmtree(esp); +} + +static void +fu_uefi_plugin_cod_func(void) +{ + gboolean ret; + g_autoptr(GBytes) blob = g_bytes_new_static("GUIDGUIDGUIDGUID", 16); + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuFirmware) firmware = fu_firmware_new_from_bytes(blob); + g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuVolume) esp = fu_uefi_plugin_fake_esp_new(); + g_autoptr(GByteArray) buf_last = NULL; + g_autoptr(GError) error = NULL; + + /* override ESP */ + fu_context_add_esp_volume(ctx, esp); + + /* set up system */ + buf_last = fu_utf8_to_utf16_byte_array("Capsule0001", + G_LITTLE_ENDIAN, + FU_UTF_CONVERT_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_nonnull(buf_last); + ret = fu_efivars_set_data(fu_context_get_efivars(ctx), + FU_EFIVARS_GUID_EFI_CAPSULE_REPORT, + "CapsuleLast", + buf_last->data, + buf_last->len, + 0x0, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* load dummy hwids */ + ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_LOAD_CONFIG, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* create plugin, and ->startup then ->coldplug */ + plugin = + g_object_new(FU_TYPE_UEFI_CAPSULE_PLUGIN, "context", ctx, "name", "uefi_capsule", NULL); + ret = fu_plugin_runner_startup(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_plugin_runner_coldplug(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* test with a dummy device that just writes the splash */ + device = g_object_new(FU_TYPE_UEFI_COD_DEVICE, + "context", + ctx, + "fw-class", + "cc4cbfa9-bf9d-540b-b92b-172ce31013c1", + NULL); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_private_flag(device, FU_UEFI_CAPSULE_DEVICE_FLAG_NO_UX_CAPSULE); + fu_uefi_capsule_device_set_esp(FU_UEFI_CAPSULE_DEVICE(device), esp); + + /* write default capsule */ + ret = fu_plugin_runner_write_firmware(plugin, + device, + firmware, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_true(fu_uefi_plugin_esp_file_exists( + esp, + "EFI/UpdateCapsule/fwupd-cc4cbfa9-bf9d-540b-b92b-172ce31013c1.cap")); + + /* try again with a different filename */ + fu_device_add_private_flag(device, FU_UEFI_CAPSULE_DEVICE_FLAG_COD_DELL_RECOVERY); + ret = fu_plugin_runner_write_firmware(plugin, + device, + firmware, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_true(fu_uefi_plugin_esp_file_exists(esp, "EFI/dell/bios/recovery/BIOS_TRS.rcv")); + + /* try again with a different filename */ + fu_device_add_private_flag(device, FU_UEFI_CAPSULE_DEVICE_FLAG_COD_INDEXED_FILENAME); + ret = fu_plugin_runner_write_firmware(plugin, + device, + firmware, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_true( + fu_uefi_plugin_esp_file_exists(esp, "EFI/UpdateCapsule/CapsuleUpdateFile0000.bin")); + + /* get results */ + ret = fu_device_get_results(device, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* cleanup */ + fu_uefi_plugin_esp_rmtree(esp); +} + +static void +fu_uefi_plugin_grub_func(void) +{ + gboolean ret; + g_autoptr(GBytes) blob = g_bytes_new_static("GUIDGUIDGUIDGUID", 16); + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuFirmware) firmware = fu_firmware_new_from_bytes(blob); + g_autoptr(FuPlugin) plugin = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuVolume) esp = fu_uefi_plugin_fake_esp_new(); + g_autoptr(GError) error = NULL; + +#ifndef __x86_64__ + g_test_skip("ESRT is mocked only for x86_64"); + return; +#endif + + /* set up system so that secure boot is on */ + ret = fu_efivars_set_secure_boot(fu_context_get_efivars(ctx), TRUE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* load dummy hwids */ + ret = fu_context_load_hwinfo(ctx, progress, FU_CONTEXT_HWID_FLAG_LOAD_CONFIG, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* override ESP */ + fu_context_add_esp_volume(ctx, esp); + + /* create plugin, and ->startup then ->coldplug */ + plugin = + g_object_new(FU_TYPE_UEFI_CAPSULE_PLUGIN, "context", ctx, "name", "uefi_capsule", NULL); + ret = fu_plugin_runner_startup(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_plugin_runner_coldplug(plugin, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* test with a dummy device */ + device = g_object_new(FU_TYPE_UEFI_GRUB_DEVICE, + "context", + ctx, + "fw-class", + "cc4cbfa9-bf9d-540b-b92b-172ce31013c1", + NULL); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_private_flag(device, FU_UEFI_CAPSULE_DEVICE_FLAG_NO_UX_CAPSULE); + fu_uefi_capsule_device_set_esp(FU_UEFI_CAPSULE_DEVICE(device), esp); + + /* write */ + ret = fu_device_prepare(device, progress, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_plugin_runner_write_firmware(plugin, + device, + firmware, + progress, + FWUPD_INSTALL_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_true(fu_uefi_plugin_esp_file_exists( + esp, + "EFI/" EFI_OS_DIR "/fw/fwupd-cc4cbfa9-bf9d-540b-b92b-172ce31013c1.cap")); + + /* cleanup */ + fu_uefi_plugin_esp_rmtree(esp); +} + +static void +fu_uefi_update_info_xml_func(void) +{ + gboolean ret; + g_autofree gchar *filename = NULL; + g_autofree gchar *csum1 = NULL; + g_autofree gchar *csum2 = NULL; + g_autofree gchar *xml_out = NULL; + g_autofree gchar *xml_src = NULL; + g_autoptr(FuFirmware) firmware1 = FU_FIRMWARE(fu_uefi_update_info_new()); + g_autoptr(FuFirmware) firmware2 = FU_FIRMWARE(fu_uefi_update_info_new()); + g_autoptr(FuFirmware) firmware3 = FU_FIRMWARE(fu_uefi_update_info_new()); + g_autoptr(GBytes) fw = NULL; + g_autoptr(GError) error = NULL; + + /* build and write */ + filename = + g_test_build_filename(G_TEST_DIST, "tests", "uefi-update-info.builder.xml", NULL); + ret = g_file_get_contents(filename, &xml_src, NULL, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_firmware_build_from_xml(firmware1, xml_src, &error); + g_assert_no_error(error); + g_assert_true(ret); + fw = fu_firmware_write(firmware1, &error); + g_assert_no_error(error); + g_assert_nonnull(fw); + csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); + g_assert_no_error(error); + g_assert_cmpstr(csum1, ==, "18e8c43a912d3918498723340ae80a57d8b0657c"); + + /* ensure we can parse */ + ret = fu_firmware_parse_bytes(firmware3, fw, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* ensure we can round-trip */ + xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + g_assert_no_error(error); + g_assert_true(ret); + csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); + g_assert_cmpstr(csum1, ==, csum2); +} + static void fu_uefi_update_info_func(void) { @@ -393,16 +999,25 @@ main(int argc, char **argv) { g_autofree gchar *testdatadir = NULL; + g_autofree gchar *testdatadir_mut = NULL; (void)g_setenv("G_TEST_SRCDIR", SRCDIR, FALSE); g_test_init(&argc, &argv, NULL); testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); + testdatadir_mut = g_test_build_filename(G_TEST_BUILT, "tests", NULL); (void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE); (void)g_setenv("FWUPD_EFIVARS", "dummy", TRUE); (void)g_setenv("FWUPD_SYSFSDRIVERDIR", testdatadir, TRUE); (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); + (void)g_setenv("FWUPD_DATADIR_QUIRKS", testdatadir, TRUE); + (void)g_setenv("FWUPD_HOSTFS_BOOT", testdatadir, TRUE); + (void)g_setenv("FWUPD_EFIAPPDIR", testdatadir_mut, TRUE); + (void)g_setenv("FWUPD_ACPITABLESDIR", testdatadir_mut, TRUE); + (void)g_setenv("FWUPD_DATADIR", g_test_get_dir(G_TEST_BUILT), TRUE); (void)g_setenv("FWUPD_UEFI_TEST", "1", TRUE); + (void)g_setenv("LANGUAGE", "en", TRUE); + (void)g_setenv("PATH", testdatadir, TRUE); /* only critical and error are fatal */ g_log_set_fatal_mask(NULL, G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL); @@ -417,6 +1032,12 @@ g_test_add_func("/uefi/bitmap", fu_uefi_bitmap_func); g_test_add_func("/uefi/cod-device", fu_uefi_cod_device_func); g_test_add_func("/uefi/update-info", fu_uefi_update_info_func); - g_test_add_func("/uefi/plugin", fu_uefi_plugin_func); + g_test_add_func("/uefi/update-info{xml}", fu_uefi_update_info_xml_func); + g_test_add_func("/uefi/plugin{no-coalesce}", fu_uefi_plugin_no_coalesce_func); + g_test_add_func("/uefi/plugin{no-flashes-left}", fu_uefi_plugin_no_flashes_func); + g_test_add_func("/uefi/plugin{nvram}", fu_uefi_plugin_nvram_func); + g_test_add_func("/uefi/plugin{cod}", fu_uefi_plugin_cod_func); + g_test_add_func("/uefi/plugin{no-cod}", fu_uefi_plugin_no_cod_func); + g_test_add_func("/uefi/plugin{grub}", fu_uefi_plugin_grub_func); return g_test_run(); } diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-bgrt.c fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-bgrt.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-bgrt.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-bgrt.c 2026-02-26 11:36:18.000000000 +0000 @@ -27,14 +27,12 @@ guint64 version; g_autofree gchar *bgrtdir = NULL; g_autofree gchar *imagefn = NULL; - g_autofree gchar *sysfsfwdir = NULL; g_autoptr(FuBitmapImage) bmp_image = fu_bitmap_image_new(); g_autoptr(GFile) file = NULL; g_return_val_if_fail(FU_IS_UEFI_BGRT(self), FALSE); - sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - bgrtdir = g_build_filename(sysfsfwdir, "acpi", "bgrt", NULL); + bgrtdir = fu_path_build(FU_PATH_KIND_SYSFSDIR_FW, "acpi", "bgrt", NULL); if (!g_file_test(bgrtdir, G_FILE_TEST_EXISTS)) { g_set_error_literal(error, FWUPD_ERROR, @@ -66,8 +64,11 @@ self->yoffset = fu_uefi_read_file_as_uint64(bgrtdir, "yoffset"); imagefn = g_build_filename(bgrtdir, "image", NULL); file = g_file_new_build_filename(bgrtdir, "image", NULL); - if (!fu_firmware_parse_file(FU_FIRMWARE(bmp_image), file, FWUPD_INSTALL_FLAG_NONE, error)) { - g_prefix_error(error, "BGRT image invalid: "); + if (!fu_firmware_parse_file(FU_FIRMWARE(bmp_image), + file, + FU_FIRMWARE_PARSE_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "BGRT image invalid: "); return FALSE; } self->width = fu_bitmap_image_get_width(bmp_image); diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-bootmgr.c fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-bootmgr.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-bootmgr.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-bootmgr.c 2026-02-26 11:36:18.000000000 +0000 @@ -142,7 +142,7 @@ if (!fu_firmware_parse_bytes(FU_FIRMWARE(loadopt_tmp), loadopt_blob_tmp, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error_local)) { g_debug("%s -> load option was invalid: %s", name, error_local->message); continue; @@ -165,11 +165,12 @@ if (!fu_bytes_compare(loadopt_blob, loadopt_blob_old, NULL)) { g_debug("%s: updating existing boot entry", name); if (!fu_efivars_set_boot_data(efivars, boot_next, loadopt_blob, error)) { - g_prefix_error(error, "could not set boot variable active: "); + g_prefix_error_literal(error, + "could not set boot variable active: "); return FALSE; } } else { - g_debug("%s: re-using existing boot entry", name); + g_debug("%s: reusing existing boot entry", name); } /* create a new one */ } else { @@ -194,9 +195,9 @@ FU_EFIVARS_GUID_EFI_GLOBAL, boot_next_name, loadopt_blob, - FU_EFIVARS_ATTR_NON_VOLATILE | - FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVARS_ATTR_RUNTIME_ACCESS, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE | + FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS, error)) { g_prefix_error(error, "could not set boot variable %s: ", boot_next_name); return FALSE; @@ -230,7 +231,7 @@ g_autoptr(GPtrArray) shim_entries = NULL; file = g_file_new_for_path(source_shim); - if (!fu_firmware_parse_file(shim, file, FWUPD_INSTALL_FLAG_NONE, error)) { + if (!fu_firmware_parse_file(shim, file, FU_FIRMWARE_PARSE_FLAG_NONE, error)) { g_prefix_error(error, "failed to load %s: ", source_shim); return FALSE; } @@ -255,9 +256,9 @@ if (!fu_firmware_parse_bytes(current_sbatlevel, current_sbatlevel_bytes, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to load SbatLevelRT: "); + g_prefix_error_literal(error, "failed to load SbatLevelRT: "); return FALSE; } @@ -295,11 +296,12 @@ "(missing entry in current UEFI variable)", source_shim, entry_id); - } else { - g_prefix_error(&error_local, - "while looking for entry in current sbatlevel: "); - g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; } + g_propagate_prefixed_error( + error, + g_steal_pointer(&error_local), + "while looking for entry in current sbatlevel: "); return FALSE; } @@ -354,10 +356,7 @@ g_autofree gchar *esp_path = fu_volume_get_mount_point(esp); g_autoptr(FuEfiDevicePathList) dp_buf = NULL; g_autoptr(FuEfiLoadOption) loadopt = fu_efi_load_option_new(); - - /* skip for self tests */ - if (g_getenv("FWUPD_UEFI_TEST") != NULL) - return TRUE; + g_autoptr(GError) error_local = NULL; /* if secure boot was turned on this might need to be installed separately */ source_app = fu_uefi_get_built_app_path(efivars, "fwupd", error); @@ -365,8 +364,8 @@ return FALSE; /* test if we should use shim */ - if (!fu_efivars_get_secure_boot(efivars, &secureboot_enabled, error)) - return FALSE; + if (!fu_efivars_get_secure_boot(efivars, &secureboot_enabled, &error_local)) + g_debug("ignoring: %s", error_local->message); if (secureboot_enabled) { shim_app = fu_uefi_get_esp_app_path(esp_path, "shim", error); if (shim_app == NULL) @@ -437,7 +436,8 @@ dp_buf = fu_uefi_capsule_device_build_dp_buf(esp, filepath, error); if (dp_buf == NULL) return FALSE; - fu_firmware_add_image(FU_FIRMWARE(loadopt), FU_FIRMWARE(dp_buf)); + if (!fu_firmware_add_image(FU_FIRMWARE(loadopt), FU_FIRMWARE(dp_buf), error)) + return FALSE; fu_firmware_set_id(FU_FIRMWARE(loadopt), description); /* save as BootNext */ diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-bootmgr.h fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-bootmgr.h --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-bootmgr.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-bootmgr.h 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,7 @@ FU_UEFI_BOOTMGR_FLAG_USE_SHIM_UNIQUE = 1 << 1, FU_UEFI_BOOTMGR_FLAG_MODIFY_BOOTORDER = 1 << 2, FU_UEFI_BOOTMGR_FLAG_LAST -} FuUefiBootmgrFlags; +} G_GNUC_FLAG_ENUM FuUefiBootmgrFlags; gboolean fu_uefi_bootmgr_verify_fwupd(FuEfivars *efivars, GError **error); diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-capsule-backend-freebsd.c fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-backend-freebsd.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-capsule-backend-freebsd.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-backend-freebsd.c 2026-02-26 11:36:18.000000000 +0000 @@ -45,7 +45,7 @@ g_autofree gchar *fw_class = NULL; uint32_t status; - uuid_to_string(&entry->fw_class, &fw_class, &status); + uuid_to_string((const uuid_t *)&entry->fw_class, &fw_class, &status); if (status != uuid_s_ok) { g_set_error_literal(error, FWUPD_ERROR, @@ -87,18 +87,21 @@ #endif static gboolean -fu_uefi_capsule_backend_freebsd_setup(FuBackend *backend, FuBackendSetupFlags flags, GError **error) +fu_uefi_capsule_backend_freebsd_setup(FuBackend *backend, + FuBackendSetupFlags flags, + FuProgress *progress, + GError **error) { g_autofree gchar *efi_ver = fu_kenv_get_string("efi-version", error); if (efi_ver == NULL) { - g_prefix_error(error, "System does not support UEFI mode, no efi-version kenv: "); + g_prefix_error_literal(error, "does not support UEFI, no efi-version kenv: "); return FALSE; } if (fu_version_compare(efi_ver, "2.0.0.0", FWUPD_VERSION_FORMAT_QUAD) < 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "System does not support UEFI mode, got efi-version of %s", + "does not support UEFI, got efi-version of %s", efi_ver); return FALSE; } @@ -106,7 +109,7 @@ } static gboolean -fu_uefi_capsule_backend_freebsd_coldplug(FuBackend *backend, GError **error) +fu_uefi_capsule_backend_freebsd_coldplug(FuBackend *backend, FuProgress *progress, GError **error) { #ifdef HAVE_FREEBSD_ESRT FuUefiCapsuleBackend *self = FU_UEFI_CAPSULE_BACKEND(backend); @@ -126,7 +129,7 @@ return FALSE; } - if (ioctl(efi_fd, EFIIOC_GET_TABLE, &table) == -1) { + if (ioctl(efi_fd, EFIIOC_GET_TABLE, &table) == -1) /* nocheck:blocked */ { g_close(efi_fd, NULL); g_set_error_literal(error, FWUPD_ERROR, @@ -177,11 +180,6 @@ #endif } -void -fu_uefi_capsule_backend_freebsd_set_device_gtype(FuBackend *backend, GType device_gtype) -{ -} - static void fu_uefi_capsule_backend_freebsd_init(FuUefiCapsuleBackendFreebsd *self) { diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-capsule-backend-linux.c fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-backend-linux.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-capsule-backend-linux.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-backend-linux.c 2026-02-26 11:36:18.000000000 +0000 @@ -88,8 +88,8 @@ fu_uefi_capsule_backend_linux_check_efivarfs(FuUefiCapsuleBackendLinux *self, GError **error) { gboolean is_readonly; - g_autofree gchar *sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - g_autofree gchar *sysfsefivardir = g_build_filename(sysfsfwdir, "efi", "efivars", NULL); + g_autofree gchar *sysfsefivardir = + fu_path_build(FU_PATH_KIND_SYSFSDIR_FW, "efi", "efivars", NULL); g_autoptr(GUnixMountEntry) mount = NULL; /* in the self tests */ @@ -139,7 +139,6 @@ const gchar *fn; g_autofree gchar *esrt_entries = NULL; g_autofree gchar *esrt_path = NULL; - g_autofree gchar *sysfsfwdir = NULL; g_autoptr(GDir) dir = NULL; /* make sure that efivarfs is suitable */ @@ -147,8 +146,7 @@ return FALSE; /* get the directory of ESRT entries */ - sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - esrt_path = g_build_filename(sysfsfwdir, "efi", "esrt", NULL); + esrt_path = fu_path_build(FU_PATH_KIND_SYSFSDIR_FW, "efi", "esrt", NULL); esrt_entries = g_build_filename(esrt_path, "entries", NULL); dir = g_dir_open(esrt_entries, 0, error); if (dir == NULL) @@ -167,78 +165,34 @@ } static gboolean -fu_uefi_capsule_backend_linux_check_smbios_enabled(FuContext *ctx, GError **error) -{ - GBytes *bios_blob; - const guint8 *data; - gsize sz; - g_autoptr(GPtrArray) bios_tables = NULL; - - bios_tables = fu_context_get_smbios_data(ctx, 0, FU_SMBIOS_STRUCTURE_LENGTH_ANY, NULL); - if (bios_tables == NULL) { - const gchar *tmp = g_getenv("FWUPD_DELL_FAKE_SMBIOS"); - if (tmp != NULL) - return TRUE; - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "SMBIOS not supported"); - return FALSE; - } - bios_blob = g_ptr_array_index(bios_tables, 0); - data = g_bytes_get_data(bios_blob, &sz); - if (sz < 0x14) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "offset bigger than size %" G_GSIZE_FORMAT, - sz); - return FALSE; - } - if (data[1] < 0x14) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "SMBIOS 2.3 not supported"); - return FALSE; - } - if (!(data[0x13] & (1 << 3))) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "System does not support UEFI mode"); - return FALSE; - } - return TRUE; -} - -static gboolean fu_uefi_capsule_backend_linux_setup(FuBackend *backend, FuBackendSetupFlags flags, FuProgress *progress, GError **error) { - g_autoptr(GError) error_local = NULL; + FuContext *ctx = fu_backend_get_context(backend); /* using a pre-cooked SMBIOS */ if (g_getenv("FWUPD_SYSFSFWDIR") != NULL) return TRUE; /* check SMBIOS for 'UEFI Specification is supported' */ - if (!fu_uefi_capsule_backend_linux_check_smbios_enabled(fu_backend_get_context(backend), - &error_local)) { - g_autofree gchar *fw = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - g_autofree gchar *fn = g_build_filename(fw, "efi", NULL); + if (!fu_context_has_flag(ctx, FU_CONTEXT_FLAG_SMBIOS_UEFI_ENABLED)) { + g_autofree gchar *fn = fu_path_build(FU_PATH_KIND_SYSFSDIR_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); + "UEFI specification is unsupported, but %s exists!", + fn); return TRUE; } - g_propagate_error(error, g_steal_pointer(&error_local)); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "system does not support UEFI mode"); return FALSE; } + + /* success */ return TRUE; } diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-capsule-device.c fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-device.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-capsule-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,7 @@ typedef struct { FuVolume *esp; - FuDeviceLocker *esp_locker; + FuVolumeLocker *esp_locker; gchar *fw_class; FuUefiCapsuleDeviceKind kind; guint32 capsule_flags; @@ -240,6 +240,23 @@ return priv->capsule_flags; } +static void +fu_uefi_capsule_device_set_capsule_flags(FuUefiCapsuleDevice *self, guint32 capsule_flags) +{ + FuUefiCapsuleDevicePrivate *priv = GET_PRIVATE(self); + +#ifdef __x86_64__ + if ((capsule_flags & (FU_EFI_CAPSULE_HEADER_FLAG_PERSIST_ACROSS_RESET | + FU_EFI_CAPSULE_HEADER_FLAG_INITIATE_RESET)) == 0) { + capsule_flags |= FU_EFI_CAPSULE_HEADER_FLAG_PERSIST_ACROSS_RESET; + capsule_flags |= FU_EFI_CAPSULE_HEADER_FLAG_INITIATE_RESET; + g_debug("adding PersistAcrossReset and InitiateReset as missing in ESRT"); + } +#endif + + priv->capsule_flags = capsule_flags; +} + const gchar * fu_uefi_capsule_device_get_guid(FuUefiCapsuleDevice *self) { @@ -273,7 +290,11 @@ fw = fu_efivars_get_data_bytes(efivars, FU_EFIVARS_GUID_FWUPDATE, varname, NULL, error); if (fw == NULL) return NULL; - if (!fu_firmware_parse_bytes(FU_FIRMWARE(info), fw, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_bytes(FU_FIRMWARE(info), + fw, + 0x0, + FU_FIRMWARE_PARSE_FLAG_NONE, + error)) return NULL; return g_steal_pointer(&info); } @@ -286,7 +307,7 @@ gsize datasz = 0; g_autofree gchar *varname = fu_uefi_capsule_device_build_varname(self); g_autofree guint8 *data = NULL; - g_autoptr(GByteArray) st_inf = NULL; + g_autoptr(FuStructEfiUpdateInfo) st_inf = NULL; g_return_val_if_fail(FU_IS_UEFI_CAPSULE_DEVICE(self), FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); @@ -302,22 +323,23 @@ return FALSE; st_inf = fu_struct_efi_update_info_parse(data, datasz, 0x0, error); if (st_inf == NULL) { - g_prefix_error(error, "EFI variable is corrupt: "); + g_prefix_error_literal(error, "EFI variable is corrupt: "); return FALSE; } /* just copy the new EfiUpdateInfo and save it back */ fu_struct_efi_update_info_set_status(st_inf, FU_UEFI_UPDATE_INFO_STATUS_UNKNOWN); - memcpy(data, st_inf->data, st_inf->len); /* nocheck:blocked */ + memcpy(data, st_inf->buf->data, st_inf->buf->len); /* nocheck:blocked */ if (!fu_efivars_set_data(efivars, FU_EFIVARS_GUID_FWUPDATE, varname, data, datasz, - FU_EFIVARS_ATTR_NON_VOLATILE | FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVARS_ATTR_RUNTIME_ACCESS, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE | + FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS, error)) { - g_prefix_error(error, "could not set EfiUpdateInfo: "); + g_prefix_error_literal(error, "could not set EfiUpdateInfo: "); return FALSE; } @@ -339,8 +361,10 @@ name_with_root = g_strdup_printf("/%s", capsule_path); if (!fu_efi_file_path_device_path_set_name(dp_file, name_with_root, error)) return NULL; - fu_firmware_add_image(FU_FIRMWARE(dp_buf), FU_FIRMWARE(dp_hd)); - fu_firmware_add_image(FU_FIRMWARE(dp_buf), FU_FIRMWARE(dp_file)); + if (!fu_firmware_add_image(FU_FIRMWARE(dp_buf), FU_FIRMWARE(dp_hd), error)) + return NULL; + if (!fu_firmware_add_image(FU_FIRMWARE(dp_buf), FU_FIRMWARE(dp_file), error)) + return NULL; return g_steal_pointer(&dp_buf); } @@ -353,7 +377,7 @@ gsize bufsz; const guint8 *buf = g_bytes_get_data(fw, &bufsz); g_autofree gchar *guid_new = NULL; - g_autoptr(GByteArray) st_cap = fu_struct_efi_capsule_header_new(); + g_autoptr(FuStructEfiCapsuleHeader) st_cap = fu_struct_efi_capsule_header_new(); g_return_val_if_fail(FU_IS_UEFI_CAPSULE_DEVICE(self), NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); @@ -371,7 +395,7 @@ guid_new = fwupd_guid_to_string((fwupd_guid_t *)buf, FWUPD_GUID_FLAG_MIXED_ENDIAN); /* ESRT header matches payload */ - if (g_strcmp0(fu_uefi_capsule_device_get_guid(self), guid_new) == 0) { + if (g_strcmp0(priv->fw_class, guid_new) == 0) { g_debug("ESRT matches payload GUID"); return g_bytes_ref(fw); } @@ -387,23 +411,23 @@ fu_struct_efi_capsule_header_set_flags(st_cap, priv->capsule_flags); fu_struct_efi_capsule_header_set_header_size(st_cap, hdrsize); fu_struct_efi_capsule_header_set_image_size(st_cap, bufsz + hdrsize); - if (fu_uefi_capsule_device_get_guid(self) == NULL) { + if (priv->fw_class == NULL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no GUID set"); return NULL; } - if (!fwupd_guid_from_string(fu_uefi_capsule_device_get_guid(self), + if (!fwupd_guid_from_string(priv->fw_class, &esrt_guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, error)) { - g_prefix_error(error, "Invalid ESRT GUID: "); + g_prefix_error_literal(error, "Invalid ESRT GUID: "); return NULL; } fu_struct_efi_capsule_header_set_guid(st_cap, &esrt_guid); /* pad to the headersize then add the payload */ - fu_byte_array_set_size(st_cap, hdrsize, 0x00); - g_byte_array_append(st_cap, buf, bufsz); - return g_bytes_new(st_cap->data, st_cap->len); + fu_byte_array_set_size(st_cap->buf, hdrsize, 0x00); + g_byte_array_append(st_cap->buf, buf, bufsz); + return fu_struct_efi_capsule_header_to_bytes(st_cap); } gboolean @@ -419,13 +443,7 @@ fwupd_guid_t guid = {0x0}; g_autoptr(FuEfiDevicePathList) dp_buf = NULL; g_autoptr(GBytes) dp_blob = NULL; - g_autoptr(GByteArray) st_inf = fu_struct_efi_update_info_new(); - - /* set the body as the device path */ - if (g_getenv("FWUPD_UEFI_TEST") != NULL) { - g_debug("not building device path, in tests...."); - return TRUE; - } + g_autoptr(FuStructEfiUpdateInfo) st_inf = fu_struct_efi_update_info_new(); /* convert to EFI device path */ dp_buf = fu_uefi_capsule_device_build_dp_buf(priv->esp, capsule_path, error); @@ -442,14 +460,15 @@ fu_struct_efi_update_info_set_hw_inst(st_inf, priv->fmp_hardware_instance); fu_struct_efi_update_info_set_status(st_inf, FU_UEFI_UPDATE_INFO_STATUS_ATTEMPT_UPDATE); fu_struct_efi_update_info_set_guid(st_inf, &guid); - fu_byte_array_append_bytes(st_inf, dp_blob); + fu_byte_array_append_bytes(st_inf->buf, dp_blob); if (!fu_efivars_set_data(efivars, FU_EFIVARS_GUID_FWUPDATE, varname, - st_inf->data, - st_inf->len, - FU_EFIVARS_ATTR_NON_VOLATILE | FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVARS_ATTR_RUNTIME_ACCESS, + st_inf->buf->data, + st_inf->buf->len, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE | + FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS, error)) { g_prefix_error(error, "could not set DP_BUF with %s: ", capsule_path); return FALSE; @@ -466,9 +485,10 @@ FuEfivars *efivars = fu_context_get_efivars(ctx); gboolean secureboot_enabled = FALSE; g_autofree gchar *source_app = NULL; + g_autoptr(GError) error_local = NULL; - if (!fu_efivars_get_secure_boot(efivars, &secureboot_enabled, error)) - return FALSE; + if (!fu_efivars_get_secure_boot(efivars, &secureboot_enabled, &error_local)) + g_debug("ignoring: %s", error_local->message); /* if fwupd-efi isn't in use, skip checks for the signed binary */ if (!fu_device_has_private_flag(FU_DEVICE(self), FU_UEFI_CAPSULE_DEVICE_FLAG_USE_FWUPD_EFI)) @@ -476,7 +496,7 @@ source_app = fu_uefi_get_built_app_path(efivars, "fwupd", error); if (source_app == NULL && secureboot_enabled) { - g_prefix_error(error, "missing signed bootloader for secure boot: "); + g_prefix_error_literal(error, "missing signed bootloader for secure boot: "); return FALSE; } @@ -492,8 +512,14 @@ FuUefiCapsuleDevice *self = FU_UEFI_CAPSULE_DEVICE(device); FuUefiCapsuleDevicePrivate *priv = GET_PRIVATE(self); + /* sanity check */ + if (priv->esp == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no ESP"); + return FALSE; + } + /* mount if required */ - priv->esp_locker = fu_volume_locker(priv->esp, error); + priv->esp_locker = fu_volume_locker_new(priv->esp, error); if (priv->esp_locker == NULL) return FALSE; @@ -509,8 +535,14 @@ FuUefiCapsuleDevice *self = FU_UEFI_CAPSULE_DEVICE(device); FuUefiCapsuleDevicePrivate *priv = GET_PRIVATE(self); + /* sanity check */ + if (priv->esp_locker == NULL) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "no ESP locker"); + return FALSE; + } + /* unmount ESP if we opened it */ - if (!fu_device_locker_close(priv->esp_locker, error)) + if (!fu_volume_locker_close(priv->esp_locker, error)) return FALSE; g_clear_object(&priv->esp_locker); @@ -555,17 +587,9 @@ fu_device_set_version_lowest(device, version_lowest); } - /* set flags */ - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT); - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_REQUIRE_AC); - fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_MD_SET_VERFMT); - fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_MD_SET_ICON); - fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_MD_SET_VENDOR); - /* add icons */ if (priv->kind == FU_UEFI_CAPSULE_DEVICE_KIND_SYSTEM_FIRMWARE) { - fu_device_add_icon(device, "computer"); + fu_device_add_icon(device, FU_DEVICE_ICON_COMPUTER); fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE); } @@ -580,9 +604,9 @@ } static void -fu_uefi_capsule_device_capture_efi_debugging(FuDevice *device) +fu_uefi_capsule_device_capture_efi_debugging(FuUefiCapsuleDevice *self) { - FuContext *ctx = fu_device_get_context(device); + FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); FuEfivars *efivars = fu_context_get_efivars(ctx); g_autofree gchar *str = NULL; g_autoptr(GBytes) buf = NULL; @@ -624,11 +648,11 @@ "FWUPDATE_VERBOSE", &data, sizeof(data), - FU_EFIVARS_ATTR_NON_VOLATILE | - FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVARS_ATTR_RUNTIME_ACCESS, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE | + FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS, error)) { - g_prefix_error(error, "failed to enable debugging: "); + g_prefix_error_literal(error, "failed to enable debugging: "); return FALSE; } return TRUE; @@ -655,7 +679,7 @@ /* capture EFI binary debug output */ if (fu_device_has_private_flag(device, FU_UEFI_CAPSULE_DEVICE_FLAG_ENABLE_DEBUGGING)) - fu_uefi_capsule_device_capture_efi_debugging(device); + fu_uefi_capsule_device_capture_efi_debugging(self); /* just set the update error */ fu_uefi_capsule_device_set_status(self, priv->last_attempt_status); @@ -673,7 +697,7 @@ fu_uefi_capsule_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuUefiCapsuleDevice *self = FU_UEFI_CAPSULE_DEVICE(device); @@ -729,7 +753,7 @@ priv->kind = g_value_get_uint(value); break; case PROP_CAPSULE_FLAGS: - priv->capsule_flags = g_value_get_uint(value); + fu_uefi_capsule_device_set_capsule_flags(self, g_value_get_uint(value)); break; case PROP_FW_VERSION: priv->fw_version = g_value_get_uint(value); @@ -753,7 +777,7 @@ } static void -fu_uefi_capsule_device_set_progress(FuDevice *self, FuProgress *progress) +fu_uefi_capsule_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -767,6 +791,13 @@ fu_uefi_capsule_device_init(FuUefiCapsuleDevice *self) { fu_device_add_protocol(FU_DEVICE(self), "org.uefi.capsule"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_REQUIRE_AC); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_LAZY_VERFMT); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_VERFMT); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_ICON); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_VENDOR); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_SIGNED); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS); fu_device_register_private_flag(FU_DEVICE(self), FU_UEFI_CAPSULE_DEVICE_FLAG_NO_UX_CAPSULE); diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-capsule-device.h fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-device.h --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-capsule-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -9,7 +9,6 @@ #include -#include "fu-uefi-capsule-device.h" #include "fu-uefi-update-info.h" #define FU_TYPE_UEFI_CAPSULE_DEVICE (fu_uefi_capsule_device_get_type()) diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-capsule-plugin.c fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-plugin.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-capsule-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -36,11 +36,18 @@ G_DEFINE_TYPE(FuUefiCapsulePlugin, fu_uefi_capsule_plugin, FU_TYPE_PLUGIN) +FuBackend * +fu_uefi_capsule_plugin_get_backend(FuUefiCapsulePlugin *self) +{ + return self->backend; +} + static void fu_uefi_capsule_plugin_to_string(FuPlugin *plugin, guint idt, GString *str) { FuUefiCapsulePlugin *self = FU_UEFI_CAPSULE_PLUGIN(plugin); - fu_backend_add_string(self->backend, idt, str); + if (self->backend != NULL) + fu_backend_add_string(self->backend, idt, str); fwupd_codec_string_append_int(str, idt, "ScreenWidth", self->screen_width); fwupd_codec_string_append_int(str, idt, "ScreenHeight", self->screen_height); if (self->bgrt != NULL) { @@ -52,13 +59,14 @@ } static gboolean -fu_uefi_capsule_plugin_fwupd_efi_parse(FuUefiCapsulePlugin *self, GError **error) +fu_uefi_capsule_plugin_fwupd_efi_parse_fallback(FuUefiCapsulePlugin *self, GError **error) { FuContext *ctx = fu_plugin_get_context(FU_PLUGIN(self)); const guint8 needle[] = "f\0w\0u\0p\0d\0-\0e\0f\0i\0 \0v\0e\0r\0s\0i\0o\0n\0 "; gsize offset = 0; g_autofree gchar *fn = g_file_get_path(self->fwupd_efi_file); g_autofree gchar *version = NULL; + g_autofree gchar *version_safe = NULL; g_autoptr(GBytes) buf = NULL; g_autoptr(GBytes) ubuf = NULL; @@ -85,9 +93,68 @@ g_prefix_error(error, "converting %s: ", fn); return FALSE; } + version_safe = fu_version_ensure_semver(version, FWUPD_VERSION_FORMAT_PAIR); + + /* success */ + fu_context_add_runtime_version(ctx, "org.freedesktop.fwupd-efi", version_safe); + return TRUE; +} + +static void +fu_uefi_capsule_plugin_fwupd_efi_add_sbom(FuUefiCapsulePlugin *self, + const gchar *product, + const gchar *version) +{ + FuContext *ctx = fu_plugin_get_context(FU_PLUGIN(self)); + if (g_strcmp0(product, "fwupdx64") == 0) { + fu_context_add_runtime_version(ctx, "org.freedesktop.fwupd-efi", version); + return; + } + if (g_strcmp0(product, "gnu-efi") == 0) { + fu_context_add_runtime_version(ctx, "com.github.ncroxon.gnu-efi", version); + return; + } +} + +static gboolean +fu_uefi_capsule_plugin_fwupd_efi_parse_sbom(FuUefiCapsulePlugin *self, GError **error) +{ + g_autoptr(FuFirmware) fw = fu_pefile_firmware_new(); + g_autoptr(FuFirmware) fw_sbom = NULL; + g_autoptr(GPtrArray) fw_coswids = NULL; + + if (!fu_firmware_parse_file(fw, self->fwupd_efi_file, FU_FIRMWARE_PARSE_FLAG_NONE, error)) + return FALSE; + fw_sbom = fu_firmware_get_image_by_id(fw, ".sbom", error); + if (fw_sbom == NULL) + return FALSE; + fw_coswids = fu_firmware_get_images(fw_sbom); + for (guint i = 0; i < fw_coswids->len; i++) { + FuFirmware *fw_coswid = g_ptr_array_index(fw_coswids, i); + if (!FU_IS_COSWID_FIRMWARE(fw_coswid)) + continue; + fu_uefi_capsule_plugin_fwupd_efi_add_sbom( + self, + fu_coswid_firmware_get_product(FU_COSWID_FIRMWARE(fw_coswid)), + fu_firmware_get_version(fw_coswid)); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_uefi_capsule_plugin_fwupd_efi_parse(FuUefiCapsulePlugin *self, GError **error) +{ + g_autoptr(GError) error_local = NULL; + + /* try parsing the SBOM, then fall back to a well-known token */ + if (!fu_uefi_capsule_plugin_fwupd_efi_parse_sbom(self, &error_local)) { + g_debug("failed to parse SBOM, using fallback: %s", error_local->message); + return fu_uefi_capsule_plugin_fwupd_efi_parse_fallback(self, error); + } /* success */ - fu_context_add_runtime_version(ctx, "org.freedesktop.fwupd-efi", version); return TRUE; } @@ -143,28 +210,6 @@ return fu_uefi_capsule_device_clear_status(device_uefi, error); } -static gchar * -fu_uefi_capsule_plugin_efivars_attrs_to_string(guint32 attrs) -{ - const gchar *data[7] = {0}; - guint idx = 0; - if (attrs & FU_EFIVARS_ATTR_NON_VOLATILE) - data[idx++] = "non-volatile"; - if (attrs & FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS) - data[idx++] = "bootservice-access"; - if (attrs & FU_EFIVARS_ATTR_RUNTIME_ACCESS) - data[idx++] = "runtime-access"; - if (attrs & FU_EFIVARS_ATTR_HARDWARE_ERROR_RECORD) - data[idx++] = "hardware-error-record"; - if (attrs & FU_EFIVARS_ATTR_AUTHENTICATED_WRITE_ACCESS) - data[idx++] = "authenticated-write-access"; - if (attrs & FU_EFIVARS_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS) - data[idx++] = "time-based-authenticated-write-access"; - if (attrs & FU_EFIVARS_ATTR_APPEND_WRITE) - data[idx++] = "append-write"; - return g_strjoinv(",", (gchar **)data); -} - static void fu_uefi_capsule_plugin_add_security_attrs_secureboot(FuPlugin *plugin, FuSecurityAttrs *attrs) { @@ -233,13 +278,15 @@ error_local->message); continue; } - if ((data_attr & FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS) > 0 && - (data_attr & FU_EFIVARS_ATTR_RUNTIME_ACCESS) == 0) { + if ((data_attr & FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS) > 0 && + (data_attr & FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS) == 0) { + g_autofree gchar *flags = + fu_efi_variable_attrs_to_string(data_attr); g_debug("%s-%s attr of size 0x%x had flags %s", name, guids[j], (guint)data_sz, - fu_uefi_capsule_plugin_efivars_attrs_to_string(data_attr)); + flags); fwupd_security_attr_add_metadata(attr, "guid", guids[j]); fwupd_security_attr_add_metadata(attr, "name", name); fwupd_security_attr_add_flag( @@ -264,19 +311,18 @@ fu_uefi_capsule_plugin_add_security_attrs_bootservices(plugin, attrs); } +#ifdef FWUPD_UEFI_CAPSULE_SPLASH_ENABLED static GBytes * fu_uefi_capsule_plugin_get_splash_data(guint width, guint height, GError **error) { const gchar *const *langs = g_get_language_names(); - g_autofree gchar *datadir_pkg = NULL; g_autofree gchar *filename_archive = NULL; g_autofree gchar *langs_str = NULL; g_autoptr(FuArchive) archive = NULL; g_autoptr(GInputStream) stream_archive = NULL; /* load archive */ - datadir_pkg = fu_path_from_kind(FU_PATH_KIND_DATADIR_PKG); - filename_archive = g_build_filename(datadir_pkg, "uefi-capsule-ux.tar.xz", NULL); + filename_archive = fu_path_build(FU_PATH_KIND_DATADIR_PKG, "uefi-capsule-ux.tar.xz", NULL); stream_archive = fu_input_stream_from_path(filename_archive, error); if (stream_archive == NULL) return NULL; @@ -306,7 +352,7 @@ FWUPD_ERROR_NOT_SUPPORTED, "failed to get splash file for %s in %s", langs_str, - datadir_pkg); + filename_archive); return NULL; } @@ -326,18 +372,17 @@ g_autofree gchar *basename = NULL; g_autofree gchar *directory = NULL; g_autoptr(FuBitmapImage) bmp_image = fu_bitmap_image_new(); - g_autoptr(GByteArray) st_cap = fu_struct_efi_capsule_header_new(); - g_autoptr(GByteArray) st_uxh = fu_struct_efi_ux_capsule_header_new(); - g_autoptr(GFile) ofile = NULL; + g_autoptr(FuStructEfiCapsuleHeader) st_cap = fu_struct_efi_capsule_header_new(); + g_autoptr(FuStructEfiUxCapsuleHeader) st_uxh = fu_struct_efi_ux_capsule_header_new(); g_autoptr(GOutputStream) ostream = NULL; /* get screen dimensions */ if (!fu_firmware_parse_bytes(FU_FIRMWARE(bmp_image), blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, error)) { - g_prefix_error(error, "splash invalid: "); + g_prefix_error_literal(error, "splash invalid: "); return FALSE; } width = fu_bitmap_image_get_width(bmp_image); @@ -350,14 +395,12 @@ fn = g_build_filename(esp_path, capsule_path, NULL); if (!fu_path_mkdir_parent(fn, error)) return FALSE; - ofile = g_file_new_for_path(fn); - ostream = - G_OUTPUT_STREAM(g_file_replace(ofile, NULL, FALSE, G_FILE_CREATE_NONE, NULL, error)); + ostream = fu_output_stream_from_path(fn, error); if (ostream == NULL) return FALSE; fu_struct_efi_capsule_header_set_flags(st_cap, - EFI_CAPSULE_HEADER_FLAGS_PERSIST_ACROSS_RESET); + FU_EFI_CAPSULE_HEADER_FLAG_PERSIST_ACROSS_RESET); if (!fwupd_guid_from_string(FU_EFIVARS_GUID_UX_CAPSULE, &guid, FWUPD_GUID_FLAG_MIXED_ENDIAN, @@ -381,20 +424,19 @@ }; /* header, payload and image has to add to zero */ - csum += fu_sum8(st_cap->data, st_cap->len); - csum += fu_sum8(st_uxh->data, st_uxh->len); + csum += fu_sum8(st_cap->buf->data, st_cap->buf->len); + csum += fu_sum8(st_uxh->buf->data, st_uxh->buf->len); csum += fu_sum8_bytes(blob); fu_struct_efi_ux_capsule_header_set_checksum(st_uxh, 0x100 - csum); /* write capsule file */ - size = g_output_stream_write(ostream, st_cap->data, st_cap->len, NULL, error); + size = g_output_stream_write(ostream, st_cap->buf->data, st_cap->buf->len, NULL, error); if (size < 0) return FALSE; - size = g_output_stream_write(ostream, st_uxh->data, st_uxh->len, NULL, error); + size = g_output_stream_write(ostream, st_uxh->buf->data, st_uxh->buf->len, NULL, error); if (size < 0) return FALSE; - size = g_output_stream_write_bytes(ostream, blob, NULL, error); - if (size < 0) + if (!fu_output_stream_write_bytes(ostream, blob, NULL, error)) return FALSE; /* write display capsule location as UPDATE_INFO */ @@ -485,6 +527,7 @@ /* perform the upload */ return fu_uefi_capsule_plugin_write_splash_data(self, device, image_bmp, error); } +#endif // FWUPD_UEFI_CAPSULE_SPLASH_ENABLED static gboolean fu_uefi_capsule_plugin_write_firmware(FuPlugin *plugin, @@ -524,8 +567,10 @@ /* perform the update */ fu_progress_set_status(progress, FWUPD_STATUS_SCHEDULING); +#ifdef FWUPD_UEFI_CAPSULE_SPLASH_ENABLED if (!fu_uefi_capsule_plugin_update_splash(plugin, device, &error_splash)) g_info("failed to upload UEFI UX capsule text: %s", error_splash->message); +#endif return fu_device_write_firmware(device, firmware, progress, flags, error); } @@ -755,15 +800,13 @@ fu_uefi_capsule_plugin_parse_acpi_uefi(FuUefiCapsulePlugin *self, GError **error) { g_autofree gchar *fn = NULL; - g_autofree gchar *path = NULL; g_autoptr(FuFirmware) firmware = fu_acpi_uefi_new(); g_autoptr(GFile) file = NULL; /* if we have a table, parse it and validate it */ - path = fu_path_from_kind(FU_PATH_KIND_ACPI_TABLES); - fn = g_build_filename(path, "UEFI", NULL); + fn = fu_path_build(FU_PATH_KIND_ACPI_TABLES, "UEFI", NULL); file = g_file_new_for_path(fn); - if (!fu_firmware_parse_file(firmware, file, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_file(firmware, file, FU_FIRMWARE_PARSE_FLAG_NONE, error)) return NULL; return g_steal_pointer(&firmware); } @@ -784,7 +827,7 @@ G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "failed to parse ScreenWidth: "); + g_prefix_error_literal(error, "failed to parse ScreenWidth: "); return FALSE; } self->screen_width = (guint32)tmp64; @@ -798,7 +841,7 @@ G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "failed to parse ScreenHeight: "); + g_prefix_error_literal(error, "failed to parse ScreenHeight: "); return FALSE; } self->screen_height = (guint32)tmp64; @@ -837,15 +880,9 @@ FuUefiCapsulePlugin *self = FU_UEFI_CAPSULE_PLUGIN(plugin); FuContext *ctx = fu_plugin_get_context(plugin); FuEfivars *efivars = fu_context_get_efivars(ctx); - guint64 nvram_total; - g_autofree gchar *nvram_total_str = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(GError) error_acpi_uefi = NULL; - /* don't let user's environment influence test suite failures */ - if (g_getenv("FWUPD_UEFI_TEST") != NULL) - return TRUE; - /* for the uploaded report */ if (fu_context_has_hwid_flag(ctx, "use-legacy-bootmgr-desc")) fu_plugin_add_report_metadata(plugin, "BootMgrDesc", "legacy"); @@ -887,21 +924,11 @@ /* are the EFI dirs set up so we can update each device */ if (!fu_efivars_supported(efivars, error)) return FALSE; - nvram_total = fu_efivars_space_used(efivars, error); - if (nvram_total == G_MAXUINT64) - return FALSE; - nvram_total_str = g_strdup_printf("%" G_GUINT64_FORMAT, nvram_total); - fu_plugin_add_report_metadata(plugin, "EfivarsNvramUsed", nvram_total_str); /* we use this both for quirking the CoD implementation sanity and the CoD filename */ self->acpi_uefi = fu_uefi_capsule_plugin_parse_acpi_uefi(self, &error_acpi_uefi); - if (self->acpi_uefi == NULL) { + if (self->acpi_uefi == NULL) g_debug("failed to load ACPI UEFI table: %s", error_acpi_uefi->message); - } else { - /* we do not need to read from this again */ - if (!fu_firmware_set_stream(self->acpi_uefi, NULL, error)) - 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 */ @@ -911,9 +938,11 @@ static gboolean fu_uefi_capsule_plugin_unlock(FuPlugin *plugin, FuDevice *device, GError **error) { - FuUefiCapsuleDevice *device_uefi = FU_UEFI_CAPSULE_DEVICE(device); + /* not us */ + if (!FU_IS_UEFI_CAPSULE_DEVICE(device)) + return TRUE; - if (fu_uefi_capsule_device_get_kind(device_uefi) != + if (fu_uefi_capsule_device_get_kind(FU_UEFI_CAPSULE_DEVICE(device)) != FU_UEFI_CAPSULE_DEVICE_KIND_DELL_TPM_FIRMWARE) { g_set_error(error, FWUPD_ERROR, @@ -934,6 +963,7 @@ { FuDevice *device = FU_DEVICE(object); GPtrArray *devices; + g_autofree gchar *id_display = fu_device_get_id_display(device); g_autofree gchar *msg = NULL; /* device is not in needs-reboot state */ @@ -945,9 +975,7 @@ return; /* mark every other device for this plugin as non-updatable */ - msg = g_strdup_printf("Cannot update as %s [%s] needs reboot", - fu_device_get_name(device), - fu_device_get_id(device)); + msg = g_strdup_printf("Cannot update as %s needs reboot", id_display); devices = fu_plugin_get_devices(plugin); for (guint i = 0; i < devices->len; i++) { FuDevice *device_tmp = g_ptr_array_index(devices, i); @@ -973,7 +1001,7 @@ &bufsz, NULL, error)) { - g_prefix_error(error, "failed to read EFI variable: "); + g_prefix_error_literal(error, "failed to read EFI variable: "); return FALSE; } if (!fu_memread_uint64_safe(buf, bufsz, 0x0, &value, G_LITTLE_ENDIAN, error)) @@ -1019,22 +1047,20 @@ FuUefiCapsulePlugin *self = FU_UEFI_CAPSULE_PLUGIN(plugin); FuContext *ctx = fu_plugin_get_context(plugin); const gchar *str; - gboolean has_fde = FALSE; gboolean bootloader_supports_fwupd = fu_uefi_capsule_plugin_bootloader_supports_fwupd(ctx); - g_autoptr(GError) error_fde = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(GPtrArray) devices = NULL; /* progress */ fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "check-cod"); - fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 8, "check-bitlocker"); - fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 64, "coldplug"); + fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 72, "coldplug"); fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 26, "add-devices"); fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 1, "setup-bgrt"); /* firmware may lie */ - if (!fu_plugin_get_config_value_boolean(plugin, "DisableCapsuleUpdateOnDisk")) { + if (!fu_plugin_get_config_value_boolean(plugin, "DisableCapsuleUpdateOnDisk") && + !fu_context_has_hwid_flag(ctx, "no-capsule-on-disk")) { g_autoptr(GError) error_cod = NULL; if (!fu_uefi_capsule_plugin_check_cod_support(self, &error_cod)) { g_debug("not using CapsuleOnDisk support: %s", error_cod->message); @@ -1046,11 +1072,6 @@ } fu_progress_step_done(progress); - /* warn the user that BitLocker might ask for recovery key after fw update */ - if (fu_context_has_flag(ctx, FU_CONTEXT_FLAG_FDE_BITLOCKER)) - has_fde = TRUE; - fu_progress_step_done(progress); - /* add each device */ if (!fu_backend_coldplug(self->backend, fu_progress_get_child(progress), error)) return FALSE; @@ -1062,8 +1083,8 @@ if (!fu_uefi_capsule_plugin_coldplug_device(plugin, dev, &error_device)) { if (g_error_matches(error_device, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { - g_warning("skipping device that failed coldplug: %s", - error_device->message); + g_debug("skipping device that failed coldplug: %s", + error_device->message); continue; } g_propagate_error(error, g_steal_pointer(&error_device)); @@ -1081,8 +1102,7 @@ /* system firmware "BIOS" can change the PCRx registers */ if (fu_uefi_capsule_device_get_kind(dev) == - FU_UEFI_CAPSULE_DEVICE_KIND_SYSTEM_FIRMWARE && - has_fde) + FU_UEFI_CAPSULE_DEVICE_KIND_SYSTEM_FIRMWARE) fu_device_add_flag(FU_DEVICE(dev), FWUPD_DEVICE_FLAG_AFFECTS_FDE); /* load all configuration variables */ @@ -1105,6 +1125,9 @@ str = fu_uefi_bgrt_get_supported(self->bgrt) ? "Enabled" : "Disabled"; g_info("UX capsule support : %s", str); fu_plugin_add_report_metadata(plugin, "UEFIUXCapsule", str); + str = bootloader_supports_fwupd ? "True" : "False"; + g_info("bootloader supports fwupd: %s", str); + fu_plugin_add_report_metadata(plugin, "BootloaderSupportsFwupd", str); fu_progress_step_done(progress); return TRUE; @@ -1115,14 +1138,14 @@ { g_autofree gchar *esp_path = NULL; g_autofree gchar *pattern = NULL; - g_autoptr(FuDeviceLocker) esp_locker = NULL; + g_autoptr(FuVolumeLocker) esp_locker = NULL; g_autoptr(GPtrArray) files = NULL; if (self->esp == NULL) return TRUE; /* delete any files matching the glob in the ESP */ - esp_locker = fu_volume_locker(self->esp, error); + esp_locker = fu_volume_locker_new(self->esp, error); if (esp_locker == NULL) return FALSE; esp_path = fu_volume_get_mount_point(self->esp); @@ -1176,8 +1199,8 @@ g_debug("EFI LoadOption: %s", loadoptstr); if (g_strcmp0(fu_firmware_get_id(FU_FIRMWARE(loadopt)), "Linux Firmware Updater") == 0 || g_strcmp0(fu_firmware_get_id(FU_FIRMWARE(loadopt)), "Linux-Firmware-Updater") == 0) { - g_warning("BootNext was not deleted automatically, so removing: " - "this normally indicates a BIOS bug"); + g_debug("BootNext was not deleted automatically, so removing: " + "this normally indicates a BIOS bug"); if (!fu_efivars_delete(efivars, FU_EFIVARS_GUID_EFI_GLOBAL, "BootNext", error)) return FALSE; } diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-capsule-plugin.h fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-plugin.h --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-capsule-plugin.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-capsule-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,4 +8,8 @@ #include +#define FU_TYPE_UEFI_CAPSULE_PLUGIN (fu_uefi_capsule_plugin_get_type()) G_DECLARE_FINAL_TYPE(FuUefiCapsulePlugin, fu_uefi_capsule_plugin, FU, UEFI_CAPSULE_PLUGIN, FuPlugin) + +FuBackend * +fu_uefi_capsule_plugin_get_backend(FuUefiCapsulePlugin *self); diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-cod-device.c fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-cod-device.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-cod-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-cod-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,54 +18,47 @@ G_DEFINE_TYPE(FuUefiCodDevice, fu_uefi_cod_device, FU_TYPE_UEFI_CAPSULE_DEVICE) static gboolean -fu_uefi_cod_device_get_results_for_idx(FuDevice *device, guint idx, GError **error) +fu_uefi_cod_device_get_results_for_idx(FuUefiCodDevice *self, guint idx, GError **error) { - FuContext *ctx = fu_device_get_context(device); + FuContext *ctx = fu_device_get_context(FU_DEVICE(self)); FuEfivars *efivars = fu_context_get_efivars(ctx); - FuUefiCapsuleDevice *device_uefi = FU_UEFI_CAPSULE_DEVICE(device); - fwupd_guid_t guid = {0x0}; - gsize bufsz = 0; - guint32 status = 0; - guint32 total_size = 0; + FuUefiCapsuleDevice *device_uefi = FU_UEFI_CAPSULE_DEVICE(self); g_autofree gchar *guidstr = NULL; g_autofree gchar *name = NULL; - g_autofree guint8 *buf = NULL; + g_autoptr(GBytes) blob = NULL; + g_autoptr(FuStructEfiCapsuleResultVariableHeader) st = NULL; /* read out result */ name = g_strdup_printf("Capsule%04u", idx); - if (!fu_efivars_get_data(efivars, - FU_EFIVARS_GUID_EFI_CAPSULE_REPORT, - name, - &buf, - &bufsz, - NULL, - error)) { + blob = fu_efivars_get_data_bytes(efivars, + FU_EFIVARS_GUID_EFI_CAPSULE_REPORT, + name, + NULL, + error); + if (blob == NULL) { g_prefix_error(error, "failed to read %s: ", name); return FALSE; } + st = fu_struct_efi_capsule_result_variable_header_parse_bytes(blob, 0x0, error); + if (st == NULL) { + g_prefix_error(error, "failed to parse %s: ", name); + return FALSE; + } /* sanity check */ - if (!fu_memread_uint32_safe(buf, bufsz, 0x00, &total_size, G_LITTLE_ENDIAN, error)) - return FALSE; - if (total_size < 0x3A) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "EFI_CAPSULE_RESULT_VARIABLE_HEADER too small"); + if (fu_struct_efi_capsule_result_variable_header_get_total_size(st) < + FU_STRUCT_EFI_CAPSULE_RESULT_VARIABLE_HEADER_SIZE) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "EFI_CAPSULE_RESULT_VARIABLE_HEADER too small: 0x%x", + (guint)fu_struct_efi_capsule_result_variable_header_get_total_size(st)); return FALSE; } /* verify guid */ - if (!fu_memcpy_safe(guid, - sizeof(guid), - 0x0, /* dst */ - buf, - bufsz, - 0x08, /* src */ - sizeof(guid), - error)) - return FALSE; - guidstr = fwupd_guid_to_string(&guid, FWUPD_GUID_FLAG_MIXED_ENDIAN); + guidstr = fwupd_guid_to_string(fu_struct_efi_capsule_result_variable_header_get_guid(st), + FWUPD_GUID_FLAG_MIXED_ENDIAN); if (g_strcmp0(guidstr, fu_uefi_capsule_device_get_guid(device_uefi)) != 0) { g_set_error(error, FWUPD_ERROR, @@ -76,10 +69,42 @@ return FALSE; } - /* get status */ - if (!fu_memread_uint32_safe(buf, bufsz, 0x28, &status, G_LITTLE_ENDIAN, error)) - return FALSE; - fu_uefi_capsule_device_set_status(device_uefi, status); + /* map status to capsule device status */ + switch (fu_struct_efi_capsule_result_variable_header_get_status(st)) { + case FU_EFI_STATUS_SUCCESS: + fu_uefi_capsule_device_set_status(device_uefi, + FU_UEFI_CAPSULE_DEVICE_STATUS_SUCCESS); + break; + case FU_EFI_STATUS_OUT_OF_RESOURCES: + case FU_EFI_STATUS_VOLUME_FULL: + fu_uefi_capsule_device_set_status( + device_uefi, + FU_UEFI_CAPSULE_DEVICE_STATUS_ERROR_INSUFFICIENT_RESOURCES); + break; + case FU_EFI_STATUS_INCOMPATIBLE_VERSION: + fu_uefi_capsule_device_set_status( + device_uefi, + FU_UEFI_CAPSULE_DEVICE_STATUS_ERROR_INCORRECT_VERSION); + break; + case FU_EFI_STATUS_LOAD_ERROR: + case FU_EFI_STATUS_UNSUPPORTED: + case FU_EFI_STATUS_BAD_BUFFER_SIZE: + case FU_EFI_STATUS_INVALID_PARAMETER: + case FU_EFI_STATUS_BUFFER_TOO_SMALL: + fu_uefi_capsule_device_set_status( + device_uefi, + FU_UEFI_CAPSULE_DEVICE_STATUS_ERROR_INVALID_FORMAT); + break; + case FU_EFI_STATUS_ACCESS_DENIED: + case FU_EFI_STATUS_SECURITY_VIOLATION: + fu_uefi_capsule_device_set_status(device_uefi, + FU_UEFI_CAPSULE_DEVICE_STATUS_ERROR_AUTH_ERROR); + break; + default: + fu_uefi_capsule_device_set_status(device_uefi, + FU_UEFI_CAPSULE_DEVICE_STATUS_ERROR_UNSUCCESSFUL); + break; + } return TRUE; } @@ -140,7 +165,7 @@ return FALSE; for (guint i = 0; i <= capsule_last; i++) { g_autoptr(GError) error_local = NULL; - if (fu_uefi_cod_device_get_results_for_idx(device, i, &error_local)) + if (fu_uefi_cod_device_get_results_for_idx(self, i, &error_local)) return TRUE; if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { g_propagate_error(error, g_steal_pointer(&error_local)); @@ -180,10 +205,19 @@ static gchar * fu_uefi_cod_device_get_filename(FuUefiCapsuleDevice *self, GError **error) { - g_autofree gchar *esp_path = - fu_volume_get_mount_point(fu_uefi_capsule_device_get_esp(self)); + FuVolume *esp = fu_uefi_capsule_device_get_esp(self); + g_autofree gchar *esp_path = NULL; g_autofree gchar *basename = NULL; + if (esp == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no ESP set on device"); + return NULL; + } + esp_path = fu_volume_get_mount_point(esp); + /* InsydeH2O */ if (fu_device_has_private_flag(FU_DEVICE(self), FU_UEFI_CAPSULE_DEVICE_FLAG_COD_INDEXED_FILENAME)) @@ -281,11 +315,11 @@ "OsIndications", (guint8 *)&os_indications, sizeof(os_indications), - FU_EFIVARS_ATTR_NON_VOLATILE | - FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVARS_ATTR_RUNTIME_ACCESS, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE | + FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS, error)) { - g_prefix_error(error, "Could not set OsIndications: "); + g_prefix_error_literal(error, "Could not set OsIndications: "); return FALSE; } } @@ -305,6 +339,7 @@ static void fu_uefi_cod_device_init(FuUefiCodDevice *self) { + fu_device_add_private_flag(FU_DEVICE(self), FU_UEFI_CAPSULE_DEVICE_FLAG_NO_UX_CAPSULE); fu_device_set_summary(FU_DEVICE(self), "UEFI System Resource Table device (Updated via capsule-on-disk)"); } diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-common.c fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-common.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -33,8 +33,7 @@ {32, "arm"}, #endif {0, NULL}}; - g_autofree gchar *sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - g_autofree gchar *sysfsefidir = g_build_filename(sysfsfwdir, "efi", NULL); + g_autofree gchar *sysfsefidir = fu_path_build(FU_PATH_KIND_SYSFSDIR_FW, "efi", NULL); firmware_bits = fu_uefi_read_file_as_uint64(sysfsefidir, "fw_platform_size"); if (firmware_bits == 0) { g_set_error(error, @@ -95,6 +94,7 @@ gboolean secureboot_enabled = FALSE; gboolean source_path_exists = FALSE; gboolean source_path_signed_exists = FALSE; + g_autoptr(GError) error_local = NULL; suffix = fu_uefi_bootmgr_get_suffix(error); if (suffix == NULL) @@ -107,8 +107,8 @@ source_path_exists = g_file_test(source_path, G_FILE_TEST_EXISTS); source_path_signed_exists = g_file_test(source_path_signed, G_FILE_TEST_EXISTS); - if (!fu_efivars_get_secure_boot(efivars, &secureboot_enabled, error)) - return NULL; + if (!fu_efivars_get_secure_boot(efivars, &secureboot_enabled, &error_local)) + g_debug("ignoring: %s", error_local->message); if (secureboot_enabled) { if (!source_path_signed_exists) { g_set_error(error, @@ -140,11 +140,12 @@ { guint32 height_tmp; guint32 width_tmp; - g_autofree gchar *sysfsdriverdir = NULL; g_autofree gchar *fbdir = NULL; - sysfsdriverdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_DRIVERS); - fbdir = g_build_filename(sysfsdriverdir, "efi-framebuffer", "efi-framebuffer.0", NULL); + fbdir = fu_path_build(FU_PATH_KIND_SYSFSDIR_DRIVERS, + "efi-framebuffer", + "efi-framebuffer.0", + NULL); if (!g_file_test(fbdir, G_FILE_TEST_EXISTS)) { g_set_error_literal(error, FWUPD_ERROR, @@ -301,7 +302,7 @@ NULL, NULL, error)) { - g_prefix_error(error, "Failed to copy %s to %s: ", source_fn, target_fn); + g_prefix_error(error, "failed to copy %s to %s: ", source_fn, target_fn); return FALSE; } diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-common.h fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-common.h --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -13,10 +13,6 @@ #include #endif -#define EFI_CAPSULE_HEADER_FLAGS_PERSIST_ACROSS_RESET 0x00010000 -#define EFI_CAPSULE_HEADER_FLAGS_POPULATE_SYSTEM_TABLE 0x00020000 -#define EFI_CAPSULE_HEADER_FLAGS_INITIATE_RESET 0x00040000 - #define EFI_OS_INDICATIONS_FILE_CAPSULE_DELIVERY_SUPPORTED 0x0000000000000004ULL gchar * diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-grub-device.c fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-grub-device.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-grub-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-grub-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,30 +17,32 @@ G_DEFINE_TYPE(FuUefiGrubDevice, fu_uefi_grub_device, FU_TYPE_UEFI_CAPSULE_DEVICE) static gboolean -fu_uefi_grub_device_mkconfig(FuDevice *device, +fu_uefi_grub_device_mkconfig(FuUefiCapsuleDevice *self, const gchar *esp_path, const gchar *target_app, GError **error) { - const gchar *argv_mkconfig[] = {"", "-o", "/boot/grub/grub.cfg", NULL}; + g_autofree gchar *fn_grub_cfg = NULL; + const gchar *argv_mkconfig[] = {"", "-o", "grub.cfg", NULL}; const gchar *argv_reboot[] = {"", "fwupd", NULL}; gboolean exists_mkconfig = FALSE; g_autofree gchar *grub_mkconfig = NULL; g_autofree gchar *grub_reboot = NULL; g_autofree gchar *grub_target = NULL; - g_autofree gchar *localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); g_autofree gchar *output = NULL; g_autoptr(GString) str = g_string_new(NULL); /* find grub.conf */ - if (!fu_device_query_file_exists(FU_DEVICE(device), - argv_mkconfig[2], - &exists_mkconfig, - error)) - return FALSE; - if (!exists_mkconfig) - argv_mkconfig[2] = "/boot/grub2/grub.cfg"; - if (!fu_device_query_file_exists(device, argv_mkconfig[2], &exists_mkconfig, error)) + fn_grub_cfg = fu_path_build(FU_PATH_KIND_HOSTFS_BOOT, "grub", "grub.cfg", NULL); + if (!fu_device_query_file_exists(FU_DEVICE(self), fn_grub_cfg, &exists_mkconfig, error)) + return FALSE; + + /* try harder */ + if (!exists_mkconfig) { + g_free(fn_grub_cfg); + fn_grub_cfg = fu_path_build(FU_PATH_KIND_HOSTFS_BOOT, "grub2", "grub.cfg", NULL); + } + if (!fu_device_query_file_exists(FU_DEVICE(self), fn_grub_cfg, &exists_mkconfig, error)) return FALSE; if (!exists_mkconfig) { g_set_error_literal(error, @@ -78,12 +80,15 @@ g_string_append_printf(str, "EFI_PATH=%s\n", target_app); g_string_replace(str, esp_path, "", 0); g_string_append_printf(str, "ESP=%s\n", esp_path); - grub_target = g_build_filename(localstatedir, "uefi_capsule.conf", NULL); - if (!g_file_set_contents(grub_target, str->str, -1, error)) + grub_target = fu_path_build(FU_PATH_KIND_LOCALSTATEDIR_PKG, "uefi_capsule.conf", NULL); + if (!g_file_set_contents(grub_target, str->str, -1, error)) { + fwupd_error_convert(error); return FALSE; + } /* refresh GRUB configuration */ argv_mkconfig[0] = grub_mkconfig; + argv_mkconfig[2] = fn_grub_cfg; if (!g_spawn_sync(NULL, (gchar **)argv_mkconfig, NULL, @@ -93,22 +98,34 @@ &output, NULL, NULL, - error)) + error)) { + fwupd_error_convert(error); return FALSE; + } g_debug("%s", output); + /* skip for self tests */ + if (g_getenv("FWUPD_UEFI_TEST") != NULL) + return TRUE; + /* make fwupd default */ argv_reboot[0] = grub_reboot; - return g_spawn_sync(NULL, - (gchar **)argv_reboot, - NULL, - G_SPAWN_DEFAULT, - NULL, - NULL, - NULL, - NULL, - NULL, - error); + if (!g_spawn_sync(NULL, + (gchar **)argv_reboot, + NULL, + G_SPAWN_DEFAULT, + NULL, + NULL, + NULL, + NULL, + NULL, + error)) { + fwupd_error_convert(error); + return FALSE; + } + + /* success */ + return TRUE; } static gboolean @@ -161,10 +178,6 @@ if (!fu_bytes_set_contents(fn, fixed_fw, error)) return FALSE; - /* skip for self tests */ - if (g_getenv("FWUPD_UEFI_TEST") != NULL) - return TRUE; - /* enable debugging in the EFI binary */ if (!fu_uefi_capsule_device_perhaps_enable_debugging(self, error)) return FALSE; @@ -197,7 +210,7 @@ } /* we are using GRUB instead of NVRAM variables */ - return fu_uefi_grub_device_mkconfig(device, esp_path, target_app, error); + return fu_uefi_grub_device_mkconfig(self, esp_path, target_app, error); } static void diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-nvram-device.c fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-nvram-device.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-nvram-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-nvram-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -30,12 +30,13 @@ if (fu_device_has_private_flag( device, FU_UEFI_CAPSULE_DEVICE_FLAG_SUPPORTS_BOOT_ORDER_LOCK)) { - g_prefix_error(&error_local, - "boot entry missing; " - "perhaps 'Boot Order Lock' enabled in the BIOS: "); + g_prefix_error_literal(&error_local, /* nocheck:error */ + "boot entry missing; " + "perhaps 'Boot Order Lock' enabled in the BIOS: "); fu_device_set_update_state(device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT); } else { - g_prefix_error(&error_local, "boot entry missing: "); + g_prefix_error_literal(&error_local, /* nocheck:error */ + "boot entry missing: "); fu_device_set_update_state(device, FWUPD_UPDATE_STATE_FAILED); } fu_device_set_update_error(device, error_local->message); diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-update-info.c fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-update-info.c --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi-update-info.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi-update-info.c 2026-02-26 11:36:18.000000000 +0000 @@ -130,32 +130,33 @@ g_autoptr(FuEfiFilePathDevicePath) dp_fp = fu_efi_file_path_device_path_new(); if (!fu_efi_file_path_device_path_set_name(dp_fp, self->capsule_fn, error)) return NULL; - fu_firmware_add_image(FU_FIRMWARE(dp_list), FU_FIRMWARE(dp_fp)); + if (!fu_firmware_add_image(FU_FIRMWARE(dp_list), FU_FIRMWARE(dp_fp), error)) + return NULL; dpbuf = fu_firmware_write(FU_FIRMWARE(dp_list), error); if (dpbuf == NULL) return NULL; - fu_byte_array_append_bytes(st, dpbuf); + fu_byte_array_append_bytes(st->buf, dpbuf); } /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static gboolean fu_uefi_update_info_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuUefiUpdateInfo *self = FU_UEFI_UPDATE_INFO(firmware); gsize streamsz = 0; - g_autoptr(GByteArray) st_inf = NULL; + g_autoptr(FuStructEfiUpdateInfo) st_inf = NULL; g_return_val_if_fail((self), FALSE); st_inf = fu_struct_efi_update_info_parse_stream(stream, 0x0, error); if (st_inf == NULL) { - g_prefix_error(error, "EFI variable is corrupt: "); + g_prefix_error_literal(error, "EFI variable is corrupt: "); return FALSE; } fu_firmware_set_version_raw(firmware, fu_struct_efi_update_info_get_version(st_inf)); @@ -172,9 +173,9 @@ if (!fu_firmware_parse_stream(FU_FIRMWARE(dpbuf), stream, FU_STRUCT_EFI_UPDATE_INFO_SIZE, - FWUPD_INSTALL_FLAG_NONE, + flags, error)) { - g_prefix_error(error, "failed to parse dpbuf: "); + g_prefix_error_literal(error, "failed to parse dpbuf: "); return FALSE; } dp = fu_firmware_get_image_by_gtype(FU_FIRMWARE(dpbuf), diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fu-uefi.rs fwupd-2.0.20/plugins/uefi-capsule/fu-uefi.rs --- fwupd-2.0.8/plugins/uefi-capsule/fu-uefi.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fu-uefi.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,10 @@ // Copyright 2023 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later +use fwupd::efi::FuEfiCapsuleHeaderFlags; +use fwupd::efi::FuEfiStatus; +use fwupd::efi::FuStructEfiTime; + #[derive(New, Getters, Default)] #[repr(C, packed)] struct FuStructEfiUxCapsuleHeader { @@ -13,7 +17,7 @@ y_offset: u32le, } -#[derive(New, Getters, Default)] +#[derive(New, Getters, ToBytes, Default)] #[repr(C, packed)] struct FuStructEfiCapsuleHeader { guid: Guid, @@ -37,11 +41,21 @@ guid: Guid, flags: u32le, hw_inst: u64le, - time_attempted: [u8; 16], // a EFI_TIME_T + time_attempted: FuStructEfiTime, status: FuUefiUpdateInfoStatus, // EFI_DEVICE_PATH goes here } +#[derive(New, ParseBytes, ToBytes)] +#[repr(C, packed)] +struct FuStructEfiCapsuleResultVariableHeader { + total_size: u32le, + reserved: u32le, + guid: Guid, + processed: FuStructEfiTime, + status: FuEfiStatus, +} + #[derive(ParseStream)] #[repr(C, packed)] struct FuStructAcpiInsydeQuirk { diff -Nru fwupd-2.0.8/plugins/uefi-capsule/fwupd.grub.conf.in fwupd-2.0.20/plugins/uefi-capsule/fwupd.grub.conf.in --- fwupd-2.0.8/plugins/uefi-capsule/fwupd.grub.conf.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/fwupd.grub.conf.in 2026-02-26 11:36:18.000000000 +0000 @@ -7,18 +7,18 @@ . "$pkgdatadir/grub-mkconfig_lib" if [ -f @localstatedir@/lib/fwupd/uefi_capsule.conf ] && - ls /sys/firmware/efi/efivars/fwupd-*-0abba7dc-e516-4167-bbf5-4d9d1c739416 1>/dev/null 2>&1; then - . @localstatedir@/lib/fwupd/uefi_capsule.conf - if [ "${EFI_PATH}" != "" ] && [ "${ESP}" != "" ]; then - echo "Adding Linux Firmware Updater entry" >&2 -cat << EOF + ls /sys/firmware/efi/efivars/fwupd-*-0abba7dc-e516-4167-bbf5-4d9d1c739416 1>/dev/null 2>&1; then + . @localstatedir@/lib/fwupd/uefi_capsule.conf + if [ "${EFI_PATH}" != "" ] && [ "${ESP}" != "" ]; then + echo "Adding Linux Firmware Updater entry" >&2 + cat < /dev/null - prepare_grub_to_access_device "$(${grub_probe} --target=device ${ESP})" | sed -e "s/^/\t/" -cat << EOF + ${grub_probe:?} --version >/dev/null + prepare_grub_to_access_device "$(${grub_probe} --target=device ${ESP})" | sed -e "s/^/\t/" + cat <= 1.6', fallback: ['fwupd-efi', 'fwupd_efi_dep']) endif +ux_capsule_tar = [] if get_option('plugin_uefi_capsule_splash') # add the archive of pregenerated images splash_deps = run_command([ @@ -78,7 +82,7 @@ if splash_deps.returncode() != 0 error(splash_deps.stderr().strip()) endif - custom_target('ux-capsule-tar', + ux_capsule_tar = custom_target('ux-capsule-tar', input: [ join_paths(meson.project_source_root(), 'po', 'LINGUAS'), files('make-images.py'), @@ -93,6 +97,7 @@ '--out', '@OUTPUT@', ], install: true, + install_tag: 'runtime', install_dir: join_paths(datadir, 'fwupd'), ) endif @@ -105,6 +110,9 @@ e = executable( 'uefi-self-test', rustgen.process('fu-uefi.rs'), + uefi_insyde_blob, + fwupdx64_efi_signed, + ux_capsule_tar, sources: [ 'fu-self-test.c', ], @@ -145,5 +153,3 @@ 'efi binary': get_option('efi_binary'), 'capsule splash': get_option('plugin_uefi_capsule_splash'), }, section:'uefi capsule options') -endif - diff -Nru fwupd-2.0.8/plugins/uefi-capsule/tests/build-fwupdx64-efi-signed.py fwupd-2.0.20/plugins/uefi-capsule/tests/build-fwupdx64-efi-signed.py --- fwupd-2.0.8/plugins/uefi-capsule/tests/build-fwupdx64-efi-signed.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/tests/build-fwupdx64-efi-signed.py 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +#!/usr/bin/env python3 +# Copyright 2025 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# pylint: disable=invalid-name,missing-module-docstring + +import sys + +with open(sys.argv[1], "wb") as f: + f.write("fwupd-efi version 1.2\0".encode("utf-16") + b"PADDING" * 10) diff -Nru fwupd-2.0.8/plugins/uefi-capsule/tests/build-uefi-insyde.py fwupd-2.0.20/plugins/uefi-capsule/tests/build-uefi-insyde.py --- fwupd-2.0.8/plugins/uefi-capsule/tests/build-uefi-insyde.py 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/tests/build-uefi-insyde.py 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# Copyright 2025 Richard Hughes +# +# SPDX-License-Identifier: LGPL-2.1-or-later +# +# pylint: disable=invalid-name,missing-module-docstring + +import sys +import struct +import uuid + +buf = b"" +buf += struct.pack("<4s", b"UEFI") # signature +buf += struct.pack(" firmware.bin +echo -n "hello world" >firmware.bin fwupdtool build-cabinet firmware.cab firmware.bin firmware.metainfo.xml diff -Nru fwupd-2.0.8/plugins/uefi-capsule/tests/grub2/grub.cfg fwupd-2.0.20/plugins/uefi-capsule/tests/grub2/grub.cfg --- fwupd-2.0.8/plugins/uefi-capsule/tests/grub2/grub.cfg 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/tests/grub2/grub.cfg 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1 @@ +# hello world diff -Nru fwupd-2.0.8/plugins/uefi-capsule/tests/grub2-mkconfig fwupd-2.0.20/plugins/uefi-capsule/tests/grub2-mkconfig --- fwupd-2.0.8/plugins/uefi-capsule/tests/grub2-mkconfig 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/tests/grub2-mkconfig 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1 @@ +#!/usr/bin/env python3 diff -Nru fwupd-2.0.8/plugins/uefi-capsule/tests/grub2-reboot fwupd-2.0.20/plugins/uefi-capsule/tests/grub2-reboot --- fwupd-2.0.8/plugins/uefi-capsule/tests/grub2-reboot 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/tests/grub2-reboot 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1 @@ +#!/usr/bin/env python3 diff -Nru fwupd-2.0.8/plugins/uefi-capsule/tests/meson.build fwupd-2.0.20/plugins/uefi-capsule/tests/meson.build --- fwupd-2.0.8/plugins/uefi-capsule/tests/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/tests/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,29 @@ +if get_option('tests') + uefi_insyde_blob = custom_target('UEFI-insyde', + output: 'UEFI', + command: [ + python3.full_path(), + files('build-uefi-insyde.py'), + '@OUTPUT@', + ], + install: true, + install_dir: installed_test_datadir, + ) + fwupdx64_efi_signed = custom_target('fwupdx64.efi.signed', + output: 'fwupdx64.efi.signed', + command: [ + python3.full_path(), + files('build-fwupdx64-efi-signed.py'), + '@OUTPUT@', + ], + install: true, + install_dir: installed_test_datadir, + ) + install_data([ + 'grub2/grub.cfg', + 'test.quirk', + 'uefi-update-info.builder.xml', + ], + install_dir: installed_test_datadir, + ) +endif diff -Nru fwupd-2.0.8/plugins/uefi-capsule/tests/test.quirk fwupd-2.0.20/plugins/uefi-capsule/tests/test.quirk --- fwupd-2.0.8/plugins/uefi-capsule/tests/test.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/tests/test.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,2 @@ +[d09738c8-2210-5611-9f88-c6ef8f8055be] +Flags = no-coalesce,no-capsule-on-disk diff -Nru fwupd-2.0.8/plugins/uefi-capsule/tests/uefi-update-info.builder.xml fwupd-2.0.20/plugins/uefi-capsule/tests/uefi-update-info.builder.xml --- fwupd-2.0.8/plugins/uefi-capsule/tests/uefi-update-info.builder.xml 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/tests/uefi-update-info.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,7 @@ + + 697bd920-12cf-4da9-8385-996909bc6559 + foo.cap + 0x1 + 0x2 + attempt-update + diff -Nru fwupd-2.0.8/plugins/uefi-capsule/uefi-capsule.quirk fwupd-2.0.20/plugins/uefi-capsule/uefi-capsule.quirk --- fwupd-2.0.8/plugins/uefi-capsule/uefi-capsule.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-capsule/uefi-capsule.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -18,9 +18,63 @@ [6de5d951-d755-576b-bd09-c5cf66b27234] Flags = supports-boot-order-lock,use-legacy-bootmgr-desc,no-ux-capsule,no-lid-closed,modify-bootorder +# Lenovo ThinkStation P5 +[04438294-462d-5010-8761-5466e1e5f6c1] +Flags = no-capsule-on-disk + +# Lenovo ThinkStation P7 +[72de847f-43d5-59fc-832a-d9b829b59bfc] +Flags = no-capsule-on-disk + +# Lenovo ThinkStation PX +[ec21890e-61c9-5827-ab8b-0ec6f5aef57f] +Flags = no-capsule-on-disk + +# Lenovo ThinkStation P620 +[f34d76b0-884a-55ff-837c-66284d290ebc] +Flags = no-capsule-on-disk +[958b05d4-211e-57ae-8a54-e9514a9f8a27] +Flags = no-capsule-on-disk + +# Lenovo ThinkStation P3 Ultra G2 +[41a50049-0de1-5f50-b2c4-31286499b526] +Flags = no-capsule-on-disk + +# Lenovo ThinkStation P8 +[94ac1885-3df4-5720-86b3-3018c8083b68] +Flags = no-capsule-on-disk + +# Lenovo ThinkStation P358 +[4196c391-d492-51a1-af54-f731ec88490e] +Flags = no-capsule-on-disk + +# Lenovo ThinkStation P3Tiny +[33841716-92b3-5240-8545-5f522847c9f5] +Flags = no-capsule-on-disk + +# Lenovo ThinkStation P360 Ultra +[c215a1ff-e168-5bc8-be70-e306a82f5183] +Flags = no-capsule-on-disk + +# Lenovo ThinkStation P3 Ultra +[8742804b-1183-5616-9758-3b721dd97ad8] +Flags = no-capsule-on-disk + +# Lenovo ThinkStation P360Tiny +[55db0253-c51a-527d-a7ec-6a8fd1bd07eb] +Flags = no-capsule-on-disk + +# Lenovo ThinkStation P360TWR +[518479dc-8767-510b-a103-8baaa51a81e1] +Flags = no-capsule-on-disk + +# Intel CSME reference value +[23192307-d667-4bdf-af1a-6059db171246] +Flags = ~md-set-vendor + # Dell Inc. [85d38fda-fc0e-5c6f-808f-076984ae7978] -Flags = modify-bootorder,cod-dell-recovery +Flags = modify-bootorder # ASUSTeK COMPUTER INC. [65605ed1-09e2-57aa-bd0f-a26c1d35433f] diff -Nru fwupd-2.0.8/plugins/uefi-db/README.md fwupd-2.0.20/plugins/uefi-db/README.md --- fwupd-2.0.8/plugins/uefi-db/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-db/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -48,10 +48,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.8`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Richard Hughes: @hughsie diff -Nru fwupd-2.0.8/plugins/uefi-db/fu-uefi-db-device.c fwupd-2.0.20/plugins/uefi-db/fu-uefi-db-device.c --- fwupd-2.0.8/plugins/uefi-db/fu-uefi-db-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-db/fu-uefi-db-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,6 +14,8 @@ G_DEFINE_TYPE(FuUefiDbDevice, fu_uefi_db_device, FU_TYPE_UEFI_DEVICE) +#define FU_UEFI_DB_DEVICE_DEFAULT_REQUIRED_FREE (16 * 1024) /* bytes */ + static gboolean fu_uefi_db_device_probe(FuDevice *device, GError **error) { @@ -27,9 +29,12 @@ return FALSE; /* add each subdevice */ - siglist = fu_device_read_firmware(device, progress, error); + siglist = fu_device_read_firmware(device, + progress, + FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM, + error); if (siglist == NULL) { - g_prefix_error(error, "failed to parse db: "); + g_prefix_error_literal(error, "failed to parse db: "); return FALSE; } sigs = fu_efi_signature_list_get_newest(FU_EFI_SIGNATURE_LIST(siglist)); @@ -39,16 +44,12 @@ if (fu_efi_signature_get_kind(sig) != FU_EFI_SIGNATURE_KIND_X509) continue; x509_device = fu_efi_x509_device_new(ctx, FU_EFI_X509_SIGNATURE(sig)); + fu_device_add_flag(FU_DEVICE(x509_device), FWUPD_DEVICE_FLAG_AFFECTS_FDE); fu_device_set_physical_id(FU_DEVICE(x509_device), "db"); fu_device_set_proxy(FU_DEVICE(x509_device), device); fu_device_add_child(device, FU_DEVICE(x509_device)); } - /* set in the subdevice */ - fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); - fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); - fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY); - /* success */ return TRUE; } @@ -74,10 +75,10 @@ FU_EFIVARS_GUID_SECURITY_DATABASE, fu_device_get_physical_id(device), fw, - FU_EFIVARS_ATTR_APPEND_WRITE | - FU_EFIVARS_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | - FU_EFIVARS_ATTR_RUNTIME_ACCESS | FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVARS_ATTR_NON_VOLATILE, + FU_EFI_VARIABLE_ATTR_APPEND_WRITE | + FU_EFI_VARIABLE_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS | FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS | + FU_EFI_VARIABLE_ATTR_NON_VOLATILE, error)) { return FALSE; } @@ -105,13 +106,13 @@ for (guint i = 0; i < children->len; i++) { FuDevice *child = g_ptr_array_index(children, i); if (fu_device_has_instance_id(child, - "UEFI\\CRT_A5B7C551CEDC06B94D0C5B920F473E03C2F142F2", + "UEFI\\CRT_7CD7437C555F89E7C2B50E21937E420C4E583E80", FU_DEVICE_INSTANCE_FLAG_VISIBLE)) { seen_new = TRUE; break; } if (fu_device_has_instance_id(child, - "UEFI\\CRT_03DE12BE14CA397DF20CEE646C7D9B727FCCE5F8", + "UEFI\\CRT_E30CF09DABEAB32A6E3B07A7135245DE05FFB658", FU_DEVICE_INSTANCE_FLAG_VISIBLE)) { seen_old = TRUE; break; @@ -130,7 +131,7 @@ } static void -fu_uefi_db_device_set_progress(FuDevice *self, FuProgress *progress) +fu_uefi_db_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -146,9 +147,10 @@ { fu_device_set_physical_id(FU_DEVICE(self), "db"); fu_device_set_name(FU_DEVICE(self), "UEFI Signature Database"); - fu_device_add_parent_guid(FU_DEVICE(self), "main-system-firmware"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_EFI_SIGNATURE_LIST); - fu_device_add_icon(FU_DEVICE(self), "application-certificate"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_APPLICATION_CERTIFICATE); + fu_device_set_required_free(FU_DEVICE(self), FU_UEFI_DB_DEVICE_DEFAULT_REQUIRED_FREE); } static void diff -Nru fwupd-2.0.8/plugins/uefi-db/fu-uefi-db-plugin.c fwupd-2.0.20/plugins/uefi-db/fu-uefi-db-plugin.c --- fwupd-2.0.8/plugins/uefi-db/fu-uefi-db-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-db/fu-uefi-db-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -24,6 +24,7 @@ fu_uefi_db_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "uefi_pk"); fu_plugin_add_device_gtype(plugin, FU_TYPE_UEFI_DB_DEVICE); } diff -Nru fwupd-2.0.8/plugins/uefi-db/meson.build fwupd-2.0.20/plugins/uefi-db/meson.build --- fwupd-2.0.8/plugins/uefi-db/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-db/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,5 @@ +allow_uefi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginUefiDb"'] plugins += {meson.current_source_dir().split('/')[-1]: true} diff -Nru fwupd-2.0.8/plugins/uefi-dbx/README.md fwupd-2.0.20/plugins/uefi-dbx/README.md --- fwupd-2.0.8/plugins/uefi-dbx/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -91,8 +91,9 @@ remote found in `/usr/share/fwupd/remotes.d/vendor/firmware/` which allows the version-fixup to work even when offline -- although using the LVFS source is recommended for most users. -The *last-entry checksum* can be found from the -`fwupdtool firmware-parse DBXUpdate-$VERSION$.x64.bin efi-signature-list` command. +> [!TIP] +> The *last-entry checksum* can be found from the +> `fwupdtool firmware-parse DBXUpdate-$VERSION$.x64.bin efi-signature-list` command. ## Update Behavior diff -Nru fwupd-2.0.8/plugins/uefi-dbx/fu-dbxtool.c fwupd-2.0.20/plugins/uefi-dbx/fu-dbxtool.c --- fwupd-2.0.8/plugins/uefi-dbx/fu-dbxtool.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/fu-dbxtool.c 2026-02-26 11:36:18.000000000 +0000 @@ -39,7 +39,7 @@ error); if (blob == NULL) return NULL; - if (!fu_firmware_parse_bytes(dbx, blob, 0x0, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + if (!fu_firmware_parse_bytes(dbx, blob, 0x0, FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return NULL; return g_steal_pointer(&dbx); } @@ -50,7 +50,7 @@ g_autoptr(GFile) file = NULL; g_autoptr(FuFirmware) siglist = fu_efi_signature_list_new(); file = g_file_new_for_path(filename); - if (!fu_firmware_parse_file(siglist, file, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_file(siglist, file, FU_FIRMWARE_PARSE_FLAG_NONE, error)) return NULL; return g_steal_pointer(&siglist); } @@ -80,6 +80,8 @@ return "zero"; if (g_strcmp0(guid, FU_EFI_SIGNATURE_GUID_MICROSOFT) == 0) return "microsoft"; + if (g_strcmp0(guid, FU_EFI_SIGNATURE_GUID_FRAMEWORK) == 0) + return "framework"; if (g_strcmp0(guid, FU_EFI_SIGNATURE_GUID_OVMF) == 0 || g_strcmp0(guid, FU_EFI_SIGNATURE_GUID_OVMF_LEGACY) == 0) return "ovmf"; @@ -280,7 +282,7 @@ if (!fu_firmware_parse_bytes(dbx_update, blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error)) { /* TRANSLATORS: could not parse file */ g_printerr("%s: %s\n", _("Failed to parse local dbx"), error->message); @@ -301,7 +303,7 @@ g_print("%s\n", _("Validating ESP contents…")); if (!fu_uefi_dbx_signature_list_validate(ctx, FU_EFI_SIGNATURE_LIST(dbx_update), - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error)) { g_printerr("%s: %s\n", /* TRANSLATORS: something with a blocked hash exists @@ -319,10 +321,11 @@ FU_EFIVARS_GUID_SECURITY_DATABASE, "dbx", blob, - FU_EFIVARS_ATTR_APPEND_WRITE | - FU_EFIVARS_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | - FU_EFIVARS_ATTR_RUNTIME_ACCESS | FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVARS_ATTR_NON_VOLATILE, + FU_EFI_VARIABLE_ATTR_APPEND_WRITE | + FU_EFI_VARIABLE_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS | + FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS | + FU_EFI_VARIABLE_ATTR_NON_VOLATILE, &error)) { /* TRANSLATORS: dbx file failed to be applied as an update */ g_printerr("%s: %s\n", _("Failed to apply update"), error->message); diff -Nru fwupd-2.0.8/plugins/uefi-dbx/fu-self-test.c fwupd-2.0.20/plugins/uefi-dbx/fu-self-test.c --- fwupd-2.0.8/plugins/uefi-dbx/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,19 +6,73 @@ #include "config.h" -#include -#include - -#include "fwupd-error.h" +#include #include "fu-context-private.h" -#include "fu-device-private.h" -#include "fu-plugin-private.h" +#include "fu-efi-signature-private.h" #include "fu-uefi-dbx-device.h" -#include "fu-uefi-dbx-plugin.h" #include "fu-uefi-device-private.h" static void +fu_uefi_dbx_zero_func(void) +{ + gboolean ret; + g_autoptr(FuContext) ctx = fu_context_new(); + g_autoptr(FuDevice) device = g_object_new(FU_TYPE_UEFI_DBX_DEVICE, "context", ctx, NULL); + g_autoptr(FuEfiSignature) sig = fu_efi_signature_new(FU_EFI_SIGNATURE_KIND_SHA256); + g_autoptr(FuFirmware) siglist = fu_efi_signature_list_new(); + g_autoptr(GBytes) blob = NULL; + g_autoptr(GBytes) csum = NULL; + g_autoptr(GError) error = NULL; + + /* do not save silo */ + ret = fu_context_load_quirks(ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* zero hash = empty */ + csum = + fu_bytes_from_string("0000000000000000000000000000000000000000000000000000000000000000", + &error); + g_assert_no_error(error); + g_assert_nonnull(csum); + fu_firmware_set_bytes(FU_FIRMWARE(sig), csum); + fu_firmware_add_image(siglist, FU_FIRMWARE(sig), NULL); + blob = fu_firmware_write(siglist, &error); + g_assert_no_error(error); + g_assert_nonnull(blob); + + /* create a pausible KEK */ + fu_uefi_device_set_guid(FU_UEFI_DEVICE(device), FU_EFIVARS_GUID_EFI_GLOBAL); + fu_uefi_device_set_name(FU_UEFI_DEVICE(device), "KEK"); + ret = fu_uefi_device_set_efivar_bytes(FU_UEFI_DEVICE(device), + FU_EFIVARS_GUID_EFI_GLOBAL, + "KEK", + blob, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* create an "empty" dbx */ + ret = fu_uefi_device_set_efivar_bytes(FU_UEFI_DEVICE(device), + FU_EFIVARS_GUID_SECURITY_DATABASE, + "dbx", + blob, + FU_EFI_VARIABLE_ATTR_NON_VOLATILE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* detect version number */ + ret = fu_device_probe(device, &error); + g_assert_no_error(error); + g_assert_true(ret); + g_assert_cmpint(fu_device_get_version_raw(device), ==, 0); + g_assert_cmpstr(fu_device_get_version(device), ==, "0"); +} + +static void fu_efi_image_func(void) { struct { @@ -44,7 +98,9 @@ g_test_skip(msg); return; } - ret = fu_firmware_parse_file(firmware, file, FWUPD_INSTALL_FLAG_NONE, &error); + ret = fu_firmware_parse_file(firmware, file, FU_FIRMWARE_PARSE_FLAG_NONE, &error); + if (!ret) + g_prefix_error(&error, "%s: ", map[i].basename); g_assert_no_error(error); g_assert_true(ret); @@ -55,575 +111,9 @@ } } -typedef struct { - gboolean running_in_snap; - gboolean snapd_fde_detected; - gboolean snapd_supported; - const gchar *mock_snapd_scenario; -} FuTestCase; - -typedef struct { - FuContext *ctx; - - gboolean mock_snapd_available; - - CURL *mock_snapd_curl; - struct curl_slist *mock_curl_hdrs; -} FuTestFixture; - -static gboolean -fu_self_test_mock_snapd_easy_post_request(FuTestFixture *fixture, - const gchar *endpoint, - const gchar *data, - gsize len) -{ - CURL *curl = curl_easy_duphandle(fixture->mock_snapd_curl); - CURLcode res = -1; - glong status_code = 0; - g_autofree gchar *endpoint_str = g_strdup_printf("http://localhost%s", endpoint); - - g_debug("mock snapd request to %s with data: '%s'", endpoint_str, data); - - /* use snap dedicated socket when running inside a snap */ - (void)curl_easy_setopt(curl, CURLOPT_URL, endpoint_str); - (void)curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len); - (void)curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); - res = curl_easy_perform(curl); - g_debug("curl error: %u %s", res, curl_easy_strerror(res)); - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code); - curl_easy_cleanup(curl); - return res == CURLE_OK && status_code == 200; -} - -static size_t -fu_self_test_curl_write_callback(char *ptr, size_t size, size_t nmemb, void *userdata) -{ - GByteArray *bufarr = (GByteArray *)userdata; - gsize sz = size * nmemb; - g_byte_array_append(bufarr, (const guint8 *)ptr, sz); - return sz; -} - -static GBytes * -fu_self_test_mock_snapd_easy_get_request(FuTestFixture *fixture, const gchar *endpoint) -{ - CURL *curl = curl_easy_duphandle(fixture->mock_snapd_curl); - CURLcode res = -1; - glong status_code = 0; - g_autoptr(GByteArray) buf = g_byte_array_new(); - g_autofree gchar *endpoint_str = g_strdup_printf("http://localhost%s", endpoint); - - /* use snap dedicated socket when running inside a snap */ - (void)curl_easy_setopt(curl, CURLOPT_URL, endpoint_str); - (void)curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fu_self_test_curl_write_callback); - (void)curl_easy_setopt(curl, CURLOPT_WRITEDATA, buf); - res = curl_easy_perform(curl); - g_debug("curl error: %u %s", res, curl_easy_strerror(res)); - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code); - curl_easy_cleanup(curl); - - g_assert_true(res == CURLE_OK); - g_assert_true(status_code == 200); - - g_debug("rsp:\n%s", buf->data); - - return g_bytes_new(buf->data, buf->len); -} - -typedef struct { - guint startup; - guint prepare; - guint cleanup; -} FuTestSnapdCalls; - -static void -fu_self_test_mock_snapd_assert_calls(FuTestFixture *fixture, FuTestSnapdCalls calls) -{ - guint64 val = 0xffffff; - g_autoptr(GBytes) rsp = NULL; - g_autoptr(GKeyFile) kf = g_key_file_new(); - g_autoptr(GError) error = NULL; - - rsp = fu_self_test_mock_snapd_easy_get_request(fixture, "/test/stats"); - - g_key_file_load_from_bytes(kf, rsp, 0, &error); - g_assert_no_error(error); - - val = g_key_file_get_uint64(kf, "stats", "efi-secureboot-update-startup", &error); - g_assert_no_error(error); - g_assert_cmpuint(val, ==, calls.startup); - - val = g_key_file_get_uint64(kf, "stats", "efi-secureboot-update-db-prepare", &error); - g_assert_no_error(error); - g_assert_cmpuint(val, ==, calls.prepare); - - val = g_key_file_get_uint64(kf, "stats", "efi-secureboot-update-db-cleanup", &error); - g_assert_no_error(error); - g_assert_cmpuint(val, ==, calls.cleanup); -} - -static gboolean -fu_self_test_mock_snapd_reset(FuTestFixture *fixture) -{ - return fu_self_test_mock_snapd_easy_post_request(fixture, "/test/reset", NULL, 0); -} - -static gboolean -fu_self_test_mock_snapd_setup_scenario(FuTestFixture *fixture, const gchar *scenario) -{ - g_autofree gchar *scenario_request = g_strdup_printf("{\"scenario\":\"%s\"}", scenario); - - return fu_self_test_mock_snapd_easy_post_request(fixture, - "/test/setup", - scenario_request, - strlen(scenario_request)); -} - -static gboolean -fu_self_test_mock_snapd_init(FuTestFixture *fixture) -{ - CURL *curl = curl_easy_init(); - struct curl_slist *req_hdrs = NULL; - const char *mock_snapd_snap_socket = g_getenv("FWUPD_SNAPD_SNAP_SOCKET"); - - g_assert_nonnull(mock_snapd_snap_socket); - - /* use snap dedicated socket when running inside a snap */ - (void)curl_easy_setopt(curl, CURLOPT_UNIX_SOCKET_PATH, mock_snapd_snap_socket); - (void)curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L); - req_hdrs = curl_slist_append(req_hdrs, "Content-Type: application/json"); - (void)curl_easy_setopt(curl, CURLOPT_HTTPHEADER, req_hdrs); - - fixture->mock_snapd_curl = curl; - fixture->mock_curl_hdrs = req_hdrs; - - return fu_self_test_mock_snapd_reset(fixture); -} - -static void -fu_self_test_set_up(FuTestFixture *fixture, gconstpointer user_data) -{ - FuTestCase *tc = (FuTestCase *)user_data; - gboolean ret; - g_autoptr(GError) error = NULL; - - fixture->ctx = fu_context_new(); - - if ((tc->running_in_snap || tc->snapd_fde_detected) && - fu_self_test_mock_snapd_init(fixture)) { - fixture->mock_snapd_available = TRUE; - - if (tc->running_in_snap) - (void)g_setenv("SNAP", "fwupd", TRUE); - if (tc->snapd_fde_detected) - fu_context_add_flag(fixture->ctx, FU_CONTEXT_FLAG_FDE_SNAPD); - - fu_self_test_mock_snapd_setup_scenario(fixture, tc->mock_snapd_scenario); - } - - fu_context_add_flag(fixture->ctx, FU_CONTEXT_FLAG_INHIBIT_VOLUME_MOUNT); - ret = fu_context_load_quirks(fixture->ctx, - FU_QUIRKS_LOAD_FLAG_NO_CACHE | FU_QUIRKS_LOAD_FLAG_NO_VERIFY, - &error); - g_assert_no_error(error); - g_assert_true(ret); -} - -static void -fu_self_test_tear_down(FuTestFixture *fixture, gconstpointer user_data) -{ - FuTestCase *tc = (FuTestCase *)user_data; - if (tc->running_in_snap) - g_unsetenv("SNAP"); - if (tc->running_in_snap || tc->snapd_fde_detected) { - if (fixture->mock_snapd_available) - fu_self_test_mock_snapd_reset(fixture); - - if (fixture->mock_snapd_curl) - curl_easy_cleanup(fixture->mock_snapd_curl); - - if (fixture->mock_curl_hdrs) - curl_slist_free_all(fixture->mock_curl_hdrs); - } - - g_object_unref(fixture->ctx); -} - -static gboolean -fu_test_mock_efivar_content(FuEfivars *efivars, - const gchar *guid, - const gchar *name, - const gchar *path, - GError **error) -{ - gchar *mock_blob = NULL; - gsize mock_blob_size = 0; - g_autoptr(GBytes) mock_bytes = NULL; - - if (!g_file_get_contents(path, &mock_blob, &mock_blob_size, error)) - return FALSE; - - mock_bytes = g_bytes_new_take(mock_blob, mock_blob_size); - - return fu_efivars_set_data_bytes(efivars, guid, name, mock_bytes, 0, error); -} - -static gboolean -fu_test_mock_dbx_efivars(FuEfivars *efivars, GError **error) -{ - g_autofree gchar *mock_kek_path = - g_test_build_filename(G_TEST_DIST, - "tests/KEK-8be4df61-93ca-11d2-aa0d-00e098032b8c", - NULL); - g_autofree gchar *mock_dbx_path = - g_test_build_filename(G_TEST_DIST, - "tests/dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f", - NULL); - - if (!fu_test_mock_efivar_content(efivars, - FU_EFIVARS_GUID_EFI_GLOBAL, - "KEK", - mock_kek_path, - error)) - return FALSE; - - return fu_test_mock_efivar_content(efivars, - FU_EFIVARS_GUID_SECURITY_DATABASE, - "dbx", - mock_dbx_path, - error); -} - -static FuFirmware * -fu_test_mock_dbx_update_firmware(void) -{ - gboolean ret; - gchar *mock_blob = NULL; - gsize mock_blob_size = 0; - g_autoptr(GError) error = NULL; - g_autoptr(GBytes) mock_bytes = NULL; - g_autofree gchar *mock_dbx_update_path = - g_test_build_filename(G_TEST_DIST, "tests/dbx-update.auth", NULL); - - ret = g_file_get_contents(mock_dbx_update_path, &mock_blob, &mock_blob_size, &error); - g_assert_no_error(error); - g_assert_true(ret); - - mock_bytes = g_bytes_new_take(mock_blob, mock_blob_size); - return fu_firmware_new_from_bytes(mock_bytes); -} - -static void -fu_uefi_dbx_test_plugin_update(FuTestFixture *fixture, gconstpointer user_data) -{ - /* run though an update */ - FuTestCase *tc = (FuTestCase *)user_data; - gboolean ret; - FuContext *ctx = fixture->ctx; - FuEfivars *efivars = fu_context_get_efivars(ctx); - g_autoptr(GError) error = NULL; - g_autoptr(FuFirmware) firmware = NULL; - g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); - g_autoptr(FuPlugin) plugin = fu_plugin_new_from_gtype(fu_uefi_dbx_plugin_get_type(), ctx); - g_autoptr(FuUefiDbxDevice) uefi_device = NULL; - gboolean expect_snapd_calls = tc->running_in_snap || tc->snapd_fde_detected; - - /* progress */ - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_NO_PROFILE); - fu_progress_add_step(progress, FWUPD_STATUS_LOADING, 33, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 33, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 33, NULL); - - if (expect_snapd_calls && !fixture->mock_snapd_available) { - g_test_skip("mock snapd not available"); - return; - } - - if (!fu_test_mock_dbx_efivars(efivars, &error) && - g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { - g_test_skip("test assets unavailable"); - return; - } - g_assert_no_error(error); - - ret = fu_plugin_runner_startup(plugin, fu_progress_get_child(progress), &error); - g_assert_no_error(error); - g_assert_true(ret); - fu_progress_step_done(progress); - - uefi_device = g_object_new(FU_TYPE_UEFI_DBX_DEVICE, "context", ctx, NULL); - fu_uefi_device_set_guid(FU_UEFI_DEVICE(uefi_device), FU_EFIVARS_GUID_EFI_GLOBAL); - fu_uefi_device_set_name(FU_UEFI_DEVICE(uefi_device), "KEK"); - ret = fu_plugin_runner_device_created(plugin, FU_DEVICE(uefi_device), &error); - g_assert_no_error(error); - g_assert_true(ret); - - firmware = fu_test_mock_dbx_update_firmware(); - ret = fu_plugin_runner_write_firmware(plugin, - FU_DEVICE(uefi_device), - firmware, - fu_progress_get_child(progress), - /* skip verification of ESP binaries*/ - FWUPD_INSTALL_FLAG_FORCE, - &error); - g_assert_no_error(error); - g_assert_true(ret); - g_assert_true(fu_device_has_flag(FU_DEVICE(uefi_device), FWUPD_DEVICE_FLAG_NEEDS_REBOOT)); - fu_progress_step_done(progress); - - /* this is normally invoked by FuEngine */ - ret = fu_device_cleanup(FU_DEVICE(uefi_device), fu_progress_get_child(progress), 0, &error); - g_assert_true(ret); - g_assert_no_error(error); - fu_progress_step_done(progress); - - if (expect_snapd_calls) { - fu_self_test_mock_snapd_assert_calls(fixture, - (FuTestSnapdCalls){ - .startup = 1, - .prepare = 1, - .cleanup = 1, - }); - } -} - -static void -fu_uefi_dbx_test_plugin_failed_update(FuTestFixture *fixture, gconstpointer user_data) -{ - /* update which fails at either write or cleanup steps, this test can only - * properly mock the environment when using snapd integration */ - - FuTestCase *tc = (FuTestCase *)user_data; - gboolean ret; - FuContext *ctx = fixture->ctx; - FuEfivars *efivars = fu_context_get_efivars(ctx); - g_autoptr(GError) error = NULL; - g_autoptr(FuFirmware) firmware = NULL; - g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); - g_autoptr(FuProgress) progress_write = fu_progress_new(G_STRLOC); - g_autoptr(FuPlugin) plugin = fu_plugin_new_from_gtype(fu_uefi_dbx_plugin_get_type(), ctx); - g_autoptr(FuUefiDbxDevice) uefi_device = NULL; - - if (!tc->running_in_snap && !tc->snapd_fde_detected) { - g_test_skip("only supports snapd integration variant"); - return; - } - - if (!fixture->mock_snapd_available) { - g_test_skip("mock snapd not available"); - return; - } - - if (!fu_test_mock_dbx_efivars(efivars, &error) && - g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { - g_test_skip("test assets unavailable"); - return; - } - g_assert_no_error(error); - - ret = fu_plugin_runner_startup(plugin, progress, &error); - g_assert_no_error(error); - g_assert_true(ret); - - uefi_device = g_object_new(FU_TYPE_UEFI_DBX_DEVICE, "context", ctx, NULL); - fu_uefi_device_set_guid(FU_UEFI_DEVICE(uefi_device), FU_EFIVARS_GUID_EFI_GLOBAL); - fu_uefi_device_set_name(FU_UEFI_DEVICE(uefi_device), "KEK"); - ret = fu_plugin_runner_device_created(plugin, FU_DEVICE(uefi_device), &error); - g_assert_no_error(error); - g_assert_true(ret); - - firmware = fu_test_mock_dbx_update_firmware(); - ret = fu_plugin_runner_write_firmware(plugin, - FU_DEVICE(uefi_device), - firmware, - progress_write, - /* skip verification of ESP binaries*/ - FWUPD_INSTALL_FLAG_FORCE, - &error); - if (g_str_equal(tc->mock_snapd_scenario, "failed-prepare")) { - g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL); - g_clear_error(&error); - } else { - g_assert_no_error(error); - g_assert_true(ret); - } - - /* engine invokes cleanup */ - ret = fu_device_cleanup(FU_DEVICE(uefi_device), progress, 0, &error); - if (g_str_equal(tc->mock_snapd_scenario, "failed-cleanup")) { - g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL); - } else { - g_assert_no_error(error); - g_assert_true(ret); - } - - fu_self_test_mock_snapd_assert_calls(fixture, - (FuTestSnapdCalls){ - .startup = 1, - .prepare = 1, - .cleanup = 1, - }); -} - -static void -fu_uefi_dbx_test_plugin_coldplug_probed_device(FuTestFixture *fixture, gconstpointer user_data) -{ - FuTestCase *tc = (FuTestCase *)user_data; - gboolean ret; - FuContext *ctx = fixture->ctx; - FuEfivars *efivars = fu_context_get_efivars(ctx); - g_autoptr(GError) error = NULL; - g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); - g_autoptr(FuPlugin) plugin = fu_plugin_new_from_gtype(fu_uefi_dbx_plugin_get_type(), ctx); - g_autoptr(FuUefiDbxDevice) uefi_device = NULL; - gboolean expect_snapd_calls = tc->running_in_snap || tc->snapd_fde_detected; - - if (expect_snapd_calls && !fixture->mock_snapd_available) { - g_test_skip("mock snapd not available"); - return; - } - - if (!fu_test_mock_dbx_efivars(efivars, &error) && - g_error_matches(error, G_FILE_ERROR, G_FILE_ERROR_NOENT)) { - g_test_skip("test assets unavailable"); - return; - } - g_assert_no_error(error); - - ret = fu_plugin_runner_startup(plugin, progress, &error); - g_assert_no_error(error); - g_assert_true(ret); - - uefi_device = g_object_new(FU_TYPE_UEFI_DBX_DEVICE, "context", ctx, NULL); - fu_uefi_device_set_guid(FU_UEFI_DEVICE(uefi_device), FU_EFIVARS_GUID_EFI_GLOBAL); - fu_uefi_device_set_name(FU_UEFI_DEVICE(uefi_device), "KEK"); - ret = fu_plugin_runner_device_created(plugin, FU_DEVICE(uefi_device), &error); - g_assert_no_error(error); - g_assert_true(ret); - - ret = fu_device_has_inhibit(FU_DEVICE(uefi_device), "no-snapd-dbx"); - if (expect_snapd_calls && tc->snapd_supported && - g_str_equal(tc->mock_snapd_scenario, "failed-startup")) { - /* startup failed for whatever reason, device updates are inhibited */ - g_assert_true(ret); - } else - g_assert_false(ret); - - if (expect_snapd_calls) { - fu_self_test_mock_snapd_assert_calls(fixture, - (FuTestSnapdCalls){ - .startup = 1, - }); - } -} - -static void -fu_uefi_dbx_test_plugin_startup(FuTestFixture *fixture, gconstpointer user_data) -{ - FuTestCase *tc = (FuTestCase *)user_data; - gboolean ret; - FuContext *ctx = fixture->ctx; - g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); - g_autoptr(GError) error = NULL; - g_autoptr(FuPlugin) plugin = fu_plugin_new_from_gtype(fu_uefi_dbx_plugin_get_type(), ctx); - - if (tc->running_in_snap && !fixture->mock_snapd_available) { - g_test_skip("mock snapd not available"); - return; - } - - ret = fu_context_load_quirks(ctx, - FU_QUIRKS_LOAD_FLAG_NO_CACHE | FU_QUIRKS_LOAD_FLAG_NO_VERIFY, - &error); - g_assert_no_error(error); - g_assert_true(ret); - - ret = fu_plugin_runner_startup(plugin, progress, &error); - g_assert_no_error(error); - g_assert_true(ret); - - if (tc->running_in_snap) { - fu_self_test_mock_snapd_assert_calls(fixture, - (FuTestSnapdCalls){ - .startup = 1, - }); - } -} - int main(int argc, char **argv) { - FuTestCase simple = { - .running_in_snap = FALSE, - }; - /* test variants with snapd, see tests/snapd.py for what a specific scenario - * supports */ - FuTestCase running_in_snap = { - .running_in_snap = TRUE, - .snapd_supported = TRUE, - .mock_snapd_scenario = "happy-startup", - }; - FuTestCase snapd_fde_detected = { - .snapd_fde_detected = TRUE, - .snapd_supported = TRUE, - .mock_snapd_scenario = "happy-startup", - }; - FuTestCase running_in_snap_snapd_fde_detected = { - .snapd_fde_detected = TRUE, - .running_in_snap = TRUE, - .snapd_supported = TRUE, - .mock_snapd_scenario = "happy-startup", - }; - FuTestCase running_in_snap_no_support = { - .running_in_snap = TRUE, - .snapd_supported = FALSE, - .mock_snapd_scenario = "not-supported", - }; - FuTestCase snapd_fde_detected_no_support = { - .snapd_fde_detected = TRUE, - .snapd_supported = FALSE, - .mock_snapd_scenario = "not-supported", - }; - FuTestCase running_in_snap_bad_startup = { - .running_in_snap = TRUE, - .snapd_supported = TRUE, - .mock_snapd_scenario = "failed-startup", - }; - FuTestCase snapd_fde_detected_bad_startup = { - .snapd_fde_detected = TRUE, - .snapd_supported = TRUE, - .mock_snapd_scenario = "failed-startup", - }; - FuTestCase running_in_snap_update = { - .running_in_snap = TRUE, - .snapd_supported = TRUE, - .mock_snapd_scenario = "happy-update", - }; - FuTestCase snapd_fde_detected_update = { - .snapd_fde_detected = TRUE, - .snapd_supported = TRUE, - .mock_snapd_scenario = "happy-update", - }; - FuTestCase running_in_snap_update_failed_prepare = { - .running_in_snap = TRUE, - .snapd_supported = TRUE, - .mock_snapd_scenario = "failed-prepare", - }; - FuTestCase snapd_fde_detected_update_failed_prepare = { - .snapd_fde_detected = TRUE, - .snapd_supported = TRUE, - .mock_snapd_scenario = "failed-prepare", - }; - FuTestCase running_in_snap_update_failed_cleanup = { - .running_in_snap = TRUE, - .snapd_supported = TRUE, - .mock_snapd_scenario = "failed-cleanup", - }; - FuTestCase snapd_fde_detected_update_failed_cleanup = { - .snapd_fde_detected = TRUE, - .snapd_supported = TRUE, - .mock_snapd_scenario = "failed-cleanup", - }; g_autofree gchar *testdatadir = NULL; (void)g_setenv("G_TEST_SRCDIR", SRCDIR, FALSE); @@ -634,155 +124,12 @@ (void)g_setenv("G_MESSAGES_DEBUG", "all", TRUE); testdatadir = g_test_build_filename(G_TEST_DIST, "tests", NULL); - (void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE); (void)g_setenv("FWUPD_EFIVARS", "dummy", TRUE); - (void)g_setenv("FWUPD_SYSFSDRIVERDIR", testdatadir, TRUE); - (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); - (void)g_setenv("FWUPD_SNAPD_SNAP_SOCKET", "/tmp/mock-snapd-test.sock", TRUE); /* tests go here */ g_test_add_func("/uefi-dbx/image", fu_efi_image_func); - - g_test_add("/uefi-dbx/startup", - FuTestFixture, - &simple, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_startup, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/update", - FuTestFixture, - &simple, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_update, - fu_self_test_tear_down); - - g_test_add("/uefi-dbx/startup/snapd/running-in-snap/supported", - FuTestFixture, - &running_in_snap, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_startup, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/startup/snapd/fde-detected/supported", - FuTestFixture, - &snapd_fde_detected, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_startup, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/startup/snapd/running-in-snap-and-fde-detected/supported", - FuTestFixture, - &running_in_snap_snapd_fde_detected, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_startup, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/startup/snapd/running-in-snap/not-supported", - FuTestFixture, - &running_in_snap_no_support, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_startup, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/startup/snapd/fde-detected/not-supported", - FuTestFixture, - &snapd_fde_detected_no_support, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_startup, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/startup/snapd/running-in-snap/supported-failure", - FuTestFixture, - &running_in_snap_bad_startup, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_startup, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/startup/snapd/fde-detected/supported-failure", - FuTestFixture, - &snapd_fde_detected_bad_startup, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_startup, - fu_self_test_tear_down); - - g_test_add("/uefi-dbx/coldplug/with-device", - FuTestFixture, - &simple, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_coldplug_probed_device, - fu_self_test_tear_down); - - g_test_add("/uefi-dbx/coldplug/snapd/running-in-snap/supported", - FuTestFixture, - &running_in_snap, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_coldplug_probed_device, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/coldplug/snapd/fde-detected/supported", - FuTestFixture, - &snapd_fde_detected, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_coldplug_probed_device, - fu_self_test_tear_down); - - g_test_add("/uefi-dbx/coldplug/snapd/running-in-snap/not-supported", - FuTestFixture, - &running_in_snap_no_support, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_coldplug_probed_device, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/coldplug/snapd/fde-detected/not-supported", - FuTestFixture, - &snapd_fde_detected_no_support, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_coldplug_probed_device, - fu_self_test_tear_down); - - g_test_add("/uefi-dbx/coldplug/snapd/running-in-snap/supported-failure", - FuTestFixture, - &running_in_snap_bad_startup, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_coldplug_probed_device, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/coldplug/snapd/fde-detected/supported-failure", - FuTestFixture, - &snapd_fde_detected_bad_startup, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_coldplug_probed_device, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/update/snapd/running-in-snap/success", - FuTestFixture, - &running_in_snap_update, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_update, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/update/snapd/fde-detected/success", - FuTestFixture, - &snapd_fde_detected_update, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_update, - fu_self_test_tear_down); - - g_test_add("/uefi-dbx/update/snapd/running-in-snap/failed-prepare", - FuTestFixture, - &running_in_snap_update_failed_prepare, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_failed_update, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/update/snapd/fde-detected/failed-prepare", - FuTestFixture, - &snapd_fde_detected_update_failed_prepare, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_failed_update, - fu_self_test_tear_down); - - g_test_add("/uefi-dbx/update/snapd/running-in-snap/failed-cleanup", - FuTestFixture, - &running_in_snap_update_failed_cleanup, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_failed_update, - fu_self_test_tear_down); - g_test_add("/uefi-dbx/update/snapd/fde-detected/failed-cleanup", - FuTestFixture, - &snapd_fde_detected_update_failed_cleanup, - fu_self_test_set_up, - fu_uefi_dbx_test_plugin_failed_update, - fu_self_test_tear_down); + g_test_add_func("/uefi-dbx/zero", fu_uefi_dbx_zero_func); return g_test_run(); } diff -Nru fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-common.c fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-common.c --- fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -16,7 +16,7 @@ fu_uefi_dbx_get_efi_arch(void) { #ifdef HAVE_UTSNAME_H - struct utsname name_tmp; + struct utsname name_tmp = {0}; struct { const gchar *arch; const gchar *arch_efi; @@ -29,7 +29,6 @@ {"riscv64", "riscv64"}, }; - memset(&name_tmp, 0, sizeof(struct utsname)); if (uname(&name_tmp) < 0) return NULL; for (guint i = 0; i < G_N_ELEMENTS(map); i++) { @@ -45,7 +44,7 @@ { g_autoptr(FuFirmware) firmware = fu_pefile_firmware_new(); g_autoptr(GFile) file = g_file_new_for_path(fn); - if (!fu_firmware_parse_file(firmware, file, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_file(firmware, file, FU_FIRMWARE_PARSE_FLAG_NONE, error)) return NULL; return fu_firmware_get_checksum(firmware, G_CHECKSUM_SHA256, error); } @@ -54,7 +53,7 @@ fu_uefi_dbx_signature_list_validate_filename(FuContext *ctx, FuEfiSignatureList *siglist, const gchar *fn, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autofree gchar *checksum = NULL; @@ -68,7 +67,7 @@ return TRUE; } - /* Authenticode signature is present in dbx! */ + /* authenticode signature is present in dbx! */ g_debug("fn=%s, checksum=%s", fn, checksum); img = fu_firmware_get_image_by_checksum(FU_FIRMWARE(siglist), checksum, NULL); if (img != NULL) { @@ -88,7 +87,7 @@ gboolean fu_uefi_dbx_signature_list_validate(FuContext *ctx, FuEfiSignatureList *siglist, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(GPtrArray) files = NULL; diff -Nru fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-common.h fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-common.h --- fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -13,5 +13,5 @@ gboolean fu_uefi_dbx_signature_list_validate(FuContext *ctx, FuEfiSignatureList *siglist, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error); diff -Nru fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-device.c fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-device.c --- fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,38 +8,14 @@ #include "fu-uefi-dbx-common.h" #include "fu-uefi-dbx-device.h" -#include "fu-uefi-dbx-snapd-notifier.h" struct _FuUefiDbxDevice { FuUefiDevice parent_instance; - FuUefiDbxSnapdNotifier *snapd_notifier; }; G_DEFINE_TYPE(FuUefiDbxDevice, fu_uefi_dbx_device, FU_TYPE_UEFI_DEVICE) -void -fu_uefi_dbx_device_set_snapd_notifier(FuUefiDbxDevice *self, FuUefiDbxSnapdNotifier *obs) -{ - g_set_object(&self->snapd_notifier, obs); -} - -static gboolean -fu_uefi_dbx_device_maybe_notify_snapd_prepare(FuUefiDbxDevice *self, GBytes *data, GError **error) -{ - if (self->snapd_notifier == NULL) - return TRUE; - - return fu_uefi_dbx_snapd_notifier_dbx_update_prepare(self->snapd_notifier, data, error); -} - -static gboolean -fu_uefi_dbx_device_maybe_notify_snapd_cleanup(FuUefiDbxDevice *self, GError **error) -{ - if (self->snapd_notifier == NULL) - return TRUE; - - return fu_uefi_dbx_snapd_notifier_dbx_update_cleanup(self->snapd_notifier, error); -} +#define FU_UEFI_DBX_DEVICE_DEFAULT_REQUIRED_FREE (30 * 1024) /* bytes */ static gboolean fu_uefi_dbx_device_write_firmware(FuDevice *device, @@ -55,9 +31,6 @@ if (fw == NULL) return FALSE; - if (!fu_uefi_dbx_device_maybe_notify_snapd_prepare(FU_UEFI_DBX_DEVICE(device), fw, error)) - return FALSE; - /* write entire chunk to efivarsfs */ fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_WRITE); if (!fu_uefi_device_set_efivar_bytes( @@ -65,10 +38,10 @@ FU_EFIVARS_GUID_SECURITY_DATABASE, "dbx", fw, - FU_EFIVARS_ATTR_APPEND_WRITE | - FU_EFIVARS_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | - FU_EFIVARS_ATTR_RUNTIME_ACCESS | FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVARS_ATTR_NON_VOLATILE, + FU_EFI_VARIABLE_ATTR_APPEND_WRITE | + FU_EFI_VARIABLE_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS | FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS | + FU_EFI_VARIABLE_ATTR_NON_VOLATILE, error)) { return FALSE; } @@ -121,21 +94,38 @@ error); if (dbx_blob == NULL) return FALSE; - if (!fu_firmware_parse_bytes(dbx, dbx_blob, 0x0, FWUPD_INSTALL_FLAG_NO_SEARCH, error)) + if (!fu_firmware_parse_bytes(dbx, dbx_blob, 0x0, FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return FALSE; /* add the last checksum to the device */ sigs = fu_firmware_get_images(dbx); - if (sigs->len > 0) { - FuEfiSignature *sig = g_ptr_array_index(sigs, sigs->len - 1); + + for (guint i = sigs->len; i > 0; i--) { + FuEfiSignature *sig = g_ptr_array_index(sigs, i - 1); + const gchar *owner = fu_efi_signature_get_owner(sig); g_autofree gchar *csum = fu_firmware_get_checksum(FU_FIRMWARE(sig), G_CHECKSUM_SHA256, NULL); + + if (g_strcmp0(owner, FU_EFI_SIGNATURE_GUID_MICROSOFT) != 0) { + g_debug("skipping dbx entry %s as non-microsoft (%s)", csum, owner); + continue; + } + if (csum != NULL) { if (!fu_uefi_dbx_device_set_checksum(self, csum, error)) return FALSE; + break; } } + /* special entry for "empty" */ + if (sigs->len == 1) { + FuEfiSignature *sig = g_ptr_array_index(sigs, 0); + const gchar *owner = fu_efi_signature_get_owner(sig); + if (g_strcmp0(owner, FU_EFI_SIGNATURE_GUID_ZERO) == 0) + fu_device_set_version_raw(FU_DEVICE(self), 0); + } + /* success */ return TRUE; } @@ -154,11 +144,19 @@ fu_device_set_version_lowest(device, fu_device_get_version(device)); } +static void +fu_uefi_dbx_device_vendor_notify_cb(FuDevice *device, GParamSpec *pspec, gpointer user_data) +{ + const gchar *subject_vendor = fu_device_get_vendor(device); + if (subject_vendor != NULL) + fu_device_build_vendor_id(device, "UEFI", subject_vendor); +} + static FuFirmware * fu_uefi_dbx_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuContext *ctx = fu_device_get_context(device); @@ -167,7 +165,7 @@ /* parse dbx */ if (!fu_firmware_parse_stream(siglist, stream, 0x0, flags, error)) { - g_prefix_error(error, "cannot parse DBX update: "); + g_prefix_error_literal(error, "cannot parse DBX update: "); return NULL; } @@ -178,9 +176,9 @@ FU_EFI_SIGNATURE_LIST(siglist), flags, error)) { - g_prefix_error(error, - "Blocked executable in the ESP, " - "ensure grub and shim are up to date: "); + g_prefix_error_literal(error, + "Blocked executable in the ESP, " + "ensure grub and shim are up to date: "); return NULL; } } @@ -195,15 +193,17 @@ fu_uefi_dbx_device_probe(FuDevice *device, GError **error) { FuUefiDbxDevice *self = FU_UEFI_DBX_DEVICE(device); - FuContext *ctx = fu_device_get_context(device); g_autoptr(FuFirmware) kek = NULL; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); g_autoptr(GPtrArray) sigs = NULL; /* use each of the certificates in the KEK to generate the GUIDs */ - kek = fu_device_read_firmware(device, progress, error); + kek = fu_device_read_firmware(device, + progress, + FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM, + error); if (kek == NULL) { - g_prefix_error(error, "failed to parse KEK: "); + g_prefix_error_literal(error, "failed to parse KEK: "); return FALSE; } fu_device_add_instance_strup(device, "ARCH", fu_uefi_dbx_get_efi_arch()); @@ -225,30 +225,11 @@ NULL); fu_device_build_instance_id(device, NULL, "UEFI", "CRT", "ARCH", NULL); } - - /* dbx changes are expected to change PCR7, warn the user that BitLocker might ask for - recovery key after fw update */ - if (fu_context_has_flag(ctx, FU_CONTEXT_FLAG_FDE_BITLOCKER)) - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_AFFECTS_FDE); - return fu_uefi_dbx_device_ensure_checksum(self, error); } static void -fu_uefi_dbx_device_report_metadata_pre(FuDevice *device, GHashTable *metadata) -{ - FuContext *ctx = fu_device_get_context(device); - FuEfivars *efivars = fu_context_get_efivars(ctx); - guint64 nvram_total = fu_efivars_space_used(efivars, NULL); - if (nvram_total != G_MAXUINT64) { - g_hash_table_insert(metadata, - g_strdup("EfivarsNvramUsed"), - g_strdup_printf("%" G_GUINT64_FORMAT, nvram_total)); - } -} - -static void -fu_uefi_dbx_device_set_progress(FuDevice *self, FuProgress *progress) +fu_uefi_dbx_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -258,16 +239,10 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); } -static gboolean -fu_uefi_dbx_device_cleanup(FuDevice *self, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) +static gchar * +fu_uefi_dbx_device_convert_version(FuDevice *device, guint64 version_raw) { - if (!fu_uefi_dbx_device_maybe_notify_snapd_cleanup(FU_UEFI_DBX_DEVICE(self), error)) - return FALSE; - - return TRUE; + return fu_version_from_uint64(version_raw, fu_device_get_version_format(device)); } static void @@ -280,44 +255,37 @@ fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER); fu_device_set_install_duration(FU_DEVICE(self), 1); fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_EFI_SIGNATURE_LIST); - fu_device_add_icon(FU_DEVICE(self), "application-certificate"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_APPLICATION_CERTIFICATE); + fu_device_set_required_free(FU_DEVICE(self), FU_UEFI_DBX_DEVICE_DEFAULT_REQUIRED_FREE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_AFFECTS_FDE); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_ONLY_CHECKSUM); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_VERSION); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_INHIBIT_CHILDREN); g_signal_connect(FWUPD_DEVICE(self), "notify::version", G_CALLBACK(fu_uefi_dbx_device_version_notify_cb), NULL); -} - -static void -fu_uefi_dbx_device_finalize(GObject *object) -{ - FuUefiDbxDevice *self = FU_UEFI_DBX_DEVICE(object); - - if (self->snapd_notifier != NULL) - g_object_unref(self->snapd_notifier); - - G_OBJECT_CLASS(fu_uefi_dbx_device_parent_class)->finalize(object); + g_signal_connect(FU_DEVICE(self), + "notify::vendor", + G_CALLBACK(fu_uefi_dbx_device_vendor_notify_cb), + NULL); } static void fu_uefi_dbx_device_class_init(FuUefiDbxDeviceClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS(klass); FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); device_class->probe = fu_uefi_dbx_device_probe; device_class->reload = fu_uefi_dbx_device_reload; device_class->write_firmware = fu_uefi_dbx_device_write_firmware; device_class->prepare_firmware = fu_uefi_dbx_device_prepare_firmware; device_class->set_progress = fu_uefi_dbx_device_set_progress; - device_class->report_metadata_pre = fu_uefi_dbx_device_report_metadata_pre; - device_class->cleanup = fu_uefi_dbx_device_cleanup; - - object_class->finalize = fu_uefi_dbx_device_finalize; + device_class->convert_version = fu_uefi_dbx_device_convert_version; } diff -Nru fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-device.h fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-device.h --- fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,10 +8,5 @@ #include -#include "fu-uefi-dbx-snapd-notifier.h" - #define FU_TYPE_UEFI_DBX_DEVICE (fu_uefi_dbx_device_get_type()) G_DECLARE_FINAL_TYPE(FuUefiDbxDevice, fu_uefi_dbx_device, FU, UEFI_DBX_DEVICE, FuUefiDevice) - -void -fu_uefi_dbx_device_set_snapd_notifier(FuUefiDbxDevice *self, FuUefiDbxSnapdNotifier *obs); diff -Nru fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-plugin.c fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-plugin.c --- fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,13 +8,9 @@ #include "fu-uefi-dbx-device.h" #include "fu-uefi-dbx-plugin.h" -#include "fu-uefi-dbx-snapd-notifier.h" struct _FuUefiDbxPlugin { FuPlugin parent_instance; - - FuUefiDbxSnapdNotifier *snapd_notifier; - gboolean snapd_integration_supported; }; G_DEFINE_TYPE(FuUefiDbxPlugin, fu_uefi_dbx_plugin, FU_TYPE_PLUGIN) @@ -22,25 +18,12 @@ static gboolean fu_uefi_dbx_plugin_device_created(FuPlugin *plugin, FuDevice *device, GError **error) { - FuUefiDbxPlugin *self = FU_UEFI_DBX_PLUGIN(plugin); - gboolean inhibited = FALSE; + FuContext *ctx = fu_plugin_get_context(plugin); - if (fu_context_has_hwid_flag(fu_plugin_get_context(plugin), "no-dbx-updates")) { + if (fu_context_has_hwid_flag(ctx, "no-dbx-updates")) { fu_device_inhibit(FU_DEVICE(device), "no-dbx", "System firmware cannot accept DBX updates"); - inhibited = TRUE; - } - - if (self->snapd_notifier != NULL) { - fu_uefi_dbx_device_set_snapd_notifier(FU_UEFI_DBX_DEVICE(device), - self->snapd_notifier); - } else if (!inhibited && self->snapd_integration_supported) { - /* if snapd integration is supported, but we are unable to use the snapd notifier, - then we should inhibit the update if it isn't already inhibited */ - fu_device_inhibit(FU_DEVICE(device), - "no-snapd-dbx", - "Snapd integration for DBX update is not available"); } /* success */ @@ -53,62 +36,20 @@ } static void -fu_uefi_dbx_plugin_finalize(GObject *object) -{ - FuUefiDbxPlugin *self = FU_UEFI_DBX_PLUGIN(object); - if (self->snapd_notifier != NULL) { - g_object_unref(self->snapd_notifier); - self->snapd_notifier = NULL; - } - - G_OBJECT_CLASS(fu_uefi_dbx_plugin_parent_class)->finalize(object); -} - -static gboolean -fu_uefi_dbx_plugin_snapd_notify_init(FuUefiDbxPlugin *self, GError **error) -{ - g_autoptr(FuUefiDbxSnapdNotifier) obs = fu_uefi_dbx_snapd_notifier_new(); - - if (!fu_uefi_dbx_snapd_notifier_dbx_manager_startup(obs, error)) - return FALSE; - - g_set_object(&self->snapd_notifier, obs); - return TRUE; -} - -static void fu_uefi_dbx_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); g_autoptr(FuVolume) esp = NULL; g_autoptr(GError) error_udisks2 = NULL; - FuUefiDbxPlugin *self = FU_UEFI_DBX_PLUGIN(plugin); FuContext *ctx = fu_plugin_get_context(plugin); fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "uefi_capsule"); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "uefi_pk"); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_EFI_SIGNATURE_LIST); fu_plugin_add_device_gtype(plugin, FU_TYPE_UEFI_DBX_DEVICE); - /* only enable snapd integration if either running inside a snap or we detect that this is a - snapd FDE setup. either of these cases makes snapd integration mandatory */ - if (fu_snap_is_in_snap() || fu_context_has_flag(ctx, FU_CONTEXT_FLAG_FDE_SNAPD)) { - g_autoptr(GError) error_local = NULL; - if (!fu_uefi_dbx_plugin_snapd_notify_init(FU_UEFI_DBX_PLUGIN(obj), &error_local)) { - /* unless we got specific error code indicating lack of relevant APIs, snapd - integration is considered to be supported, even if snapd itself cannot be - reached */ - self->snapd_integration_supported = - !g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); - - g_info("snapd integration non-functional: %s", error_local->message); - } else { - g_info("snapd integration enabled"); - self->snapd_integration_supported = TRUE; - } - } - /* ensure that an ESP was found */ - esp = fu_context_get_default_esp(fu_plugin_get_context(plugin), &error_udisks2); + esp = fu_context_get_default_esp(ctx, &error_udisks2); if (esp == NULL) { g_info("cannot find default ESP: %s", error_udisks2->message); fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_ESP_NOT_FOUND); @@ -120,11 +61,7 @@ static void fu_uefi_dbx_plugin_class_init(FuUefiDbxPluginClass *klass) { - GObjectClass *object_class = G_OBJECT_CLASS(klass); FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); - plugin_class->constructed = fu_uefi_dbx_plugin_constructed; plugin_class->device_created = fu_uefi_dbx_plugin_device_created; - - object_class->finalize = fu_uefi_dbx_plugin_finalize; } diff -Nru fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-snapd-notifier.c fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-snapd-notifier.c --- fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-snapd-notifier.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-snapd-notifier.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,236 +0,0 @@ -/* - * Copyright 2024 Maciej Borzecki - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ -#include "config.h" - -#include -#include - -#include "fu-uefi-dbx-snapd-notifier.h" -#include "glib.h" - -struct _FuUefiDbxSnapdNotifier { - GObject parent_instance; - CURL *curl_template; - struct curl_slist *req_hdrs; -}; - -G_DEFINE_TYPE(FuUefiDbxSnapdNotifier, fu_uefi_dbx_snapd_notifier, G_TYPE_OBJECT) - -G_DEFINE_AUTOPTR_CLEANUP_FUNC(CURL, curl_easy_cleanup); - -FuUefiDbxSnapdNotifier * -fu_uefi_dbx_snapd_notifier_new(void) -{ - return g_object_new(FU_TYPE_UEFI_DBX_SNAPD_NOTIFIER, NULL); -} - -static void -fu_uefi_dbx_snapd_notifier_init(FuUefiDbxSnapdNotifier *self) -{ - /* default path is different inside the snap sandbox vs out */ - const char *snapd_snap_socket = fu_snap_is_in_snap() ? "/run/snapd-snap.socket" - : "/run/snapd.socket"; - const char *snapd_snap_socket_override = g_getenv("FWUPD_SNAPD_SNAP_SOCKET"); - - self->curl_template = curl_easy_init(); - - if (snapd_snap_socket_override != NULL) - snapd_snap_socket = snapd_snap_socket_override; - - /* use snap dedicated socket when running inside a snap */ - (void)curl_easy_setopt(self->curl_template, CURLOPT_UNIX_SOCKET_PATH, snapd_snap_socket); - - self->req_hdrs = curl_slist_append(self->req_hdrs, "Content-Type: application/json"); - (void)curl_easy_setopt(self->curl_template, CURLOPT_HTTPHEADER, self->req_hdrs); -} - -static void -fu_uefi_dbx_snapd_notifier_finalize(GObject *object) -{ - FuUefiDbxSnapdNotifier *self = FU_UEFI_DBX_SNAPD_NOTIFIER(object); - curl_slist_free_all(self->req_hdrs); - curl_easy_cleanup(self->curl_template); - G_OBJECT_CLASS(fu_uefi_dbx_snapd_notifier_parent_class)->finalize(object); -} - -static void -fu_uefi_dbx_snapd_notifier_class_init(FuUefiDbxSnapdNotifierClass *klass) -{ - GObjectClass *object_class = G_OBJECT_CLASS(klass); - object_class->finalize = fu_uefi_dbx_snapd_notifier_finalize; -} - -/* see CURLOPT_WRITEFUNCTION(3) */ -static size_t -fu_uefi_dbx_snapd_notifier_rsp_cb(char *ptr, size_t size, size_t nmemb, void *userdata) -{ - GByteArray *bufarr = (GByteArray *)userdata; - gsize sz = size * nmemb; - g_byte_array_append(bufarr, (const guint8 *)ptr, sz); - return sz; -} - -static gboolean -fu_uefi_dbx_snapd_notifier_simple_req(FuUefiDbxSnapdNotifier *self, - const char *endpoint, - const char *data, - gsize len, - GError **error) -{ - CURLcode res = -1; - glong status_code = 0; - g_autoptr(CURL) curl = NULL; - g_autofree gchar *endpoint_str = NULL; - g_autoptr(GByteArray) rsp_buf = g_byte_array_new(); - - /* duplicate a preconfigured curl handle */ - curl = curl_easy_duphandle(self->curl_template); - - endpoint_str = g_strdup_printf("http://localhost%s", endpoint); - - (void)curl_easy_setopt(curl, CURLOPT_URL, endpoint_str); - - (void)curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len); - (void)curl_easy_setopt(curl, CURLOPT_POSTFIELDS, data); - - /* collect response for debugging */ - (void)curl_easy_setopt(curl, CURLOPT_WRITEDATA, rsp_buf); - (void)curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fu_uefi_dbx_snapd_notifier_rsp_cb); - - res = curl_easy_perform(curl); - if (res != CURLE_OK) { - /* TODO inspect the error */ - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to communicate with snapd: %s", - curl_easy_strerror(res)); - return FALSE; - } - - curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &status_code); - - if (status_code == 404) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "snapd notification endpoint not supported by snapd API"); - return FALSE; - } - - if (status_code != 200) { - g_autofree gchar *rsp = NULL; - if (rsp_buf->len > 0) { - /* make sure the response is printable */ - rsp = fu_strsafe((const char *)rsp_buf->data, rsp_buf->len + 1); - } - - /* TODO check whether the response is even printable? */ - g_info("snapd request failed with status %ld, response: %s", - (glong)status_code, - rsp != NULL ? rsp : ""); - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "snapd request failed with status %ld", - (glong)status_code); - return FALSE; - } - - return TRUE; -} - -/** - * fu_uefi_dbx_snapd_notifier_dbx_manager_startup: - * @self: a #FuUefiDbxSnapdNotifier - * @error: (nullable): optional return location for an error - * - * Notify snapd of that the DBX manager has started. - * - * Returns: #TRUE if the notification was successful. - **/ -gboolean -fu_uefi_dbx_snapd_notifier_dbx_manager_startup(FuUefiDbxSnapdNotifier *self, GError **error) -{ - const char *startup_msg = "{\"action\":\"efi-secureboot-update-startup\"}"; - - if (!fu_uefi_dbx_snapd_notifier_simple_req(self, - "/v2/system-secureboot", - startup_msg, - strlen(startup_msg), - error)) { - g_prefix_error(error, "failed to notify snapd of startup: "); - return FALSE; - } - - return TRUE; -} - -/** - * fu_uefi_dbx_snapd_notifier_dbx_update_prepare: - * @self: a #FuUefiDbxSnapdNotifier - * @fw_payload: payload used for the update - * @error: (nullable): optional return location for an error - * - * Notify of an upcoming update to the DBX. A successful call shall initiate a - * change tracking an update to the DBX on the snapd side. - * - * Returns: #TRUE if the notification was successful. - **/ -gboolean -fu_uefi_dbx_snapd_notifier_dbx_update_prepare(FuUefiDbxSnapdNotifier *self, - GBytes *fw_payload, - GError **error) -{ - gsize bufsz = 0; - const guint8 *buf = g_bytes_get_data(fw_payload, &bufsz); - g_autofree gchar *b64data = g_base64_encode(buf, bufsz); - g_autofree gchar *msg = g_strdup_printf("{" - "\"action\":\"efi-secureboot-update-db-prepare\"," - "\"key-database\":\"DBX\"," - "\"payload\":\"%s\"" - "}", - b64data); - - if (!fu_uefi_dbx_snapd_notifier_simple_req(self, - "/v2/system-secureboot", - msg, - strlen(msg), - error)) { - g_prefix_error(error, "failed to notify snapd of prepare: "); - return FALSE; - } - - return TRUE; -} - -/** - * fu_uefi_dbx_snapd_notifier_dbx_update_cleanup: - * @self: a #FuUefiDbxSnapdNotifier - * @error: (nullable): optional return location for an error - * - * Notify of an completed update to one of secureboot key databases. A - * successful call shall result in completion of a corresponding change on the - * snapd side. - * - * Returns: #TRUE if the notification was successful. - **/ -gboolean -fu_uefi_dbx_snapd_notifier_dbx_update_cleanup(FuUefiDbxSnapdNotifier *self, GError **error) -{ - const char *msg = "{\"action\":\"efi-secureboot-update-db-cleanup\"}"; - - if (!fu_uefi_dbx_snapd_notifier_simple_req(self, - "/v2/system-secureboot", - msg, - strlen(msg), - error)) { - g_prefix_error(error, "failed to notify snapd of cleanup: "); - return FALSE; - } - - return TRUE; -} diff -Nru fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-snapd-notifier.h fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-snapd-notifier.h --- fwupd-2.0.8/plugins/uefi-dbx/fu-uefi-dbx-snapd-notifier.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/fu-uefi-dbx-snapd-notifier.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,30 +0,0 @@ -/* - * Copyright 2024 Maciej Borzecki - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#define FU_TYPE_UEFI_DBX_SNAPD_NOTIFIER (fu_uefi_dbx_snapd_notifier_get_type()) -G_DECLARE_FINAL_TYPE(FuUefiDbxSnapdNotifier, - fu_uefi_dbx_snapd_notifier, - FU, - UEFI_DBX_SNAPD_NOTIFIER, - GObject) - -FuUefiDbxSnapdNotifier * -fu_uefi_dbx_snapd_notifier_new(void); - -gboolean -fu_uefi_dbx_snapd_notifier_dbx_manager_startup(FuUefiDbxSnapdNotifier *self, GError **error); - -gboolean -fu_uefi_dbx_snapd_notifier_dbx_update_prepare(FuUefiDbxSnapdNotifier *self, - GBytes *fw_payload, - GError **error); - -gboolean -fu_uefi_dbx_snapd_notifier_dbx_update_cleanup(FuUefiDbxSnapdNotifier *self, GError **error); diff -Nru fwupd-2.0.8/plugins/uefi-dbx/meson.build fwupd-2.0.20/plugins/uefi-dbx/meson.build --- fwupd-2.0.8/plugins/uefi-dbx/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,5 @@ +allow_uefi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginUefiDbx"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -7,23 +9,18 @@ 'fu-uefi-dbx-plugin.c', 'fu-uefi-dbx-common.c', 'fu-uefi-dbx-device.c', - 'fu-uefi-dbx-snapd-notifier.c', ], include_directories: plugin_incdirs, link_with: plugin_libs, c_args: cargs, dependencies: [ plugin_deps, - libcurl, ], ) plugin_builtins += plugin_builtin_uefi_dbx device_tests += files('tests/uefi-dbx.json') if get_option('tests') - install_data(['tests/snapd.py'], - install_dir: join_paths(installed_test_datadir, 'tests')) - env = environment() env.set('G_TEST_SRCDIR', meson.current_source_dir()) env.set('G_TEST_BUILDDIR', meson.current_build_dir()) @@ -44,6 +41,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ cargs, @@ -79,6 +77,7 @@ '--replace', 'PACKAGE_VERSION', fwupd_version, ], install: true, + install_tag: 'man', install_dir: join_paths(mandir, 'man1'), ) endif diff -Nru fwupd-2.0.8/plugins/uefi-dbx/tests/snapd.py fwupd-2.0.20/plugins/uefi-dbx/tests/snapd.py --- fwupd-2.0.8/plugins/uefi-dbx/tests/snapd.py 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/tests/snapd.py 1970-01-01 00:00:00.000000000 +0000 @@ -1,272 +0,0 @@ -#!/usr/bin/env python3 -# -# Copyright 2024 Maciej Borzecki -# -# SPDX-License-Identifier: LGPL-2.1-or-later - -"""The app provides a mock snapd API for use with fwupd unit tests. The -/v2/system-secureboot endpoint mimics the behavior of snapd wrt. DBX updates. - -The app exposes a couple of test specific endpoinds, /test/scenario, which picks -one of the scenarios (expected to be called in when setting a test fixture), -/test/reset, for resetting the state (usually called in tear down), and -/test/stats which provides the count of actions invoked since last reset. -""" - -import argparse -import logging -import base64 -import json -import os.path -from io import StringIO -from dataclasses import dataclass -from collections.abc import Callable -from typing import Any, Optional - -from flask import Flask, Response, request, current_app - - -@dataclass -class Scenario: - handler: Callable[[dict[str, Any]], Response] - - -@dataclass -class State: - scenario: Optional[str] - stats: dict[str, int] - - def __init__(self): - self.scenario = None - self.stats = {k: 0 for k in supported_actions} - - -app = Flask(__name__) - -supported_actions = [ - "efi-secureboot-update-startup", - "efi-secureboot-update-db-cleanup", - "efi-secureboot-update-db-prepare", -] - - -def assert_no_extra_keys(d: dict[str, Any], allowed: list[str]): - unexpected = [a for a in d.keys() if a not in allowed] - assert len(unexpected) == 0, f"unexpected keys in request data: {unexpected}" - - -def assert_prepare_req(req: dict[str, Any]): - assert req.get("action") == "efi-secureboot-update-db-prepare", "unexpected action" - assert "payload" in req, "missing payload field" - assert "key-database" in req, "missing key-database field" - assert req.get("key-database") == "DBX", "unexpected key database" - - payload = req.get("payload") - assert_no_extra_keys(req, ["action", "payload", "key-database"]) - assert payload is not None, "no update payload" - # payload must be valid base64 - try: - raw_payload = base64.urlsafe_b64decode(payload) - with open(os.path.join(current_app.datadir, "dbx-update.auth"), "rb") as inf: - reference_payload = inf.read() - - assert raw_payload == reference_payload, "unexpected payload content" - except Exception as err: - raise AssertionError("invalid base64 data in request") from err - - -def assert_startup_req(req: dict[str, Any]): - assert req.get("action") == "efi-secureboot-update-startup", "unexpected action" - assert_no_extra_keys(req, ["action"]) - - -def assert_cleanup_req(req: dict[str, Any]): - assert req.get("action") == "efi-secureboot-update-db-cleanup", "unexpected action" - assert_no_extra_keys(req, ["action"]) - - -def happy_startup(req: dict[str, Any]) -> Response: - assert_startup_req(req) - return Response(None, status=200) - - -def not_supported(req: dict[str, Any]) -> Response: - assert_startup_req(req) - # pretend relevant APIs are missing, hence 404 - return Response("", status=404) - - -def failed_startup(req: dict[str, Any]) -> Response: - assert_startup_req(req) - return Response(None, status=400) - - -def happy_prepare(req: dict[str, Any]) -> Response: - assert_prepare_req(req) - return Response("", status=200) - - -def failed_prepare(req: dict[str, Any]) -> Response: - action = req.get("action") - if action == "efi-secureboot-update-db-cleanup": - return happy_cleanup(req) - if action == "efi-secureboot-update-db-prepare": - assert_prepare_req(req) - return Response( - json.dumps( - { - "status": "500", - "error": { - "kind": "internal-error", - "message": "cannot reseal keys in prepare", - }, - } - ), - status=500, - ) - if action == "efi-secureboot-update-startup": - return happy_startup(req) - - raise AssertionError(f"unexpected action {action}") - - -def failed_cleanup(req: dict[str, Any]) -> Response: - action = req.get("action") - if action == "efi-secureboot-update-db-cleanup": - assert_cleanup_req(req) - return Response( - json.dumps( - { - "status": "500", - "error": { - "kind": "internal-error", - "message": "cannot reseal keys in cleanup", - }, - } - ), - status=500, - ) - if action == "efi-secureboot-update-db-prepare": - return happy_prepare(req) - if action == "efi-secureboot-update-startup": - return happy_startup(req) - - raise AssertionError(f"unexpected action {action}") - - -def happy_cleanup(req: dict[str, Any]) -> Response: - assert_cleanup_req(req) - return Response("", status=200) - - -def happy_update(req: dict[str, Any]) -> Response: - action = req.get("action") - if action == "efi-secureboot-update-db-cleanup": - return happy_cleanup(req) - if action == "efi-secureboot-update-db-prepare": - return happy_prepare(req) - if action == "efi-secureboot-update-startup": - return happy_startup(req) - - raise AssertionError(f"unexpected action {action}") - - -playbook: dict[str, Scenario] = { - # successful startup - "happy-startup": Scenario(handler=happy_startup), - # startup with mock failure - "failed-startup": Scenario(handler=failed_startup), - # 404 on the API endpoint, indicating lack of support on the snapd side - "not-supported": Scenario(handler=not_supported), - # prepare step fails - "failed-prepare": Scenario(handler=failed_prepare), - # prepare is successful, but cleanup fails - "failed-cleanup": Scenario(handler=failed_cleanup), - # successful update cycle - "happy-update": Scenario(handler=happy_update), -} - - -def assert_scenario(f): - def do(): - with app.app_context(): - assert current_app.state.scenario is not None, "test scenario is not set" - return f() - - return do - - -def app_init_state(): - with app.app_context(): - current_app.state = State() - - -@app.route("/v2/system-secureboot", methods=["POST"]) -@assert_scenario -def system_secureboot(): - assert len(request.view_args) == 0 - assert request.headers.get("Content-Type") == "application/json" - - req = request.get_json() - logging.debug("req: %r", req) - action = req.get("action") - - assert action in supported_actions, f"unknown action {action}" - - current_app.state.stats[action] += 1 - - return playbook[current_app.state.scenario].handler(req) - - -@app.route("/test/setup", methods=["POST"]) -def test_setup(): - req = request.get_json() - logging.debug("req: %r", req) - scenario = req.get("scenario") - - assert scenario in playbook, f"unknown scenario {scenario}" - - logging.debug("setting scenario to '%s'", scenario) - current_app.state.scenario = scenario - return Response("", status=200) - - -@app.route("/test/reset", methods=["POST"]) -def test_reset(): - app_init_state() - return Response("", status=200) - - -@app.route("/test/stats") -def test_stats(): - out = StringIO() - # use a key-file format so that glib side parsing is easy - out.write("[stats]\n") - for action in sorted(current_app.state.stats.keys()): - out.write(f"{action}={current_app.state.stats[action]}\n") - return Response(out.getvalue(), status=200) - - -def parse_arguments(): - parser = argparse.ArgumentParser(description="mock snapd APIs") - parser.add_argument( - "--socket", help="socket path", default="/tmp/mock-snapd-test.sock" - ) - parser.add_argument( - "--datadir", - default=os.path.dirname(__file__), - help="path to directory with test data files", - ) - - return parser.parse_args() - - -if __name__ == "__main__": - opts = parse_arguments() - logging.basicConfig(level=logging.DEBUG) - logging.debug("socket path: %s", opts.socket) - logging.debug("data dir: %s", opts.datadir) - with app.app_context(): - current_app.datadir = opts.datadir - app_init_state() - app.run(host="unix://" + opts.socket) diff -Nru fwupd-2.0.8/plugins/uefi-dbx/tests/uefi-dbx.json fwupd-2.0.20/plugins/uefi-dbx/tests/uefi-dbx.json --- fwupd-2.0.8/plugins/uefi-dbx/tests/uefi-dbx.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/tests/uefi-dbx.json 2026-02-26 11:36:18.000000000 +0000 @@ -6,8 +6,8 @@ ], "steps": [ { - "url": "https://fwupd.org/downloads/d661d4a0aaca09dfa9e56967ca2467b0575fc07cb704d182fa8c68225452957f-DBXUpdate-20241101-x64.cab", - "emulation-url": "https://fwupd.org/downloads/d6e6c3566479b4724d117107ef0d34012b96c8088f2bf9498194fdf1dc32c256-emulation.zip", + "url": "d661d4a0aaca09dfa9e56967ca2467b0575fc07cb704d182fa8c68225452957f-DBXUpdate-20241101-x64.cab", + "emulation-url": "d6e6c3566479b4724d117107ef0d34012b96c8088f2bf9498194fdf1dc32c256-emulation.zip", "components": [ { "version": "20241101", diff -Nru fwupd-2.0.8/plugins/uefi-dbx/uefi-dbx.quirk fwupd-2.0.20/plugins/uefi-dbx/uefi-dbx.quirk --- fwupd-2.0.8/plugins/uefi-dbx/uefi-dbx.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-dbx/uefi-dbx.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -4,10 +4,14 @@ # x64 [UEFI\CSUM_5391C3A2FB112102A6AA1EDC25AE77E19F5D6F09CD09EEB2509922BFCD5992EA] Version = 20100307 +[UEFI\CSUM_10D45FCBA396AEF3153EE8F6ECAE58AFE8476A280A2026FC71F6217DCF49BA2F] +Version = 20100507 [UEFI\CSUM_90FBE70E69D633408D3E170C6832DBB2D209E0272527DFB63D49D29572A6F44C] Version = 20140413 [UEFI\CSUM_45C7C8AE750ACFBB48FC37527D6412DD644DAED8913CCD8A24C94D856967DF8E] Version = 20160809 +[UEFI\CSUM_939AEEF4F5FA51E23340C3F2E49048CE8872526AFDF752C3A7F3A3F2BC9F6049] +Version = 20180401 [UEFI\CSUM_540801DD345DC1C33EF431B35BF4C0E68BD319B577B9ABE1A9CFF1CBC39F548F] Version = 20200701 [UEFI\CSUM_AF79B14064601BC0987D4747AF1E914A228C05D622CEDA03B7A4F67014FEE767] @@ -22,6 +26,10 @@ Version = 20240301 [UEFI\CSUM_CDB7C90D3AB8833D5324F5D8516D41FA990B9CA721FE643FFFAEF9057D9F9E48] Version = 20241101 +[UEFI\CSUM_6B4328EBCBE46ED9118FF2D4472DE329D70BA83016DF7A6F50F8AF923883BC54] +Version = 20250507 +[UEFI\CSUM_E14C88DC48339C0555686849A4E3F8986D558E65C4FC863A1A4F1D40478BD47C] +Version = 20250902 # aa64 [UEFI\CSUM_8C8183AD9B96FE1F3C74DEDB8087469227B642AFE2E80F8FD22E0137C11C7D90] @@ -44,26 +52,88 @@ Version = 20230501 [UEFI\CSUM_E9E4B5A51F6A5575B9F5BFAB1852B0CB2795C66FF4B28135097CBA671A5491B9] Version = 20241101 +[UEFI\CSUM_97F9D7B439E283993DCB15F7778B00EAF0621EE0617BD725495770FB44921A82] +Version = 20250902 # Microsoft Corporation KEK CA 2011 [UEFI\CRT_A1117F516A32CEFCBA3F2D1ACE10A87972FD6BBE8FE0D0B996E09E65D802A503] -VendorId = UEFI:Microsoft +Vendor = Microsoft # Microsoft Windows IoT KEK CA 2015 [UEFI\CRT_CF74CD44C2BB5EF55836290AE926C660F884BF11C342E0934226463380BF2468] -VendorId = UEFI:Microsoft +Vendor = Microsoft # Microsoft Corporation KEK 2K CA 2023 [UEFI\CRT_3CD3F0309EDAE228767A976DD40D9F4AFFC4FBD5218F2E8CC3C9DD97E8AC6F9D] -VendorId = UEFI:Microsoft +Vendor = Microsoft # Microsoft Corporation KEK 3K CA 2023 [UEFI\CRT_56A752AD97ACE87A4BDC375591166C81265D58EE0D731D07D767436505C37BD0] -VendorId = UEFI:Microsoft +Vendor = Microsoft # Microsoft Corporation KEK 4K CA 2023 [UEFI\CRT_2128ED80A17DA1DE2A9C8CC7AADE9D44A80182B15B57A08681DC7E2EFBEC848D] -VendorId = UEFI:Microsoft +Vendor = Microsoft + +# Framework Laptop 12 Intel Core 13th Gen KEK +[UEFI\CRT_1114BC1CFF44E31CF564AD37F33524C3FDE190C6] +Vendor = Framework +# Framework Laptop 12 Intel Core 13th Gen DB +[UEFI\CRT_92F6D304EB1B6A7456441039759036B85B3C9637] +Vendor = Framework + +# Framework Laptop 13 Intel Core 11th Gen KEK +[UEFI\CRT_210C4030B80543635790E4BDA46A00FD27834CDB] +Vendor = Framework +# Framework Laptop 13 Intel Core 11th Gen DB +[UEFI\CRT_19F679938822703CD791AC7856334CA319C82BFD] +Vendor = Framework + +# Framework Laptop 13 Intel Core 12th Gen KEK +# Framework Laptop 13 Intel Core 13th Gen KEK +[UEFI\CRT_DE95199137A93F8550755E024B0E6A748928E075] +Vendor = Framework +# Framework Laptop 13 Intel Core 12th Gen DB +# Framework Laptop 13 Intel Core 13th Gen DB +[UEFI\CRT_601B0AD982DA21E29DF4E3DF3213DF382B2DF359] +Vendor = Framework + +# Framework Laptop 13 Intel Core Ultra Series 1 KEK +[UEFI\CRT_DE95199137A93F8550755E024B0E6A748928E075] +Vendor = Framework +# Framework Laptop 13 Intel Core Ultra Series 1 DB +[UEFI\CRT_601B0AD982DA21E29DF4E3DF3213DF382B2DF359] +Vendor = Framework + +# Framework Laptop 13 AMD Ryzen AI 300 Series KEK +[UEFI\CRT_466E3BB31CA82A0D1579FB7DC35CF8460C80B912] +Vendor = Framework +# Framework Laptop 13 AMD Ryzen AI 300 Series DB +[UEFI\CRT_92F6D304EB1B6A7456441039759036B85B3C9637] +Vendor = Framework + +# Framework Laptop 13 AMD Ryzen 7040 Series KEK +# Framework Laptop 16 AMD Ryzen 7040 Series KEK +[UEFI\CRT_1114BC1CFF44E31CF564AD37F33524C3FDE190C6] +Vendor = Framework +# Framework Laptop 13 AMD Ryzen 7040 Series DB +# Framework Laptop 16 AMD Ryzen 7040 Series DB +[UEFI\CRT_92F6D304EB1B6A7456441039759036B85B3C9637] +Vendor = Framework + +# Framework Laptop 16 AMD Ryzen AI 300 Series KEK +[UEFI\CRT_92F6D304EB1B6A7456441039759036B85B3C9637] +Vendor = Framework + +# Framework Desktop AMD Ryzen AI 300 Series KEK +[UEFI\CRT_1114BC1CFF44E31CF564AD37F33524C3FDE190C6] +Vendor = Framework + +# Manufacturer=AiStone +# BaseboardManufacturer=AiStone +# BaseboardProduct=X5KK4NAG +[916406dd-3efc-5181-956b-8b26e31dce6e] +Flags = no-dbx-updates # Manufacturer=Apple Inc. [80f95c96-a739-5ef5-8482-3d65cb39ff55] @@ -183,6 +253,36 @@ [3f4f41b0-f419-5715-879e-73500cdb3c5d] Flags = no-dbx-updates +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=872C +[883be004-ac69-56bb-9c80-38fc031f3880] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=878C +[4fc65dfe-4dd8-5d24-86f9-3d05e926f379] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=88F9 +[b6eb0c7f-0bfc-5a3f-b2d7-cd2a38aa6d17] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8C6A +[baddb32a-7407-56a8-b1ff-cadefcf2fce7] +Flags = no-dbx-updates + +# Manufacturer=HP +# BaseboardManufacturer=HP +# BaseboardProduct=8B6E +[7121aa03-6ee3-5987-b04a-84aa6866a580] +Flags = no-dbx-updates + # Manufacturer=LENOVO # Family=ideacentre 300-20ISH [fbc17b82-0f29-561d-be80-578285ec4477] @@ -192,3 +292,14 @@ # Family=Galaxy Book2 360 [c4cd9095-8757-593c-8cbd-c7c281c699a7] Flags = no-dbx-updates + +# Manufacturer=CHUWI Innovation And Technology(ShenZhen)co.,Ltd +# ProductName=MiniBook X +[52e98239-4cfc-5a22-bf17-98539e439dd9] +Flags = no-dbx-updates + +# Manufacturer=HUAWEI +# BaseboardManufacturer=HUAWEI +# BaseboardProduct=BOHK-WAX9X-PCB +[a98d46c9-ed25-54da-8510-ef9035cecdd1] +Flags = no-dbx-updates diff -Nru fwupd-2.0.8/plugins/uefi-esrt/fu-uefi-esrt-plugin.c fwupd-2.0.20/plugins/uefi-esrt/fu-uefi-esrt-plugin.c --- fwupd-2.0.8/plugins/uefi-esrt/fu-uefi-esrt-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-esrt/fu-uefi-esrt-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,12 +21,10 @@ static gboolean fu_uefi_esrt_plugin_check_esrt(void) { - g_autofree gchar *sysfsfwdir = NULL; g_autofree gchar *esrtdir = NULL; /* already exists */ - sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - esrtdir = g_build_filename(sysfsfwdir, "efi", "esrt", NULL); + esrtdir = fu_path_build(FU_PATH_KIND_SYSFSDIR_FW, "efi", "esrt", NULL); return g_file_test(esrtdir, G_FILE_TEST_EXISTS); } diff -Nru fwupd-2.0.8/plugins/uefi-esrt/meson.build fwupd-2.0.20/plugins/uefi-esrt/meson.build --- fwupd-2.0.8/plugins/uefi-esrt/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-esrt/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if allow_uefi_capsule +allow_uefi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginUefiEsrt"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -15,5 +16,4 @@ plugin_deps, ], ) -endif diff -Nru fwupd-2.0.8/plugins/uefi-kek/README.md fwupd-2.0.20/plugins/uefi-kek/README.md --- fwupd-2.0.8/plugins/uefi-kek/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-kek/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -47,10 +47,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.8`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Richard Hughes: @hughsie diff -Nru fwupd-2.0.8/plugins/uefi-kek/fu-uefi-kek-device.c fwupd-2.0.20/plugins/uefi-kek/fu-uefi-kek-device.c --- fwupd-2.0.8/plugins/uefi-kek/fu-uefi-kek-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-kek/fu-uefi-kek-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,6 +14,8 @@ G_DEFINE_TYPE(FuUefiKekDevice, fu_uefi_kek_device, FU_TYPE_UEFI_DEVICE) +#define FU_UEFI_KEK_DEVICE_DEFAULT_REQUIRED_FREE (4 * 1024) /* bytes */ + static gboolean fu_uefi_kek_device_probe(FuDevice *device, GError **error) { @@ -27,9 +29,12 @@ return FALSE; /* add each subdevice */ - siglist = fu_device_read_firmware(device, progress, error); + siglist = fu_device_read_firmware(device, + progress, + FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM, + error); if (siglist == NULL) { - g_prefix_error(error, "failed to parse kek: "); + g_prefix_error_literal(error, "failed to parse kek: "); return FALSE; } sigs = fu_efi_signature_list_get_newest(FU_EFI_SIGNATURE_LIST(siglist)); @@ -40,14 +45,14 @@ continue; x509_device = fu_efi_x509_device_new(ctx, FU_EFI_X509_SIGNATURE(sig)); fu_device_set_physical_id(FU_DEVICE(x509_device), "kek"); + fu_device_add_flag(FU_DEVICE(x509_device), FWUPD_DEVICE_FLAG_AFFECTS_FDE); + fu_device_add_flag(FU_DEVICE(x509_device), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); fu_device_set_proxy(FU_DEVICE(x509_device), device); fu_device_add_child(device, FU_DEVICE(x509_device)); } - /* set in the subdevice */ - fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); - fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); - fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY); + if (fu_context_has_flag(fu_device_get_context(device), FU_CONTEXT_FLAG_INSECURE_UEFI)) + fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_INSECURE_PLATFORM); /* success */ return TRUE; @@ -74,10 +79,10 @@ FU_EFIVARS_GUID_EFI_GLOBAL, fu_device_get_physical_id(device), fw, - FU_EFIVARS_ATTR_APPEND_WRITE | - FU_EFIVARS_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | - FU_EFIVARS_ATTR_RUNTIME_ACCESS | FU_EFIVARS_ATTR_BOOTSERVICE_ACCESS | - FU_EFIVARS_ATTR_NON_VOLATILE, + FU_EFI_VARIABLE_ATTR_APPEND_WRITE | + FU_EFI_VARIABLE_ATTR_TIME_BASED_AUTHENTICATED_WRITE_ACCESS | + FU_EFI_VARIABLE_ATTR_RUNTIME_ACCESS | FU_EFI_VARIABLE_ATTR_BOOTSERVICE_ACCESS | + FU_EFI_VARIABLE_ATTR_NON_VOLATILE, error)) { return FALSE; } @@ -87,7 +92,7 @@ } static void -fu_uefi_kek_device_set_progress(FuDevice *self, FuProgress *progress) +fu_uefi_kek_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -103,9 +108,10 @@ { fu_device_set_physical_id(FU_DEVICE(self), "KEK"); fu_device_set_name(FU_DEVICE(self), "UEFI Key Exchange Key"); - fu_device_add_parent_guid(FU_DEVICE(self), "main-system-firmware"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_EFI_SIGNATURE_LIST); - fu_device_add_icon(FU_DEVICE(self), "application-certificate"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_APPLICATION_CERTIFICATE); + fu_device_set_required_free(FU_DEVICE(self), FU_UEFI_KEK_DEVICE_DEFAULT_REQUIRED_FREE); } static void diff -Nru fwupd-2.0.8/plugins/uefi-kek/fu-uefi-kek-plugin.c fwupd-2.0.20/plugins/uefi-kek/fu-uefi-kek-plugin.c --- fwupd-2.0.8/plugins/uefi-kek/fu-uefi-kek-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-kek/fu-uefi-kek-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -24,6 +24,7 @@ fu_uefi_kek_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_rule(plugin, FU_PLUGIN_RULE_METADATA_SOURCE, "uefi_pk"); fu_plugin_add_device_gtype(plugin, FU_TYPE_UEFI_KEK_DEVICE); } diff -Nru fwupd-2.0.8/plugins/uefi-kek/meson.build fwupd-2.0.20/plugins/uefi-kek/meson.build --- fwupd-2.0.8/plugins/uefi-kek/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-kek/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,5 @@ +allow_uefi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginUefiKek"'] plugins += {meson.current_source_dir().split('/')[-1]: true} diff -Nru fwupd-2.0.8/plugins/uefi-mok/README.md fwupd-2.0.20/plugins/uefi-mok/README.md --- fwupd-2.0.8/plugins/uefi-mok/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-mok/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -53,10 +53,3 @@ ## Version Considerations This plugin has been available since fwupd version `2.0.7`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Richard Hughes: @hughsie diff -Nru fwupd-2.0.8/plugins/uefi-mok/fu-uefi-mok-plugin.c fwupd-2.0.20/plugins/uefi-mok/fu-uefi-mok-plugin.c --- fwupd-2.0.8/plugins/uefi-mok/fu-uefi-mok-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-mok/fu-uefi-mok-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,8 +18,7 @@ static gchar * fu_uefi_mok_plugin_get_filename(void) { - g_autofree gchar *sysfsdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW); - return g_build_filename(sysfsdir, "efi", "mok-variables", "HSIStatus", NULL); + return fu_path_build(FU_PATH_KIND_SYSFSDIR_FW, "efi", "mok-variables", "HSIStatus", NULL); } static gboolean diff -Nru fwupd-2.0.8/plugins/uefi-mok/meson.build fwupd-2.0.20/plugins/uefi-mok/meson.build --- fwupd-2.0.8/plugins/uefi-mok/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-mok/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,5 @@ +allow_uefi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginUefiMok"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -45,6 +47,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ cargs, diff -Nru fwupd-2.0.8/plugins/uefi-pk/fu-uefi-pk-device.c fwupd-2.0.20/plugins/uefi-pk/fu-uefi-pk-device.c --- fwupd-2.0.8/plugins/uefi-pk/fu-uefi-pk-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-pk/fu-uefi-pk-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,10 +11,13 @@ struct _FuUefiPkDevice { FuUefiDevice parent_instance; gboolean has_pk_test_key; + gchar *key_id; }; G_DEFINE_TYPE(FuUefiPkDevice, fu_uefi_pk_device, FU_TYPE_UEFI_DEVICE) +#define FU_UEFI_PK_DEVICE_DEFAULT_REQUIRED_FREE (8 * 1024) /* bytes */ + static void fu_uefi_pk_device_to_string(FuDevice *device, guint idt, GString *str) { @@ -45,9 +48,26 @@ return TRUE; } +const gchar * +fu_uefi_pk_device_get_key_id(FuUefiPkDevice *self) +{ + g_return_val_if_fail(FU_IS_UEFI_PK_DEVICE(self), NULL); + return self->key_id; +} + +static void +fu_uefi_pk_device_set_key_id(FuUefiPkDevice *self, const gchar *key_id) +{ + g_free(self->key_id); + self->key_id = g_strdup(key_id); +} + static gboolean fu_uefi_pk_device_parse_certificate(FuUefiPkDevice *self, FuEfiX509Signature *sig, GError **error) { + const gchar *subject_name = fu_efi_x509_signature_get_subject_name(sig); + const gchar *subject_vendor = fu_efi_x509_signature_get_subject_vendor(sig); + /* look in issuer and subject */ if (fu_efi_x509_signature_get_issuer(sig) != NULL) { if (!fu_uefi_pk_device_check(self, fu_efi_x509_signature_get_issuer(sig), error)) @@ -58,21 +78,23 @@ return FALSE; } - /* these have to exist */ - fu_device_add_instance_strsafe(FU_DEVICE(self), - "VENDOR", - fu_efi_x509_signature_get_subject_vendor(sig)); - fu_device_add_instance_strsafe(FU_DEVICE(self), - "NAME", - fu_efi_x509_signature_get_subject_name(sig)); - if (!fu_device_build_instance_id(FU_DEVICE(self), error, "UEFI", "VENDOR", "NAME", NULL)) - return FALSE; - fu_device_set_name(FU_DEVICE(self), fu_efi_x509_signature_get_subject_name(sig)); - fu_device_set_vendor(FU_DEVICE(self), fu_efi_x509_signature_get_subject_vendor(sig)); + if (self->has_pk_test_key) { + fu_context_add_flag(fu_device_get_context(FU_DEVICE(self)), + FU_CONTEXT_FLAG_INSECURE_UEFI); + fu_device_add_problem(FU_DEVICE(self), FWUPD_DEVICE_PROBLEM_INSECURE_PLATFORM); + } + + /* the O= key may not exist */ + fu_device_add_instance_strsafe(FU_DEVICE(self), "VENDOR", subject_vendor); + fu_device_add_instance_strsafe(FU_DEVICE(self), "NAME", subject_name); + fu_device_build_instance_id(FU_DEVICE(self), NULL, "UEFI", "VENDOR", "NAME", NULL); + fu_device_set_name(FU_DEVICE(self), subject_name != NULL ? subject_name : "Unknown"); + fu_device_set_vendor(FU_DEVICE(self), subject_vendor != NULL ? subject_vendor : "Unknown"); fu_device_set_version_raw(FU_DEVICE(self), fu_firmware_get_version_raw(FU_FIRMWARE(sig))); + fu_uefi_pk_device_set_key_id(self, fu_firmware_get_id(FU_FIRMWARE(sig))); /* success, certificate was parsed correctly */ - fu_device_add_instance_strup(FU_DEVICE(self), "CRT", fu_firmware_get_id(FU_FIRMWARE(sig))); + fu_device_add_instance_strup(FU_DEVICE(self), "CRT", self->key_id); return fu_device_build_instance_id(FU_DEVICE(self), error, "UEFI", "CRT", NULL); } @@ -85,9 +107,16 @@ g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); g_autoptr(GPtrArray) sigs = NULL; - pk = fu_device_read_firmware(device, progress, error); + /* FuUefiDevice->probe */ + if (!FU_DEVICE_CLASS(fu_uefi_pk_device_parent_class)->probe(device, error)) + return FALSE; + + pk = fu_device_read_firmware(device, + progress, + FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM, + error); if (pk == NULL) { - g_prefix_error(error, "failed to parse PK: "); + g_prefix_error_literal(error, "failed to parse PK: "); return FALSE; } @@ -144,16 +173,27 @@ { fu_device_set_physical_id(FU_DEVICE(self), "pk"); fu_device_set_summary(FU_DEVICE(self), "UEFI Platform Key"); - fu_device_add_parent_guid(FU_DEVICE(self), "main-system-firmware"); - fu_device_add_icon(FU_DEVICE(self), "application-certificate"); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_APPLICATION_CERTIFICATE); fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_EFI_SIGNATURE_LIST); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER); + fu_device_set_required_free(FU_DEVICE(self), FU_UEFI_PK_DEVICE_DEFAULT_REQUIRED_FREE); +} + +static void +fu_uefi_pk_device_finalize(GObject *object) +{ + FuUefiPkDevice *self = FU_UEFI_PK_DEVICE(object); + g_free(self->key_id); + G_OBJECT_CLASS(fu_uefi_pk_device_parent_class)->finalize(object); } static void fu_uefi_pk_device_class_init(FuUefiPkDeviceClass *klass) { FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + GObjectClass *object_class = G_OBJECT_CLASS(klass); + object_class->finalize = fu_uefi_pk_device_finalize; device_class->to_string = fu_uefi_pk_device_to_string; device_class->add_security_attrs = fu_uefi_pk_device_add_security_attrs; device_class->probe = fu_uefi_pk_device_probe; diff -Nru fwupd-2.0.8/plugins/uefi-pk/fu-uefi-pk-device.h fwupd-2.0.20/plugins/uefi-pk/fu-uefi-pk-device.h --- fwupd-2.0.8/plugins/uefi-pk/fu-uefi-pk-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-pk/fu-uefi-pk-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -10,3 +10,6 @@ #define FU_TYPE_UEFI_PK_DEVICE (fu_uefi_pk_device_get_type()) G_DECLARE_FINAL_TYPE(FuUefiPkDevice, fu_uefi_pk_device, FU, UEFI_PK_DEVICE, FuUefiDevice) + +const gchar * +fu_uefi_pk_device_get_key_id(FuUefiPkDevice *self) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/uefi-pk/fu-uefi-pk-plugin.c fwupd-2.0.20/plugins/uefi-pk/fu-uefi-pk-plugin.c --- fwupd-2.0.8/plugins/uefi-pk/fu-uefi-pk-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-pk/fu-uefi-pk-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -27,9 +27,21 @@ fu_plugin_set_device_gtype_default(plugin, FU_TYPE_UEFI_PK_DEVICE); } +static gboolean +fu_uefi_pk_plugin_device_created(FuPlugin *plugin, FuDevice *device, GError **error) +{ + if (!fu_device_probe(device, error)) + return FALSE; + fu_plugin_add_report_metadata(plugin, + "UefiPkKeyId", + fu_uefi_pk_device_get_key_id(FU_UEFI_PK_DEVICE(device))); + return TRUE; +} + static void fu_uefi_pk_plugin_class_init(FuUefiPkPluginClass *klass) { FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); plugin_class->constructed = fu_uefi_pk_plugin_constructed; + plugin_class->device_created = fu_uefi_pk_plugin_device_created; } diff -Nru fwupd-2.0.8/plugins/uefi-pk/meson.build fwupd-2.0.20/plugins/uefi-pk/meson.build --- fwupd-2.0.8/plugins/uefi-pk/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-pk/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,5 @@ +allow_uefi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginUefiPk"'] plugins += {meson.current_source_dir().split('/')[-1]: true} diff -Nru fwupd-2.0.8/plugins/uefi-pk/tests/uefi-pk.json fwupd-2.0.20/plugins/uefi-pk/tests/uefi-pk.json --- fwupd-2.0.8/plugins/uefi-pk/tests/uefi-pk.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-pk/tests/uefi-pk.json 2026-02-26 11:36:18.000000000 +0000 @@ -7,7 +7,7 @@ "components": [ { "guids": [ - "71599d14-9b31-5270-b3bd-74c494585820" + "6c200e38-b742-59d4-8928-51274abd4b8c" ] } ] diff -Nru fwupd-2.0.8/plugins/uefi-recovery/fu-uefi-recovery-plugin.c fwupd-2.0.20/plugins/uefi-recovery/fu-uefi-recovery-plugin.c --- fwupd-2.0.8/plugins/uefi-recovery/fu-uefi-recovery-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-recovery/fu-uefi-recovery-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -38,7 +38,7 @@ 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"); + fu_device_add_icon(device, FU_DEVICE_ICON_COMPUTER); for (guint i = 0; i < hwids->len; i++) { const gchar *hwid = g_ptr_array_index(hwids, i); fu_device_add_instance_id(device, hwid); diff -Nru fwupd-2.0.8/plugins/uefi-recovery/meson.build fwupd-2.0.20/plugins/uefi-recovery/meson.build --- fwupd-2.0.8/plugins/uefi-recovery/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-recovery/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if allow_uefi_capsule +allow_uefi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginUefiRecovery"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -14,4 +15,3 @@ ], dependencies: plugin_deps, ) -endif diff -Nru fwupd-2.0.8/plugins/uefi-sbat/README.md fwupd-2.0.20/plugins/uefi-sbat/README.md --- fwupd-2.0.8/plugins/uefi-sbat/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-sbat/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -4,7 +4,7 @@ ## Introduction -SBAT is a generation number based revocation mechanism that is used as a successsor to dbx checksum +SBAT is a generation number based revocation mechanism that is used as a successor to dbx checksum blocklisting. See [https://github.com/rhboot/shim/blob/main/SBAT.md](the specification) for more details. @@ -130,10 +130,3 @@ $ sudo fwupdtool firmware-build ../plugins/uefi-sbat/revocation.builder.xml revocation.efi Decompressing… [************ ] SBAT level is too old on /boot/efi/EFI/fedora/grubx64.efi: ESP file /boot/efi/EFI/fedora/shimx64.efi has SBAT entry sbat v1, but revocation has v2 - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Peter Jones: @vathpela diff -Nru fwupd-2.0.8/plugins/uefi-sbat/fu-uefi-sbat-device.c fwupd-2.0.20/plugins/uefi-sbat/fu-uefi-sbat-device.c --- fwupd-2.0.8/plugins/uefi-sbat/fu-uefi-sbat-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-sbat/fu-uefi-sbat-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -57,7 +57,7 @@ fu_uefi_sbat_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuContext *ctx = fu_device_get_context(device); @@ -82,7 +82,7 @@ FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_SECOND_STAGE, error); if (esp_files == NULL) { - g_prefix_error(error, "failed to get files on ESP: "); + g_prefix_error_literal(error, "failed to get files on ESP: "); return NULL; } for (guint i = 0; i < esp_files->len; i++) { @@ -114,7 +114,7 @@ g_autofree gchar *filename_revocation = NULL; g_autofree gchar *fp_name = NULL; g_autofree gchar *mount_point = NULL; - g_autoptr(FuDeviceLocker) volume_locker = NULL; + g_autoptr(FuVolumeLocker) volume_locker = NULL; g_autoptr(FuEfiLoadOption) entry = NULL; g_autoptr(FuFirmware) dp_fp = NULL; g_autoptr(FuFirmware) dp_hdd = NULL; @@ -146,7 +146,7 @@ error); if (volume == NULL) return FALSE; - volume_locker = fu_volume_locker(volume, error); + volume_locker = fu_volume_locker_new(volume, error); if (volume_locker == NULL) return FALSE; mount_point = fu_volume_get_mount_point(volume); @@ -185,7 +185,7 @@ } static void -fu_uefi_sbat_device_set_progress(FuDevice *self, FuProgress *progress) +fu_uefi_sbat_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -202,7 +202,7 @@ fu_device_set_summary(FU_DEVICE(self), "Generation number based revocation mechanism"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); fu_device_add_protocol(FU_DEVICE(self), "com.uefi.sbat"); - fu_device_add_icon(FU_DEVICE(self), "application-certificate"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_APPLICATION_CERTIFICATE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); @@ -231,7 +231,7 @@ g_return_val_if_fail(error == NULL || *error == NULL, NULL); /* copy the version across */ - if (!fu_firmware_parse_bytes(firmware, blob, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_bytes(firmware, blob, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, error)) return NULL; self = g_object_new(FU_TYPE_UEFI_SBAT_DEVICE, "context", ctx, NULL); fu_device_set_version(FU_DEVICE(self), fu_firmware_get_version(firmware)); diff -Nru fwupd-2.0.8/plugins/uefi-sbat/fu-uefi-sbat-firmware.c fwupd-2.0.20/plugins/uefi-sbat/fu-uefi-sbat-firmware.c --- fwupd-2.0.8/plugins/uefi-sbat/fu-uefi-sbat-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-sbat/fu-uefi-sbat-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,7 +17,7 @@ static gboolean fu_uefi_sbat_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { guint semver[] = {0, 0, 0}; @@ -70,7 +70,7 @@ static gboolean fu_uefi_sbat_firmware_check_compatible(FuFirmware *firmware, FuFirmware *other, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) esp_sbat = NULL; diff -Nru fwupd-2.0.8/plugins/uefi-sbat/fu-uefi-sbat-plugin.c fwupd-2.0.20/plugins/uefi-sbat/fu-uefi-sbat-plugin.c --- fwupd-2.0.8/plugins/uefi-sbat/fu-uefi-sbat-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-sbat/fu-uefi-sbat-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -57,7 +57,7 @@ device = fu_uefi_sbat_device_new(ctx, blob, error); if (device == NULL) return FALSE; - locker = fu_device_locker_new(device, error); + locker = fu_device_locker_new(FU_DEVICE(device), error); if (locker == NULL) return FALSE; fu_plugin_device_add(plugin, FU_DEVICE(device)); diff -Nru fwupd-2.0.8/plugins/uefi-sbat/meson.build fwupd-2.0.20/plugins/uefi-sbat/meson.build --- fwupd-2.0.8/plugins/uefi-sbat/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uefi-sbat/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,3 +1,5 @@ +allow_uefi or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginUefiSbat"'] plugins += {meson.current_source_dir().split('/')[-1]: true} diff -Nru fwupd-2.0.8/plugins/uf2/README.md fwupd-2.0.20/plugins/uf2/README.md --- fwupd-2.0.8/plugins/uf2/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uf2/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,7 @@ A UF2 device exposes a VFAT block device which has a virtual file `INFO_UF2.TXT` where metadata can be read from. It may also have a the current firmware exported as a file `CURRENT.UF2` which is in a 512 -byte-block UF2 format. +byte-block [UF2 format](https://github.com/Microsoft/uf2>). Writing any file to the MSD will cause the firmware to be written. Sometimes the device will restart and the volume will be unmounted and then @@ -20,12 +20,9 @@ Match the block devices using the VID, PID and UUID, and then create a UF2 device which can be used to flash firmware. -Note: We only read metadata from allow-listed IDs to avoid causing regressions -on non-UF2 volumes. To get the UUID you can use commands like: - - udisksctl info -b /dev/sda1 - -The UF2 format is specified [here](https://github.com/Microsoft/uf2>). +> [!IMPORTANT] +> We only read metadata from allow-listed IDs to avoid causing regressions on non-UF2 volumes. +> To get the UUID you can use commands like `udisksctl info -b /dev/sda1` ## Firmware Format diff -Nru fwupd-2.0.8/plugins/uf2/fu-self-test.c fwupd-2.0.20/plugins/uf2/fu-self-test.c --- fwupd-2.0.8/plugins/uf2/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uf2/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -19,6 +19,7 @@ g_autofree gchar *xml_src = NULL; g_autoptr(FuFirmware) firmware1 = fu_uf2_firmware_new(); g_autoptr(FuFirmware) firmware2 = fu_uf2_firmware_new(); + g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; /* build and write */ @@ -31,14 +32,19 @@ g_assert_true(ret); csum1 = fu_firmware_get_checksum(firmware1, G_CHECKSUM_SHA1, &error); g_assert_no_error(error); - g_assert_cmpstr(csum1, ==, "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed"); + g_assert_cmpstr(csum1, ==, "4e130c6617496bee0dfbdff48f7248eccb1c696d"); + blob = fu_firmware_write(firmware1, &error); + g_assert_no_error(error); + g_assert_nonnull(blob); /* ensure we can round-trip */ - xml_out = fu_firmware_export_to_xml(firmware1, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); - g_assert_no_error(error); - ret = fu_firmware_build_from_xml(firmware2, xml_out, &error); + ret = fu_firmware_parse_bytes(firmware2, blob, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); + xml_out = fu_firmware_export_to_xml(firmware2, FU_FIRMWARE_EXPORT_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_nonnull(xml_out); + g_debug("%s", xml_out); csum2 = fu_firmware_get_checksum(firmware2, G_CHECKSUM_SHA1, &error); g_assert_cmpstr(csum1, ==, csum2); } @@ -47,6 +53,7 @@ main(int argc, char **argv) { (void)g_setenv("G_TEST_SRCDIR", SRCDIR, FALSE); + (void)g_setenv("FWUPD_VERBOSE", "1", TRUE); g_test_init(&argc, &argv, NULL); /* only critical and error are fatal */ diff -Nru fwupd-2.0.8/plugins/uf2/fu-uf2-device.c fwupd-2.0.20/plugins/uf2/fu-uf2-device.c --- fwupd-2.0.8/plugins/uf2/fu-uf2-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uf2/fu-uf2-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -23,7 +23,7 @@ fu_uf2_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuUf2Device *self = FU_UF2_DEVICE(device); @@ -52,25 +52,25 @@ } static gboolean -fu_uf2_device_probe_current_fw(FuDevice *device, GBytes *fw, GError **error) +fu_uf2_device_probe_current_fw(FuUf2Device *self, GBytes *fw, GError **error) { g_autofree gchar *csum_sha256 = NULL; g_autoptr(FuFirmware) firmware = fu_uf2_firmware_new(); g_autoptr(GBytes) fw_raw = NULL; /* parse to get version */ - if (!fu_firmware_parse_bytes(firmware, fw, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_bytes(firmware, fw, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, error)) return FALSE; if (fu_firmware_get_version(firmware) != NULL) - fu_device_set_version(device, fu_firmware_get_version(firmware)); + fu_device_set_version(FU_DEVICE(self), fu_firmware_get_version(firmware)); /* add instance ID for quirks */ if (fu_firmware_get_idx(firmware) != 0x0) { - fu_device_add_instance_u32(device, + fu_device_add_instance_u32(FU_DEVICE(self), "FAMILY", (guint32)fu_firmware_get_idx(firmware)); } - (void)fu_device_build_instance_id_full(device, + (void)fu_device_build_instance_id_full(FU_DEVICE(self), FU_DEVICE_INSTANCE_FLAG_QUIRKS, NULL, "UF2", @@ -82,8 +82,8 @@ if (fw_raw == NULL) return FALSE; csum_sha256 = g_compute_checksum_for_bytes(G_CHECKSUM_SHA256, fw_raw); - fu_device_add_checksum(device, csum_sha256); - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_checksum(FU_DEVICE(self), csum_sha256); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); /* success */ return TRUE; @@ -144,7 +144,7 @@ fn = fu_uf2_device_get_full_path(self, "CURRENT.UF2", error); if (fn == NULL) return NULL; - return fu_device_get_contents_bytes(device, fn, progress, error); + return fu_device_get_contents_bytes(device, fn, G_MAXUINT32, progress, error); } static FuFirmware * @@ -156,7 +156,7 @@ fw = fu_device_dump_firmware(device, progress, error); if (fw == NULL) return NULL; - if (!fu_firmware_parse_bytes(firmware, fw, 0x0, FWUPD_INSTALL_FLAG_NONE, error)) + if (!fu_firmware_parse_bytes(firmware, fw, 0x0, FU_FIRMWARE_PARSE_FLAG_NONE, error)) return NULL; return g_steal_pointer(&firmware); @@ -298,7 +298,7 @@ fn1 = fu_uf2_device_get_full_path(self, "INFO_UF2.TXT", error); if (fn1 == NULL) return FALSE; - blob_txt = fu_device_get_contents_bytes(device, fn1, NULL, error); + blob_txt = fu_device_get_contents_bytes(device, fn1, G_MAXUINT32, NULL, error); lines = fu_strsplit_bytes(blob_txt, "\n", -1); for (guint i = 0; lines[i] != NULL; i++) { if (g_str_has_prefix(lines[i], "Model: ")) { @@ -313,9 +313,9 @@ fn2 = fu_uf2_device_get_full_path(self, "CURRENT.UF2", error); if (fn2 == NULL) return FALSE; - fw = fu_device_get_contents_bytes(device, fn2, NULL, NULL); + fw = fu_device_get_contents_bytes(device, fn2, G_MAXUINT32, NULL, NULL); if (fw != NULL) { - if (!fu_uf2_device_probe_current_fw(device, fw, error)) + if (!fu_uf2_device_probe_current_fw(self, fw, error)) return FALSE; } else { fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER); @@ -385,7 +385,7 @@ } static void -fu_uf2_device_set_progress(FuDevice *self, FuProgress *progress) +fu_uf2_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); diff -Nru fwupd-2.0.8/plugins/uf2/fu-uf2-firmware.c fwupd-2.0.20/plugins/uf2/fu-uf2-firmware.c --- fwupd-2.0.8/plugins/uf2/fu-uf2-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uf2/fu-uf2-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,18 +15,72 @@ G_DEFINE_TYPE(FuUf2Firmware, fu_uf2_firmware, FU_TYPE_FIRMWARE) -#define FU_UF2_FIRMWARE_BLOCK_FLAG_NONE 0x00000000 -#define FU_UF2_FIRMWARE_BLOCK_FLAG_NOFLASH 0x00000001 -#define FU_UF2_FIRMWARE_BLOCK_FLAG_IS_CONTAINER 0x00001000 -#define FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_FAMILY 0x00002000 -#define FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_MD5 0x00004000 -#define FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_EXTENSION_TAG 0x00008000 - -#define FU_U2F_FIRMWARE_TAG_VERSION 0x9fc7bc /* semver of firmware file (UTF-8) */ -#define FU_U2F_FIRMWARE_TAG_DESCRIPTION 0x650d9d /* description of device (UTF-8) */ -#define FU_U2F_FIRMWARE_TAG_PAGE_SZ 0x0be9f7 /* page size of target device (uint32_t) */ -#define FU_U2F_FIRMWARE_TAG_SHA1 0xb46db0 /* SHA-2 checksum of firmware */ -#define FU_U2F_FIRMWARE_TAG_DEVICE_ID 0xc8a729 /* device type identifier (uint32_t or uint64_t) */ +static gboolean +fu_uf2_firmware_parse_extensions(FuUf2Firmware *self, + const guint8 *buf, + gsize bufsz, + gsize offset, + GError **error) +{ + while (offset < bufsz) { + guint8 sz = 0; + FuUf2FirmwareTag tag = 0; + g_autoptr(FuStructUf2Extension) st_ext = NULL; + + st_ext = fu_struct_uf2_extension_parse(buf, bufsz, offset, error); + if (st_ext == NULL) + return FALSE; + sz = fu_struct_uf2_extension_get_size(st_ext); + if (sz == 0) + break; + if (sz < 4) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "invalid extension tag 0x%x [%s] size 0x%x", + tag, + fu_uf2_firmware_tag_to_string(tag), + (guint)sz); + return FALSE; + } + tag = fu_struct_uf2_extension_get_tag(st_ext); + if (tag == 0) + break; + if (tag == FU_UF2_FIRMWARE_TAG_VERSION) { + g_autofree gchar *str = NULL; + str = fu_memstrsafe(buf, + bufsz, + offset + st_ext->buf->len, + sz - st_ext->buf->len, + error); + if (str == NULL) + return FALSE; + fu_firmware_set_version(FU_FIRMWARE(self), str); + } else if (tag == FU_UF2_FIRMWARE_TAG_DESCRIPTION) { + g_autofree gchar *str = NULL; + str = fu_memstrsafe(buf, + bufsz, + offset + st_ext->buf->len, + sz - st_ext->buf->len, + error); + if (str == NULL) + return FALSE; + fu_firmware_set_id(FU_FIRMWARE(self), str); + } else { + if (g_getenv("FWUPD_FUZZER_RUNNING") == NULL) { + g_warning("unknown tag 0x%06x [%s]", + tag, + fu_uf2_firmware_tag_to_string(tag)); + } + } + + /* next! */ + offset += fu_common_align_up(sz, FU_FIRMWARE_ALIGNMENT_4); + } + + /* success */ + return TRUE; +} static gboolean fu_uf2_firmware_parse_chunk(FuUf2Firmware *self, FuChunk *chk, GByteArray *tmp, GError **error) @@ -35,7 +89,7 @@ const guint8 *buf = fu_chunk_get_data(chk); guint32 flags = 0; guint32 datasz = 0; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructUf2) st = NULL; /* parse */ st = fu_struct_uf2_parse(fu_chunk_get_data(chk), @@ -105,61 +159,12 @@ } } if (flags & FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_EXTENSION_TAG) { - gsize offset = FU_STRUCT_UF2_OFFSET_DATA; - while (offset < bufsz) { - guint8 sz = 0; - guint32 tag = 0; - - /* [SZ][TAG][TAG][TAG][TAG][DATA....] */ - if (!fu_memread_uint8_safe(buf, bufsz, offset, &sz, error)) - return FALSE; - if (sz < 4) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid extension tag size"); - return FALSE; - } - if (!fu_memread_uint32_safe(buf, - bufsz, - offset, - &tag, - G_LITTLE_ENDIAN, - error)) - return FALSE; - tag &= 0xFFFFFF; - if (tag == FU_U2F_FIRMWARE_TAG_VERSION) { - g_autofree gchar *utf8buf = g_malloc0(sz); - if (!fu_memcpy_safe((guint8 *)utf8buf, - sz, - 0x0, /* dst */ - buf, - bufsz, - offset + 0x4, /* src */ - sz - 4, - error)) - return FALSE; - fu_firmware_set_version(FU_FIRMWARE(self), utf8buf); - } else if (tag == FU_U2F_FIRMWARE_TAG_DESCRIPTION) { - g_autofree gchar *utf8buf = g_malloc0(sz); - if (!fu_memcpy_safe((guint8 *)utf8buf, - sz, - 0x0, /* dst */ - buf, - bufsz, - offset + 0x4, /* src */ - sz - 4, - error)) - return FALSE; - fu_firmware_set_id(FU_FIRMWARE(self), utf8buf); - } else { - if (g_getenv("FWUPD_FUZZER_RUNNING") == NULL) - g_warning("unknown tag 0x%06x", tag); - } - - /* next! */ - offset += sz; - } + if (!fu_uf2_firmware_parse_extensions(self, + buf, + bufsz, + datasz + FU_STRUCT_UF2_OFFSET_DATA, + error)) + return FALSE; } /* success */ @@ -169,7 +174,7 @@ static gboolean fu_uf2_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuUf2Firmware *self = FU_UF2_FIRMWARE(firmware); @@ -202,17 +207,51 @@ return TRUE; } +static FuStructUf2Extension * +fu_uf2_firmware_build_utf8_extension(FuUf2FirmwareTag tag, const gchar *str) +{ + g_autoptr(FuStructUf2Extension) st = fu_struct_uf2_extension_new(); + fu_struct_uf2_extension_set_tag(st, tag); + fu_struct_uf2_extension_set_size(st, st->buf->len + strlen(str)); + g_byte_array_append(st->buf, (const guint8 *)str, strlen(str)); + fu_byte_array_align_up(st->buf, FU_FIRMWARE_ALIGNMENT_4, 0x0); + return g_steal_pointer(&st); +} + static GByteArray * fu_uf2_firmware_write_chunk(FuUf2Firmware *self, FuChunk *chk, guint chk_len, GError **error) { + gsize offset_ext = FU_STRUCT_UF2_OFFSET_DATA + fu_chunk_get_data_sz(chk); guint32 addr = fu_firmware_get_addr(FU_FIRMWARE(self)); guint32 flags = FU_UF2_FIRMWARE_BLOCK_FLAG_NONE; - g_autoptr(GByteArray) st = fu_struct_uf2_new(); + g_autoptr(FuStructUf2) st = fu_struct_uf2_new(); + g_autoptr(GPtrArray) extensions = + g_ptr_array_new_with_free_func((GDestroyNotify)fu_struct_uf2_extension_unref); /* optional */ if (fu_firmware_get_idx(FU_FIRMWARE(self)) > 0) flags |= FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_FAMILY; + /* build extensions */ + if (fu_firmware_get_idx(FU_FIRMWARE(self)) == 0x0) { + if (fu_firmware_get_id(FU_FIRMWARE(self)) != NULL) { + g_ptr_array_add(extensions, + fu_uf2_firmware_build_utf8_extension( + FU_UF2_FIRMWARE_TAG_DESCRIPTION, + fu_firmware_get_id(FU_FIRMWARE(self)))); + } + if (fu_firmware_get_version(FU_FIRMWARE(self)) != NULL) { + g_ptr_array_add(extensions, + fu_uf2_firmware_build_utf8_extension( + FU_UF2_FIRMWARE_TAG_VERSION, + fu_firmware_get_version(FU_FIRMWARE(self)))); + } + if (extensions->len > 0) { + g_ptr_array_add(extensions, fu_struct_uf2_extension_new()); + flags |= FU_UF2_FIRMWARE_BLOCK_FLAG_HAS_EXTENSION_TAG; + } + } + /* offset from base address */ addr += fu_chunk_get_idx(chk) * fu_chunk_get_data_sz(chk); @@ -226,8 +265,23 @@ if (!fu_struct_uf2_set_data(st, fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), error)) return NULL; + /* copy in any extensions */ + for (guint i = 0; i < extensions->len; i++) { + FuStructUf2Extension *st_ext = g_ptr_array_index(extensions, i); + if (!fu_memcpy_safe(st->buf->data, + st->buf->len, + offset_ext, + st_ext->buf->data, + st_ext->buf->len, + 0x0, + st_ext->buf->len, + error)) + return NULL; + offset_ext += st_ext->buf->len; + } + /* success */ - return g_steal_pointer(&st); + return g_steal_pointer(&st->buf); } static GByteArray * diff -Nru fwupd-2.0.8/plugins/uf2/fu-uf2.rs fwupd-2.0.20/plugins/uf2/fu-uf2.rs --- fwupd-2.0.8/plugins/uf2/fu-uf2.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uf2/fu-uf2.rs 2026-02-26 11:36:18.000000000 +0000 @@ -1,12 +1,39 @@ // Copyright 2023 Richard Hughes // SPDX-License-Identifier: LGPL-2.1-or-later +#[repr(u32le)] +enum FuUf2FirmwareBlockFlags { + None = 0x00000000, + Noflash = 0x00000001, + IsContainer = 0x00001000, + HasFamily = 0x00002000, + HasMd5 = 0x00004000, + HasExtensionTag = 0x00008000, +} + +#[repr(u24le)] +#[derive(ToString)] +enum FuUf2FirmwareTag { + Version = 0x9FC7BC, // semver of firmware file (UTF-8) + Description = 0x650D9D, // description of device (UTF-8) + PageSz = 0x0BE9F7, // page size of target device (uint32_t) + Sha2 = 0xB46DB0, // checksum of firmware + DeviceId = 0xC8A729, // device type identifier (uint32_t or uint64_t) +} + +#[derive(New, Parse)] +#[repr(C, packed)] +struct FuStructUf2Extension { + size: u8, + tag: FuUf2FirmwareTag, +} + #[derive(New, Parse, Default)] #[repr(C, packed)] struct FuStructUf2 { magic0: u32le == 0x0A324655, magic1: u32le == 0x9E5D5157, - flags: u32le, + flags: FuUf2FirmwareBlockFlags, target_addr: u32le, payload_size: u32le, block_no: u32le, diff -Nru fwupd-2.0.8/plugins/uf2/meson.build fwupd-2.0.20/plugins/uf2/meson.build --- fwupd-2.0.8/plugins/uf2/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uf2/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + cargs = ['-DG_LOG_DOMAIN="FuPluginUf2"'] plugins += {meson.current_source_dir().split('/')[-1]: true} @@ -38,6 +39,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ '-DSRCDIR="' + meson.current_source_dir() + '"', @@ -45,4 +47,3 @@ ) test('uf2-self-test', e, env: env) endif -endif diff -Nru fwupd-2.0.8/plugins/uf2/tests/uf2.builder.xml fwupd-2.0.20/plugins/uf2/tests/uf2.builder.xml --- fwupd-2.0.8/plugins/uf2/tests/uf2.builder.xml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/uf2/tests/uf2.builder.xml 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,6 @@ + test + 1.2.3 0x0100 aGVsbG8gd29ybGQ= diff -Nru fwupd-2.0.8/plugins/upower/fu-upower-plugin.c fwupd-2.0.20/plugins/upower/fu-upower-plugin.c --- fwupd-2.0.8/plugins/upower/fu-upower-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/upower/fu-upower-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,17 +14,6 @@ GDBusProxy *proxy_manager; /* nullable */ }; -typedef enum { - UP_DEVICE_STATE_UNKNOWN, - UP_DEVICE_STATE_CHARGING, - UP_DEVICE_STATE_DISCHARGING, - UP_DEVICE_STATE_EMPTY, - UP_DEVICE_STATE_FULLY_CHARGED, - UP_DEVICE_STATE_PENDING_CHARGE, - UP_DEVICE_STATE_PENDING_DISCHARGE, - UP_DEVICE_STATE_LAST -} UpDeviceState; - G_DEFINE_TYPE(FuUpowerPlugin, fu_upower_plugin, FU_TYPE_PLUGIN) static void @@ -39,7 +28,6 @@ /* check that we "have" a battery */ type_val = g_dbus_proxy_get_cached_property(self->proxy, "Type"); if (type_val == NULL || g_variant_get_uint32(type_val) == 0) { - fu_context_set_power_state(ctx, FU_POWER_STATE_UNKNOWN); fu_context_set_battery_level(ctx, FWUPD_BATTERY_LEVEL_INVALID); return; } @@ -57,35 +45,12 @@ state_val = g_dbus_proxy_get_cached_property(self->proxy, "State"); if (state_val == NULL || g_variant_get_uint32(state_val) == 0) { g_warning("failed to query power state"); - fu_context_set_power_state(ctx, FU_POWER_STATE_UNKNOWN); fu_context_set_battery_level(ctx, FWUPD_BATTERY_LEVEL_INVALID); - return; - } - - /* map from UpDeviceState to FuPowerState */ - switch (g_variant_get_uint32(state_val)) { - case UP_DEVICE_STATE_CHARGING: - case UP_DEVICE_STATE_PENDING_CHARGE: - fu_context_set_power_state(ctx, FU_POWER_STATE_AC_CHARGING); - break; - case UP_DEVICE_STATE_DISCHARGING: - case UP_DEVICE_STATE_PENDING_DISCHARGE: - fu_context_set_power_state(ctx, FU_POWER_STATE_BATTERY_DISCHARGING); - break; - case UP_DEVICE_STATE_EMPTY: - fu_context_set_power_state(ctx, FU_POWER_STATE_BATTERY_EMPTY); - break; - case UP_DEVICE_STATE_FULLY_CHARGED: - fu_context_set_power_state(ctx, FU_POWER_STATE_AC_FULLY_CHARGED); - break; - default: - fu_context_set_power_state(ctx, FU_POWER_STATE_UNKNOWN); - break; } } static void -fu_upower_plugin_rescan_manager(FuPlugin *plugin) +fu_upower_plugin_update_lid(FuPlugin *plugin) { FuUpowerPlugin *self = FU_UPOWER_PLUGIN(plugin); FuContext *ctx = fu_plugin_get_context(plugin); @@ -112,6 +77,31 @@ } static void +fu_upower_plugin_update_battery(FuPlugin *plugin) +{ + FuUpowerPlugin *self = FU_UPOWER_PLUGIN(plugin); + FuContext *ctx = fu_plugin_get_context(plugin); + g_autoptr(GVariant) on_battery = NULL; + + on_battery = g_dbus_proxy_get_cached_property(self->proxy_manager, "OnBattery"); + if (on_battery == NULL) { + fu_context_set_power_state(ctx, FU_POWER_STATE_AC); + return; + } + if (g_variant_get_boolean(on_battery)) + fu_context_set_power_state(ctx, FU_POWER_STATE_BATTERY); + else + fu_context_set_power_state(ctx, FU_POWER_STATE_AC); +} + +static void +fu_upower_plugin_rescan_manager(FuPlugin *plugin) +{ + fu_upower_plugin_update_lid(plugin); + fu_upower_plugin_update_battery(plugin); +} + +static void fu_upower_plugin_proxy_changed_cb(GDBusProxy *proxy, GVariant *changed_properties, GStrv invalidated_properties, @@ -136,7 +126,7 @@ NULL, error); if (self->proxy_manager == NULL) { - g_prefix_error(error, "failed to connect to upower: "); + g_prefix_error_literal(error, "failed to connect to upower: "); return FALSE; } self->proxy = g_dbus_proxy_new_for_bus_sync(G_BUS_TYPE_SYSTEM, @@ -148,7 +138,7 @@ NULL, error); if (self->proxy == NULL) { - g_prefix_error(error, "failed to connect to upower: "); + g_prefix_error_literal(error, "failed to connect to upower: "); return FALSE; } name_owner = g_dbus_proxy_get_name_owner(self->proxy); diff -Nru fwupd-2.0.8/plugins/upower/meson.build fwupd-2.0.20/plugins/upower/meson.build --- fwupd-2.0.8/plugins/upower/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/upower/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + plugins += {meson.current_source_dir().split('/')[-1]: true} cargs = ['-DG_LOG_DOMAIN="FuPluginUpower"'] @@ -11,5 +12,3 @@ c_args: cargs, dependencies: plugin_deps, ) - -endif diff -Nru fwupd-2.0.8/plugins/usi-dock/README.md fwupd-2.0.20/plugins/usi-dock/README.md --- fwupd-2.0.8/plugins/usi-dock/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/usi-dock/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -59,10 +59,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.7.4`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Victor Cheng: @victor-cheng diff -Nru fwupd-2.0.8/plugins/usi-dock/fu-usi-dock-child-device.c fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-child-device.c --- fwupd-2.0.8/plugins/usi-dock/fu-usi-dock-child-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-child-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -43,14 +43,12 @@ fu_usi_dock_child_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { - FuDevice *parent = fu_device_get_parent(device); - if (parent == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no parent"); + FuDevice *parent = fu_device_get_parent(device, error); + if (parent == NULL) return NULL; - } return fu_device_prepare_firmware(parent, stream, progress, flags, error); } @@ -63,11 +61,9 @@ GError **error) { FuUsiDockChildDevice *self = FU_USI_DOCK_CHILD_DEVICE(device); - FuDevice *parent = fu_device_get_parent(device); - if (parent == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no parent"); + FuDevice *parent = fu_device_get_parent(device, error); + if (parent == NULL) return FALSE; - } return fu_usi_dock_mcu_device_write_firmware_with_idx(FU_USI_DOCK_MCU_DEVICE(parent), firmware, self->chip_idx, @@ -80,6 +76,7 @@ fu_usi_dock_child_device_init(FuUsiDockChildDevice *self) { fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PARENT_FOR_OPEN); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); } static void diff -Nru fwupd-2.0.8/plugins/usi-dock/fu-usi-dock-dmc-device.c fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-dmc-device.c --- fwupd-2.0.8/plugins/usi-dock/fu-usi-dock-dmc-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-dmc-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,17 +17,11 @@ static void fu_usi_dock_dmc_device_parent_notify_cb(FuDevice *device, GParamSpec *pspec, gpointer user_data) { - FuDevice *parent = fu_device_get_parent(device); + FuDevice *parent = fu_device_get_parent(device, NULL); if (parent != NULL) { g_autoptr(GError) error = NULL; const gchar *serialnum; - /* slightly odd: the MCU device uses the DMC version number */ - g_info("absorbing DMC version into MCU"); - fu_device_set_version_format(parent, fu_device_get_version_format(device)); - fu_device_set_version(parent, fu_device_get_version(device)); - fu_device_set_serial(parent, fu_device_get_serial(device)); - /* allow matching firmware */ fu_device_add_instance_str(parent, "CID", fu_device_get_name(device)); if (!fu_device_build_instance_id(parent, @@ -79,6 +73,12 @@ } } + /* slightly odd: the MCU device uses the DMC version number */ + g_info("absorbing DMC version into MCU"); + fu_device_set_version_format(parent, fu_device_get_version_format(device)); + fu_device_set_version(parent, fu_device_get_version(device)); + fu_device_set_serial(parent, fu_device_get_serial(device)); + /* use a better device name */ fu_device_set_name(device, "Dock Management Controller Information"); } diff -Nru fwupd-2.0.8/plugins/usi-dock/fu-usi-dock-mcu-device.c fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-mcu-device.c --- fwupd-2.0.8/plugins/usi-dock/fu-usi-dock-mcu-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-mcu-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,7 +10,6 @@ #include "fu-usi-dock-child-device.h" #include "fu-usi-dock-dmc-device.h" #include "fu-usi-dock-mcu-device.h" -#include "fu-usi-dock-struct.h" struct _FuUsiDockMcuDevice { FuHidDevice parent_instance; @@ -31,6 +30,12 @@ #define FU_USI_DOCK_DEVICE_FLAG_VERFMT_HP "verfmt-hp" #define FU_USI_DOCK_DEVICE_FLAG_SET_CHIP_TYPE "set-chip-type" #define FU_USI_DOCK_DEVICE_FLAG_WAITING_FOR_UNPLUG "waiting-for-unplug" +#define FU_USI_DOCK_DEVICE_FLAG_NO_REPLUG "no-replug" + +#define FU_USI_DOCK_DEVICE_PHASE2_DELAY 0x0F +#define FU_USI_DOCK_DEVICE_PHASE2_TIMEOUT 0x5A +#define FU_USI_DOCK_DEVICE_40B0_DEVID "USB\\VID_17EF&PID_30B4&CID_40B0" +#define FU_USI_DOCK_DEVICE_NOREPLUG_MIN_VERSION "10.18" static gboolean fu_usi_dock_mcu_device_tx(FuUsiDockMcuDevice *self, @@ -39,7 +44,7 @@ gsize bufsz, GError **error) { - g_autoptr(GByteArray) st = fu_struct_usi_dock_mcu_cmd_req_new(); + g_autoptr(FuStructUsiDockMcuCmdReq) st = fu_struct_usi_dock_mcu_cmd_req_new(); fu_struct_usi_dock_mcu_cmd_req_set_length(st, 0x3 + bufsz); fu_struct_usi_dock_mcu_cmd_req_set_tag3(st, tag2); @@ -49,14 +54,14 @@ } /* special cases */ - if (st->data[FU_STRUCT_USI_DOCK_MCU_CMD_REQ_OFFSET_BUF + 0] == + if (st->buf->data[FU_STRUCT_USI_DOCK_MCU_CMD_REQ_OFFSET_BUF + 0] == FU_USI_DOCK_MCU_CMD_FW_UPDATE) - st->data[FU_STRUCT_USI_DOCK_MCU_CMD_REQ_OFFSET_BUF + 1] = 0xFF; + st->buf->data[FU_STRUCT_USI_DOCK_MCU_CMD_REQ_OFFSET_BUF + 1] = 0xFF; return fu_hid_device_set_report(FU_HID_DEVICE(self), USB_HID_REPORT_ID2, - st->data, - st->len, + st->buf->data, + st->buf->len, FU_USI_DOCK_MCU_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, error); @@ -70,7 +75,7 @@ GError **error) { guint8 buf[64] = {0}; - g_autoptr(GByteArray) st_rsp = NULL; + g_autoptr(FuStructUsiDockMcuCmdRes) st_rsp = NULL; if (!fu_hid_device_get_report(FU_HID_DEVICE(self), USB_HID_REPORT_ID2, @@ -109,16 +114,30 @@ GError **error) { if (!fu_usi_dock_mcu_device_tx(self, tag2, inbuf, inbufsz, error)) { - g_prefix_error(error, "failed to transmit: "); + g_prefix_error_literal(error, "failed to transmit: "); return FALSE; } if (!fu_usi_dock_mcu_device_rx(self, FU_USI_DOCK_MCU_CMD_ALL, outbuf, outbufsz, error)) { - g_prefix_error(error, "failed to receive: "); + g_prefix_error_literal(error, "failed to receive: "); return FALSE; } return TRUE; } +FuDevice * +fu_usi_dock_mcu_device_find_child(FuUsiDockMcuDevice *self, FuUsiDockFirmwareIdx chip_idx) +{ + GPtrArray *children = fu_device_get_children(FU_DEVICE(self)); + for (guint i = 0; i < children->len; i++) { + FuDevice *device_tmp = g_ptr_array_index(children, i); + if (fu_usi_dock_child_device_get_chip_idx(FU_USI_DOCK_CHILD_DEVICE(device_tmp)) == + chip_idx) { + return g_object_ref(device_tmp); + } + } + return NULL; +} + static gboolean fu_usi_dock_mcu_device_get_status(FuUsiDockMcuDevice *self, GError **error) { @@ -132,7 +151,7 @@ &response, sizeof(response), error)) { - g_prefix_error(error, "failed to send CMD MCU: "); + g_prefix_error_literal(error, "failed to send CMD MCU: "); return FALSE; } if (response == 0x1) { @@ -149,6 +168,46 @@ } static gboolean +fu_usi_dock_mcu_device_send_phase2_parameters(FuUsiDockMcuDevice *self, GError **error) +{ + guint8 inbuf[] = {FU_USI_DOCK_MCU_CMD_SET_PHASE2_DISCONNECT, 0}; + + if (!fu_usi_dock_mcu_device_tx(self, + FU_USI_DOCK_TAG2_CMD_MCU, + inbuf, + sizeof(inbuf), + error)) { + g_prefix_error_literal(error, "failed to transmit set-phase2-no-replug: "); + return FALSE; + } + + inbuf[0] = FU_USI_DOCK_MCU_CMD_SET_PHASE2_DELAY; + inbuf[1] = FU_USI_DOCK_DEVICE_PHASE2_DELAY; + if (!fu_usi_dock_mcu_device_tx(self, + FU_USI_DOCK_TAG2_CMD_MCU, + inbuf, + sizeof(inbuf), + error)) { + g_prefix_error_literal(error, "failed to transmit set-phase2-delay: "); + return FALSE; + } + + inbuf[0] = FU_USI_DOCK_MCU_CMD_SET_PHASE2_TIMEOUT; + inbuf[1] = FU_USI_DOCK_DEVICE_PHASE2_TIMEOUT; + if (!fu_usi_dock_mcu_device_tx(self, + FU_USI_DOCK_TAG2_CMD_MCU, + inbuf, + sizeof(inbuf), + error)) { + g_prefix_error_literal(error, "failed to transmit set-phase2-timeout: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean fu_usi_dock_mcu_device_enumerate_children(FuUsiDockMcuDevice *self, GError **error) { guint8 inbuf[] = {FU_USI_DOCK_MCU_CMD_READ_MCU_VERSIONPAGE, @@ -217,8 +276,8 @@ } if (g_strcmp0(components[i].name, "DMC") == 0) { - if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) || - (val[2] == 0xFF && val[3] == 0xFF && val[4] == 0xFF)) { + if ((val[2] == 0x00 && val[3] == 0x00) || + (val[2] == 0xFF && val[3] == 0xFF)) { g_debug("ignoring %s", components[i].name); continue; } @@ -254,7 +313,7 @@ version = g_strdup_printf("%02x.%02x.%02x", val[1], val[2], val[3]); fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_version(child, version); - fu_device_add_icon(child, "thunderbolt"); + fu_device_add_icon(child, FU_DEVICE_ICON_THUNDERBOLT); fu_device_set_name(child, "Thunderbolt 4 Controller"); } else if (g_strcmp0(components[i].name, "DP5x") == 0) { if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) || @@ -265,7 +324,7 @@ version = g_strdup_printf("%d.%02d.%03d", val[2], val[3], val[4]); fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_version(child, version); - fu_device_add_icon(child, "video-display"); + fu_device_add_icon(child, FU_DEVICE_ICON_VIDEO_DISPLAY); fu_device_set_name(child, "Display Port 5"); } else if (g_strcmp0(components[i].name, "DP6x") == 0) { if ((val[2] == 0x00 && val[3] == 0x00 && val[4] == 0x00) || @@ -285,7 +344,7 @@ fu_device_set_name(child, "Display Port 6"); } fu_device_set_version(child, version); - fu_device_add_icon(child, "video-display"); + fu_device_add_icon(child, FU_DEVICE_ICON_VIDEO_DISPLAY); } else if (g_strcmp0(components[i].name, "USB3") == 0) { if ((val[3] == 0x00 && val[4] == 0x00) || (val[3] == 0xFF && val[4] == 0xFF)) { @@ -328,7 +387,7 @@ version = g_strdup_printf("%u.%u.%u", (guint)(val[2] >> 4), val[3], val[4]); fu_device_set_version_format(child, FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_version(child, version); - fu_device_add_icon(child, "network-wired"); + fu_device_add_icon(child, FU_DEVICE_ICON_NETWORK_WIRED); fu_device_set_name(child, "Ethernet Adapter"); } else if (g_strcmp0(components[i].name, "MCU") == 0) { if ((val[0] == 0x00 && val[1] == 0x00) || @@ -373,21 +432,83 @@ } static gboolean +fu_usi_dock_mcu_device_reset_usb2(FuUsiDockMcuDevice *self, GError **error) +{ + g_autofree gchar *sysfs_path = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuDevice) parent_dev = NULL; + g_autoptr(FuBackend) backend = NULL; + + /* get backend */ + backend = + fu_context_get_backend_by_name(fu_device_get_context(FU_DEVICE(self)), "udev", error); + + if (backend == NULL) + return FALSE; + + sysfs_path = g_path_get_dirname(fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(self))); + + /* create parent USB device */ + parent_dev = fu_backend_create_device(backend, sysfs_path, error); + if (parent_dev == NULL) { + g_prefix_error(error, "create usb2 hub device(%s) failed: ", sysfs_path); + return FALSE; + } + + /* wait for device stabled */ + fu_device_sleep(parent_dev, 10000); + + if (!g_file_test(sysfs_path, G_FILE_TEST_EXISTS)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "parent sysfs path not found: %s", + sysfs_path); + return FALSE; + } + + /* open parent device */ + locker = fu_device_locker_new(parent_dev, error); + if (locker == NULL) + return FALSE; + + /* reset */ + if (!fu_usb_device_reset(FU_USB_DEVICE(parent_dev), error)) { + g_prefix_error_literal(error, "failed to reset parent hub: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean fu_usi_dock_mcu_device_setup(FuDevice *device, GError **error) { FuUsiDockMcuDevice *self = FU_USI_DOCK_MCU_DEVICE(device); + g_autoptr(GError) error_local = NULL; /* FuUsbDevice->setup */ if (!FU_DEVICE_CLASS(fu_usi_dock_mcu_device_parent_class)->setup(device, error)) return FALSE; /* get status and component versions */ - if (!fu_usi_dock_mcu_device_get_status(self, error)) { - g_prefix_error(error, "failed to get status: "); - return FALSE; + if (!fu_usi_dock_mcu_device_get_status(self, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + /* work around FL5801-1Q2 errata by resetting U2 */ + if (!fu_usi_dock_mcu_device_reset_usb2(self, error)) + return FALSE; + if (!fu_usi_dock_mcu_device_get_status(self, error)) + return FALSE; + } else { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to get status: "); + return FALSE; + } } if (!fu_usi_dock_mcu_device_enumerate_children(self, error)) { - g_prefix_error(error, "failed to enumerate children: "); + g_prefix_error_literal(error, "failed to enumerate children: "); return FALSE; } @@ -398,12 +519,12 @@ static gboolean fu_usi_dock_mcu_device_write_chunk(FuUsiDockMcuDevice *self, FuChunk *chk, GError **error) { - g_autoptr(GByteArray) st_req = fu_struct_usi_dock_hid_req_new(); + g_autoptr(FuStructUsiDockHidReq) st_req = fu_struct_usi_dock_hid_req_new(); fu_struct_usi_dock_hid_req_set_length(st_req, fu_chunk_get_data_sz(chk)); fu_struct_usi_dock_hid_req_set_tag3(st_req, FU_USI_DOCK_TAG2_MASS_DATA_SPI); - if (!fu_memcpy_safe(st_req->data, - st_req->len, + if (!fu_memcpy_safe(st_req->buf->data, + st_req->buf->len, FU_STRUCT_USI_DOCK_HID_REQ_OFFSET_BUF, /* dst */ fu_chunk_get_data(chk), fu_chunk_get_data_sz(chk), @@ -413,8 +534,8 @@ return FALSE; if (!fu_hid_device_set_report(FU_HID_DEVICE(self), USB_HID_REPORT_ID2, - st_req->data, - st_req->len, + st_req->buf->data, + st_req->buf->len, FU_USI_DOCK_MCU_DEVICE_TIMEOUT, FU_HID_DEVICE_FLAG_USE_INTERRUPT_TRANSFER, error)) @@ -572,7 +693,7 @@ 30, NULL, error)) { - g_prefix_error(error, "failed to wait for initial: "); + g_prefix_error_literal(error, "failed to wait for initial: "); return FALSE; } fu_progress_step_done(progress); @@ -589,10 +710,10 @@ return FALSE; if (!fu_device_retry(FU_DEVICE(self), fu_usi_dock_mcu_device_wait_for_spi_ready_cb, - 30, + 60, NULL, error)) { - g_prefix_error(error, "failed to wait for erase: "); + g_prefix_error_literal(error, "failed to wait for erase: "); return FALSE; } fu_progress_step_done(progress); @@ -642,7 +763,7 @@ 300, &checksum, error)) { - g_prefix_error(error, "failed to wait for checksum: "); + g_prefix_error_literal(error, "failed to wait for checksum: "); return FALSE; } @@ -656,6 +777,12 @@ } fu_progress_step_done(progress); + /* set parameters for devices with new enough firmware to avoid the manual replug */ + if (fu_device_has_private_flag(FU_DEVICE(self), FU_USI_DOCK_DEVICE_FLAG_NO_REPLUG)) { + if (!fu_usi_dock_mcu_device_send_phase2_parameters(self, error)) + return FALSE; + } + /* internal flash */ cmd = FU_USI_DOCK_MCU_CMD_FW_UPDATE; if (!fu_usi_dock_mcu_device_txrx(self, @@ -668,6 +795,10 @@ return FALSE; fu_progress_step_done(progress); + /* device can self-reboot */ + if (fu_device_has_private_flag(FU_DEVICE(self), FU_USI_DOCK_DEVICE_FLAG_NO_REPLUG)) + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + /* success */ return TRUE; } @@ -711,7 +842,10 @@ static gboolean fu_usi_dock_mcu_device_attach(FuDevice *device, FuProgress *progress, GError **error) { - fu_device_set_remove_delay(device, 900000); + /* device can self-reboot */ + if (fu_device_has_private_flag(device, FU_USI_DOCK_DEVICE_FLAG_NO_REPLUG)) + return TRUE; + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); /* success */ @@ -761,9 +895,12 @@ { g_autoptr(FwupdRequest) request = fwupd_request_new(); + /* device can self-reboot */ + if (fu_device_has_private_flag(device, FU_USI_DOCK_DEVICE_FLAG_NO_REPLUG)) + return TRUE; + /* wait for the user to unplug then start the 40 second timer */ fu_device_add_private_flag(device, FU_USI_DOCK_DEVICE_FLAG_WAITING_FOR_UNPLUG); - fu_device_set_remove_delay(device, 900000); fu_device_add_flag(device, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_BUSY); @@ -779,10 +916,32 @@ { if (fu_device_has_private_flag(donor, FU_USI_DOCK_DEVICE_FLAG_SET_CHIP_TYPE)) fu_device_add_private_flag(device, FU_USI_DOCK_DEVICE_FLAG_SET_CHIP_TYPE); + if (fu_device_has_private_flag(donor, FU_USI_DOCK_DEVICE_FLAG_NO_REPLUG)) + fu_device_add_private_flag(device, FU_USI_DOCK_DEVICE_FLAG_NO_REPLUG); +} + +static gboolean +fu_usi_dock_mcu_device_prepare(FuDevice *device, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + if (fu_device_has_instance_id(device, + FU_USI_DOCK_DEVICE_40B0_DEVID, + FU_DEVICE_INSTANCE_FLAG_VISIBLE) && + fu_version_compare(fu_device_get_version(device), + FU_USI_DOCK_DEVICE_NOREPLUG_MIN_VERSION, + fu_device_get_version_format(device)) >= 0) { + fu_device_add_private_flag(device, FU_USI_DOCK_DEVICE_FLAG_NO_REPLUG); + } else { + fu_device_remove_private_flag(device, FU_USI_DOCK_DEVICE_FLAG_NO_REPLUG); + } + + return TRUE; } static void -fu_usi_dock_mcu_device_set_progress(FuDevice *self, FuProgress *progress) +fu_usi_dock_mcu_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -812,12 +971,13 @@ fu_device_register_private_flag(FU_DEVICE(self), FU_USI_DOCK_DEVICE_FLAG_SET_CHIP_TYPE); fu_device_register_private_flag(FU_DEVICE(self), FU_USI_DOCK_DEVICE_FLAG_WAITING_FOR_UNPLUG); + fu_device_register_private_flag(FU_DEVICE(self), FU_USI_DOCK_DEVICE_FLAG_NO_REPLUG); fu_hid_device_add_flag(FU_HID_DEVICE(self), FU_HID_DEVICE_FLAG_AUTODETECT_EPS); fu_device_add_protocol(FU_DEVICE(self), "com.usi.dock"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_NUMBER); - fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_remove_delay(FU_DEVICE(self), 900000); fu_device_retry_set_delay(FU_DEVICE(self), 1000); - fu_device_add_icon(FU_DEVICE(self), "dock"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_DOCK); } static void @@ -832,4 +992,5 @@ device_class->cleanup = fu_usi_dock_mcu_device_cleanup; device_class->reload = fu_usi_dock_mcu_device_reload; device_class->replace = fu_usi_dock_mcu_device_replace; + device_class->prepare = fu_usi_dock_mcu_device_prepare; } diff -Nru fwupd-2.0.8/plugins/usi-dock/fu-usi-dock-mcu-device.h fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-mcu-device.h --- fwupd-2.0.8/plugins/usi-dock/fu-usi-dock-mcu-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-mcu-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,6 +8,8 @@ #include +#include "fu-usi-dock-struct.h" + #define FU_TYPE_USI_DOCK_MCU_DEVICE (fu_usi_dock_mcu_device_get_type()) G_DECLARE_FINAL_TYPE(FuUsiDockMcuDevice, fu_usi_dock_mcu_device, @@ -21,3 +23,7 @@ FuProgress *progress, FwupdInstallFlags flags, GError **error); + +FuDevice * +fu_usi_dock_mcu_device_find_child(FuUsiDockMcuDevice *self, FuUsiDockFirmwareIdx chip_idx) + G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/plugins/usi-dock/fu-usi-dock-plugin.c fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-plugin.c --- fwupd-2.0.8/plugins/usi-dock/fu-usi-dock-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/usi-dock/fu-usi-dock-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,44 +17,36 @@ struct _FuUsiDockPlugin { FuPlugin parent_instance; FuDevice *device_tbt; + FuDevice *device_usb2; }; G_DEFINE_TYPE(FuUsiDockPlugin, fu_usi_dock_plugin, FU_TYPE_PLUGIN) -static FuDevice * -fu_usi_dock_plugin_find_tbt_device(FuPlugin *plugin, FuDevice *device) -{ - GPtrArray *children = fu_device_get_children(device); - for (guint i = 0; i < children->len; i++) { - FuDevice *device_tmp = g_ptr_array_index(children, i); - if (fu_usi_dock_child_device_get_chip_idx(FU_USI_DOCK_CHILD_DEVICE(device_tmp)) == - FU_USI_DOCK_FIRMWARE_IDX_TBT4) { - return g_object_ref(device_tmp); - } - } - return NULL; -} - -static FuDevice * +static FuUsiDockMcuDevice * fu_usi_dock_plugin_find_mcu_device(FuPlugin *plugin) { GPtrArray *devices = fu_plugin_get_devices(plugin); for (guint i = 0; i < devices->len; i++) { FuDevice *device_tmp = g_ptr_array_index(devices, i); if (FU_IS_USI_DOCK_MCU_DEVICE(device_tmp)) - return fu_usi_dock_plugin_find_tbt_device(plugin, device_tmp); + return FU_USI_DOCK_MCU_DEVICE(g_object_ref(device_tmp)); } return NULL; } static void -fu_usi_dock_plugin_ensure_equivalent_id(FuPlugin *plugin) +fu_usi_dock_plugin_ensure_tbt4(FuPlugin *plugin) { FuUsiDockPlugin *self = FU_USI_DOCK_PLUGIN(plugin); g_autoptr(FuDevice) device_usi = NULL; + g_autoptr(FuUsiDockMcuDevice) device_mcu = NULL; + if (self->device_tbt == NULL) return; - device_usi = fu_usi_dock_plugin_find_mcu_device(plugin); + device_mcu = fu_usi_dock_plugin_find_mcu_device(plugin); + if (device_mcu == NULL) + return; + device_usi = fu_usi_dock_mcu_device_find_child(device_mcu, FU_USI_DOCK_FIRMWARE_IDX_TBT4); if (device_usi == NULL) return; fu_device_set_priority(device_usi, fu_device_get_priority(self->device_tbt) + 1); @@ -63,9 +55,28 @@ } static void +fu_usi_dock_plugin_ensure_usb2(FuPlugin *plugin) +{ + FuUsiDockPlugin *self = FU_USI_DOCK_PLUGIN(plugin); + g_autoptr(FuDevice) device_usi = NULL; + g_autoptr(FuUsiDockMcuDevice) device_mcu = NULL; + + if (self->device_usb2 == NULL) + return; + device_mcu = fu_usi_dock_plugin_find_mcu_device(plugin); + if (device_mcu == NULL) + return; + device_usi = fu_usi_dock_mcu_device_find_child(device_mcu, FU_USI_DOCK_FIRMWARE_IDX_USB2); + if (device_usi == NULL) + return; + fu_device_set_proxy(device_usi, self->device_usb2); +} + +static void fu_usi_dock_plugin_device_added(FuPlugin *plugin, FuDevice *device) { - fu_usi_dock_plugin_ensure_equivalent_id(plugin); + fu_usi_dock_plugin_ensure_tbt4(plugin); + fu_usi_dock_plugin_ensure_usb2(plugin); } static void @@ -77,19 +88,27 @@ if (g_strcmp0(fu_device_get_plugin(device), "thunderbolt") == 0 && fu_device_has_guid(device, USI_DOCK_TBT_INSTANCE_ID)) { g_set_object(&self->device_tbt, device); - fu_usi_dock_plugin_ensure_equivalent_id(plugin); + fu_usi_dock_plugin_ensure_tbt4(plugin); + } + + /* we may need to reset this manually */ + if (fu_device_get_vid(device) == 0x17EF && fu_device_get_pid(device) == 0x30BA) { + g_set_object(&self->device_usb2, device); + fu_usi_dock_plugin_ensure_usb2(plugin); } } static void fu_usi_dock_plugin_init(FuUsiDockPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void fu_usi_dock_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_USI_DOCK_MCU_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_USI_DOCK_DMC_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_USI_DOCK_CHILD_DEVICE); /* coverage */ @@ -100,6 +119,7 @@ { FuUsiDockPlugin *self = FU_USI_DOCK_PLUGIN(obj); g_clear_object(&self->device_tbt); + g_clear_object(&self->device_usb2); G_OBJECT_CLASS(fu_usi_dock_plugin_parent_class)->finalize(obj); } diff -Nru fwupd-2.0.8/plugins/usi-dock/fu-usi-dock.rs fwupd-2.0.20/plugins/usi-dock/fu-usi-dock.rs --- fwupd-2.0.8/plugins/usi-dock/fu-usi-dock.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/usi-dock/fu-usi-dock.rs 2026-02-26 11:36:18.000000000 +0000 @@ -56,6 +56,10 @@ FwUpdate = 0x0B, FwTargetChecksum = 0x0C, FwIspEnd = 0x0D, + SetPhase2Disconnect = 0x12, + SetPhase2Delay = 0x13, + SetPhase2Timeout = 0x14, + GetPhase2Parameter = 0x15, All = 0xFF, } diff -Nru fwupd-2.0.8/plugins/usi-dock/usi-dock.quirk fwupd-2.0.20/plugins/usi-dock/usi-dock.quirk --- fwupd-2.0.8/plugins/usi-dock/usi-dock.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/usi-dock/usi-dock.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -27,3 +27,8 @@ GType = FuUsiDockMcuDevice Name = USB-C G2 Multiport Hub MCU Controller Flags = verfmt-hp + +[USB\VID_17EF&PID_30BA] +Name = Parent U2 hub +Plugin = parade_usbhub +Flags = ~updatable diff -Nru fwupd-2.0.8/plugins/vbe/README.md fwupd-2.0.20/plugins/vbe/README.md --- fwupd-2.0.8/plugins/vbe/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vbe/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -211,7 +211,7 @@ * [VBE](https://docs.google.com/document/d/e/2PACX-1vQjXLPWMIyVktaTMf8edHZYDrEvMYD_iNzIj1FgPmKF37fpglAC47Tt5cvPBC5fvTdoK-GA5Zv1wifo/pub) * [VBE Bootflows](https://docs.google.com/document/d/e/2PACX-1vR0OzhuyRJQ8kdeOibS3xB1rVFy3J4M_QKTM5-3vPIBNcdvR0W8EXu9ymG-yWfqthzWoM4JUNhqwydN/pub) * [VBE Firmware update](https://docs.google.com/document/d/e/2PACX-1vTnlIL17vVbl6TVoTHWYMED0bme7oHHNk-g5VGxblbPiKIdGDALE1HKId8Go5f0g1eziLsv4h9bocbk/pub) -* [FIT](https://github.com/u-boot/u-boot/blob/master/doc/uImage.FIT/source_file_format.txt) +* [FIT](https://fitspec.osfw.foundation/) * [U-Boot Standard boot](https://u-boot.readthedocs.io/en/latest/develop/bootstd.html) ## Useful information @@ -269,10 +269,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.8.2`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Simon Glass: @sjg20 diff -Nru fwupd-2.0.8/plugins/vbe/fu-vbe-device.c fwupd-2.0.20/plugins/vbe/fu-vbe-device.c --- fwupd-2.0.8/plugins/vbe/fu-vbe-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vbe/fu-vbe-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -63,7 +63,7 @@ fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE); fu_device_set_physical_id(FU_DEVICE(self), "vbe"); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); - fu_device_add_icon(FU_DEVICE(self), "computer"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_COMPUTER); } static gboolean diff -Nru fwupd-2.0.8/plugins/vbe/fu-vbe-plugin.c fwupd-2.0.20/plugins/vbe/fu-vbe-plugin.c --- fwupd-2.0.8/plugins/vbe/fu-vbe-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vbe/fu-vbe-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -33,7 +33,7 @@ FU_FIT_FIRMWARE_ATTR_COMPATIBLE, &compatible, error)) { - g_prefix_error(error, "missing update mechanism: "); + g_prefix_error_literal(error, "missing update mechanism: "); return FALSE; } split = g_strsplit(compatible, ",", 2); @@ -113,10 +113,10 @@ /* nothing found? */ if (fu_plugin_get_devices(plugin)->len == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no valid VBE update mechanism found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no valid VBE update mechanism found"); return FALSE; } diff -Nru fwupd-2.0.8/plugins/vbe/fu-vbe-simple-device.c fwupd-2.0.20/plugins/vbe/fu-vbe-simple-device.c --- fwupd-2.0.8/plugins/vbe/fu-vbe-simple-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vbe/fu-vbe-simple-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -138,7 +138,7 @@ FWUPD_ERROR_NOT_SUPPORTED, "cannot open %s [%s]", self->devname, - g_strerror(errno)); + fwupd_strerror(errno)); #else g_set_error(error, FWUPD_ERROR, @@ -216,7 +216,7 @@ fu_vbe_simple_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuVbeSimpleDevice *self = FU_VBE_SIMPLE_DEVICE(device); @@ -248,7 +248,8 @@ fu_fdt_firmware_get_image_by_path(FU_FDT_FIRMWARE(firmware), path, error); if (img_firmware == NULL) return NULL; - fu_firmware_add_image(firmware_container, FU_FIRMWARE(img_firmware)); + if (!fu_firmware_add_image(firmware_container, FU_FIRMWARE(img_firmware), error)) + return NULL; } /* success: return the container */ @@ -301,7 +302,7 @@ /* seek to correct address */ seek_to = self->area_start + store_offset + self->skip_offset; - g_debug("writing image '%s' bufsz 0x%x (skipping 0x%x) to store_offset 0x%x, seek 0x%x\n", + g_debug("writing image '%s' bufsz 0x%x (skipping 0x%x) to store_offset 0x%x, seek 0x%x", fu_firmware_get_id(FU_FIRMWARE(img)), (guint)bufsz, (guint)self->skip_offset, @@ -316,7 +317,7 @@ "cannot seek file '%s' to 0x%x [%s]", self->devname, (guint)seek_to, - g_strerror(errno)); + fwupd_strerror(errno)); #else g_set_error(error, FWUPD_ERROR, @@ -337,7 +338,7 @@ FWUPD_ERROR_WRITE, "cannot write file '%s' [%s]", self->devname, - g_strerror(errno)); + fwupd_strerror(errno)); #else g_set_error(error, FWUPD_ERROR, @@ -403,7 +404,7 @@ "cannot seek file %s to 0x%x [%s]", self->devname, (guint)self->area_start, - g_strerror(errno)); + fwupd_strerror(errno)); #else g_set_error(error, FWUPD_ERROR, @@ -441,7 +442,7 @@ } static void -fu_vbe_simple_device_set_progress(FuDevice *self, FuProgress *progress) +fu_vbe_simple_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/vendor-example/fu-vendor-example-device.c.in fwupd-2.0.20/plugins/vendor-example/fu-vendor-example-device.c.in --- fwupd-2.0.8/plugins/vendor-example/fu-vendor-example-device.c.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vendor-example/fu-vendor-example-device.c.in 2026-02-26 11:36:18.000000000 +0000 @@ -209,7 +209,7 @@ 5000, /* ms */ FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to send packet: "); + g_prefix_error_literal(error, "failed to send packet: "); return FALSE; } if (!fu_hid_device_get_report(FU_HID_DEVICE(self), @@ -219,7 +219,7 @@ 5000, /* ms */ FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to receive packet: "); + g_prefix_error_literal(error, "failed to receive packet: "); return FALSE; } {%- endif %} diff -Nru fwupd-2.0.8/plugins/vendor-example/fu-vendor-example-firmware.c.in fwupd-2.0.20/plugins/vendor-example/fu-vendor-example-firmware.c.in --- fwupd-2.0.8/plugins/vendor-example/fu-vendor-example-firmware.c.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vendor-example/fu-vendor-example-firmware.c.in 2026-02-26 11:36:18.000000000 +0000 @@ -55,8 +55,7 @@ static gboolean fu_{{vendor_example}}_firmware_parse(FuFirmware *firmware, - GInputStream *stream, - FwupdInstallFlags flags, + GInputStream *stream, FuFirmwareParseFlags flags, GError **error) { Fu{{VendorExample}}Firmware *self = FU_{{VENDOR_EXAMPLE}}_FIRMWARE(firmware); diff -Nru fwupd-2.0.8/plugins/vendor-example/fu-vendor-example-plugin.c.in fwupd-2.0.20/plugins/vendor-example/fu-vendor-example-plugin.c.in --- fwupd-2.0.8/plugins/vendor-example/fu-vendor-example-plugin.c.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vendor-example/fu-vendor-example-plugin.c.in 2026-02-26 11:36:18.000000000 +0000 @@ -19,6 +19,7 @@ static void fu_{{vendor_example}}_plugin_init(Fu{{VendorExample}}Plugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void @@ -27,6 +28,11 @@ FuPlugin *plugin = FU_PLUGIN(obj); FuContext *ctx = fu_plugin_get_context(plugin); fu_context_add_quirk_key(ctx, "{{VendorExample}}StartAddr"); +{%- if Parent == 'Usb' %} + fu_plugin_add_udev_subsystem(plugin, "usb"); +{%- elif Parent == 'Hid' %} + fu_plugin_add_udev_subsystem(plugin, "hidraw"); +{%- endif %} fu_plugin_add_device_gtype(plugin, FU_TYPE_{{VENDOR_EXAMPLE}}_DEVICE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_{{VENDOR_EXAMPLE}}_FIRMWARE); } diff -Nru fwupd-2.0.8/plugins/vendor-example/meson.build.in fwupd-2.0.20/plugins/vendor-example/meson.build.in --- fwupd-2.0.8/plugins/vendor-example/meson.build.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vendor-example/meson.build.in 2026-02-26 11:36:18.000000000 +0000 @@ -1,5 +1,5 @@ {%- if Parent == 'Udev' -%} -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() {%- endif %} cargs = ['-DG_LOG_DOMAIN="FuPlugin{{VendorExample}}"'] @@ -23,6 +23,3 @@ 'tests/vendor-example.json', 'tests/vendor-example-setup.json', ) -{%- if Parent == 'Udev' %} -endif -{%- endif %} diff -Nru fwupd-2.0.8/plugins/vendor-example/tests/vendor-example.json fwupd-2.0.20/plugins/vendor-example/tests/vendor-example.json --- fwupd-2.0.8/plugins/vendor-example/tests/vendor-example.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vendor-example/tests/vendor-example.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,9 +3,9 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/{{vendor}}-{{example}}-1.2.3.cab", + "url": "{{vendor}}-{{example}}-1.2.3.cab", "emulation-file": "@enumeration_datadir@/{{vendor}}-{{example}}-setup.json", - "emulation-url": "https://fwupd.org/downloads/{{vendor}}-{{example}}-1.2.3.zip", + "emulation-url": "{{vendor}}-{{example}}-1.2.3.zip", "components": [ { "version": "1.2.3", @@ -16,8 +16,8 @@ ] }, { - "url": "https://fwupd.org/downloads/{{vendor}}-{{example}}-1.2.4.cab", - "emulation-url": "https://fwupd.org/downloads/{{vendor}}-{{example}}-1.2.4.zip", + "url": "{{vendor}}-{{example}}-1.2.4.cab", + "emulation-url": "{{vendor}}-{{example}}-1.2.4.zip", "components": [ { "version": "1.2.4", diff -Nru fwupd-2.0.8/plugins/vli/README.md fwupd-2.0.20/plugins/vli/README.md --- fwupd-2.0.8/plugins/vli/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -132,10 +132,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.3.3`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Emily Miller: @memily diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-device.c fwupd-2.0.20/plugins/vli/fu-vli-device.c --- fwupd-2.0.8/plugins/vli/fu-vli-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -37,7 +37,7 @@ FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self); if (klass->spi_write_enable != NULL) { if (!klass->spi_write_enable(self, error)) { - g_prefix_error(error, "failed to write enable SPI: "); + g_prefix_error_literal(error, "failed to write enable SPI: "); return FALSE; } } @@ -50,7 +50,7 @@ FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self); if (klass->spi_chip_erase != NULL) { if (!klass->spi_chip_erase(self, error)) { - g_prefix_error(error, "failed to erase SPI data: "); + g_prefix_error_literal(error, "failed to erase SPI data: "); return FALSE; } } @@ -76,7 +76,7 @@ FuVliDeviceClass *klass = FU_VLI_DEVICE_GET_CLASS(self); if (klass->spi_read_status != NULL) { if (!klass->spi_read_status(self, status, error)) { - g_prefix_error(error, "failed to read status: "); + g_prefix_error_literal(error, "failed to read status: "); return FALSE; } } @@ -150,7 +150,7 @@ } fu_device_sleep(FU_DEVICE(self), 500); /* ms */ } - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT, "failed to wait for SPI"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT, "failed to wait for SPI"); return FALSE; } @@ -161,23 +161,23 @@ /* erase sector */ if (!fu_vli_device_spi_write_enable(self, error)) { - g_prefix_error(error, "->spi_write_enable failed: "); + g_prefix_error_literal(error, "->spi_write_enable failed: "); return FALSE; } if (!fu_vli_device_spi_write_status(self, 0x00, error)) { - g_prefix_error(error, "->spi_write_status failed: "); + g_prefix_error_literal(error, "->spi_write_status failed: "); return FALSE; } if (!fu_vli_device_spi_write_enable(self, error)) { - g_prefix_error(error, "->spi_write_enable failed: "); + g_prefix_error_literal(error, "->spi_write_enable failed: "); return FALSE; } if (!fu_vli_device_spi_sector_erase(self, addr, error)) { - g_prefix_error(error, "->spi_sector_erase failed: "); + g_prefix_error_literal(error, "->spi_sector_erase failed: "); return FALSE; } if (!fu_vli_device_spi_wait_finish(self, error)) { - g_prefix_error(error, "->spi_wait_finish failed: "); + g_prefix_error_literal(error, "->spi_wait_finish failed: "); return FALSE; } @@ -185,7 +185,7 @@ for (guint32 offset = 0; offset < bufsz; offset += FU_VLI_DEVICE_TXSIZE) { guint8 buf[FU_VLI_DEVICE_TXSIZE] = {0x0}; if (!fu_vli_device_spi_read_block(self, addr + offset, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read back empty: "); + g_prefix_error_literal(error, "failed to read back empty: "); return FALSE; } for (guint i = 0; i < sizeof(buf); i++) { @@ -258,18 +258,18 @@ /* write */ g_debug("writing 0x%x block @0x%x", (guint)bufsz, address); if (!fu_vli_device_spi_write_enable(self, error)) { - g_prefix_error(error, "enabling SPI write failed: "); + g_prefix_error_literal(error, "enabling SPI write failed: "); return FALSE; } if (!fu_vli_device_spi_write_data(self, address, buf, bufsz, error)) { - g_prefix_error(error, "SPI data write failed: "); + g_prefix_error_literal(error, "SPI data write failed: "); return FALSE; } fu_device_sleep(FU_DEVICE(self), 1); /* ms */ /* verify */ if (!fu_vli_device_spi_read_block(self, address, buf_tmp, bufsz, error)) { - g_prefix_error(error, "SPI data read failed: "); + g_prefix_error_literal(error, "SPI data read failed: "); return FALSE; } return fu_memcmp_safe(buf, bufsz, 0, buf_tmp, bufsz, 0, bufsz, error); @@ -324,7 +324,7 @@ fu_chunk_get_data_sz(chk), fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write CRC block: "); + g_prefix_error_literal(error, "failed to write CRC block: "); return FALSE; } fu_progress_step_done(progress); @@ -362,7 +362,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_READ, - "failed to verify erase @0x%x: ", + "failed to verify erase @0x%x", addr); return FALSE; } @@ -557,7 +557,7 @@ FU_VLI_DEVICE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to read chip ID: "); + g_prefix_error_literal(error, "failed to read chip ID: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, "SpiCmdReadId", buf, sizeof(buf)); @@ -596,7 +596,7 @@ /* get the flash chip attached */ if (priv->spi_auto_detect) { if (!fu_vli_device_spi_read_flash_id(self, error)) { - g_prefix_error(error, "failed to read SPI chip ID: "); + g_prefix_error_literal(error, "failed to read SPI chip ID: "); return FALSE; } if (priv->flash_id != 0x0) { diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-pd-common.h fwupd-2.0.20/plugins/vli/fu-vli-pd-common.h --- fwupd-2.0.8/plugins/vli/fu-vli-pd-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-pd-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -14,24 +14,5 @@ #define VLI_USBHUB_PD_FLASHMAP_ADDR_LEGACY 0x4000 #define VLI_USBHUB_PD_FLASHMAP_ADDR 0x1003 -#define FU_VLI_DEVICE_FW_TAG_VL100A 0x01 -#define FU_VLI_DEVICE_FW_TAG_VL100B 0x02 -#define FU_VLI_DEVICE_FW_TAG_VL100C 0x03 -#define FU_VLI_DEVICE_FW_TAG_VL101A 0x04 -#define FU_VLI_DEVICE_FW_TAG_VL101B 0x05 -#define FU_VLI_DEVICE_FW_TAG_VL101C 0x06 -#define FU_VLI_DEVICE_FW_TAG_VL102A 0x07 -#define FU_VLI_DEVICE_FW_TAG_VL102B 0x08 -#define FU_VLI_DEVICE_FW_TAG_VL103A 0x09 -#define FU_VLI_DEVICE_FW_TAG_VL103B 0x0A -#define FU_VLI_DEVICE_FW_TAG_VL104 0x0B -#define FU_VLI_DEVICE_FW_TAG_VL105 0x0C -#define FU_VLI_DEVICE_FW_TAG_VL106 0x0D -#define FU_VLI_DEVICE_FW_TAG_VL107 0x0E -#define FU_VLI_DEVICE_FW_TAG_VL108A 0xA1 -#define FU_VLI_DEVICE_FW_TAG_VL108B 0xB1 -#define FU_VLI_DEVICE_FW_TAG_VL109A 0xA2 -#define FU_VLI_DEVICE_FW_TAG_VL109B 0xB2 - FuVliDeviceKind fu_vli_pd_common_guess_device_kind(guint32 fwver); diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-pd-device.c fwupd-2.0.20/plugins/vli/fu-vli-pd-device.c --- fwupd-2.0.8/plugins/vli/fu-vli-pd-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-pd-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -33,7 +33,7 @@ FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - 0xe0, + FU_VLI_PD_DEVICE_CMD_ACCESS_REGISTER, ((addr & 0xff) << 8) | 0x01, addr >> 8, buf, @@ -64,7 +64,7 @@ FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - 0xe0, + FU_VLI_PD_DEVICE_CMD_ACCESS_REGISTER, ((addr & 0xff) << 8) | 0x02, addr >> 8, &value, @@ -92,7 +92,7 @@ FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - 0xc5, + FU_VLI_PD_DEVICE_SPI_CMD_READ_STATUS, spi_cmd, 0x0000, status, @@ -124,7 +124,7 @@ FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - 0xc4, + FU_VLI_PD_DEVICE_SPI_CMD_READ_DATA, value, index, buf, @@ -150,7 +150,7 @@ FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - 0xd8, + FU_VLI_PD_DEVICE_SPI_CMD_WRITE_STATUS, value, 0x0, NULL, @@ -180,7 +180,7 @@ FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - 0xd4, + FU_VLI_PD_DEVICE_SPI_CMD_WRITE_ENABLE, spi_cmd, 0x0000, NULL, @@ -207,7 +207,7 @@ FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - 0xd1, + FU_VLI_PD_DEVICE_SPI_CMD_CHIP_ERASE, spi_cmd, 0x0000, NULL, @@ -238,7 +238,7 @@ FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - 0xd2, + FU_VLI_PD_DEVICE_SPI_CMD_SECTOR_ERASE, value, index, NULL, @@ -276,7 +276,7 @@ FU_USB_DIRECTION_HOST_TO_DEVICE, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - 0xdc, + FU_VLI_PD_DEVICE_SPI_CMD_WRITE_DATA, value, index, buf_mut, @@ -307,7 +307,7 @@ return TRUE; } if (!fu_device_setup(dev, error)) { - g_prefix_error(error, "failed to set up parade device: "); + g_prefix_error_literal(error, "failed to set up parade device: "); return FALSE; } fu_device_add_child(FU_DEVICE(self), dev); @@ -332,7 +332,7 @@ FU_USB_DIRECTION_DEVICE_TO_HOST, FU_USB_REQUEST_TYPE_VENDOR, FU_USB_RECIPIENT_DEVICE, - 0xe2, + FU_VLI_PD_DEVICE_CMD_GET_FW_VERSION, 0x0001, 0x0000, verbuf, @@ -341,7 +341,7 @@ 1000, NULL, error)) { - g_prefix_error(error, "failed to get version: "); + g_prefix_error_literal(error, "failed to get version: "); return FALSE; } if (!fu_memread_uint32_safe(verbuf, sizeof(verbuf), 0x0, &version_raw, G_BIG_ENDIAN, error)) @@ -445,7 +445,7 @@ fu_vli_pd_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuVliPdDevice *self = FU_VLI_PD_DEVICE(device); @@ -560,7 +560,7 @@ return FALSE; } if (!fu_memread_uint16_safe(sbuf, sbufsz, sbufsz - 2, &crc_file, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to read file CRC: "); + g_prefix_error_literal(error, "failed to read file CRC: "); return FALSE; } crc_actual = fu_crc16(FU_CRC_KIND_B16_USB, sbuf, sbufsz - 2); @@ -658,7 +658,7 @@ if (!fu_vli_device_spi_erase_all(FU_VLI_DEVICE(self), fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to erase all: "); + g_prefix_error_literal(error, "failed to erase all: "); return FALSE; } fu_progress_step_done(progress); @@ -731,6 +731,7 @@ NULL, &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL) || + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_READ) || g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { g_debug("ignoring %s", error_local->message); } else { @@ -877,7 +878,7 @@ } static void -fu_vli_pd_device_set_progress(FuDevice *self, FuProgress *progress) +fu_vli_pd_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -896,7 +897,7 @@ static void fu_vli_pd_device_init(FuVliPdDevice *self) { - fu_device_add_icon(FU_DEVICE(self), "usb-hub"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_USB_HUB); fu_device_add_protocol(FU_DEVICE(self), "com.vli.pd"); fu_device_set_summary(FU_DEVICE(self), "USB power distribution device"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-pd-device.h fwupd-2.0.20/plugins/vli/fu-vli-pd-device.h --- fwupd-2.0.8/plugins/vli/fu-vli-pd-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-pd-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -12,8 +12,3 @@ #define FU_TYPE_VLI_PD_DEVICE (fu_vli_pd_device_get_type()) G_DECLARE_FINAL_TYPE(FuVliPdDevice, fu_vli_pd_device, FU, VLI_PD_DEVICE, FuVliDevice) - -#define FU_VLI_PD_REGISTER_ADDRESS_PROJ_ID_HIGH 0X009C -#define FU_VLI_PD_REGISTER_ADDRESS_PROJ_ID_LOW 0X009D -#define FU_VLI_PD_REGISTER_ADDRESS_PROJ_LEGACY 0X0018 -#define FU_VLI_PD_REGISTER_ADDRESS_GPIO_CONTROL_A 0X0003 diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-pd-firmware.c fwupd-2.0.20/plugins/vli/fu-vli-pd-firmware.c --- fwupd-2.0.8/plugins/vli/fu-vli-pd-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-pd-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,7 +12,7 @@ #include "fu-vli-struct.h" struct _FuVliPdFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; FuVliDeviceKind device_kind; }; @@ -37,18 +37,18 @@ static gboolean fu_vli_pd_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuVliPdFirmware *self = FU_VLI_PD_FIRMWARE(firmware); gsize streamsz = 0; guint32 fwver; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructVliPdHdr) st = NULL; /* parse */ st = fu_struct_vli_pd_hdr_parse_stream(stream, VLI_USBHUB_PD_FLASHMAP_ADDR, error); if (st == NULL) { - g_prefix_error(error, "failed to read header: "); + g_prefix_error_literal(error, "failed to read header: "); return FALSE; } if (!fu_input_stream_size(stream, &streamsz, error)) @@ -79,7 +79,7 @@ } /* check CRC */ - if ((flags & FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM) == 0) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM) == 0) { guint16 crc_actual = 0xFFFF; guint16 crc_file = 0x0; g_autoptr(GInputStream) stream_tmp = NULL; @@ -96,7 +96,7 @@ &crc_file, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to read file CRC: "); + g_prefix_error_literal(error, "failed to read file CRC: "); return FALSE; } stream_tmp = fu_partial_input_stream_new(stream, 0, streamsz - 2, error); diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-pd-parade-device.c fwupd-2.0.20/plugins/vli/fu-vli-pd-parade-device.c --- fwupd-2.0.8/plugins/vli/fu-vli-pd-parade-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-pd-parade-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,6 +11,11 @@ #include "fu-vli-pd-parade-device.h" #include "fu-vli-struct.h" +/* + * NOTE: DO NOT ALLOW ANY MORE MAGIC CONSTANTS IN THIS FILE + * nocheck:magic-inlines=100 + */ + struct _FuVliPdParadeDevice { FuUsbDevice parent_instance; FuVliDeviceKind device_kind; @@ -47,7 +52,10 @@ /* sanity check */ if (bufsz > 0x40) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "request too large"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "request too large"); return FALSE; } @@ -109,7 +117,7 @@ fu_vli_pd_parade_device_start_mcu(FuVliPdParadeDevice *self, GError **error) { if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xBC, 0x00, error)) { - g_prefix_error(error, "failed to start MCU: "); + g_prefix_error_literal(error, "failed to start MCU: "); return FALSE; } return TRUE; @@ -119,11 +127,11 @@ fu_vli_pd_parade_device_stop_mcu(FuVliPdParadeDevice *self, GError **error) { if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xBC, 0xC0, error)) { - g_prefix_error(error, "failed to stop MCU: "); + g_prefix_error_literal(error, "failed to stop MCU: "); return FALSE; } if (!fu_vli_pd_parade_device_i2c_write(self, self->page2, 0xBC, 0x40, error)) { - g_prefix_error(error, "failed to stop MCU 2nd: "); + g_prefix_error_literal(error, "failed to stop MCU 2nd: "); return FALSE; } return TRUE; @@ -257,10 +265,10 @@ } } if (!ret) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to wait for SPI not BUSY"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to wait for SPI not BUSY"); return FALSE; } @@ -293,10 +301,10 @@ } } if (!ret2) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to wait for SPI CMD done"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to wait for SPI CMD done"); return FALSE; } @@ -315,10 +323,10 @@ } } if (!ret) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to wait for SPI status clear"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to wait for SPI status clear"); return FALSE; } @@ -395,7 +403,7 @@ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "Erase failed @0x%x", + "erase failed @0x%x", addr); return FALSE; } @@ -462,8 +470,8 @@ GError **error) { FuVliPdParadeDevice *self = FU_VLI_PD_PARADE_DEVICE(device); - FuVliPdDevice *parent = FU_VLI_PD_DEVICE(fu_device_get_parent(device)); - guint8 buf[0x20]; + FuDevice *parent; + guint8 buf[0x20] = {0}; guint block_idx_tmp; g_autoptr(FuDeviceLocker) locker = NULL; g_autoptr(GByteArray) buf_verify = NULL; @@ -486,6 +494,9 @@ return FALSE; /* open device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -646,13 +657,16 @@ static GBytes * fu_vli_pd_parade_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) { - FuVliPdDevice *parent = FU_VLI_PD_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; FuVliPdParadeDevice *self = FU_VLI_PD_PARADE_DEVICE(device); g_autoptr(FuDeviceLocker) locker = NULL; g_autoptr(GByteArray) fw = g_byte_array_new(); g_autoptr(GPtrArray) blocks = NULL; /* open device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return NULL; locker = fu_device_locker_new(parent, error); if (locker == NULL) return NULL; @@ -695,7 +709,7 @@ } static void -fu_vli_pd_parade_device_set_progress(FuDevice *self, FuProgress *progress) +fu_vli_pd_parade_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -712,7 +726,7 @@ self->device_kind = FU_VLI_DEVICE_KIND_PS186; self->page2 = 0x14; self->page7 = 0x1E; - fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_VIDEO_DISPLAY); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_TRIPLET); diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-plugin.c fwupd-2.0.20/plugins/vli/fu-vli-plugin.c --- fwupd-2.0.8/plugins/vli/fu-vli-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -34,6 +34,7 @@ FuContext *ctx = fu_plugin_get_context(plugin); fu_context_add_quirk_key(ctx, "VliDeviceKind"); fu_context_add_quirk_key(ctx, "VliSpiAutoDetect"); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_VLI_USBHUB_FIRMWARE); fu_plugin_add_firmware_gtype(plugin, NULL, FU_TYPE_VLI_PD_FIRMWARE); fu_plugin_add_device_gtype(plugin, FU_TYPE_VLI_USBHUB_DEVICE); diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-usbhub-common.h fwupd-2.0.20/plugins/vli/fu-vli-usbhub-common.h --- fwupd-2.0.8/plugins/vli/fu-vli-usbhub-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-usbhub-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -29,11 +29,3 @@ #define VLI_USBHUB_FLASHMAP_IDX_HD1 0x00 /* factory firmware */ #define VLI_USBHUB_FLASHMAP_IDX_HD2 0x80 /* update firmware */ #define VLI_USBHUB_FLASHMAP_IDX_INVALID 0xff - -#define VLI_USBHUB_FLASHMAP_ADDR_HD1 0x0 -#define VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP 0x1800 -#define VLI_USBHUB_FLASHMAP_ADDR_HD2 0x1000 -#define VLI_USBHUB_FLASHMAP_ADDR_FW 0x2000 -#define VLI_USBHUB_FLASHMAP_ADDR_PD_LEGACY 0x10000 -#define VLI_USBHUB_FLASHMAP_ADDR_PD 0x20000 -#define VLI_USBHUB_FLASHMAP_ADDR_PD_BACKUP 0x30000 diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-usbhub-device.c fwupd-2.0.20/plugins/vli/fu-vli-usbhub-device.c --- fwupd-2.0.8/plugins/vli/fu-vli-usbhub-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-usbhub-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -17,12 +17,17 @@ #include "fu-vli-usbhub-pd-device.h" #include "fu-vli-usbhub-rtd21xx-device.h" +/* + * NOTE: DO NOT ALLOW ANY MORE MAGIC CONSTANTS IN THIS FILE + * nocheck:magic-inlines=120 + */ + struct _FuVliUsbhubDevice { FuVliDevice parent_instance; gboolean disable_powersave; guint8 update_protocol; - GByteArray *st_hd1; /* factory */ - GByteArray *st_hd2; /* update */ + FuStructVliUsbhubHdr *st_hd1; /* factory */ + FuStructVliUsbhubHdr *st_hd2; /* update */ }; G_DEFINE_TYPE(FuVliUsbhubDevice, fu_vli_usbhub_device, FU_TYPE_VLI_DEVICE) @@ -76,7 +81,7 @@ FU_VLI_DEVICE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to UnLock_VL813: "); + g_prefix_error_literal(error, "failed to UnLock_VL813: "); return FALSE; } return TRUE; @@ -234,7 +239,7 @@ FU_VLI_DEVICE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to write enable SPI: "); + g_prefix_error_literal(error, "failed to write enable SPI: "); return FALSE; } return TRUE; @@ -473,7 +478,7 @@ static gboolean fu_vli_usbhub_device_guess_kind(FuVliUsbhubDevice *self, GError **error) { - guint8 b811P812 = 0x0; + guint8 b811p812 = 0x0; guint8 pkgtype = 0x0; guint8 chipid1 = 0x0; guint8 chipid2 = 0x0; @@ -484,42 +489,42 @@ gint tPid = fu_device_get_pid(FU_DEVICE(self)) & 0x0fff; if (!fu_vli_usbhub_device_read_reg(self, 0xf88c, &chipver, error)) { - g_prefix_error(error, "Read_ChipVer failed: "); + g_prefix_error_literal(error, "Read_ChipVer failed: "); return FALSE; } g_debug("chipver = 0x%02x", chipver); if (!fu_vli_usbhub_device_read_reg(self, 0xf63f, &chipver2, error)) { - g_prefix_error(error, "Read_ChipVer2 failed: "); + g_prefix_error_literal(error, "Read_ChipVer2 failed: "); return FALSE; } g_debug("chipver2 = 0x%02x", chipver2); - if (!fu_vli_usbhub_device_read_reg(self, 0xf800, &b811P812, error)) { - g_prefix_error(error, "Read_811P812 failed: "); + if (!fu_vli_usbhub_device_read_reg(self, 0xf800, &b811p812, error)) { + g_prefix_error_literal(error, "Read_811P812 failed: "); return FALSE; } - g_debug("b811P812 = 0x%02x", b811P812); + g_debug("b811p812 = 0x%02x", b811p812); if (!fu_vli_usbhub_device_read_reg(self, 0xf88e, &chipid1, error)) { - g_prefix_error(error, "Read_ChipID1 failed: "); + g_prefix_error_literal(error, "Read_ChipID1 failed: "); return FALSE; } g_debug("chipid1 = 0x%02x", chipid1); if (!fu_vli_usbhub_device_read_reg(self, 0xf88f, &chipid2, error)) { - g_prefix_error(error, "Read_ChipID2 failed: "); + g_prefix_error_literal(error, "Read_ChipID2 failed: "); return FALSE; } g_debug("chipid2 = 0x%02x", chipid2); if (!fu_vli_usbhub_device_read_reg(self, 0xf64e, &chipid12, error)) { - g_prefix_error(error, "Read_ChipID12 failed: "); + g_prefix_error_literal(error, "Read_ChipID12 failed: "); return FALSE; } g_debug("chipid12 = 0x%02x", chipid12); if (!fu_vli_usbhub_device_read_reg(self, 0xf64f, &chipid22, error)) { - g_prefix_error(error, "Read_ChipID22 failed: "); + g_prefix_error_literal(error, "Read_ChipID22 failed: "); return FALSE; } g_debug("chipid22 = 0x%02x", chipid22); if (!fu_vli_usbhub_device_read_reg(self, 0xf651, &pkgtype, error)) { - g_prefix_error(error, "Read_820Q7Q8 failed: "); + g_prefix_error_literal(error, "Read_820Q7Q8 failed: "); return FALSE; } g_debug("pkgtype = 0x%02x", pkgtype); @@ -566,10 +571,10 @@ FU_VLI_DEVICE_KIND_VL819Q8); break; default: - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "packet Type match failed: "); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "packet type match failed"); return FALSE; } } else { @@ -592,10 +597,10 @@ if (chipver == 0xC0 || chipver == 0xC1) fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL822C0); else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "not supported 99 type"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "not supported 99 type"); return FALSE; } } else if (chipid2 == 0x35 && chipid1 == 0x66) { @@ -613,23 +618,23 @@ fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL810); } else if (tPid == 0x811) { fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL811); - } else if ((b811P812 & ((1 << 5) | (1 << 4))) == 0) { + } else if ((b811p812 & ((1 << 5) | (1 << 4))) == 0) { if (chipver == 0x10) fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL811PB0); else fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL811PB3); - } else if ((b811P812 & ((1 << 5) | (1 << 4))) == (1 << 4)) { + } else if ((b811p812 & ((1 << 5) | (1 << 4))) == (1 << 4)) { fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL812Q4S); - } else if ((b811P812 & ((1 << 5) | (1 << 4))) == ((1 << 5) | (1 << 4))) { + } else if ((b811p812 & ((1 << 5) | (1 << 4))) == ((1 << 5) | (1 << 4))) { if (chipver == 0x10) fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL812B0); else fu_vli_device_set_kind(FU_VLI_DEVICE(self), FU_VLI_DEVICE_KIND_VL812B3); } else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "hardware is not supported"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "hardware is not supported"); return FALSE; } @@ -749,18 +754,18 @@ if (fu_device_has_private_flag(device, FU_VLI_USBHUB_DEVICE_FLAG_UNLOCK_LEGACY813) && !fu_vli_device_spi_read_block(FU_VLI_DEVICE(self), 0x0, - self->st_hd1->data, - self->st_hd1->len, + self->st_hd1->buf->data, + self->st_hd1->buf->len, &error_tmp)) { g_warning("failed to read, trying to unlock 813: %s", error_tmp->message); if (!fu_vli_usbhub_device_vdr_unlock_813(self, error)) return FALSE; if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(self), 0x0, - self->st_hd1->data, - self->st_hd1->len, + self->st_hd1->buf->data, + self->st_hd1->buf->len, error)) { - g_prefix_error(error, "813 unlock fail: "); + g_prefix_error_literal(error, "813 unlock fail: "); return FALSE; } g_debug("813 unlock OK"); @@ -773,11 +778,11 @@ /* read HD1 (factory) header */ if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD1, - self->st_hd1->data, - self->st_hd1->len, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD1, + self->st_hd1->buf->data, + self->st_hd1->buf->len, error)) { - g_prefix_error(error, "failed to read HD1 header: "); + g_prefix_error_literal(error, "failed to read HD1 header: "); return FALSE; } @@ -827,11 +832,11 @@ /* read HD2 (update) header */ if (self->update_protocol >= 0x2) { if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD2, - self->st_hd2->data, - self->st_hd2->len, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD2, + self->st_hd2->buf->data, + self->st_hd2->buf->len, error)) { - g_prefix_error(error, "failed to read HD2 header: "); + g_prefix_error_literal(error, "failed to read HD2 header: "); return FALSE; } } @@ -861,7 +866,7 @@ fu_vli_usbhub_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE(device); @@ -923,7 +928,7 @@ if (!fu_vli_device_spi_erase_all(FU_VLI_DEVICE(self), fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to erase chip: "); + g_prefix_error_literal(error, "failed to erase chip: "); return FALSE; } fu_progress_step_done(progress); @@ -973,7 +978,7 @@ /* write in chunks */ if (!fu_vli_device_spi_write(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD1, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD1, buf, bufsz, fu_progress_get_child(progress), @@ -986,51 +991,54 @@ } static gboolean -fu_vli_usbhub_device_hd1_is_valid(GByteArray *hdr) +fu_vli_usbhub_device_hd1_is_valid(FuStructVliUsbhubHdr *st_hdr) { - if (fu_struct_vli_usbhub_hdr_get_prev_ptr(hdr) != VLI_USBHUB_FLASHMAP_IDX_INVALID) + if (fu_struct_vli_usbhub_hdr_get_prev_ptr(st_hdr) != VLI_USBHUB_FLASHMAP_IDX_INVALID) return FALSE; - if (fu_struct_vli_usbhub_hdr_get_checksum(hdr) != fu_vli_usbhub_device_header_crc8(hdr)) + if (fu_struct_vli_usbhub_hdr_get_checksum(st_hdr) != + fu_vli_usbhub_device_header_crc8(st_hdr->buf)) return FALSE; return TRUE; } static gboolean fu_vli_usbhub_device_hd1_recover(FuVliUsbhubDevice *self, - GByteArray *hdr, + FuStructVliUsbhubHdr *st_hdr, FuProgress *progress, GError **error) { /* point to HD2, i.e. updated firmware */ - if (fu_struct_vli_usbhub_hdr_get_next_ptr(hdr) != VLI_USBHUB_FLASHMAP_IDX_HD2) { - fu_struct_vli_usbhub_hdr_set_next_ptr(hdr, VLI_USBHUB_FLASHMAP_IDX_HD2); - fu_struct_vli_usbhub_hdr_set_checksum(hdr, fu_vli_usbhub_device_header_crc8(hdr)); + if (fu_struct_vli_usbhub_hdr_get_next_ptr(st_hdr) != VLI_USBHUB_FLASHMAP_IDX_HD2) { + fu_struct_vli_usbhub_hdr_set_next_ptr(st_hdr, VLI_USBHUB_FLASHMAP_IDX_HD2); + fu_struct_vli_usbhub_hdr_set_checksum( + st_hdr, + fu_vli_usbhub_device_header_crc8(st_hdr->buf)); } /* write new header block */ if (!fu_vli_device_spi_erase_sector(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD1, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD1, error)) { g_prefix_error(error, "failed to erase header1 sector at 0x%x: ", - (guint)VLI_USBHUB_FLASHMAP_ADDR_HD1); + (guint)FU_VLI_USBHUB_FLASHMAP_ADDR_HD1); return FALSE; } if (!fu_vli_device_spi_write_block(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD1, - hdr->data, - hdr->len, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD1, + st_hdr->buf->data, + st_hdr->buf->len, progress, error)) { g_prefix_error(error, "failed to write header1 block at 0x%x: ", - (guint)VLI_USBHUB_FLASHMAP_ADDR_HD1); + (guint)FU_VLI_USBHUB_FLASHMAP_ADDR_HD1); return FALSE; } /* update the cached copy */ - g_byte_array_unref(self->st_hd1); - self->st_hd1 = g_byte_array_ref(hdr); + fu_struct_vli_usbhub_hdr_unref(self->st_hd1); + self->st_hd1 = fu_struct_vli_usbhub_hdr_ref(st_hdr); return TRUE; } @@ -1046,7 +1054,7 @@ guint32 hd2_fw_addr; guint32 hd2_fw_offset; const guint8 *buf_fw; - g_autoptr(GByteArray) st_hd = NULL; + g_autoptr(FuStructVliUsbhubHdr) st_hd = NULL; g_autoptr(GBytes) fw = NULL; /* simple image */ @@ -1061,25 +1069,25 @@ VLI_USBHUB_FLASHMAP_IDX_HD2) { /* backup HD1 before recovering */ if (!fu_vli_device_spi_erase_sector(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD2, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD2, error)) { - g_prefix_error(error, "failed to erase sector at header 1: "); + g_prefix_error_literal(error, "failed to erase sector at hdr 1: "); return FALSE; } if (!fu_vli_device_spi_write_block(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP, - self->st_hd1->data, - self->st_hd1->len, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP, + self->st_hd1->buf->data, + self->st_hd1->buf->len, progress, error)) { - g_prefix_error(error, "failed to write block at header 1: "); + g_prefix_error_literal(error, "failed to write block at hdr 1: "); return FALSE; } if (!fu_vli_usbhub_device_hd1_recover(self, self->st_hd1, progress, error)) { - g_prefix_error(error, "failed to write header: "); + g_prefix_error_literal(error, "failed to write header: "); return FALSE; } } @@ -1088,13 +1096,13 @@ /* copy the header from the backup zone */ g_info("HD1 was invalid, reading backup"); if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP, - self->st_hd1->data, - self->st_hd1->len, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP, + self->st_hd1->buf->data, + self->st_hd1->buf->len, error)) { g_prefix_error(error, "failed to read root header from 0x%x: ", - (guint)VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP); + (guint)FU_VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP); return FALSE; } if (!fu_vli_usbhub_device_hd1_is_valid(self->st_hd1)) { @@ -1102,7 +1110,7 @@ return fu_vli_usbhub_device_update_v2_recovery(self, fw, progress, error); } if (!fu_vli_usbhub_device_hd1_recover(self, self->st_hd1, progress, error)) { - g_prefix_error(error, "failed to get root header in backup zone: "); + g_prefix_error_literal(error, "failed to get root hdr in backup zone: "); return FALSE; } } @@ -1118,7 +1126,7 @@ return FALSE; } hd2_fw_addr = (hd1_fw_sz + 0xfff) & 0xf000; - hd2_fw_addr += VLI_USBHUB_FLASHMAP_ADDR_FW; + hd2_fw_addr += FU_VLI_USBHUB_FLASHMAP_ADDR_FW; /* get the size and offset of the update firmware */ buf_fw = g_bytes_get_data(fw, &buf_fwsz); @@ -1159,7 +1167,7 @@ hd2_fw_sz, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write payload: "); + g_prefix_error_literal(error, "failed to write payload: "); return FALSE; } fu_progress_step_done(progress); @@ -1169,27 +1177,27 @@ fu_struct_vli_usbhub_hdr_set_usb3_fw_addr_high(st_hd, hd2_fw_addr >> 16); fu_struct_vli_usbhub_hdr_set_prev_ptr(st_hd, VLI_USBHUB_FLASHMAP_IDX_HD1); fu_struct_vli_usbhub_hdr_set_next_ptr(st_hd, VLI_USBHUB_FLASHMAP_IDX_INVALID); - fu_struct_vli_usbhub_hdr_set_checksum(st_hd, fu_vli_usbhub_device_header_crc8(st_hd)); + fu_struct_vli_usbhub_hdr_set_checksum(st_hd, fu_vli_usbhub_device_header_crc8(st_hd->buf)); if (!fu_vli_device_spi_erase_sector(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD2, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD2, error)) { - g_prefix_error(error, "failed to erase sectors for HD2: "); + g_prefix_error_literal(error, "failed to erase sectors for HD2: "); return FALSE; } if (!fu_vli_device_spi_write_block(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD2, - st_hd->data, - st_hd->len, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD2, + st_hd->buf->data, + st_hd->buf->len, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write HD2: "); + g_prefix_error_literal(error, "failed to write HD2: "); return FALSE; } fu_progress_step_done(progress); /* success */ - g_byte_array_unref(self->st_hd2); - self->st_hd2 = g_byte_array_ref(st_hd); + fu_struct_vli_usbhub_hdr_unref(self->st_hd2); + self->st_hd2 = fu_struct_vli_usbhub_hdr_ref(st_hd); return TRUE; } @@ -1204,7 +1212,7 @@ guint32 hd2_fw_addr; guint32 hd2_fw_offset; const guint8 *buf_fw; - g_autoptr(GByteArray) st_hd = NULL; + g_autoptr(FuStructVliUsbhubHdr) st_hd = NULL; g_autoptr(GBytes) fw = NULL; /* simple image */ @@ -1219,25 +1227,25 @@ VLI_USBHUB_FLASHMAP_IDX_HD2) { /* backup HD1 before recovering */ if (!fu_vli_device_spi_erase_sector(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD2, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD2, error)) { - g_prefix_error(error, "failed to erase sector at header 1: "); + g_prefix_error_literal(error, "failed to erase sector at hdr 1: "); return FALSE; } if (!fu_vli_device_spi_write_block(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP, - self->st_hd1->data, - self->st_hd1->len, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP, + self->st_hd1->buf->data, + self->st_hd1->buf->len, progress, error)) { - g_prefix_error(error, "failed to write block at header 1: "); + g_prefix_error_literal(error, "failed to write block at hdr 1: "); return FALSE; } if (!fu_vli_usbhub_device_hd1_recover(self, self->st_hd1, progress, error)) { - g_prefix_error(error, "failed to write header: "); + g_prefix_error_literal(error, "failed to write header: "); return FALSE; } } @@ -1246,13 +1254,13 @@ /* copy the header from the backup zone */ g_info("HD1 was invalid, reading backup"); if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP, - self->st_hd1->data, - self->st_hd1->len, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP, + self->st_hd1->buf->data, + self->st_hd1->buf->len, error)) { g_prefix_error(error, "failed to read root header from 0x%x: ", - (guint)VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP); + (guint)FU_VLI_USBHUB_FLASHMAP_ADDR_HD1_BACKUP); return FALSE; } if (!fu_vli_usbhub_device_hd1_is_valid(self->st_hd1)) { @@ -1260,7 +1268,7 @@ return fu_vli_usbhub_device_update_v2_recovery(self, fw, progress, error); } if (!fu_vli_usbhub_device_hd1_recover(self, self->st_hd1, progress, error)) { - g_prefix_error(error, "failed to get root header in backup zone: "); + g_prefix_error_literal(error, "failed to get root hdr in backup zone: "); return FALSE; } } @@ -1312,7 +1320,7 @@ hd2_fw_sz, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write payload: "); + g_prefix_error_literal(error, "failed to write payload: "); return FALSE; } fu_progress_step_done(progress); @@ -1322,27 +1330,27 @@ fu_struct_vli_usbhub_hdr_set_usb3_fw_addr_high(st_hd, hd2_fw_addr >> 16); fu_struct_vli_usbhub_hdr_set_prev_ptr(st_hd, VLI_USBHUB_FLASHMAP_IDX_HD1); fu_struct_vli_usbhub_hdr_set_next_ptr(st_hd, VLI_USBHUB_FLASHMAP_IDX_INVALID); - fu_struct_vli_usbhub_hdr_set_checksum(st_hd, fu_vli_usbhub_device_header_crc8(st_hd)); + fu_struct_vli_usbhub_hdr_set_checksum(st_hd, fu_vli_usbhub_device_header_crc8(st_hd->buf)); if (!fu_vli_device_spi_erase_sector(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD2, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD2, error)) { - g_prefix_error(error, "failed to erase sectors for HD2: "); + g_prefix_error_literal(error, "failed to erase sectors for HD2: "); return FALSE; } if (!fu_vli_device_spi_write_block(FU_VLI_DEVICE(self), - VLI_USBHUB_FLASHMAP_ADDR_HD2, - st_hd->data, - st_hd->len, + FU_VLI_USBHUB_FLASHMAP_ADDR_HD2, + st_hd->buf->data, + st_hd->buf->len, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write HD2: "); + g_prefix_error_literal(error, "failed to write HD2: "); return FALSE; } fu_progress_step_done(progress); /* success */ - g_byte_array_unref(self->st_hd2); - self->st_hd2 = g_byte_array_ref(st_hd); + fu_struct_vli_usbhub_hdr_unref(self->st_hd2); + self->st_hd2 = fu_struct_vli_usbhub_hdr_ref(st_hd); return TRUE; } @@ -1370,7 +1378,7 @@ /* disable powersaving if required */ if (self->disable_powersave) { if (!fu_vli_usbhub_device_disable_u1u2(self, error)) { - g_prefix_error(error, "disabling powersave failed: "); + g_prefix_error_literal(error, "disabling powersave failed: "); return FALSE; } } @@ -1393,7 +1401,7 @@ } static void -fu_vli_usbhub_device_set_progress(FuDevice *self, FuProgress *progress) +fu_vli_usbhub_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -1408,7 +1416,7 @@ { self->st_hd1 = fu_struct_vli_usbhub_hdr_new(); self->st_hd2 = fu_struct_vli_usbhub_hdr_new(); - fu_device_add_icon(FU_DEVICE(self), "usb-hub"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_USB_HUB); fu_device_add_protocol(FU_DEVICE(self), "com.vli.usbhub"); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_USE_PROXY_FALLBACK); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_AUTO_PARENT_CHILDREN); @@ -1433,8 +1441,8 @@ fu_vli_usbhub_device_finalize(GObject *obj) { FuVliUsbhubDevice *self = FU_VLI_USBHUB_DEVICE(obj); - g_byte_array_unref(self->st_hd1); - g_byte_array_unref(self->st_hd2); + fu_struct_vli_usbhub_hdr_unref(self->st_hd1); + fu_struct_vli_usbhub_hdr_unref(self->st_hd2); G_OBJECT_CLASS(fu_vli_usbhub_device_parent_class)->finalize(obj); } diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-usbhub-firmware.c fwupd-2.0.20/plugins/vli/fu-vli-usbhub-firmware.c --- fwupd-2.0.8/plugins/vli/fu-vli-usbhub-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-usbhub-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,7 +11,7 @@ #include "fu-vli-usbhub-firmware.h" struct _FuVliUsbhubFirmware { - FuFirmwareClass parent_instance; + FuFirmware parent_instance; FuVliDeviceKind device_kind; guint16 dev_id; }; @@ -42,41 +42,27 @@ } static gboolean -fu_vli_usbhub_firmware_parse(FuFirmware *firmware, - GInputStream *stream, - FwupdInstallFlags flags, - GError **error) +fu_vli_usbhub_firmware_parse_version(FuVliUsbhubFirmware *self, + GInputStream *stream, + guint8 strapping1, + GError **error) { - FuVliUsbhubFirmware *self = FU_VLI_USBHUB_FIRMWARE(firmware); guint16 adr_ofs = 0; guint16 version = 0x0; guint32 adr_ofs32 = 0; guint8 tmp = 0x0; - guint8 fwtype = 0x0; - guint8 strapping1; - g_autoptr(GByteArray) st = NULL; - /* map into header */ - st = fu_struct_vli_usbhub_hdr_parse_stream(stream, 0x0, error); - if (st == NULL) { - g_prefix_error(error, "failed to read header: "); - return FALSE; - } - self->dev_id = fu_struct_vli_usbhub_hdr_get_dev_id(st); - strapping1 = fu_struct_vli_usbhub_hdr_get_strapping1(st); - - /* get firmware versions */ switch (self->dev_id) { case 0x0d12: /* VL81x */ if (!fu_input_stream_read_u16(stream, 0x1f4c, &version, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to get version: "); + g_prefix_error_literal(error, "failed to get version: "); return FALSE; } version |= (strapping1 >> 4) & 0x07; if ((version & 0x0f) == 0x04) { if (!fu_input_stream_read_u8(stream, 0x700d, &tmp, error)) { - g_prefix_error(error, "failed to get version increment: "); + g_prefix_error_literal(error, "failed to get version increment: "); return FALSE; } if (tmp & 0x40) @@ -86,7 +72,7 @@ case 0x0507: /* VL210 */ if (!fu_input_stream_read_u16(stream, 0x8f0c, &version, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to get version: "); + g_prefix_error_literal(error, "failed to get version: "); return FALSE; } version |= (strapping1 >> 4) & 0x07; @@ -96,7 +82,7 @@ case 0x0566: /* U4ID_Address_In_FW_Zone */ if (!fu_input_stream_read_u24(stream, 0x3F80, &adr_ofs32, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to get offset addr: "); + g_prefix_error_literal(error, "failed to get offset addr: "); return FALSE; } if (adr_ofs32 < 0x20000 + 0x2000 + 4) { @@ -112,7 +98,7 @@ &version, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to get offset version: "); + g_prefix_error_literal(error, "failed to get offset version: "); return FALSE; } version |= (strapping1 >> 4) & 0x07; @@ -120,7 +106,7 @@ default: /* U3ID_Address_In_FW_Zone */ if (!fu_input_stream_read_u16(stream, 0x8000, &adr_ofs, G_BIG_ENDIAN, error)) { - g_prefix_error(error, "failed to get offset addr: "); + g_prefix_error_literal(error, "failed to get offset addr: "); return FALSE; } if (!fu_input_stream_read_u16(stream, @@ -128,7 +114,7 @@ &version, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to get offset version: "); + g_prefix_error_literal(error, "failed to get offset version: "); return FALSE; } version |= (strapping1 >> 4) & 0x07; @@ -136,7 +122,35 @@ /* version is set */ if (version != 0x0) - fu_firmware_set_version_raw(firmware, version); + fu_firmware_set_version_raw(FU_FIRMWARE(self), version); + return TRUE; +} + +static gboolean +fu_vli_usbhub_firmware_parse(FuFirmware *firmware, + GInputStream *stream, + FuFirmwareParseFlags flags, + GError **error) +{ + FuVliUsbhubFirmware *self = FU_VLI_USBHUB_FIRMWARE(firmware); + guint16 adr_ofs = 0; + guint8 tmp = 0x0; + guint8 fwtype = 0x0; + guint8 strapping1; + g_autoptr(FuStructVliUsbhubHdr) st = NULL; + + /* map into header */ + st = fu_struct_vli_usbhub_hdr_parse_stream(stream, 0x0, error); + if (st == NULL) { + g_prefix_error_literal(error, "failed to read header: "); + return FALSE; + } + self->dev_id = fu_struct_vli_usbhub_hdr_get_dev_id(st); + strapping1 = fu_struct_vli_usbhub_hdr_get_strapping1(st); + + /* get firmware versions */ + if (!fu_vli_usbhub_firmware_parse_version(self, stream, strapping1, error)) + return FALSE; /* get device type from firmware image */ switch (self->dev_id) { @@ -150,7 +164,7 @@ &binver1, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to get binver1: "); + g_prefix_error_literal(error, "failed to get binver1: "); return FALSE; } if (!fu_input_stream_read_u16(stream, @@ -158,7 +172,7 @@ &binver2, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to get binver2: "); + g_prefix_error_literal(error, "failed to get binver2: "); return FALSE; } @@ -200,18 +214,18 @@ case 0x0518: /* VL819~VL822 == VT3518 */ if (!fu_input_stream_read_u8(stream, 0x8021, &tmp, error)) { - g_prefix_error(error, "failed to get 820/822 byte: "); + g_prefix_error_literal(error, "failed to get 820/822 byte: "); return FALSE; } /* Q5/Q7/Q8 requires searching two addresses for offset value */ if (!fu_input_stream_read_u16(stream, 0x8018, &adr_ofs, G_BIG_ENDIAN, error)) { - g_prefix_error(error, "failed to get Q7/Q8 offset mapping: "); + g_prefix_error_literal(error, "failed to get Q7/Q8 offset mapping: "); return FALSE; } /* VL819, VL821, VL822 */ if (tmp == 0xF0) { if (!fu_input_stream_read_u8(stream, adr_ofs + 0x2000, &tmp, error)) { - g_prefix_error(error, "failed to get offset version: "); + g_prefix_error_literal(error, "failed to get offset version: "); return FALSE; } /* VL819 */ @@ -243,7 +257,10 @@ self->device_kind = FU_VLI_DEVICE_KIND_VL819Q8; break; default: - g_prefix_error(error, "failed to match Q5/Q7/Q8 fw type: "); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "failed to match Q5/Q7/Q8 fw type"); return FALSE; } /* VL820 */ @@ -251,7 +268,7 @@ self->device_kind = FU_VLI_DEVICE_KIND_VL822C0; } else { if (!fu_input_stream_read_u8(stream, 0xf000, &tmp, error)) { - g_prefix_error(error, "failed to get Q7/Q8 difference: "); + g_prefix_error_literal(error, "failed to get Q7/Q8 difference: "); return FALSE; } if (tmp & (1 << 0)) @@ -289,7 +306,7 @@ &binveraddr, G_LITTLE_ENDIAN, error)) { - g_prefix_error(error, "failed to get binveraddr: "); + g_prefix_error_literal(error, "failed to get binveraddr: "); return FALSE; } if (binveraddr < 0x20000 + 0x2000) { @@ -303,7 +320,7 @@ binveraddr - 0x20000 + 0x2000, &binver, error)) { - g_prefix_error(error, "failed to get binver2: "); + g_prefix_error_literal(error, "failed to get binver2: "); return FALSE; } diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-usbhub-i2c-common.c fwupd-2.0.20/plugins/vli/fu-vli-usbhub-i2c-common.c --- fwupd-2.0.8/plugins/vli/fu-vli-usbhub-i2c-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-usbhub-i2c-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,43 +12,13 @@ gboolean fu_vli_usbhub_i2c_check_status(FuVliUsbhubI2cStatus status, GError **error) { - if (status == FU_VLI_USBHUB_I2C_STATUS_OK) - return TRUE; - if (status == FU_VLI_USBHUB_I2C_STATUS_HEADER) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Incorrect header value of data frame"); - return FALSE; - } - if (status == FU_VLI_USBHUB_I2C_STATUS_COMMAND) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Invalid command data"); - return FALSE; - } - if (status == FU_VLI_USBHUB_I2C_STATUS_ADDRESS) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Invalid address range"); - return FALSE; - } - if (status == FU_VLI_USBHUB_I2C_STATUS_PACKETSIZE) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Incorrect payload data length"); - return FALSE; - } - if (status == FU_VLI_USBHUB_I2C_STATUS_CHECKSUM) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Incorrect frame data checksum"); - return FALSE; - } - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "Unknown error [0x%02x]", status); - return FALSE; + const FuErrorMapEntry entries[] = { + {FU_VLI_USBHUB_I2C_STATUS_OK, FWUPD_ERROR_LAST, NULL}, + {FU_VLI_USBHUB_I2C_STATUS_HEADER, FWUPD_ERROR_INTERNAL, "incorrect header value"}, + {FU_VLI_USBHUB_I2C_STATUS_COMMAND, FWUPD_ERROR_INTERNAL, "invalid command data"}, + {FU_VLI_USBHUB_I2C_STATUS_ADDRESS, FWUPD_ERROR_INTERNAL, "invalid address range"}, + {FU_VLI_USBHUB_I2C_STATUS_PACKETSIZE, FWUPD_ERROR_INTERNAL, "invalid payload length"}, + {FU_VLI_USBHUB_I2C_STATUS_CHECKSUM, FWUPD_ERROR_INTERNAL, "invalid frame checksum"}, + }; + return fu_error_map_entry_to_gerror(status, entries, G_N_ELEMENTS(entries), error); } diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-usbhub-msp430-device.c fwupd-2.0.20/plugins/vli/fu-vli-usbhub-msp430-device.c --- fwupd-2.0.8/plugins/vli/fu-vli-usbhub-msp430-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-usbhub-msp430-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -52,7 +52,7 @@ FU_VLI_DEVICE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to read I2C: "); + g_prefix_error_literal(error, "failed to read I2C: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, "I2cReadData", buf, bufsz); @@ -113,17 +113,20 @@ static gboolean fu_vli_usbhub_msp430_device_setup(FuDevice *device, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuVliUsbhubDevice *parent; guint8 buf[11] = {0x0}; g_autofree gchar *version = NULL; /* get versions */ + parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) + return FALSE; if (!fu_vli_usbhub_msp430_device_i2c_read(parent, I2C_CMD_READ_VERSIONS, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to read versions: "); + g_prefix_error_literal(error, "failed to read versions: "); return FALSE; } if ((buf[0] == 0x00 && buf[1] == 0x00 && buf[2] == 0x00) || @@ -144,7 +147,7 @@ static gboolean fu_vli_usbhub_msp430_device_detach(FuDevice *device, FuProgress *progress, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuVliUsbhubDevice *parent; FuVliUsbhubI2cStatus status = 0xff; g_autoptr(FuDeviceLocker) locker = NULL; const guint8 buf[] = { @@ -153,7 +156,10 @@ }; /* open device */ - locker = fu_device_locker_new(parent, error); + parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) + return FALSE; + locker = fu_device_locker_new(FU_DEVICE(parent), error); if (locker == NULL) return FALSE; if (!fu_vli_usbhub_msp430_device_i2c_write_data(parent, 0, 0, buf, sizeof(buf), error)) @@ -164,7 +170,7 @@ /* check the device came back */ if (!fu_vli_usbhub_msp430_device_i2c_read_status(parent, &status, error)) { - g_prefix_error(error, "device did not come back after detach: "); + g_prefix_error_literal(error, "device did not come back after detach: "); return FALSE; } return fu_vli_usbhub_i2c_check_status(status, error); @@ -181,10 +187,13 @@ fu_vli_usbhub_msp430_device_write_firmware_cb(FuDevice *device, gpointer user_data, GError **error) { FuVliUsbhubDeviceRequest *req = (FuVliUsbhubDeviceRequest *)user_data; - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuVliUsbhubDevice *parent; FuVliUsbhubI2cStatus status = 0xff; fu_device_sleep(device, 5); /* ms */ + parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) + return FALSE; if (fu_usb_device_get_spec(FU_USB_DEVICE(parent)) >= 0x0300 || req->bufsz <= 32) { if (!fu_vli_usbhub_msp430_device_i2c_write_data(parent, 0, @@ -224,12 +233,15 @@ FwupdInstallFlags flags, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuVliUsbhubDevice *parent; GPtrArray *records = fu_ihex_firmware_get_records(FU_IHEX_FIRMWARE(firmware)); g_autoptr(FuDeviceLocker) locker = NULL; /* open device */ - locker = fu_device_locker_new(parent, error); + parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) + return FALSE; + locker = fu_device_locker_new(FU_DEVICE(parent), error); if (locker == NULL) return FALSE; @@ -301,7 +313,7 @@ fu_vli_usbhub_msp430_device_probe(FuDevice *device, GError **error) { FuVliDeviceKind device_kind = FU_VLI_DEVICE_KIND_MSP430; - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device, NULL)); fu_device_set_name(device, fu_vli_device_kind_to_string(device_kind)); if (parent != NULL) { @@ -316,7 +328,7 @@ } static void -fu_vli_usbhub_msp430_device_set_progress(FuDevice *self, FuProgress *progress) +fu_vli_usbhub_msp430_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -329,7 +341,7 @@ static void fu_vli_usbhub_msp430_device_init(FuVliUsbhubMsp430Device *self) { - fu_device_add_icon(FU_DEVICE(self), "usb-hub"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_USB_HUB); fu_device_add_protocol(FU_DEVICE(self), "com.vli.i2c"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-usbhub-pd-device.c fwupd-2.0.20/plugins/vli/fu-vli-usbhub-pd-device.c --- fwupd-2.0.8/plugins/vli/fu-vli-usbhub-pd-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-usbhub-pd-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -44,27 +44,26 @@ fu_vli_usbhub_pd_device_setup(FuDevice *device, GError **error) { FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE(device); - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuVliUsbhubDevice *parent; const gchar *name; guint32 fwver; gsize bufsz = FU_STRUCT_VLI_PD_HDR_SIZE; g_autofree guint8 *buf = g_malloc0(bufsz); - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructVliPdHdr) st = NULL; /* sanity check */ - if (parent == NULL) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no parent"); + parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) return FALSE; - } /* legacy location */ if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(parent), - VLI_USBHUB_FLASHMAP_ADDR_PD_LEGACY + + FU_VLI_USBHUB_FLASHMAP_ADDR_PD_LEGACY + VLI_USBHUB_PD_FLASHMAP_ADDR_LEGACY, buf, bufsz, error)) { - g_prefix_error(error, "failed to read legacy PD header: "); + g_prefix_error_literal(error, "failed to read legacy PD header: "); return FALSE; } st = fu_struct_vli_pd_hdr_parse(buf, bufsz, 0x0, error); @@ -75,12 +74,12 @@ if (fu_struct_vli_pd_hdr_get_vid(st) != 0x2109) { g_debug("PD VID was 0x%04x trying new location", fu_struct_vli_pd_hdr_get_vid(st)); if (!fu_vli_device_spi_read_block(FU_VLI_DEVICE(parent), - VLI_USBHUB_FLASHMAP_ADDR_PD + + FU_VLI_USBHUB_FLASHMAP_ADDR_PD + VLI_USBHUB_PD_FLASHMAP_ADDR, buf, bufsz, error)) { - g_prefix_error(error, "failed to read PD header: "); + g_prefix_error_literal(error, "failed to read PD header: "); return FALSE; } fu_struct_vli_pd_hdr_unref(st); @@ -92,7 +91,10 @@ /* just empty space */ fwver = fu_struct_vli_pd_hdr_get_fwver(st); if (fwver == G_MAXUINT32) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no PD device header found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no PD device header found"); return FALSE; } @@ -132,7 +134,8 @@ return FALSE; /* these have a backup section */ - if (fu_vli_common_device_kind_get_offset(self->device_kind) == VLI_USBHUB_FLASHMAP_ADDR_PD) + if (fu_vli_common_device_kind_get_offset(self->device_kind) == + FU_VLI_USBHUB_FLASHMAP_ADDR_PD) fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_SELF_RECOVERY); /* success */ @@ -142,10 +145,13 @@ static gboolean fu_vli_usbhub_pd_device_reload(FuDevice *device, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; g_autoptr(FuDeviceLocker) locker = NULL; /* open parent device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -156,7 +162,7 @@ fu_vli_usbhub_pd_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE(device); @@ -185,11 +191,14 @@ static GBytes * fu_vli_usbhub_pd_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE(device); g_autoptr(FuDeviceLocker) locker = NULL; /* open device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return NULL; locker = fu_device_locker_new(parent, error); if (locker == NULL) return NULL; @@ -211,7 +220,7 @@ GError **error) { FuVliUsbhubPdDevice *self = FU_VLI_USBHUB_PD_DEVICE(device); - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; gsize bufsz = 0; const guint8 *buf; g_autoptr(FuDeviceLocker) locker = NULL; @@ -228,6 +237,9 @@ return FALSE; /* open device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -260,8 +272,13 @@ static gboolean fu_vli_usbhub_pd_device_attach(FuDevice *device, FuProgress *progress, GError **error) { - FuDevice *parent = fu_device_get_parent(device); - g_autoptr(FuDeviceLocker) locker = fu_device_locker_new(parent, error); + FuDevice *parent; + g_autoptr(FuDeviceLocker) locker = NULL; + + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; return fu_device_attach_full(parent, progress, error); @@ -270,17 +287,14 @@ static gboolean fu_vli_usbhub_pd_device_probe(FuDevice *device, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); - if (parent != NULL) { - fu_device_incorporate(device, - FU_DEVICE(parent), - FU_DEVICE_INCORPORATE_FLAG_PHYSICAL_ID); - } + FuDevice *parent = fu_device_get_parent(device, NULL); + if (parent != NULL) + fu_device_incorporate(device, parent, FU_DEVICE_INCORPORATE_FLAG_PHYSICAL_ID); return TRUE; } static void -fu_vli_usbhub_pd_device_set_progress(FuDevice *self, FuProgress *progress) +fu_vli_usbhub_pd_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -300,9 +314,10 @@ static void fu_vli_usbhub_pd_device_init(FuVliUsbhubPdDevice *self) { - fu_device_add_icon(FU_DEVICE(self), "usb-hub"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_USB_HUB); fu_device_add_protocol(FU_DEVICE(self), "com.vli.usbhub"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); fu_device_set_install_duration(FU_DEVICE(self), 15); /* seconds */ diff -Nru fwupd-2.0.8/plugins/vli/fu-vli-usbhub-rtd21xx-device.c fwupd-2.0.20/plugins/vli/fu-vli-usbhub-rtd21xx-device.c --- fwupd-2.0.8/plugins/vli/fu-vli-usbhub-rtd21xx-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli-usbhub-rtd21xx-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -29,23 +29,6 @@ #define ISP_DATA_BLOCKSIZE 30 #define ISP_PACKET_SIZE 32 -typedef enum { - ISP_STATUS_BUSY = 0xBB, /* host must wait for device */ - ISP_STATUS_IDLE_SUCCESS = 0x11, /* previous command was OK */ - ISP_STATUS_IDLE_FAILURE = 0x12, /* previous command failed */ -} IspStatus; - -typedef enum { - ISP_CMD_ENTER_FW_UPDATE = 0x01, - ISP_CMD_GET_PROJECT_ID_ADDR = 0x02, - ISP_CMD_SYNC_IDENTIFY_CODE = 0x03, - ISP_CMD_GET_FW_INFO = 0x04, - ISP_CMD_FW_UPDATE_START = 0x05, - ISP_CMD_FW_UPDATE_ISP_DONE = 0x06, - ISP_CMD_FW_UPDATE_EXIT = 0x07, - ISP_CMD_FW_UPDATE_RESET = 0x08, -} IspCmd; - static gboolean fu_vli_usbhub_rtd21xx_device_i2c_write(FuVliUsbhubDevice *self, guint8 target_addr, @@ -110,7 +93,7 @@ FU_VLI_DEVICE_TIMEOUT, NULL, error)) { - g_prefix_error(error, "failed to read I2C: "); + g_prefix_error_literal(error, "failed to read I2C: "); return FALSE; } fu_dump_raw(G_LOG_DOMAIN, "I2cReadData", data, datasz); @@ -122,8 +105,12 @@ guint8 *status, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(FU_DEVICE(self))); + FuVliUsbhubDevice *parent; guint8 buf[] = {0x00}; + + parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(FU_DEVICE(self), error)); + if (parent == NULL) + return FALSE; if (!fu_vli_usbhub_rtd21xx_device_i2c_read(parent, UC_FOREGROUND_TARGET_ADDR, UC_FOREGROUND_STATUS, @@ -143,7 +130,7 @@ guint8 status = 0xfd; if (!fu_vli_usbhub_rtd21xx_device_read_status_raw(self, &status, error)) return FALSE; - if (status == ISP_STATUS_BUSY) { + if (status == FU_VLI_USBHUB_RTD21XX_ISP_STATUS_BUSY) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "status was 0x%02x", status); return FALSE; } @@ -165,18 +152,21 @@ static gboolean fu_vli_usbhub_rtd21xx_device_ensure_version_unlocked(FuVliUsbhubRtd21xxDevice *self, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(FU_DEVICE(self))); + FuVliUsbhubDevice *parent; guint8 buf_rep[7] = {0x00}; - guint8 buf_req[] = {ISP_CMD_GET_FW_INFO}; + guint8 buf_req[] = {FU_VLI_USBHUB_RTD21XX_ISP_CMD_GET_FW_INFO}; g_autofree gchar *version = NULL; + parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(FU_DEVICE(self), error)); + if (parent == NULL) + return FALSE; if (!fu_vli_usbhub_rtd21xx_device_i2c_write(parent, UC_FOREGROUND_TARGET_ADDR, UC_FOREGROUND_OPCODE, buf_req, sizeof(buf_req), error)) { - g_prefix_error(error, "failed to get version number: "); + g_prefix_error_literal(error, "failed to get version number: "); return FALSE; } @@ -188,7 +178,7 @@ buf_rep, sizeof(buf_rep), error)) { - g_prefix_error(error, "failed to get version number: "); + g_prefix_error_literal(error, "failed to get version number: "); return FALSE; } @@ -221,10 +211,14 @@ static gboolean fu_vli_usbhub_rtd21xx_device_detach_raw(FuVliUsbhubRtd21xxDevice *self, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(FU_DEVICE(self))); + FuVliUsbhubDevice *parent; guint8 buf[] = {0x03}; + + parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(FU_DEVICE(self), error)); + if (parent == NULL) + return FALSE; if (!fu_vli_usbhub_rtd21xx_device_i2c_write(parent, 0x6A, 0x31, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to detach: "); + g_prefix_error_literal(error, "failed to detach: "); return FALSE; } return TRUE; @@ -239,7 +233,7 @@ return FALSE; if (!fu_vli_usbhub_rtd21xx_device_read_status_raw(self, &status, error)) return FALSE; - if (status != ISP_STATUS_IDLE_SUCCESS) { + if (status != FU_VLI_USBHUB_RTD21XX_ISP_STATUS_IDLE_SUCCESS) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -253,10 +247,13 @@ static gboolean fu_vli_usbhub_rtd21xx_device_detach(FuDevice *device, FuProgress *progress, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; g_autoptr(FuDeviceLocker) locker = NULL; /* open device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -271,12 +268,15 @@ static gboolean fu_vli_usbhub_rtd21xx_device_attach(FuDevice *device, FuProgress *progress, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); - guint8 buf[] = {ISP_CMD_FW_UPDATE_RESET}; + FuVliUsbhubDevice *parent; + guint8 buf[] = {FU_VLI_USBHUB_RTD21XX_ISP_CMD_FW_UPDATE_RESET}; g_autoptr(FuDeviceLocker) locker = NULL; /* open device */ - locker = fu_device_locker_new(parent, error); + parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) + return FALSE; + locker = fu_device_locker_new(FU_DEVICE(parent), error); if (locker == NULL) return FALSE; if (!fu_vli_usbhub_rtd21xx_device_i2c_write(parent, @@ -285,7 +285,7 @@ buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to attach: "); + g_prefix_error_literal(error, "failed to attach: "); return FALSE; } @@ -301,7 +301,7 @@ FwupdInstallFlags flags, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuVliUsbhubDevice *parent; FuVliUsbhubRtd21xxDevice *self = FU_VLI_USBHUB_RTD21XX_DEVICE(device); guint32 project_addr; guint8 project_id_count; @@ -319,7 +319,10 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 10, NULL); /* open device */ - locker = fu_device_locker_new(parent, error); + parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device, error)); + if (parent == NULL) + return FALSE; + locker = fu_device_locker_new(FU_DEVICE(parent), error); if (locker == NULL) return FALSE; @@ -329,7 +332,7 @@ return FALSE; /* enable ISP high priority */ - write_buf[0] = ISP_CMD_ENTER_FW_UPDATE; + write_buf[0] = FU_VLI_USBHUB_RTD21XX_ISP_CMD_ENTER_FW_UPDATE; write_buf[1] = 0x01; if (!fu_vli_usbhub_rtd21xx_device_i2c_write(parent, UC_FOREGROUND_TARGET_ADDR, @@ -337,21 +340,21 @@ write_buf, 2, error)) { - g_prefix_error(error, "failed to enable ISP: "); + g_prefix_error_literal(error, "failed to enable ISP: "); return FALSE; } if (!fu_vli_usbhub_rtd21xx_device_read_status(self, NULL, error)) return FALSE; /* get project ID address */ - write_buf[0] = ISP_CMD_GET_PROJECT_ID_ADDR; + write_buf[0] = FU_VLI_USBHUB_RTD21XX_ISP_CMD_GET_PROJECT_ID_ADDR; if (!fu_vli_usbhub_rtd21xx_device_i2c_write(parent, UC_FOREGROUND_TARGET_ADDR, UC_FOREGROUND_OPCODE, write_buf, 1, error)) { - g_prefix_error(error, "failed to get project ID address: "); + g_prefix_error_literal(error, "failed to get project ID address: "); return FALSE; } @@ -363,11 +366,15 @@ read_buf, 6, error)) { - g_prefix_error(error, "failed to read project ID: "); + g_prefix_error_literal(error, "failed to read project ID: "); return FALSE; } - if (read_buf[0] != ISP_STATUS_IDLE_SUCCESS) { - g_prefix_error(error, "failed project ID with error 0x%02x: ", read_buf[0]); + if (read_buf[0] != FU_VLI_USBHUB_RTD21XX_ISP_STATUS_IDLE_SUCCESS) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed project ID with error 0x%02x", + read_buf[0]); return FALSE; } @@ -380,7 +387,7 @@ error)) return FALSE; project_id_count = read_buf[5]; - write_buf[0] = ISP_CMD_SYNC_IDENTIFY_CODE; + write_buf[0] = FU_VLI_USBHUB_RTD21XX_ISP_CMD_SYNC_IDENTIFY_CODE; if (!fu_input_stream_read_safe(stream, write_buf, sizeof(write_buf), @@ -397,14 +404,14 @@ write_buf, project_id_count + 1, error)) { - g_prefix_error(error, "failed to send fw update start cmd: "); + g_prefix_error_literal(error, "failed to send fw update start cmd: "); return FALSE; } if (!fu_vli_usbhub_rtd21xx_device_read_status(self, NULL, error)) return FALSE; /* background FW update start command */ - write_buf[0] = ISP_CMD_FW_UPDATE_START; + write_buf[0] = FU_VLI_USBHUB_RTD21XX_ISP_CMD_FW_UPDATE_START; fu_memwrite_uint16(write_buf + 1, ISP_DATA_BLOCKSIZE, G_BIG_ENDIAN); if (!fu_vli_usbhub_rtd21xx_device_i2c_write(parent, UC_FOREGROUND_TARGET_ADDR, @@ -412,7 +419,7 @@ write_buf, 3, error)) { - g_prefix_error(error, "failed to send fw update start cmd: "); + g_prefix_error_literal(error, "failed to send fw update start cmd: "); return FALSE; } fu_progress_step_done(progress); @@ -456,14 +463,14 @@ /* update finish command */ if (!fu_vli_usbhub_rtd21xx_device_read_status(self, NULL, error)) return FALSE; - write_buf[0] = ISP_CMD_FW_UPDATE_ISP_DONE; + write_buf[0] = FU_VLI_USBHUB_RTD21XX_ISP_CMD_FW_UPDATE_ISP_DONE; if (!fu_vli_usbhub_rtd21xx_device_i2c_write(parent, UC_FOREGROUND_TARGET_ADDR, UC_FOREGROUND_OPCODE, write_buf, 1, error)) { - g_prefix_error(error, "failed update finish cmd: "); + g_prefix_error_literal(error, "failed update finish cmd: "); return FALSE; } fu_progress_step_done(progress); @@ -471,14 +478,14 @@ /* exit background-fw mode */ if (!fu_vli_usbhub_rtd21xx_device_read_status(self, NULL, error)) return FALSE; - write_buf[0] = ISP_CMD_FW_UPDATE_EXIT; + write_buf[0] = FU_VLI_USBHUB_RTD21XX_ISP_CMD_FW_UPDATE_EXIT; if (!fu_vli_usbhub_rtd21xx_device_i2c_write(parent, UC_FOREGROUND_TARGET_ADDR, UC_FOREGROUND_OPCODE, write_buf, 1, error)) { - g_prefix_error(error, "FwUpdate exit: "); + g_prefix_error_literal(error, "fwUpdate exit: "); return FALSE; } @@ -494,10 +501,13 @@ static gboolean fu_vli_usbhub_rtd21xx_device_reload(FuDevice *device, GError **error) { - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuDevice *parent; g_autoptr(FuDeviceLocker) locker = NULL; /* open parent device */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; @@ -508,7 +518,7 @@ fu_vli_usbhub_rtd21xx_device_probe(FuDevice *device, GError **error) { FuVliDeviceKind device_kind = FU_VLI_DEVICE_KIND_RTD21XX; - FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device)); + FuVliUsbhubDevice *parent = FU_VLI_USBHUB_DEVICE(fu_device_get_parent(device, NULL)); fu_device_set_name(device, fu_vli_device_kind_to_string(device_kind)); if (parent != NULL) { @@ -523,7 +533,7 @@ } static void -fu_vli_usbhub_rtd21xx_device_set_progress(FuDevice *self, FuProgress *progress) +fu_vli_usbhub_rtd21xx_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); @@ -537,7 +547,7 @@ static void fu_vli_usbhub_rtd21xx_device_init(FuVliUsbhubRtd21xxDevice *self) { - fu_device_add_icon(FU_DEVICE(self), "video-display"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_VIDEO_DISPLAY); fu_device_add_protocol(FU_DEVICE(self), "com.vli.i2c"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_DUAL_IMAGE); diff -Nru fwupd-2.0.8/plugins/vli/fu-vli.rs fwupd-2.0.20/plugins/vli/fu-vli.rs --- fwupd-2.0.8/plugins/vli/fu-vli.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/fu-vli.rs 2026-02-26 11:36:18.000000000 +0000 @@ -9,6 +9,38 @@ pid: u16le, } +enum FuVliUsbhubFlashmapAddr { + Hd1 = 0x0, + Hd1Backup = 0x1800, + Hd2 = 0x1000, + Fw = 0x2000, + PdLegacy = 0x10000, + Pd = 0x20000, + PdBackup = 0x30000, +} + +enum FuVliPdRegisterAddress { + GpioControlA = 0x0003, + ProjLegacy = 0x0018, + ProjIdHigh = 0x009C, + ProjIdLow = 0x009D, +} + +enum FuVliPdDeviceSpiCmd { + ReadStatus = 0xC2, + ReadData = 0xC4, + ChipErase = 0xD1, + SectorErase = 0xD2, + WriteEnable = 0xD4, + WriteStatus = 0xD8, + WriteData = 0xDC, +} + +enum FuVliPdDeviceCmd { + AccessRegister = 0xE0, + GetFwVersion = 0xE2, +} + #[derive(New, Parse, ParseStream, ToString)] #[repr(C, packed)] struct FuStructVliUsbhubHdr { @@ -78,3 +110,42 @@ Ps186 = 0xf186, // guessed Rtd21xx = 0xff00, // guessed } + +enum FuVliDeviceFwTag { + Vl100a = 0x01, + Vl100b = 0x02, + Vl100c = 0x03, + Vl101a = 0x04, + Vl101b = 0x05, + Vl101c = 0x06, + Vl102a = 0x07, + Vl102b = 0x08, + Vl103a = 0x09, + Vl103b = 0x0A, + Vl104 = 0x0B, + Vl105 = 0x0C, + Vl106 = 0x0D, + Vl107 = 0x0E, + Vl108a = 0xA1, + Vl108b = 0xB1, + Vl109a = 0xA2, + Vl109b = 0xB2, +} + +enum FuVliUsbhubRtd21xxIspStatus { + Busy = 0xBB, // host must wait for device + IdleSuccess = 0x11, // previous command was OK + IdleFailure = 0x12, // previous command failed +} + +enum FuVliUsbhubRtd21xxIspCmd { + None, + EnterFwUpdate, + GetProjectIdAddr, + SyncIdentifyCode, + GetFwInfo, + FwUpdateStart, + FwUpdateIspDone, + FwUpdateExit, + FwUpdateReset, +} diff -Nru fwupd-2.0.8/plugins/vli/meson.build fwupd-2.0.20/plugins/vli/meson.build --- fwupd-2.0.8/plugins/vli/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -61,6 +61,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: cargs, ) diff -Nru fwupd-2.0.8/plugins/vli/tests/bizlink-no-sku-vli.json fwupd-2.0.20/plugins/vli/tests/bizlink-no-sku-vli.json --- fwupd-2.0.8/plugins/vli/tests/bizlink-no-sku-vli.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/tests/bizlink-no-sku-vli.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/e0d6f62140663744f114d59b1ec48dd3e4743006bb06d0643efe178b8bdb2a04-BizLink-Cayenne_New.cab", + "url": "e0d6f62140663744f114d59b1ec48dd3e4743006bb06d0643efe178b8bdb2a04-BizLink-Cayenne_New.cab", "components": [ { "name": "tier1", @@ -22,7 +22,7 @@ ] }, { - "url": "https://fwupd.org/downloads/baf2f1a78334b7722d913ffa468240f728a09d91d0d2f755ff5515d4fed111c1-BizLink-Cayenne_Old.cab", + "url": "baf2f1a78334b7722d913ffa468240f728a09d91d0d2f755ff5515d4fed111c1-BizLink-Cayenne_Old.cab", "components": [ { "name": "tier1", diff -Nru fwupd-2.0.8/plugins/vli/tests/hyper-no-sku-vli.json fwupd-2.0.20/plugins/vli/tests/hyper-no-sku-vli.json --- fwupd-2.0.8/plugins/vli/tests/hyper-no-sku-vli.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/tests/hyper-no-sku-vli.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/ecfebc47d63a5319e35da39bd6124b80e90138a208bc9134c9d1b256b232a704-Hyper-USB-C_Hub.cab", + "url": "ecfebc47d63a5319e35da39bd6124b80e90138a208bc9134c9d1b256b232a704-Hyper-USB-C_Hub.cab", "components": [ { "name": "tier1", @@ -22,7 +22,7 @@ ] }, { - "url": "https://fwupd.org/downloads/1694cceda16068b24d7627caace4bd373ecae73aca891f610dec0fe5a4d1207c-Hyper-USB-C_Hub_Old.cab", + "url": "1694cceda16068b24d7627caace4bd373ecae73aca891f610dec0fe5a4d1207c-Hyper-USB-C_Hub_Old.cab", "components": [ { "name": "tier1", diff -Nru fwupd-2.0.8/plugins/vli/tests/lenovo-03x7168.json fwupd-2.0.20/plugins/vli/tests/lenovo-03x7168.json --- fwupd-2.0.8/plugins/vli/tests/lenovo-03x7168.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/tests/lenovo-03x7168.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/9ef0bb237baf8043f3ff16db5a8dd23bfd63fc24ea0eca2a6af3285792aa283b-Lenovo-USB-C_to_HDMI.cab", + "url": "9ef0bb237baf8043f3ff16db5a8dd23bfd63fc24ea0eca2a6af3285792aa283b-Lenovo-USB-C_to_HDMI.cab", "components": [ { "version": "130.4.22.1", @@ -14,7 +14,7 @@ ] }, { - "url": "https://fwupd.org/downloads/5b06a36aa0f2b99fc33f43a26a553d95c2d8ac46a7f5a2e45a840570456fe29b-Lenovo-USB-C_to_HDMI.cab", + "url": "5b06a36aa0f2b99fc33f43a26a553d95c2d8ac46a7f5a2e45a840570456fe29b-Lenovo-USB-C_to_HDMI.cab", "components": [ { "version": "130.4.23.1", diff -Nru fwupd-2.0.8/plugins/vli/tests/lenovo-03x7605.json fwupd-2.0.20/plugins/vli/tests/lenovo-03x7605.json --- fwupd-2.0.8/plugins/vli/tests/lenovo-03x7605.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/tests/lenovo-03x7605.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/61b393d8e27503746bae9a16dccf54929b9f1a0d1b5106334933a7313596c00c-Lenovo-USB-C_to_HDMI.cab", + "url": "61b393d8e27503746bae9a16dccf54929b9f1a0d1b5106334933a7313596c00c-Lenovo-USB-C_to_HDMI.cab", "components": [ { "version": "153.84.6.1", @@ -14,7 +14,7 @@ ] }, { - "url": "https://fwupd.org/downloads/8be7eea7db239766cf92adf2145138c9929fd953d6d36e39d60c9dd6425638de-Lenovo-USB-C_to_HDMI.cab", + "url": "8be7eea7db239766cf92adf2145138c9929fd953d6d36e39d60c9dd6425638de-Lenovo-USB-C_to_HDMI.cab", "components": [ { "version": "153.84.7.1", diff -Nru fwupd-2.0.8/plugins/vli/tests/lenovo-03x7608-vli.json fwupd-2.0.20/plugins/vli/tests/lenovo-03x7608-vli.json --- fwupd-2.0.8/plugins/vli/tests/lenovo-03x7608-vli.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/tests/lenovo-03x7608-vli.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/ebfda3c96543d6d7ad97a972344f4d34a14549c535095bfbb1860115a88e6ff6-Lenovo-TravalHub-2020-12-11-153442.cab", + "url": "ebfda3c96543d6d7ad97a972344f4d34a14549c535095bfbb1860115a88e6ff6-Lenovo-TravalHub-2020-12-11-153442.cab", "components": [ { "name": "tier1", diff -Nru fwupd-2.0.8/plugins/vli/tests/lenovo-40au0065-vli.json fwupd-2.0.20/plugins/vli/tests/lenovo-40au0065-vli.json --- fwupd-2.0.8/plugins/vli/tests/lenovo-40au0065-vli.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/tests/lenovo-40au0065-vli.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/3b183e21869e4fce8bc81b3357de2fd1ee47b35e272e26169ebaee88faa39e03-Lenovo-Mini_Dock_New.cab", + "url": "3b183e21869e4fce8bc81b3357de2fd1ee47b35e272e26169ebaee88faa39e03-Lenovo-Mini_Dock_New.cab", "components": [ { "name": "tier1", @@ -29,7 +29,7 @@ ] }, { - "url": "https://fwupd.org/downloads/f55a307af1dc66d46bc12460ced828b31b2ee2a78c1d58d4f474958c2c6d134e-Lenovo-Mini_Dock_Old.cab", + "url": "f55a307af1dc66d46bc12460ced828b31b2ee2a78c1d58d4f474958c2c6d134e-Lenovo-Mini_Dock_Old.cab", "components": [ { "name": "tier1", diff -Nru fwupd-2.0.8/plugins/vli/tests/lenovo-GX90T33021-vli.json fwupd-2.0.20/plugins/vli/tests/lenovo-GX90T33021-vli.json --- fwupd-2.0.8/plugins/vli/tests/lenovo-GX90T33021-vli.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/vli/tests/lenovo-GX90T33021-vli.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/2004603b40bd529d85c2fcfcf9d50d77b47229e6564ef0ea9c03633bdccff94d-Lenovo-Travel_Hub_1in3_New.cab", - "emulation-url": "https://fwupd.org/downloads/de48f2281354c4479b8b2302cf85b9643b0ca248fcf4beb19a71f5aba7927278-Lenovo-Travel_Hub_1in3_New.zip", + "url": "2004603b40bd529d85c2fcfcf9d50d77b47229e6564ef0ea9c03633bdccff94d-Lenovo-Travel_Hub_1in3_New.cab", + "emulation-url": "de48f2281354c4479b8b2302cf85b9643b0ca248fcf4beb19a71f5aba7927278-Lenovo-Travel_Hub_1in3_New.zip", "components": [ { "name": "tier1", @@ -16,8 +16,8 @@ ] }, { - "url": "https://fwupd.org/downloads/5fb4f4dce233626558806c2b7474d3b5ed4f500f6013aa3b376a54322642d449-Lenovo-Travel_Hub_1in3_Old.cab", - "emulation-url": "https://fwupd.org/downloads/58221c63de8cdec9b4db3cc72b1e0303b6cfee80b7e7965edad9816c124b0cd7-Lenovo-Travel_Hub_1in3_Old.zip", + "url": "5fb4f4dce233626558806c2b7474d3b5ed4f500f6013aa3b376a54322642d449-Lenovo-Travel_Hub_1in3_Old.cab", + "emulation-url": "58221c63de8cdec9b4db3cc72b1e0303b6cfee80b7e7965edad9816c124b0cd7-Lenovo-Travel_Hub_1in3_Old.zip", "components": [ { "name": "tier1", diff -Nru fwupd-2.0.8/plugins/wacom-raw/README.md fwupd-2.0.20/plugins/wacom-raw/README.md --- fwupd-2.0.8/plugins/wacom-raw/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -65,10 +65,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.2.4`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Tatsunosuke Tobita: @flying-elephant diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-aes-device.c fwupd-2.0.20/plugins/wacom-raw/fu-wacom-aes-device.c --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-aes-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-aes-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,291 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-wacom-aes-device.h" -#include "fu-wacom-common.h" - -struct _FuWacomAesDevice { - FuWacomDevice parent_instance; -}; - -G_DEFINE_TYPE(FuWacomAesDevice, fu_wacom_aes_device, FU_TYPE_WACOM_DEVICE) - -static gboolean -fu_wacom_aes_device_add_recovery_hwid(FuWacomAesDevice *self, GError **error) -{ - guint16 pid; - g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); - g_autoptr(FuStructWacomRawBlVerifyResponse) st_rsp = NULL; - - fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); - fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_VERIFY_FLASH); - fu_struct_wacom_raw_request_set_echo(st_req, 0x01); - fu_struct_wacom_raw_request_set_addr(st_req, FU_WACOM_RAW_BL_START_ADDR); - fu_struct_wacom_raw_request_set_size8(st_req, FU_WACOM_RAW_BL_BYTES_CHECK / 8); - if (!fu_wacom_device_set_feature(FU_WACOM_DEVICE(self), st_req->data, st_req->len, error)) { - g_prefix_error(error, "failed to send: "); - return FALSE; - } - if (!fu_wacom_device_get_feature(FU_WACOM_DEVICE(self), st_req->data, st_req->len, error)) { - g_prefix_error(error, "failed to receive: "); - return FALSE; - } - st_rsp = - fu_struct_wacom_raw_bl_verify_response_parse(st_req->data, st_req->len, 0x0, error); - if (st_rsp == NULL) - return FALSE; - if (fu_struct_wacom_raw_bl_verify_response_get_size8(st_rsp) != - fu_struct_wacom_raw_request_get_size8(st_req)) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "firmware does not support this feature"); - return FALSE; - } - pid = fu_struct_wacom_raw_bl_verify_response_get_pid(st_rsp); - if (pid == 0xFFFF || pid == 0x0000) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "invalid recovery product ID %04x", - pid); - return FALSE; - } - - /* add recovery IDs */ - fu_device_add_instance_u16(FU_DEVICE(self), "VEN", 0x2D1F); - fu_device_add_instance_u16(FU_DEVICE(self), "DEV", pid); - if (!fu_device_build_instance_id(FU_DEVICE(self), error, "HIDRAW", "VID", "PID", NULL)) - return FALSE; - fu_device_add_instance_u16(FU_DEVICE(self), "VEN", 0x056A); - return fu_device_build_instance_id(FU_DEVICE(self), error, "HIDRAW", "VID", "PID", NULL); -} - -static gboolean -fu_wacom_aes_device_query_operation_mode(FuWacomAesDevice *self, - FuWacomRawOperationMode *mode, - GError **error) -{ - g_autoptr(GByteArray) st_req = fu_struct_wacom_raw_fw_query_mode_request_new(); - g_autoptr(GByteArray) st_rsp = NULL; - - if (!fu_wacom_device_get_feature(FU_WACOM_DEVICE(self), st_req->data, st_req->len, error)) - return FALSE; - st_rsp = - fu_struct_wacom_raw_fw_query_mode_response_parse(st_req->data, st_req->len, 0x0, error); - if (st_rsp == NULL) - return FALSE; - if (mode != NULL) - *mode = fu_struct_wacom_raw_fw_query_mode_response_get_mode(st_rsp); - return TRUE; -} - -static gboolean -fu_wacom_aes_device_setup(FuDevice *device, GError **error) -{ - FuWacomAesDevice *self = FU_WACOM_AES_DEVICE(device); - FuWacomRawOperationMode mode = 0; - g_autoptr(GError) error_local = NULL; - - /* find out if in bootloader mode already */ - if (!fu_wacom_aes_device_query_operation_mode(self, &mode, error)) - return FALSE; - - if (mode == FU_WACOM_RAW_OPERATION_MODE_BOOTLOADER) { - fu_device_set_version(device, "0.0"); - /* get the recovery PID if supported */ - if (!fu_wacom_aes_device_add_recovery_hwid(self, &error_local)) - g_debug("failed to get HwID: %s", error_local->message); - } else if (mode == FU_WACOM_RAW_OPERATION_MODE_RUNTIME) { - g_autofree gchar *version = NULL; - g_autoptr(FuStructWacomRawFwStatusRequest) st_req = - fu_struct_wacom_raw_fw_status_request_new(); - g_autoptr(FuStructWacomRawFwStatusResponse) st_rsp = NULL; - - /* get firmware version */ - if (!fu_wacom_device_get_feature(FU_WACOM_DEVICE(self), - st_req->data, - st_req->len, - error)) - return FALSE; - st_rsp = fu_struct_wacom_raw_fw_status_response_parse(st_req->data, - st_req->len, - 0x0, - error); - if (st_rsp == NULL) - return FALSE; - version = g_strdup_printf( - "%04x.%02x", - fu_struct_wacom_raw_fw_status_response_get_version_major(st_rsp), - fu_struct_wacom_raw_fw_status_response_get_version_minor(st_rsp)); - fu_device_set_version(device, version); - } else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to query operation mode, got 0x%x", - mode); - return FALSE; - } - - /* success */ - return TRUE; -} - -static gboolean -fu_wacom_aes_device_attach(FuDevice *device, FuProgress *progress, GError **error) -{ - FuWacomDevice *self = FU_WACOM_DEVICE(device); - g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); - - if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { - g_debug("already in runtime mode, skipping"); - return TRUE; - } - - fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_TYPE); - fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_TYPE_FINALIZER); - if (!fu_wacom_device_set_feature(self, st_req->data, st_req->len, error)) { - g_prefix_error(error, "failed to finalize the device: "); - return FALSE; - } - - /* does the device have to replug to bootloader mode */ - if (fu_device_has_private_flag(device, FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG)) { - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - } else { - /* wait for device back to runtime mode */ - fu_device_sleep(device, 500); /* ms */ - fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); - } - - /* success */ - return TRUE; -} - -static gboolean -fu_wacom_aes_device_erase_all(FuWacomAesDevice *self, FuProgress *progress, GError **error) -{ - g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); - - fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); - fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_ALL_ERASE); - fu_struct_wacom_raw_request_set_echo(st_req, - fu_wacom_device_get_echo_next(FU_WACOM_DEVICE(self))); - if (!fu_wacom_device_cmd(FU_WACOM_DEVICE(self), - st_req, - NULL, - 2000, /* this takes a long time */ - FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING, - error)) { - g_prefix_error(error, "failed to send eraseall command: "); - return FALSE; - } - fu_device_sleep_full(FU_DEVICE(self), 2000, progress); - return TRUE; -} - -static gboolean -fu_wacom_aes_device_write_block(FuWacomAesDevice *self, - guint32 idx, - guint32 address, - const guint8 *data, - gsize datasz, - GError **error) -{ - gsize blocksz = fu_wacom_device_get_block_sz(FU_WACOM_DEVICE(self)); - g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); - - /* check size */ - if (datasz != blocksz) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "block size 0x%x != 0x%x untested", - (guint)datasz, - (guint)blocksz); - return FALSE; - } - - /* write */ - fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); - fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_WRITE_FLASH); - fu_struct_wacom_raw_request_set_echo(st_req, (guint8)idx + 1); - fu_struct_wacom_raw_request_set_addr(st_req, address); - fu_struct_wacom_raw_request_set_size8(st_req, datasz / 8); - if (!fu_struct_wacom_raw_request_set_data(st_req, data, datasz, error)) - return FALSE; - if (!fu_wacom_device_cmd(FU_WACOM_DEVICE(self), - st_req, - NULL, - 1, /* ms */ - FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING, - error)) { - g_prefix_error(error, "failed to write block %u: ", idx); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_wacom_aes_device_write_firmware(FuDevice *device, - FuChunkArray *chunks, - FuProgress *progress, - GError **error) -{ - FuWacomAesDevice *self = FU_WACOM_AES_DEVICE(device); - - /* progress */ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 28, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 72, NULL); - - /* erase */ - if (!fu_wacom_aes_device_erase_all(self, fu_progress_get_child(progress), error)) - return FALSE; - fu_progress_step_done(progress); - - /* write */ - for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - g_autoptr(FuChunk) chk = NULL; - - /* prepare chunk */ - chk = fu_chunk_array_index(chunks, i, error); - if (chk == NULL) - return FALSE; - if (!fu_wacom_aes_device_write_block(self, - fu_chunk_get_idx(chk), - fu_chunk_get_address(chk), - fu_chunk_get_data(chk), - fu_chunk_get_data_sz(chk), - error)) - return FALSE; - fu_progress_set_percentage_full(fu_progress_get_child(progress), - (gsize)i, - (gsize)fu_chunk_array_length(chunks)); - } - fu_progress_step_done(progress); - return TRUE; -} - -static void -fu_wacom_aes_device_init(FuWacomAesDevice *self) -{ - fu_device_set_name(FU_DEVICE(self), "Wacom AES Device"); - fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); -} - -static void -fu_wacom_aes_device_class_init(FuWacomAesDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - FuWacomDeviceClass *wac_device_class = FU_WACOM_DEVICE_CLASS(klass); - device_class->setup = fu_wacom_aes_device_setup; - device_class->attach = fu_wacom_aes_device_attach; - wac_device_class->write_firmware = fu_wacom_aes_device_write_firmware; -} diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-aes-device.h fwupd-2.0.20/plugins/wacom-raw/fu-wacom-aes-device.h --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-aes-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-aes-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "fu-wacom-device.h" - -#define FU_TYPE_WACOM_AES_DEVICE (fu_wacom_aes_device_get_type()) -G_DECLARE_FINAL_TYPE(FuWacomAesDevice, fu_wacom_aes_device, FU, WACOM_AES_DEVICE, FuWacomDevice) diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-common.c fwupd-2.0.20/plugins/wacom-raw/fu-wacom-common.c --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-common.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,101 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include - -#include "fu-wacom-common.h" - -gboolean -fu_wacom_common_check_reply(const FuStructWacomRawRequest *st_req, - const FuStructWacomRawResponse *st_rsp, - GError **error) -{ - if (fu_struct_wacom_raw_response_get_report_id(st_rsp) != FU_WACOM_RAW_BL_REPORT_ID_GET) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "report ID failed, expected 0x%02x, got 0x%02x", - (guint)FU_WACOM_RAW_BL_REPORT_ID_GET, - fu_struct_wacom_raw_request_get_report_id(st_req)); - return FALSE; - } - if (fu_struct_wacom_raw_request_get_cmd(st_req) != - fu_struct_wacom_raw_response_get_cmd(st_rsp)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "cmd failed, expected 0x%02x, got 0x%02x", - fu_struct_wacom_raw_request_get_cmd(st_req), - fu_struct_wacom_raw_response_get_cmd(st_rsp)); - return FALSE; - } - if (fu_struct_wacom_raw_request_get_echo(st_req) != - fu_struct_wacom_raw_response_get_echo(st_rsp)) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "echo failed, expected 0x%02x, got 0x%02x", - fu_struct_wacom_raw_request_get_echo(st_req), - fu_struct_wacom_raw_response_get_echo(st_rsp)); - return FALSE; - } - return TRUE; -} - -gboolean -fu_wacom_common_rc_set_error(const FuStructWacomRawResponse *st_rsp, GError **error) -{ - FuWacomRawRc rc = fu_struct_wacom_raw_response_get_resp(st_rsp); - if (rc == FU_WACOM_RAW_RC_OK) - return TRUE; - if (rc == FU_WACOM_RAW_RC_BUSY) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_BUSY, "device is busy"); - return FALSE; - } - if (rc == FU_WACOM_RAW_RC_MCUTYPE) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "MCU type does not match"); - return FALSE; - } - if (rc == FU_WACOM_RAW_RC_PID) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_DATA, "PID does not match"); - return FALSE; - } - if (rc == FU_WACOM_RAW_RC_CHECKSUM1) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "checksum1 does not match"); - return FALSE; - } - if (rc == FU_WACOM_RAW_RC_CHECKSUM2) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "checksum2 does not match"); - return FALSE; - } - if (rc == FU_WACOM_RAW_RC_TIMEOUT) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_TIMED_OUT, "command timed out"); - return FALSE; - } - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unknown error 0x%02x", rc); - return FALSE; -} - -gboolean -fu_wacom_common_block_is_empty(const guint8 *data, guint16 datasz) -{ - for (guint16 i = 0; i < datasz; i++) { - if (data[i] != 0xff) - return FALSE; - } - return TRUE; -} diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-common.h fwupd-2.0.20/plugins/wacom-raw/fu-wacom-common.h --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-common.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,27 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#include "fu-wacom-raw-struct.h" - -#define FU_WACOM_RAW_CMD_RETRIES 1000 - -#define FU_WACOM_RAW_BL_START_ADDR (0x11FF8) -#define FU_WACOM_RAW_BL_BYTES_CHECK 8 - -#define FU_WACOM_RAW_BL_TYPE_FINALIZER 0x00 - -gboolean -fu_wacom_common_rc_set_error(const FuStructWacomRawResponse *st_rsp, GError **error); -gboolean -fu_wacom_common_check_reply(const FuStructWacomRawRequest *st_req, - const FuStructWacomRawResponse *st_rsp, - GError **error); -gboolean -fu_wacom_common_block_is_empty(const guint8 *data, guint16 datasz); diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-device.c fwupd-2.0.20/plugins/wacom-raw/fu-wacom-device.c --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,401 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include "fu-wacom-common.h" -#include "fu-wacom-device.h" - -typedef struct { - guint flash_block_size; - guint32 flash_base_addr; - guint8 echo_next; -} FuWacomDevicePrivate; - -G_DEFINE_TYPE_WITH_PRIVATE(FuWacomDevice, fu_wacom_device, FU_TYPE_HIDRAW_DEVICE) - -#define GET_PRIVATE(o) (fu_wacom_device_get_instance_private(o)) - -static void -fu_wacom_device_to_string(FuDevice *device, guint idt, GString *str) -{ - FuWacomDevice *self = FU_WACOM_DEVICE(device); - FuWacomDevicePrivate *priv = GET_PRIVATE(self); - fwupd_codec_string_append_hex(str, idt, "FlashBlockSize", priv->flash_block_size); - fwupd_codec_string_append_hex(str, idt, "FlashBaseAddr", priv->flash_base_addr); - fwupd_codec_string_append_hex(str, idt, "EchoNext", priv->echo_next); -} - -#define FU_WACOM_RAW_ECHO_MIN 0xA0 -#define FU_WACOM_RAW_ECHO_MAX 0xFE - -guint8 -fu_wacom_device_get_echo_next(FuWacomDevice *self) -{ - FuWacomDevicePrivate *priv = GET_PRIVATE(self); - priv->echo_next++; - if (priv->echo_next > FU_WACOM_RAW_ECHO_MAX) - priv->echo_next = FU_WACOM_RAW_ECHO_MIN; - return priv->echo_next; -} - -gsize -fu_wacom_device_get_block_sz(FuWacomDevice *self) -{ - FuWacomDevicePrivate *priv = GET_PRIVATE(self); - return priv->flash_block_size; -} - -gboolean -fu_wacom_device_check_mpu(FuWacomDevice *self, GError **error) -{ - guint8 rsp_value = 0; - g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); - - fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); - fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_GET_MPUTYPE); - fu_struct_wacom_raw_request_set_echo(st_req, fu_wacom_device_get_echo_next(self)); - if (!fu_wacom_device_cmd(self, - st_req, - &rsp_value, - 0, - FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK, - error)) { - g_prefix_error(error, "failed to get MPU type: "); - return FALSE; - } - - /* W9013 */ - if (rsp_value == 0x2e) { - fu_device_add_instance_id_full(FU_DEVICE(self), - "WacomEMR_W9013", - FU_DEVICE_INSTANCE_FLAG_QUIRKS); - return TRUE; - } - - /* W9021 */ - if (rsp_value == 0x45) { - fu_device_add_instance_id_full(FU_DEVICE(self), - "WacomEMR_W9021", - FU_DEVICE_INSTANCE_FLAG_QUIRKS); - return TRUE; - } - - /* unsupported */ - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "MPU is not W9013 or W9021: 0x%x", - rsp_value); - return FALSE; -} - -static gboolean -fu_wacom_device_detach(FuDevice *device, FuProgress *progress, GError **error) -{ - FuWacomDevice *self = FU_WACOM_DEVICE(device); - g_autoptr(FuStructWacomRawFwDetachRequest) st = fu_struct_wacom_raw_fw_detach_request_new(); - g_autoptr(GError) error_local = NULL; - - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { - g_debug("already in bootloader mode, skipping"); - return TRUE; - } - if (!fu_wacom_device_set_feature(self, st->data, st->len, &error_local)) { - if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL)) { - g_debug("ignoring: %s", error_local->message); - } else { - g_propagate_prefixed_error(error, - g_steal_pointer(&error_local), - "failed to switch to bootloader mode: "); - return FALSE; - } - } - - /* does the device have to replug to bootloader mode */ - if (fu_device_has_private_flag(device, FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG)) { - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - } else { - fu_device_sleep(device, 300); /* ms */ - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); - } - return TRUE; -} - -static gboolean -fu_wacom_device_check_mode(FuWacomDevice *self, GError **error) -{ - guint8 rsp_value = 0; - g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); - - fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); - fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_CHECK_MODE); - fu_struct_wacom_raw_request_set_echo(st_req, fu_wacom_device_get_echo_next(self)); - if (!fu_wacom_device_cmd(self, - st_req, - &rsp_value, - 0, - FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK, - error)) { - g_prefix_error(error, "failed to check mode: "); - return FALSE; - } - if (rsp_value != 0x06) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "check mode failed, mode=0x%02x", - rsp_value); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_wacom_device_set_version_bootloader(FuWacomDevice *self, GError **error) -{ - guint8 rsp_value = 0; - g_autofree gchar *version = NULL; - g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); - - fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); - fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_GET_BLVER); - fu_struct_wacom_raw_request_set_echo(st_req, fu_wacom_device_get_echo_next(self)); - if (!fu_wacom_device_cmd(self, - st_req, - &rsp_value, - 0, - FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK, - error)) { - g_prefix_error(error, "failed to get bootloader version: "); - return FALSE; - } - version = g_strdup_printf("%u", rsp_value); - fu_device_set_version_bootloader(FU_DEVICE(self), version); - return TRUE; -} - -static gboolean -fu_wacom_device_write_firmware(FuDevice *device, - FuFirmware *firmware, - FuProgress *progress, - FwupdInstallFlags flags, - GError **error) -{ - FuWacomDevice *self = FU_WACOM_DEVICE(device); - FuWacomDevicePrivate *priv = GET_PRIVATE(self); - FuWacomDeviceClass *klass = FU_WACOM_DEVICE_GET_CLASS(device); - g_autoptr(GBytes) fw = NULL; - g_autoptr(FuChunkArray) chunks = NULL; - - /* use the correct image from the firmware */ - g_debug("using element at addr 0x%0x", (guint)fu_firmware_get_addr(firmware)); - - /* check start address and size */ - if (fu_firmware_get_addr(firmware) != priv->flash_base_addr) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "base addr invalid: 0x%05x", - (guint)fu_firmware_get_addr(firmware)); - return FALSE; - } - fw = fu_firmware_get_bytes(firmware, error); - if (fw == NULL) - return FALSE; - - /* we're in bootloader mode now */ - if (!fu_wacom_device_check_mode(self, error)) - return FALSE; - if (!fu_wacom_device_set_version_bootloader(self, error)) - return FALSE; - - /* flash chunks */ - chunks = fu_chunk_array_new_from_bytes(fw, - priv->flash_base_addr, - FU_CHUNK_PAGESZ_NONE, - priv->flash_block_size); - return klass->write_firmware(device, chunks, progress, error); -} - -gboolean -fu_wacom_device_get_feature(FuWacomDevice *self, guint8 *data, guint datasz, GError **error) -{ - return fu_hidraw_device_get_feature(FU_HIDRAW_DEVICE(self), - data, - datasz, - FU_IOCTL_FLAG_NONE, - error); -} - -gboolean -fu_wacom_device_set_feature(FuWacomDevice *self, const guint8 *data, guint datasz, GError **error) -{ - return fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), - data, - datasz, - FU_IOCTL_FLAG_NONE, - error); -} - -static gboolean -fu_wacom_device_cmd_response(FuWacomDevice *self, - const FuStructWacomRawRequest *st_req, - guint8 *rsp_value, - FuWacomDeviceCmdFlags flags, - GError **error) -{ - guint8 buf[FU_STRUCT_WACOM_RAW_RESPONSE_SIZE] = {FU_WACOM_RAW_BL_REPORT_ID_GET, 0x0}; - g_autoptr(FuStructWacomRawRequest) st_rsp = NULL; - - if (!fu_wacom_device_get_feature(self, buf, sizeof(buf), error)) { - g_prefix_error(error, "failed to receive: "); - return FALSE; - } - st_rsp = fu_struct_wacom_raw_response_parse(buf, sizeof(buf), 0x0, error); - if (st_rsp == NULL) - return FALSE; - if (!fu_wacom_common_check_reply(st_req, st_rsp, error)) - return FALSE; - if ((flags & FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK) == 0) { - if (!fu_wacom_common_rc_set_error(st_rsp, error)) - return FALSE; - } - - /* optional */ - if (rsp_value != NULL) - *rsp_value = fu_struct_wacom_raw_response_get_resp(st_rsp); - - /* success */ - return TRUE; -} - -typedef struct { - const FuStructWacomRawRequest *st_req; - guint8 *rsp_value; - FuWacomDeviceCmdFlags flags; -} FuWacomRawResponseHelper; - -static gboolean -fu_wacom_device_cmd_response_cb(FuDevice *device, gpointer user_data, GError **error) -{ - FuWacomDevice *self = FU_WACOM_DEVICE(device); - FuWacomRawResponseHelper *helper = (FuWacomRawResponseHelper *)user_data; - return fu_wacom_device_cmd_response(self, - helper->st_req, - helper->rsp_value, - helper->flags, - error); -} - -gboolean -fu_wacom_device_cmd(FuWacomDevice *self, - const FuStructWacomRawRequest *st_req, - guint8 *rsp_value, - guint delay_ms, - FuWacomDeviceCmdFlags flags, - GError **error) -{ - if (!fu_wacom_device_set_feature(self, st_req->data, st_req->len, error)) { - g_prefix_error(error, "failed to send: "); - return FALSE; - } - fu_device_sleep(FU_DEVICE(self), delay_ms); - if (flags & FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING) { - FuWacomRawResponseHelper helper = {.st_req = st_req, - .rsp_value = rsp_value, - .flags = flags}; - return fu_device_retry_full(FU_DEVICE(self), - fu_wacom_device_cmd_response_cb, - FU_WACOM_RAW_CMD_RETRIES, - delay_ms, - &helper, - error); - } - return fu_wacom_device_cmd_response(self, st_req, rsp_value, flags, error); -} - -static gboolean -fu_wacom_device_set_quirk_kv(FuDevice *device, const gchar *key, const gchar *value, GError **error) -{ - FuWacomDevice *self = FU_WACOM_DEVICE(device); - FuWacomDevicePrivate *priv = GET_PRIVATE(self); - guint64 tmp = 0; - - if (g_strcmp0(key, "WacomI2cFlashBlockSize") == 0) { - if (!fu_strtoull(value, &tmp, 0, G_MAXSIZE, FU_INTEGER_BASE_AUTO, error)) - return FALSE; - priv->flash_block_size = tmp; - return TRUE; - } - if (g_strcmp0(key, "WacomI2cFlashBaseAddr") == 0) { - if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) - return FALSE; - priv->flash_base_addr = tmp; - return TRUE; - } - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "quirk key not supported"); - return FALSE; -} - -static void -fu_wacom_device_replace(FuDevice *device, FuDevice *donor) -{ - g_return_if_fail(FU_IS_WACOM_DEVICE(device)); - g_return_if_fail(FU_IS_WACOM_DEVICE(donor)); - - /* copy private instance data */ - if (fu_device_has_private_flag(donor, FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG)) { - fu_device_add_private_flag(device, - FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG); - } -} - -static void -fu_wacom_device_set_progress(FuDevice *self, FuProgress *progress) -{ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 4, "detach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 92, "write"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 4, "attach"); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); -} - -static void -fu_wacom_device_init(FuWacomDevice *self) -{ - FuWacomDevicePrivate *priv = GET_PRIVATE(self); - priv->echo_next = FU_WACOM_RAW_ECHO_MIN; - fu_device_add_protocol(FU_DEVICE(self), "com.wacom.raw"); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); - fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); - fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); - fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); - fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); - fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_IHEX_FIRMWARE); - fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); - fu_device_register_private_flag(FU_DEVICE(self), - FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG); -} - -static void -fu_wacom_device_class_init(FuWacomDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - device_class->to_string = fu_wacom_device_to_string; - device_class->write_firmware = fu_wacom_device_write_firmware; - device_class->detach = fu_wacom_device_detach; - device_class->set_quirk_kv = fu_wacom_device_set_quirk_kv; - device_class->set_progress = fu_wacom_device_set_progress; - device_class->replace = fu_wacom_device_replace; -} diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-device.h fwupd-2.0.20/plugins/wacom-raw/fu-wacom-device.h --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,50 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include - -#include "fu-wacom-common.h" - -#define FU_TYPE_WACOM_DEVICE (fu_wacom_device_get_type()) -G_DECLARE_DERIVABLE_TYPE(FuWacomDevice, fu_wacom_device, FU, WACOM_DEVICE, FuHidrawDevice) - -struct _FuWacomDeviceClass { - FuHidrawDeviceClass parent_class; - gboolean (*write_firmware)(FuDevice *self, - FuChunkArray *chunks, - FuProgress *progress, - GError **error); -}; - -typedef enum { - FU_WACOM_DEVICE_CMD_FLAG_NONE = 0, - FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING = 1 << 0, - FU_WACOM_DEVICE_CMD_FLAG_NO_ERROR_CHECK = 1 << 1, -} FuWacomDeviceCmdFlags; - -#define FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG "requires-wait-for-replug" - -guint8 -fu_wacom_device_get_echo_next(FuWacomDevice *self); -gboolean -fu_wacom_device_set_feature(FuWacomDevice *self, const guint8 *data, guint datasz, GError **error); -gboolean -fu_wacom_device_get_feature(FuWacomDevice *self, guint8 *data, guint datasz, GError **error); -gboolean -fu_wacom_device_cmd(FuWacomDevice *self, - const FuStructWacomRawRequest *st_req, - guint8 *rsp_value, - guint delay_ms, - FuWacomDeviceCmdFlags flags, - GError **error); -gboolean -fu_wacom_device_erase_all(FuWacomDevice *self, GError **error); -gboolean -fu_wacom_device_check_mpu(FuWacomDevice *self, GError **error); -gsize -fu_wacom_device_get_block_sz(FuWacomDevice *self); diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-emr-device.c fwupd-2.0.20/plugins/wacom-raw/fu-wacom-emr-device.c --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-emr-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-emr-device.c 1970-01-01 00:00:00.000000000 +0000 @@ -1,303 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#include "config.h" - -#include - -#include "fu-wacom-common.h" -#include "fu-wacom-emr-device.h" - -struct _FuWacomEmrDevice { - FuWacomDevice parent_instance; -}; - -G_DEFINE_TYPE(FuWacomEmrDevice, fu_wacom_emr_device, FU_TYPE_WACOM_DEVICE) - -static gboolean -fu_wacom_emr_device_setup(FuDevice *device, GError **error) -{ - FuWacomEmrDevice *self = FU_WACOM_EMR_DEVICE(device); - - /* check MPU type */ - if (!fu_wacom_device_check_mpu(FU_WACOM_DEVICE(self), error)) - return FALSE; - - /* get firmware version */ - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { - fu_device_set_version_raw(device, 0); - } else { - guint16 fw_ver; - guint8 data[19] = {0x03, 0x0}; /* 0x03 is an unknown ReportID */ - if (!fu_wacom_device_get_feature(FU_WACOM_DEVICE(self), data, sizeof(data), error)) - return FALSE; - if (!fu_memread_uint16_safe(data, - sizeof(data), - 11, - &fw_ver, - G_LITTLE_ENDIAN, - error)) - return FALSE; - fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); - fu_device_set_version_raw(device, fw_ver); - } - - /* success */ - return TRUE; -} - -static guint8 -fu_wacom_emr_device_calc_checksum(guint8 init1, const guint8 *buf, gsize bufsz) -{ - return init1 + ~(fu_sum8(buf, bufsz)) + 1; -} - -static gboolean -fu_wacom_emr_device_w9013_erase_data(FuWacomEmrDevice *self, GError **error) -{ - g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); - guint8 *buf = st_req->data + FU_STRUCT_WACOM_RAW_REQUEST_OFFSET_ADDR; - - fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); - fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_ERASE_DATAMEM); - fu_struct_wacom_raw_request_set_echo(st_req, - fu_wacom_device_get_echo_next(FU_WACOM_DEVICE(self))); - buf[0] = 0x00; /* erased block */ - buf[1] = fu_wacom_emr_device_calc_checksum(0x05 + 0x00 + 0x07 + 0x00, - (const guint8 *)&st_req, - 4); - if (!fu_wacom_device_cmd(FU_WACOM_DEVICE(self), - st_req, - NULL, - 1, /* ms */ - FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING, - error)) { - g_prefix_error(error, "failed to erase datamem: "); - return FALSE; - } - g_usleep(50); - return TRUE; -} - -static gboolean -fu_wacom_emr_device_w9013_erase_code(FuWacomEmrDevice *self, - guint8 idx, - guint8 block_nr, - GError **error) -{ - g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); - guint8 *buf = st_req->data + FU_STRUCT_WACOM_RAW_REQUEST_OFFSET_ADDR; - - fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); - fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_ERASE_FLASH); - fu_struct_wacom_raw_request_set_echo(st_req, idx); - buf[0] = block_nr; - buf[1] = fu_wacom_emr_device_calc_checksum(0x05 + 0x00 + 0x07 + 0x00, - (const guint8 *)&st_req, - 4); - if (!fu_wacom_device_cmd(FU_WACOM_DEVICE(self), - st_req, - NULL, - 1, /* ms */ - FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING, - error)) { - g_prefix_error(error, "failed to erase codemem: "); - return FALSE; - } - g_usleep(50); - return TRUE; -} - -static gboolean -fu_wacom_emr_device_w9021_erase_all(FuWacomEmrDevice *self, GError **error) -{ - g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); - - fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); - fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_ALL_ERASE); - fu_struct_wacom_raw_request_set_echo(st_req, 0x01); - if (!fu_wacom_device_cmd(FU_WACOM_DEVICE(self), - st_req, - NULL, - 2000, /* this takes a long time */ - FU_WACOM_DEVICE_CMD_FLAG_POLL_ON_WAITING, - error)) { - g_prefix_error(error, "failed to send eraseall command: "); - return FALSE; - } - g_usleep(50); - return TRUE; -} - -static gboolean -fu_wacom_emr_device_attach(FuDevice *device, FuProgress *progress, GError **error) -{ - FuWacomDevice *self = FU_WACOM_DEVICE(device); - g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); - - if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { - g_debug("already in runtime mode, skipping"); - return TRUE; - } - - fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); - fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_ATTACH); - fu_struct_wacom_raw_request_set_echo(st_req, - fu_wacom_device_get_echo_next(FU_WACOM_DEVICE(self))); - if (!fu_wacom_device_set_feature(self, st_req->data, st_req->len, error)) { - g_prefix_error(error, "failed to switch to runtime mode: "); - return FALSE; - } - - /* does the device have to replug to bootloader mode */ - if (fu_device_has_private_flag(device, FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG)) { - fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); - } else { - fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); - } - - /* success */ - return TRUE; -} - -static gboolean -fu_wacom_emr_device_write_block(FuWacomEmrDevice *self, - guint32 idx, - guint32 address, - const guint8 *data, - gsize datasz, - GError **error) -{ - gsize blocksz = fu_wacom_device_get_block_sz(FU_WACOM_DEVICE(self)); - g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); - guint8 *data_unused = st_req->data + FU_STRUCT_WACOM_RAW_REQUEST_OFFSET_DATA_UNUSED; - - /* check size */ - if (datasz > FU_STRUCT_WACOM_RAW_REQUEST_SIZE_DATA) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "data size 0x%x too large for packet", - (guint)datasz); - return FALSE; - } - if (datasz != blocksz) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "block size 0x%x != 0x%x untested", - (guint)datasz, - (guint)blocksz); - return FALSE; - } - - /* data */ - fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); - fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_WRITE_FLASH); - fu_struct_wacom_raw_request_set_echo(st_req, (guint8)idx + 1); - fu_struct_wacom_raw_request_set_addr(st_req, address); - fu_struct_wacom_raw_request_set_size8(st_req, datasz / 8); - if (!fu_struct_wacom_raw_request_set_data(st_req, data, datasz, error)) - return FALSE; - - /* cmd and data checksums */ - data_unused[0] = - fu_wacom_emr_device_calc_checksum(0x05 + 0x00 + 0x4c + 0x00, st_req->data, 8); - data_unused[1] = fu_wacom_emr_device_calc_checksum(0x00, data, datasz); - if (!fu_wacom_device_cmd(FU_WACOM_DEVICE(self), - st_req, - NULL, - 1, - FU_WACOM_DEVICE_CMD_FLAG_NONE, - error)) { - g_prefix_error(error, "failed to write at 0x%x: ", address); - return FALSE; - } - return TRUE; -} - -static gboolean -fu_wacom_emr_device_write_firmware(FuDevice *device, - FuChunkArray *chunks, - FuProgress *progress, - GError **error) -{ - FuWacomEmrDevice *self = FU_WACOM_EMR_DEVICE(device); - guint8 idx = 0; - - /* progress */ - fu_progress_set_id(progress, G_STRLOC); - fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 10, NULL); - fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); - - /* erase W9013 */ - if (fu_device_has_instance_id(device, "WacomEMR_W9013", FU_DEVICE_INSTANCE_FLAG_QUIRKS)) { - if (!fu_wacom_emr_device_w9013_erase_data(self, error)) - return FALSE; - for (guint i = 127; i >= 8; i--) { - if (!fu_wacom_emr_device_w9013_erase_code(self, idx++, i, error)) - return FALSE; - } - } - - /* erase W9021 */ - if (fu_device_has_instance_id(device, "WacomEMR_W9021", FU_DEVICE_INSTANCE_FLAG_QUIRKS)) { - if (!fu_wacom_emr_device_w9021_erase_all(self, error)) - return FALSE; - } - fu_progress_step_done(progress); - - /* write */ - for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { - g_autoptr(FuChunk) chk = NULL; - - /* prepare chunk */ - chk = fu_chunk_array_index(chunks, i, error); - if (chk == NULL) - return FALSE; - if (fu_wacom_common_block_is_empty(fu_chunk_get_data(chk), - fu_chunk_get_data_sz(chk))) - continue; - if (!fu_wacom_emr_device_write_block(self, - fu_chunk_get_idx(chk), - fu_chunk_get_address(chk), - fu_chunk_get_data(chk), - fu_chunk_get_data_sz(chk), - error)) - return FALSE; - fu_progress_set_percentage_full(fu_progress_get_child(progress), - (gsize)i + 1, - (gsize)fu_chunk_array_length(chunks)); - } - fu_progress_step_done(progress); - - return TRUE; -} - -static gchar * -fu_wacom_emr_device_convert_version(FuDevice *device, guint64 version_raw) -{ - return fu_version_from_uint32(version_raw, fu_device_get_version_format(device)); -} - -static void -fu_wacom_emr_device_init(FuWacomEmrDevice *self) -{ - fu_device_set_name(FU_DEVICE(self), "Wacom EMR Device"); - fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); -} - -static void -fu_wacom_emr_device_class_init(FuWacomEmrDeviceClass *klass) -{ - FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); - FuWacomDeviceClass *wac_device_class = FU_WACOM_DEVICE_CLASS(klass); - device_class->setup = fu_wacom_emr_device_setup; - device_class->attach = fu_wacom_emr_device_attach; - device_class->convert_version = fu_wacom_emr_device_convert_version; - wac_device_class->write_firmware = fu_wacom_emr_device_write_firmware; -} diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-emr-device.h fwupd-2.0.20/plugins/wacom-raw/fu-wacom-emr-device.h --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-emr-device.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-emr-device.h 1970-01-01 00:00:00.000000000 +0000 @@ -1,12 +0,0 @@ -/* - * Copyright 2018 Richard Hughes - * - * SPDX-License-Identifier: LGPL-2.1-or-later - */ - -#pragma once - -#include "fu-wacom-device.h" - -#define FU_TYPE_WACOM_EMR_DEVICE (fu_wacom_emr_device_get_type()) -G_DECLARE_FINAL_TYPE(FuWacomEmrDevice, fu_wacom_emr_device, FU, WACOM_EMR_DEVICE, FuWacomDevice) diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-aes-device.c fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-aes-device.c --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-aes-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-aes-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,306 @@ +/* + * Copyright 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-wacom-raw-aes-device.h" +#include "fu-wacom-raw-common.h" + +struct _FuWacomRawAesDevice { + FuWacomRawDevice parent_instance; +}; + +G_DEFINE_TYPE(FuWacomRawAesDevice, fu_wacom_raw_aes_device, FU_TYPE_WACOM_RAW_DEVICE) + +static gboolean +fu_wacom_raw_aes_device_add_recovery_hwid(FuWacomRawAesDevice *self, GError **error) +{ + guint16 pid; + g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); + g_autoptr(FuStructWacomRawBlVerifyResponse) st_rsp = NULL; + + fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); + fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_VERIFY_FLASH); + fu_struct_wacom_raw_request_set_echo(st_req, 0x01); + fu_struct_wacom_raw_request_set_addr(st_req, FU_WACOM_RAW_BL_START_ADDR); + fu_struct_wacom_raw_request_set_size8(st_req, FU_WACOM_RAW_BL_BYTES_CHECK / 8); + if (!fu_wacom_raw_device_set_feature(FU_WACOM_RAW_DEVICE(self), + st_req->buf->data, + st_req->buf->len, + error)) { + g_prefix_error_literal(error, "failed to send: "); + return FALSE; + } + if (!fu_wacom_raw_device_get_feature(FU_WACOM_RAW_DEVICE(self), + st_req->buf->data, + st_req->buf->len, + error)) { + g_prefix_error_literal(error, "failed to receive: "); + return FALSE; + } + st_rsp = fu_struct_wacom_raw_bl_verify_response_parse(st_req->buf->data, + st_req->buf->len, + 0x0, + error); + if (st_rsp == NULL) + return FALSE; + if (fu_struct_wacom_raw_bl_verify_response_get_size8(st_rsp) != + fu_struct_wacom_raw_request_get_size8(st_req)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "firmware does not support this feature"); + return FALSE; + } + pid = fu_struct_wacom_raw_bl_verify_response_get_pid(st_rsp); + if (pid == 0xFFFF || pid == 0x0000) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "invalid recovery product ID %04x", + pid); + return FALSE; + } + + /* add recovery IDs */ + fu_device_add_instance_u16(FU_DEVICE(self), "VEN", 0x2D1F); + fu_device_add_instance_u16(FU_DEVICE(self), "DEV", pid); + if (!fu_device_build_instance_id(FU_DEVICE(self), error, "HIDRAW", "VID", "PID", NULL)) + return FALSE; + fu_device_add_instance_u16(FU_DEVICE(self), "VEN", 0x056A); + return fu_device_build_instance_id(FU_DEVICE(self), error, "HIDRAW", "VID", "PID", NULL); +} + +static gboolean +fu_wacom_raw_aes_device_query_operation_mode(FuWacomRawAesDevice *self, + FuWacomRawOperationMode *mode, + GError **error) +{ + g_autoptr(FuStructWacomRawFwQueryModeRequest) st_req = + fu_struct_wacom_raw_fw_query_mode_request_new(); + g_autoptr(FuStructWacomRawFwQueryModeResponse) st_rsp = NULL; + + if (!fu_wacom_raw_device_get_feature(FU_WACOM_RAW_DEVICE(self), + st_req->buf->data, + st_req->buf->len, + error)) + return FALSE; + st_rsp = fu_struct_wacom_raw_fw_query_mode_response_parse(st_req->buf->data, + st_req->buf->len, + 0x0, + error); + if (st_rsp == NULL) + return FALSE; + if (mode != NULL) + *mode = fu_struct_wacom_raw_fw_query_mode_response_get_mode(st_rsp); + return TRUE; +} + +static gboolean +fu_wacom_raw_aes_device_setup(FuDevice *device, GError **error) +{ + FuWacomRawAesDevice *self = FU_WACOM_RAW_AES_DEVICE(device); + FuWacomRawOperationMode mode = 0; + g_autoptr(GError) error_local = NULL; + + /* find out if in bootloader mode already */ + if (!fu_wacom_raw_aes_device_query_operation_mode(self, &mode, error)) + return FALSE; + + if (mode == FU_WACOM_RAW_OPERATION_MODE_BOOTLOADER) { + fu_device_set_version(device, "0.0"); + /* get the recovery PID if supported */ + if (!fu_wacom_raw_aes_device_add_recovery_hwid(self, &error_local)) + g_debug("failed to get HwID: %s", error_local->message); + } else if (mode == FU_WACOM_RAW_OPERATION_MODE_RUNTIME) { + g_autofree gchar *version = NULL; + g_autoptr(FuStructWacomRawFwStatusRequest) st_req = + fu_struct_wacom_raw_fw_status_request_new(); + g_autoptr(FuStructWacomRawFwStatusResponse) st_rsp = NULL; + + /* get firmware version */ + if (!fu_wacom_raw_device_get_feature(FU_WACOM_RAW_DEVICE(self), + st_req->buf->data, + st_req->buf->len, + error)) + return FALSE; + st_rsp = fu_struct_wacom_raw_fw_status_response_parse(st_req->buf->data, + st_req->buf->len, + 0x0, + error); + if (st_rsp == NULL) + return FALSE; + version = g_strdup_printf( + "%04x.%02x", + fu_struct_wacom_raw_fw_status_response_get_version_major(st_rsp), + fu_struct_wacom_raw_fw_status_response_get_version_minor(st_rsp)); + fu_device_set_version(device, version); + } else { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to query operation mode, got 0x%x", + mode); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wacom_raw_aes_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuWacomRawDevice *self = FU_WACOM_RAW_DEVICE(device); + g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); + + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + + fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_TYPE); + fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_TYPE_FINALIZER); + if (!fu_wacom_raw_device_set_feature(self, st_req->buf->data, st_req->buf->len, error)) { + g_prefix_error_literal(error, "failed to finalize the device: "); + return FALSE; + } + + /* does the device have to replug to bootloader mode */ + if (fu_device_has_private_flag(device, FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG)) { + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } else { + /* wait for device back to runtime mode */ + fu_device_sleep(device, 500); /* ms */ + fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wacom_raw_aes_device_erase_all(FuWacomRawAesDevice *self, FuProgress *progress, GError **error) +{ + g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); + + fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); + fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_ALL_ERASE); + fu_struct_wacom_raw_request_set_echo( + st_req, + fu_wacom_raw_device_get_echo_next(FU_WACOM_RAW_DEVICE(self))); + if (!fu_wacom_raw_device_cmd(FU_WACOM_RAW_DEVICE(self), + st_req, + NULL, + 2000, /* this takes a long time */ + FU_WACOM_RAW_DEVICE_CMD_FLAG_POLL_ON_WAITING, + error)) { + g_prefix_error_literal(error, "failed to send eraseall command: "); + return FALSE; + } + fu_device_sleep_full(FU_DEVICE(self), 2000, progress); + return TRUE; +} + +static gboolean +fu_wacom_raw_aes_device_write_block(FuWacomRawAesDevice *self, + guint32 idx, + guint32 address, + const guint8 *data, + gsize datasz, + GError **error) +{ + gsize blocksz = fu_wacom_raw_device_get_block_sz(FU_WACOM_RAW_DEVICE(self)); + g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); + + /* check size */ + if (datasz != blocksz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "block size 0x%x != 0x%x untested", + (guint)datasz, + (guint)blocksz); + return FALSE; + } + + /* write */ + fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); + fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_WRITE_FLASH); + fu_struct_wacom_raw_request_set_echo(st_req, (guint8)idx + 1); + fu_struct_wacom_raw_request_set_addr(st_req, address); + fu_struct_wacom_raw_request_set_size8(st_req, datasz / 8); + if (!fu_struct_wacom_raw_request_set_data(st_req, data, datasz, error)) + return FALSE; + if (!fu_wacom_raw_device_cmd(FU_WACOM_RAW_DEVICE(self), + st_req, + NULL, + 1, /* ms */ + FU_WACOM_RAW_DEVICE_CMD_FLAG_POLL_ON_WAITING, + error)) { + g_prefix_error(error, "failed to write block %u: ", idx); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_wacom_raw_aes_device_write_firmware(FuDevice *device, + FuChunkArray *chunks, + FuProgress *progress, + GError **error) +{ + FuWacomRawAesDevice *self = FU_WACOM_RAW_AES_DEVICE(device); + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 28, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 72, NULL); + + /* erase */ + if (!fu_wacom_raw_aes_device_erase_all(self, fu_progress_get_child(progress), error)) + return FALSE; + fu_progress_step_done(progress); + + /* write */ + for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { + g_autoptr(FuChunk) chk = NULL; + + /* prepare chunk */ + chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) + return FALSE; + if (!fu_wacom_raw_aes_device_write_block(self, + fu_chunk_get_idx(chk), + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i, + (gsize)fu_chunk_array_length(chunks)); + } + fu_progress_step_done(progress); + return TRUE; +} + +static void +fu_wacom_raw_aes_device_init(FuWacomRawAesDevice *self) +{ + fu_device_set_name(FU_DEVICE(self), "AES Device"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); +} + +static void +fu_wacom_raw_aes_device_class_init(FuWacomRawAesDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + FuWacomRawDeviceClass *wac_device_class = FU_WACOM_RAW_DEVICE_CLASS(klass); + device_class->setup = fu_wacom_raw_aes_device_setup; + device_class->attach = fu_wacom_raw_aes_device_attach; + wac_device_class->write_firmware = fu_wacom_raw_aes_device_write_firmware; +} diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-aes-device.h fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-aes-device.h --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-aes-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-aes-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-wacom-raw-device.h" + +#define FU_TYPE_WACOM_RAW_AES_DEVICE (fu_wacom_raw_aes_device_get_type()) +G_DECLARE_FINAL_TYPE(FuWacomRawAesDevice, + fu_wacom_raw_aes_device, + FU, + WACOM_RAW_AES_DEVICE, + FuWacomRawDevice) diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-common.c fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-common.c --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-common.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,74 @@ +/* + * Copyright 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include + +#include "fu-wacom-raw-common.h" + +gboolean +fu_wacom_raw_common_check_reply(const FuStructWacomRawRequest *st_req, + const FuStructWacomRawResponse *st_rsp, + GError **error) +{ + if (fu_struct_wacom_raw_response_get_report_id(st_rsp) != FU_WACOM_RAW_BL_REPORT_ID_GET) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "report ID failed, expected 0x%02x, got 0x%02x", + (guint)FU_WACOM_RAW_BL_REPORT_ID_GET, + fu_struct_wacom_raw_request_get_report_id(st_req)); + return FALSE; + } + if (fu_struct_wacom_raw_request_get_cmd(st_req) != + fu_struct_wacom_raw_response_get_cmd(st_rsp)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "cmd failed, expected 0x%02x, got 0x%02x", + fu_struct_wacom_raw_request_get_cmd(st_req), + fu_struct_wacom_raw_response_get_cmd(st_rsp)); + return FALSE; + } + if (fu_struct_wacom_raw_request_get_echo(st_req) != + fu_struct_wacom_raw_response_get_echo(st_rsp)) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "echo failed, expected 0x%02x, got 0x%02x", + fu_struct_wacom_raw_request_get_echo(st_req), + fu_struct_wacom_raw_response_get_echo(st_rsp)); + return FALSE; + } + return TRUE; +} + +gboolean +fu_wacom_raw_common_rc_set_error(const FuStructWacomRawResponse *st_rsp, GError **error) +{ + FuWacomRawRc rc = fu_struct_wacom_raw_response_get_resp(st_rsp); + const FuErrorMapEntry entries[] = { + {FU_WACOM_RAW_RC_OK, FWUPD_ERROR_LAST, NULL}, + {FU_WACOM_RAW_RC_BUSY, FWUPD_ERROR_BUSY, NULL}, + {FU_WACOM_RAW_RC_MCUTYPE, FWUPD_ERROR_INVALID_DATA, "MCU type does not match"}, + {FU_WACOM_RAW_RC_PID, FWUPD_ERROR_INVALID_DATA, "PID does not match"}, + {FU_WACOM_RAW_RC_CHECKSUM1, FWUPD_ERROR_INVALID_DATA, "checksum1 does not match"}, + {FU_WACOM_RAW_RC_CHECKSUM2, FWUPD_ERROR_INVALID_DATA, "checksum2 does not match"}, + {FU_WACOM_RAW_RC_TIMEOUT, FWUPD_ERROR_TIMED_OUT, NULL}, + }; + return fu_error_map_entry_to_gerror(rc, entries, G_N_ELEMENTS(entries), error); +} + +gboolean +fu_wacom_raw_common_block_is_empty(const guint8 *data, guint16 datasz) +{ + for (guint16 i = 0; i < datasz; i++) { + if (data[i] != 0xff) + return FALSE; + } + return TRUE; +} diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-common.h fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-common.h --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-common.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,25 @@ +/* + * Copyright 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-wacom-raw-struct.h" + +#define FU_WACOM_RAW_CMD_RETRIES 1000 + +#define FU_WACOM_RAW_BL_START_ADDR (0x11FF8) +#define FU_WACOM_RAW_BL_BYTES_CHECK 8 + +#define FU_WACOM_RAW_BL_TYPE_FINALIZER 0x00 + +gboolean +fu_wacom_raw_common_rc_set_error(const FuStructWacomRawResponse *st_rsp, GError **error); +gboolean +fu_wacom_raw_common_check_reply(const FuStructWacomRawRequest *st_req, + const FuStructWacomRawResponse *st_rsp, + GError **error); +gboolean +fu_wacom_raw_common_block_is_empty(const guint8 *data, guint16 datasz); diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-device.c fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-device.c --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,407 @@ +/* + * Copyright 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-wacom-raw-common.h" +#include "fu-wacom-raw-device.h" + +typedef struct { + guint flash_block_size; + guint32 flash_base_addr; + guint8 echo_next; +} FuWacomRawDevicePrivate; + +G_DEFINE_TYPE_WITH_PRIVATE(FuWacomRawDevice, fu_wacom_raw_device, FU_TYPE_HIDRAW_DEVICE) + +#define GET_PRIVATE(o) (fu_wacom_raw_device_get_instance_private(o)) + +static void +fu_wacom_raw_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuWacomRawDevice *self = FU_WACOM_RAW_DEVICE(device); + FuWacomRawDevicePrivate *priv = GET_PRIVATE(self); + fwupd_codec_string_append_hex(str, idt, "FlashBlockSize", priv->flash_block_size); + fwupd_codec_string_append_hex(str, idt, "FlashBaseAddr", priv->flash_base_addr); + fwupd_codec_string_append_hex(str, idt, "EchoNext", priv->echo_next); +} + +#define FU_WACOM_RAW_ECHO_MIN 0xA0 +#define FU_WACOM_RAW_ECHO_MAX 0xFE + +guint8 +fu_wacom_raw_device_get_echo_next(FuWacomRawDevice *self) +{ + FuWacomRawDevicePrivate *priv = GET_PRIVATE(self); + priv->echo_next++; + if (priv->echo_next > FU_WACOM_RAW_ECHO_MAX) + priv->echo_next = FU_WACOM_RAW_ECHO_MIN; + return priv->echo_next; +} + +gsize +fu_wacom_raw_device_get_block_sz(FuWacomRawDevice *self) +{ + FuWacomRawDevicePrivate *priv = GET_PRIVATE(self); + return priv->flash_block_size; +} + +gboolean +fu_wacom_raw_device_check_mpu(FuWacomRawDevice *self, GError **error) +{ + guint8 rsp_value = 0; + g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); + + fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); + fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_GET_MPUTYPE); + fu_struct_wacom_raw_request_set_echo(st_req, fu_wacom_raw_device_get_echo_next(self)); + if (!fu_wacom_raw_device_cmd(self, + st_req, + &rsp_value, + 0, + FU_WACOM_RAW_DEVICE_CMD_FLAG_NO_ERROR_CHECK, + error)) { + g_prefix_error_literal(error, "failed to get MPU type: "); + return FALSE; + } + + /* W9013 */ + if (rsp_value == 0x2e) { + fu_device_add_instance_id_full(FU_DEVICE(self), + "WacomEMR_W9013", + FU_DEVICE_INSTANCE_FLAG_QUIRKS); + return TRUE; + } + + /* W9021 */ + if (rsp_value == 0x45) { + fu_device_add_instance_id_full(FU_DEVICE(self), + "WacomEMR_W9021", + FU_DEVICE_INSTANCE_FLAG_QUIRKS); + return TRUE; + } + + /* unsupported */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "MPU is not W9013 or W9021: 0x%x", + rsp_value); + return FALSE; +} + +static gboolean +fu_wacom_raw_device_detach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuWacomRawDevice *self = FU_WACOM_RAW_DEVICE(device); + g_autoptr(FuStructWacomRawFwDetachRequest) st = fu_struct_wacom_raw_fw_detach_request_new(); + g_autoptr(GError) error_local = NULL; + + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in bootloader mode, skipping"); + return TRUE; + } + if (!fu_wacom_raw_device_set_feature(self, st->buf->data, st->buf->len, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INTERNAL)) { + g_debug("ignoring: %s", error_local->message); + } else { + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to switch to bootloader mode: "); + return FALSE; + } + } + + /* does the device have to replug to bootloader mode */ + if (fu_device_has_private_flag(device, FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG)) { + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } else { + fu_device_sleep(device, 300); /* ms */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } + return TRUE; +} + +static gboolean +fu_wacom_raw_device_check_mode(FuWacomRawDevice *self, GError **error) +{ + guint8 rsp_value = 0; + g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); + + fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); + fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_CHECK_MODE); + fu_struct_wacom_raw_request_set_echo(st_req, fu_wacom_raw_device_get_echo_next(self)); + if (!fu_wacom_raw_device_cmd(self, + st_req, + &rsp_value, + 0, + FU_WACOM_RAW_DEVICE_CMD_FLAG_NO_ERROR_CHECK, + error)) { + g_prefix_error_literal(error, "failed to check mode: "); + return FALSE; + } + if (rsp_value != 0x06) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "check mode failed, mode=0x%02x", + rsp_value); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_wacom_raw_device_ensure_version_bootloader(FuWacomRawDevice *self, GError **error) +{ + guint8 rsp_value = 0; + g_autofree gchar *version = NULL; + g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); + + fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); + fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_GET_BLVER); + fu_struct_wacom_raw_request_set_echo(st_req, fu_wacom_raw_device_get_echo_next(self)); + if (!fu_wacom_raw_device_cmd(self, + st_req, + &rsp_value, + 0, + FU_WACOM_RAW_DEVICE_CMD_FLAG_NO_ERROR_CHECK, + error)) { + g_prefix_error_literal(error, "failed to get bootloader version: "); + return FALSE; + } + version = g_strdup_printf("%u", rsp_value); + fu_device_set_version_bootloader(FU_DEVICE(self), version); + return TRUE; +} + +static gboolean +fu_wacom_raw_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuWacomRawDevice *self = FU_WACOM_RAW_DEVICE(device); + FuWacomRawDevicePrivate *priv = GET_PRIVATE(self); + FuWacomRawDeviceClass *klass = FU_WACOM_RAW_DEVICE_GET_CLASS(device); + g_autoptr(GBytes) fw = NULL; + g_autoptr(FuChunkArray) chunks = NULL; + + /* use the correct image from the firmware */ + g_debug("using element at addr 0x%0x", (guint)fu_firmware_get_addr(firmware)); + + /* check start address and size */ + if (fu_firmware_get_addr(firmware) != priv->flash_base_addr) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "base addr invalid: 0x%05x", + (guint)fu_firmware_get_addr(firmware)); + return FALSE; + } + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* we're in bootloader mode now */ + if (!fu_wacom_raw_device_check_mode(self, error)) + return FALSE; + if (!fu_wacom_raw_device_ensure_version_bootloader(self, error)) + return FALSE; + + /* flash chunks */ + chunks = fu_chunk_array_new_from_bytes(fw, + priv->flash_base_addr, + FU_CHUNK_PAGESZ_NONE, + priv->flash_block_size); + return klass->write_firmware(device, chunks, progress, error); +} + +gboolean +fu_wacom_raw_device_get_feature(FuWacomRawDevice *self, guint8 *data, guint datasz, GError **error) +{ + return fu_hidraw_device_get_feature(FU_HIDRAW_DEVICE(self), + data, + datasz, + FU_IOCTL_FLAG_NONE, + error); +} + +gboolean +fu_wacom_raw_device_set_feature(FuWacomRawDevice *self, + const guint8 *data, + guint datasz, + GError **error) +{ + return fu_hidraw_device_set_feature(FU_HIDRAW_DEVICE(self), + data, + datasz, + FU_IOCTL_FLAG_NONE, + error); +} + +static gboolean +fu_wacom_raw_device_cmd_response(FuWacomRawDevice *self, + const FuStructWacomRawRequest *st_req, + guint8 *rsp_value, + FuWacomRawDeviceCmdFlags flags, + GError **error) +{ + guint8 buf[FU_STRUCT_WACOM_RAW_RESPONSE_SIZE] = {FU_WACOM_RAW_BL_REPORT_ID_GET, 0x0}; + g_autoptr(FuStructWacomRawResponse) st_rsp = NULL; + + if (!fu_wacom_raw_device_get_feature(self, buf, sizeof(buf), error)) { + g_prefix_error_literal(error, "failed to receive: "); + return FALSE; + } + st_rsp = fu_struct_wacom_raw_response_parse(buf, sizeof(buf), 0x0, error); + if (st_rsp == NULL) + return FALSE; + if (!fu_wacom_raw_common_check_reply(st_req, st_rsp, error)) + return FALSE; + if ((flags & FU_WACOM_RAW_DEVICE_CMD_FLAG_NO_ERROR_CHECK) == 0) { + if (!fu_wacom_raw_common_rc_set_error(st_rsp, error)) + return FALSE; + } + + /* optional */ + if (rsp_value != NULL) + *rsp_value = fu_struct_wacom_raw_response_get_resp(st_rsp); + + /* success */ + return TRUE; +} + +typedef struct { + const FuStructWacomRawRequest *st_req; + guint8 *rsp_value; + FuWacomRawDeviceCmdFlags flags; +} FuWacomRawResponseHelper; + +static gboolean +fu_wacom_raw_device_cmd_response_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuWacomRawDevice *self = FU_WACOM_RAW_DEVICE(device); + FuWacomRawResponseHelper *helper = (FuWacomRawResponseHelper *)user_data; + return fu_wacom_raw_device_cmd_response(self, + helper->st_req, + helper->rsp_value, + helper->flags, + error); +} + +gboolean +fu_wacom_raw_device_cmd(FuWacomRawDevice *self, + const FuStructWacomRawRequest *st_req, + guint8 *rsp_value, + guint delay_ms, + FuWacomRawDeviceCmdFlags flags, + GError **error) +{ + if (!fu_wacom_raw_device_set_feature(self, st_req->buf->data, st_req->buf->len, error)) { + g_prefix_error_literal(error, "failed to send: "); + return FALSE; + } + fu_device_sleep(FU_DEVICE(self), delay_ms); + if (flags & FU_WACOM_RAW_DEVICE_CMD_FLAG_POLL_ON_WAITING) { + FuWacomRawResponseHelper helper = {.st_req = st_req, + .rsp_value = rsp_value, + .flags = flags}; + return fu_device_retry_full(FU_DEVICE(self), + fu_wacom_raw_device_cmd_response_cb, + FU_WACOM_RAW_CMD_RETRIES, + delay_ms, + &helper, + error); + } + return fu_wacom_raw_device_cmd_response(self, st_req, rsp_value, flags, error); +} + +static gboolean +fu_wacom_raw_device_set_quirk_kv(FuDevice *device, + const gchar *key, + const gchar *value, + GError **error) +{ + FuWacomRawDevice *self = FU_WACOM_RAW_DEVICE(device); + FuWacomRawDevicePrivate *priv = GET_PRIVATE(self); + guint64 tmp = 0; + + if (g_strcmp0(key, "WacomI2cFlashBlockSize") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXSIZE, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + priv->flash_block_size = tmp; + return TRUE; + } + if (g_strcmp0(key, "WacomI2cFlashBaseAddr") == 0) { + if (!fu_strtoull(value, &tmp, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + priv->flash_base_addr = tmp; + return TRUE; + } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "quirk key not supported"); + return FALSE; +} + +static void +fu_wacom_raw_device_replace(FuDevice *device, FuDevice *donor) +{ + g_return_if_fail(FU_IS_WACOM_RAW_DEVICE(device)); + g_return_if_fail(FU_IS_WACOM_RAW_DEVICE(donor)); + + /* copy private instance data */ + if (fu_device_has_private_flag(donor, FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG)) { + fu_device_add_private_flag(device, + FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG); + } +} + +static void +fu_wacom_raw_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 4, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 92, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 4, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 2, "reload"); +} + +static void +fu_wacom_raw_device_init(FuWacomRawDevice *self) +{ + FuWacomRawDevicePrivate *priv = GET_PRIVATE(self); + priv->echo_next = FU_WACOM_RAW_ECHO_MIN; + fu_device_add_protocol(FU_DEVICE(self), "com.wacom.raw"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_INTERNAL); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_NEEDS_REBOOT); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_READ); + fu_udev_device_add_open_flag(FU_UDEV_DEVICE(self), FU_IO_CHANNEL_OPEN_FLAG_WRITE); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_REPLUG_MATCH_GUID); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); + fu_device_set_firmware_gtype(FU_DEVICE(self), FU_TYPE_IHEX_FIRMWARE); + fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_register_private_flag(FU_DEVICE(self), + FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG); +} + +static void +fu_wacom_raw_device_class_init(FuWacomRawDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->to_string = fu_wacom_raw_device_to_string; + device_class->write_firmware = fu_wacom_raw_device_write_firmware; + device_class->detach = fu_wacom_raw_device_detach; + device_class->set_quirk_kv = fu_wacom_raw_device_set_quirk_kv; + device_class->set_progress = fu_wacom_raw_device_set_progress; + device_class->replace = fu_wacom_raw_device_replace; +} diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-device.h fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-device.h --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,52 @@ +/* + * Copyright 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#include "fu-wacom-raw-common.h" +#include "fu-wacom-raw-struct.h" + +#define FU_TYPE_WACOM_RAW_DEVICE (fu_wacom_raw_device_get_type()) +G_DECLARE_DERIVABLE_TYPE(FuWacomRawDevice, + fu_wacom_raw_device, + FU, + WACOM_RAW_DEVICE, + FuHidrawDevice) + +struct _FuWacomRawDeviceClass { + FuHidrawDeviceClass parent_class; + gboolean (*write_firmware)(FuDevice *self, + FuChunkArray *chunks, + FuProgress *progress, + GError **error); +}; + +#define FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG "requires-wait-for-replug" + +guint8 +fu_wacom_raw_device_get_echo_next(FuWacomRawDevice *self); +gboolean +fu_wacom_raw_device_set_feature(FuWacomRawDevice *self, + const guint8 *data, + guint datasz, + GError **error); +gboolean +fu_wacom_raw_device_get_feature(FuWacomRawDevice *self, guint8 *data, guint datasz, GError **error); +gboolean +fu_wacom_raw_device_cmd(FuWacomRawDevice *self, + const FuStructWacomRawRequest *st_req, + guint8 *rsp_value, + guint delay_ms, + FuWacomRawDeviceCmdFlags flags, + GError **error); +gboolean +fu_wacom_raw_device_erase_all(FuWacomRawDevice *self, GError **error); +gboolean +fu_wacom_raw_device_check_mpu(FuWacomRawDevice *self, GError **error); +gsize +fu_wacom_raw_device_get_block_sz(FuWacomRawDevice *self); diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-emr-device.c fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-emr-device.c --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-emr-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-emr-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,308 @@ +/* + * Copyright 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include + +#include "fu-wacom-raw-common.h" +#include "fu-wacom-raw-emr-device.h" + +struct _FuWacomRawEmrDevice { + FuWacomRawDevice parent_instance; +}; + +G_DEFINE_TYPE(FuWacomRawEmrDevice, fu_wacom_raw_emr_device, FU_TYPE_WACOM_RAW_DEVICE) + +static gboolean +fu_wacom_raw_emr_device_setup(FuDevice *device, GError **error) +{ + FuWacomRawEmrDevice *self = FU_WACOM_RAW_EMR_DEVICE(device); + + /* check MPU type */ + if (!fu_wacom_raw_device_check_mpu(FU_WACOM_RAW_DEVICE(self), error)) + return FALSE; + + /* get firmware version */ + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + fu_device_set_version_raw(device, 0); + } else { + guint16 fw_ver; + guint8 data[19] = {0x03, 0x0}; /* 0x03 is an unknown ReportID */ + if (!fu_wacom_raw_device_get_feature(FU_WACOM_RAW_DEVICE(self), + data, + sizeof(data), + error)) + return FALSE; + if (!fu_memread_uint16_safe(data, + sizeof(data), + 11, + &fw_ver, + G_LITTLE_ENDIAN, + error)) + return FALSE; + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + fu_device_set_version_raw(device, fw_ver); + } + + /* success */ + return TRUE; +} + +static guint8 +fu_wacom_raw_emr_device_calc_checksum(guint8 init1, const guint8 *buf, gsize bufsz) +{ + return init1 + ~(fu_sum8(buf, bufsz)) + 1; +} + +static gboolean +fu_wacom_raw_emr_device_w9013_erase_data(FuWacomRawEmrDevice *self, GError **error) +{ + g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); + guint8 *buf = st_req->buf->data + FU_STRUCT_WACOM_RAW_REQUEST_OFFSET_ADDR; + + fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); + fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_ERASE_DATAMEM); + fu_struct_wacom_raw_request_set_echo( + st_req, + fu_wacom_raw_device_get_echo_next(FU_WACOM_RAW_DEVICE(self))); + buf[0] = 0x00; /* erased block */ + buf[1] = fu_wacom_raw_emr_device_calc_checksum(0x05 + 0x00 + 0x07 + 0x00, + (const guint8 *)&st_req, + 4); + if (!fu_wacom_raw_device_cmd(FU_WACOM_RAW_DEVICE(self), + st_req, + NULL, + 1, /* ms */ + FU_WACOM_RAW_DEVICE_CMD_FLAG_POLL_ON_WAITING, + error)) { + g_prefix_error_literal(error, "failed to erase datamem: "); + return FALSE; + } + g_usleep(50); + return TRUE; +} + +static gboolean +fu_wacom_raw_emr_device_w9013_erase_code(FuWacomRawEmrDevice *self, + guint8 idx, + guint8 block_nr, + GError **error) +{ + g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); + guint8 *buf = st_req->buf->data + FU_STRUCT_WACOM_RAW_REQUEST_OFFSET_ADDR; + + fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); + fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_ERASE_FLASH); + fu_struct_wacom_raw_request_set_echo(st_req, idx); + buf[0] = block_nr; + buf[1] = fu_wacom_raw_emr_device_calc_checksum(0x05 + 0x00 + 0x07 + 0x00, + (const guint8 *)&st_req, + 4); + if (!fu_wacom_raw_device_cmd(FU_WACOM_RAW_DEVICE(self), + st_req, + NULL, + 1, /* ms */ + FU_WACOM_RAW_DEVICE_CMD_FLAG_POLL_ON_WAITING, + error)) { + g_prefix_error_literal(error, "failed to erase codemem: "); + return FALSE; + } + g_usleep(50); + return TRUE; +} + +static gboolean +fu_wacom_raw_emr_device_w9021_erase_all(FuWacomRawEmrDevice *self, GError **error) +{ + g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); + + fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); + fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_ALL_ERASE); + fu_struct_wacom_raw_request_set_echo(st_req, 0x01); + if (!fu_wacom_raw_device_cmd(FU_WACOM_RAW_DEVICE(self), + st_req, + NULL, + 2000, /* this takes a long time */ + FU_WACOM_RAW_DEVICE_CMD_FLAG_POLL_ON_WAITING, + error)) { + g_prefix_error_literal(error, "failed to send eraseall command: "); + return FALSE; + } + g_usleep(50); + return TRUE; +} + +static gboolean +fu_wacom_raw_emr_device_attach(FuDevice *device, FuProgress *progress, GError **error) +{ + FuWacomRawDevice *self = FU_WACOM_RAW_DEVICE(device); + g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); + + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_IS_BOOTLOADER)) { + g_debug("already in runtime mode, skipping"); + return TRUE; + } + + fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); + fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_ATTACH); + fu_struct_wacom_raw_request_set_echo( + st_req, + fu_wacom_raw_device_get_echo_next(FU_WACOM_RAW_DEVICE(self))); + if (!fu_wacom_raw_device_set_feature(self, st_req->buf->data, st_req->buf->len, error)) { + g_prefix_error_literal(error, "failed to switch to runtime mode: "); + return FALSE; + } + + /* does the device have to replug to bootloader mode */ + if (fu_device_has_private_flag(device, FU_WACOM_RAW_DEVICE_FLAG_REQUIRES_WAIT_FOR_REPLUG)) { + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); + } else { + fu_device_remove_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_IS_BOOTLOADER); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wacom_raw_emr_device_write_block(FuWacomRawEmrDevice *self, + guint32 idx, + guint32 address, + const guint8 *data, + gsize datasz, + GError **error) +{ + gsize blocksz = fu_wacom_raw_device_get_block_sz(FU_WACOM_RAW_DEVICE(self)); + g_autoptr(FuStructWacomRawRequest) st_req = fu_struct_wacom_raw_request_new(); + guint8 *data_unused = st_req->buf->data + FU_STRUCT_WACOM_RAW_REQUEST_OFFSET_DATA_UNUSED; + + /* check size */ + if (datasz > FU_STRUCT_WACOM_RAW_REQUEST_SIZE_DATA) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "data size 0x%x too large for packet", + (guint)datasz); + return FALSE; + } + if (datasz != blocksz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "block size 0x%x != 0x%x untested", + (guint)datasz, + (guint)blocksz); + return FALSE; + } + + /* data */ + fu_struct_wacom_raw_request_set_report_id(st_req, FU_WACOM_RAW_BL_REPORT_ID_SET); + fu_struct_wacom_raw_request_set_cmd(st_req, FU_WACOM_RAW_BL_CMD_WRITE_FLASH); + fu_struct_wacom_raw_request_set_echo(st_req, (guint8)idx + 1); + fu_struct_wacom_raw_request_set_addr(st_req, address); + fu_struct_wacom_raw_request_set_size8(st_req, datasz / 8); + if (!fu_struct_wacom_raw_request_set_data(st_req, data, datasz, error)) + return FALSE; + + /* cmd and data checksums */ + data_unused[0] = + fu_wacom_raw_emr_device_calc_checksum(0x05 + 0x00 + 0x4c + 0x00, st_req->buf->data, 8); + data_unused[1] = fu_wacom_raw_emr_device_calc_checksum(0x00, data, datasz); + if (!fu_wacom_raw_device_cmd(FU_WACOM_RAW_DEVICE(self), + st_req, + NULL, + 1, + FU_WACOM_RAW_DEVICE_CMD_FLAG_NONE, + error)) { + g_prefix_error(error, "failed to write at 0x%x: ", address); + return FALSE; + } + return TRUE; +} + +static gboolean +fu_wacom_raw_emr_device_write_firmware(FuDevice *device, + FuChunkArray *chunks, + FuProgress *progress, + GError **error) +{ + FuWacomRawEmrDevice *self = FU_WACOM_RAW_EMR_DEVICE(device); + guint8 idx = 0; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 10, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 90, NULL); + + /* erase W9013 */ + if (fu_device_has_instance_id(device, "WacomEMR_W9013", FU_DEVICE_INSTANCE_FLAG_QUIRKS)) { + if (!fu_wacom_raw_emr_device_w9013_erase_data(self, error)) + return FALSE; + for (guint i = 127; i >= 8; i--) { + if (!fu_wacom_raw_emr_device_w9013_erase_code(self, idx++, i, error)) + return FALSE; + } + } + + /* erase W9021 */ + if (fu_device_has_instance_id(device, "WacomEMR_W9021", FU_DEVICE_INSTANCE_FLAG_QUIRKS)) { + if (!fu_wacom_raw_emr_device_w9021_erase_all(self, error)) + return FALSE; + } + fu_progress_step_done(progress); + + /* write */ + for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { + g_autoptr(FuChunk) chk = NULL; + + /* prepare chunk */ + chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) + return FALSE; + if (fu_wacom_raw_common_block_is_empty(fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk))) + continue; + if (!fu_wacom_raw_emr_device_write_block(self, + fu_chunk_get_idx(chk), + fu_chunk_get_address(chk), + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + fu_progress_set_percentage_full(fu_progress_get_child(progress), + (gsize)i + 1, + (gsize)fu_chunk_array_length(chunks)); + } + fu_progress_step_done(progress); + + return TRUE; +} + +static gchar * +fu_wacom_raw_emr_device_convert_version(FuDevice *device, guint64 version_raw) +{ + return fu_version_from_uint32(version_raw, fu_device_get_version_format(device)); +} + +static void +fu_wacom_raw_emr_device_init(FuWacomRawEmrDevice *self) +{ + fu_device_set_name(FU_DEVICE(self), "EMR Device"); + fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_PAIR); +} + +static void +fu_wacom_raw_emr_device_class_init(FuWacomRawEmrDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + FuWacomRawDeviceClass *wac_device_class = FU_WACOM_RAW_DEVICE_CLASS(klass); + device_class->setup = fu_wacom_raw_emr_device_setup; + device_class->attach = fu_wacom_raw_emr_device_attach; + device_class->convert_version = fu_wacom_raw_emr_device_convert_version; + wac_device_class->write_firmware = fu_wacom_raw_emr_device_write_firmware; +} diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-emr-device.h fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-emr-device.h --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-emr-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-emr-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright 2018 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include "fu-wacom-raw-device.h" + +#define FU_TYPE_WACOM_RAW_EMR_DEVICE (fu_wacom_raw_emr_device_get_type()) +G_DECLARE_FINAL_TYPE(FuWacomRawEmrDevice, + fu_wacom_raw_emr_device, + FU, + WACOM_RAW_EMR_DEVICE, + FuWacomRawDevice) diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-plugin.c fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-plugin.c --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -6,9 +6,9 @@ #include "config.h" -#include "fu-wacom-aes-device.h" -#include "fu-wacom-common.h" -#include "fu-wacom-emr-device.h" +#include "fu-wacom-raw-aes-device.h" +#include "fu-wacom-raw-common.h" +#include "fu-wacom-raw-emr-device.h" #include "fu-wacom-raw-plugin.h" struct _FuWacomRawPlugin { @@ -53,8 +53,8 @@ fu_context_add_quirk_key(ctx, "WacomI2cFlashBlockSize"); fu_context_add_quirk_key(ctx, "WacomI2cFlashBaseAddr"); fu_context_add_quirk_key(ctx, "WacomI2cFlashSize"); - fu_plugin_add_device_gtype(plugin, FU_TYPE_WACOM_AES_DEVICE); - fu_plugin_add_device_gtype(plugin, FU_TYPE_WACOM_EMR_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_WACOM_RAW_AES_DEVICE); + fu_plugin_add_device_gtype(plugin, FU_TYPE_WACOM_RAW_EMR_DEVICE); fu_plugin_add_udev_subsystem(plugin, "hidraw"); } diff -Nru fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw.rs fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw.rs --- fwupd-2.0.8/plugins/wacom-raw/fu-wacom-raw.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/fu-wacom-raw.rs 2026-02-26 11:36:18.000000000 +0000 @@ -7,6 +7,12 @@ Bootloader = 0x02, } +enum FuWacomRawDeviceCmdFlags { + None = 0, + PollOnWaiting = 1 << 0, + NoErrorCheck = 1 << 1, +} + enum FuWacomRawRc { Ok = 0x00, Busy = 0x80, diff -Nru fwupd-2.0.8/plugins/wacom-raw/meson.build fwupd-2.0.20/plugins/wacom-raw/meson.build --- fwupd-2.0.8/plugins/wacom-raw/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,5 @@ -if host_machine.system() == 'linux' +host_machine.system() == 'linux' or subdir_done() + plugins += {meson.current_source_dir().split('/')[-1]: true} cargs = ['-DG_LOG_DOMAIN="FuPluginWacomRaw"'] @@ -7,10 +8,10 @@ rustgen.process('fu-wacom-raw.rs'), sources: [ 'fu-wacom-raw-plugin.c', - 'fu-wacom-common.c', - 'fu-wacom-device.c', - 'fu-wacom-aes-device.c', - 'fu-wacom-emr-device.c', + 'fu-wacom-raw-common.c', + 'fu-wacom-raw-device.c', + 'fu-wacom-raw-aes-device.c', + 'fu-wacom-raw-emr-device.c', ], include_directories: plugin_incdirs, c_args: cargs, @@ -21,4 +22,3 @@ device_tests += files( 'tests/wacom-g14t.json', ) -endif diff -Nru fwupd-2.0.8/plugins/wacom-raw/tests/wacom-g14t.json fwupd-2.0.20/plugins/wacom-raw/tests/wacom-g14t.json --- fwupd-2.0.8/plugins/wacom-raw/tests/wacom-g14t.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/tests/wacom-g14t.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/80bfa20d1ace301fe5c4a2b514c4b58cf108992483ed92b6195296b4e54db13e-G14T_5309_v0002_rev07.cab", - "emulation-url": "https://fwupd.org/downloads/d1eefe9a81a62807755f815f6fc8eae06dd479e7238513740f347eaf5d3f01ac-G14T_5309_v0002_rev07.zip", + "url": "80bfa20d1ace301fe5c4a2b514c4b58cf108992483ed92b6195296b4e54db13e-G14T_5309_v0002_rev07.cab", + "emulation-url": "d1eefe9a81a62807755f815f6fc8eae06dd479e7238513740f347eaf5d3f01ac-G14T_5309_v0002_rev07.zip", "components": [ { "version": "0002.07", diff -Nru fwupd-2.0.8/plugins/wacom-raw/wacom-raw.quirk fwupd-2.0.20/plugins/wacom-raw/wacom-raw.quirk --- fwupd-2.0.8/plugins/wacom-raw/wacom-raw.quirk 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-raw/wacom-raw.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -140,10 +140,10 @@ FirmwareSizeMax = 0x3c000 [WacomEMR] -GType = FuWacomEmrDevice +GType = FuWacomRawEmrDevice [WacomAES] -GType = FuWacomAesDevice +GType = FuWacomRawAesDevice WacomI2cFlashBlockSize = 128 WacomI2cFlashBaseAddr = 0x8000 FirmwareSizeMax = 0x28000 diff -Nru fwupd-2.0.8/plugins/wacom-usb/README.md fwupd-2.0.20/plugins/wacom-usb/README.md --- fwupd-2.0.8/plugins/wacom-usb/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -52,10 +52,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.2.2`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Jason Gerecke: @jigpu diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-self-test.c fwupd-2.0.20/plugins/wacom-usb/fu-self-test.c --- fwupd-2.0.8/plugins/wacom-usb/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -31,7 +31,7 @@ return; } file = g_file_new_for_path(fn); - ret = fu_firmware_parse_file(firmware, file, FWUPD_INSTALL_FLAG_NO_SEARCH, &error); + ret = fu_firmware_parse_file(firmware, file, FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, &error); g_assert_no_error(error); g_assert_true(ret); diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac-android-device.c fwupd-2.0.20/plugins/wacom-usb/fu-wac-android-device.c --- fwupd-2.0.8/plugins/wacom-usb/fu-wac-android-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac-android-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,7 +18,7 @@ fu_wac_android_device_init(FuWacAndroidDevice *self) { fu_device_add_protocol(FU_DEVICE(self), "com.wacom.usb"); - fu_device_add_icon(FU_DEVICE(self), "input-tablet"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_INPUT_TABLET); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_inhibit(FU_DEVICE(self), "hw", diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac-device.c fwupd-2.0.20/plugins/wacom-usb/fu-wac-device.c --- fwupd-2.0.8/plugins/wacom-usb/fu-wac-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -578,10 +578,10 @@ /* check at least one block was written */ if (blocks_done == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "empty firmware image or all blocks write-protected"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "empty firmware image or all blocks write-protected"); return FALSE; } @@ -639,8 +639,6 @@ static gboolean fu_wac_device_add_modules_bluetooth(FuWacDevice *self, GError **error) { - g_autofree gchar *name = NULL; - g_autofree gchar *name_id6 = NULL; g_autoptr(FuWacModule) module = NULL; g_autoptr(FuWacModule) module_id6 = NULL; guint16 fw_ver; @@ -654,7 +652,8 @@ sizeof(buf), FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "Failed to get GetFirmwareVersionBluetooth: "); + g_prefix_error_literal(error, + "failed to get GetFirmwareVersionBluetooth: "); return FALSE; } if (!fu_memread_uint16_safe(buf, sizeof(buf), 1, &fw_ver, G_LITTLE_ENDIAN, error)) @@ -668,17 +667,14 @@ * Initialize both and rely on the firmware update containing the appropriate * package. */ - name = g_strdup_printf("%s [Legacy Bluetooth Module]", fu_device_get_name(FU_DEVICE(self))); module = fu_wac_module_bluetooth_new(FU_DEVICE(self)); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(module)); - fu_device_set_name(FU_DEVICE(module), name); + fu_device_set_name(FU_DEVICE(module), "Legacy Bluetooth Module"); fu_device_set_version_raw(FU_DEVICE(module), fw_ver); - name_id6 = g_strdup_printf("%s [Legacy Bluetooth Module (ID6)]", - fu_device_get_name(FU_DEVICE(self))); module_id6 = fu_wac_module_bluetooth_id6_new(FU_DEVICE(self)); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(module_id6)); - fu_device_set_name(FU_DEVICE(module_id6), name_id6); + fu_device_set_name(FU_DEVICE(module_id6), "Legacy Bluetooth Module ID6"); fu_device_set_version_raw(FU_DEVICE(module_id6), fw_ver); return TRUE; } @@ -706,7 +702,7 @@ sizeof(buf), FU_HID_DEVICE_FLAG_NONE, error)) { - g_prefix_error(error, "failed to get DeviceFirmwareDescriptor: "); + g_prefix_error_literal(error, "failed to get DeviceFirmwareDescriptor: "); return FALSE; } @@ -803,7 +799,6 @@ guint32 ver; guint16 ver2; FuWacModuleFwType fw_type; - g_autofree gchar *name = NULL; g_autoptr(FuStructModuleItem) st_item = NULL; g_autoptr(FuWacModule) module = NULL; @@ -827,61 +822,47 @@ switch (fw_type) { case FU_WAC_MODULE_FW_TYPE_TOUCH: module = fu_wac_module_touch_new(FU_DEVICE(self)); - name = g_strdup_printf("%s [Touch Module]", - fu_device_get_name(FU_DEVICE(self))); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(module)); - fu_device_set_name(FU_DEVICE(module), name); + fu_device_set_name(FU_DEVICE(module), "Touch Module"); fu_device_set_version_raw(FU_DEVICE(module), ver); break; case FU_WAC_MODULE_FW_TYPE_TOUCH_ID7: module = fu_wac_module_touch_id7_new(FU_DEVICE(self)); - name = g_strdup_printf("%s [Touch Module]", - fu_device_get_name(FU_DEVICE(self))); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(module)); - fu_device_set_name(FU_DEVICE(module), name); + fu_device_set_name(FU_DEVICE(module), "Touch Module"); fu_device_set_summary(FU_DEVICE(module), "ID7"); fu_device_set_version_raw(FU_DEVICE(module), ver); break; case FU_WAC_MODULE_FW_TYPE_BLUETOOTH: module = fu_wac_module_bluetooth_new(FU_DEVICE(self)); - name = g_strdup_printf("%s [Bluetooth Module]", - fu_device_get_name(FU_DEVICE(self))); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(module)); - fu_device_set_name(FU_DEVICE(module), name); + fu_device_set_name(FU_DEVICE(module), "Bluetooth Module"); fu_device_set_version_raw(FU_DEVICE(module), ver); break; case FU_WAC_MODULE_FW_TYPE_BLUETOOTH_ID6: module = fu_wac_module_bluetooth_id6_new(FU_DEVICE(self)); - name = g_strdup_printf("%s [Bluetooth Module]", - fu_device_get_name(FU_DEVICE(self))); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(module)); - fu_device_set_name(FU_DEVICE(module), name); + fu_device_set_name(FU_DEVICE(module), "Bluetooth Module"); fu_device_set_summary(FU_DEVICE(module), "ID6"); fu_device_set_version_raw(FU_DEVICE(module), ver); break; case FU_WAC_MODULE_FW_TYPE_SCALER: module = fu_wac_module_scaler_new(FU_DEVICE(self)); - name = g_strdup_printf("%s [Scaler Module]", - fu_device_get_name(FU_DEVICE(self))); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(module)); - fu_device_set_name(FU_DEVICE(module), name); + fu_device_set_name(FU_DEVICE(module), "Scaler Module"); fu_device_set_version_raw(FU_DEVICE(module), ver); break; case FU_WAC_MODULE_FW_TYPE_BLUETOOTH_ID9: module = fu_wac_module_bluetooth_id9_new(FU_DEVICE(self)); - name = g_strdup_printf("%s [Bluetooth Module]", - fu_device_get_name(FU_DEVICE(self))); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(module)); - fu_device_set_name(FU_DEVICE(module), name); + fu_device_set_name(FU_DEVICE(module), "Bluetooth Module"); fu_device_set_summary(FU_DEVICE(module), "ID9"); fu_device_set_version_raw(FU_DEVICE(module), ver); break; case FU_WAC_MODULE_FW_TYPE_SUB_CPU: module = fu_wac_module_sub_cpu_new(FU_DEVICE(self)); - name = g_strdup_printf("%s [Sub CPU Module]", - fu_device_get_name(FU_DEVICE(self))); fu_device_add_child(FU_DEVICE(self), FU_DEVICE(module)); - fu_device_set_name(FU_DEVICE(module), name); + fu_device_set_name(FU_DEVICE(module), "Sub CPU Module"); fu_device_set_version_raw(FU_DEVICE(module), ver); break; case FU_WAC_MODULE_FW_TYPE_MAIN: @@ -930,7 +911,7 @@ 0x00, /* HID */ FU_USB_DEVICE_CLAIM_FLAG_KERNEL_DRIVER, error)) { - g_prefix_error(error, "failed to re-attach interface: "); + g_prefix_error_literal(error, "failed to re-attach interface: "); return FALSE; } @@ -946,7 +927,7 @@ } static void -fu_wac_device_set_progress(FuDevice *self, FuProgress *progress) +fu_wac_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -973,7 +954,7 @@ self->configuration = 0xffff; self->firmware_index = 0xffff; fu_device_add_protocol(FU_DEVICE(self), "com.wacom.usb"); - fu_device_add_icon(FU_DEVICE(self), "input-tablet"); + fu_device_add_icon(FU_DEVICE(self), FU_DEVICE_ICON_INPUT_TABLET); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS); diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac-firmware.c fwupd-2.0.20/plugins/wacom-usb/fu-wac-firmware.c --- fwupd-2.0.8/plugins/wacom-usb/fu-wac-firmware.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac-firmware.c 2026-02-26 11:36:18.000000000 +0000 @@ -28,7 +28,7 @@ typedef struct { FuFirmware *firmware; - FwupdInstallFlags flags; + FuFirmwareParseFlags flags; GPtrArray *header_infos; GString *image_buffer; guint8 images_cnt; @@ -58,7 +58,7 @@ if (g_strcmp0(cmd, "") == 0) return TRUE; - /* Wacom-specific metadata */ + /* custom metadata */ if (g_strcmp0(cmd, "WA") == 0) { /* header info record */ if (token->len > 3 && memcmp(token->str + 2, "COM", 3) == 0) { @@ -236,7 +236,7 @@ if (!fu_firmware_parse_bytes(firmware_srec, blob, 0x0, - helper->flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + helper->flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) return FALSE; fw_srec = fu_firmware_get_bytes(firmware_srec, error); @@ -245,7 +245,7 @@ fu_firmware_set_bytes(img, fw_srec); fu_firmware_set_addr(img, fu_firmware_get_addr(firmware_srec)); fu_firmware_set_idx(img, helper->images_cnt); - if (!fu_firmware_add_image_full(helper->firmware, img, error)) + if (!fu_firmware_add_image(helper->firmware, img, error)) return FALSE; helper->images_cnt++; @@ -266,7 +266,7 @@ static gboolean fu_wac_firmware_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(GPtrArray) header_infos = g_ptr_array_new_with_free_func(g_free); diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-bluetooth-id6.c fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-bluetooth-id6.c --- fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-bluetooth-id6.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-bluetooth-id6.c 2026-02-26 11:36:18.000000000 +0000 @@ -139,7 +139,7 @@ /* get default image */ stream = fu_firmware_get_stream(firmware, error); if (stream == NULL) { - g_prefix_error(error, "wacom bluetooth-id6 module failed to get stream: "); + g_prefix_error_literal(error, "failed to get stream: "); return FALSE; } @@ -151,7 +151,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_BLUETOOTH_ID6_START_TIMEOUT, error)) { - g_prefix_error(error, "wacom bluetooth-id6 module failed to erase: "); + g_prefix_error_literal(error, "failed to erase: "); return FALSE; } fu_progress_step_done(progress); @@ -161,7 +161,7 @@ stream, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "wacom bluetooth-id6 module failed to write: "); + g_prefix_error_literal(error, "failed to write: "); return FALSE; } fu_progress_step_done(progress); @@ -174,7 +174,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_BLUETOOTH_ID6_END_TIMEOUT, error)) { - g_prefix_error(error, "wacom bluetooth-id6 module failed to end: "); + g_prefix_error_literal(error, "failed to end: "); return FALSE; } fu_progress_step_done(progress); diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-bluetooth-id9.c fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-bluetooth-id9.c --- fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-bluetooth-id9.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-bluetooth-id9.c 2026-02-26 11:36:18.000000000 +0000 @@ -60,30 +60,32 @@ guint8 command = full_erase ? FU_WAC_MODULE_BLUETOOTH_ID9_CMD_FULLERASE : FU_WAC_MODULE_BLUETOOTH_ID9_CMD_NORMAL; guint32 crc = ~0; - g_autoptr(GByteArray) loader_cmd = fu_struct_id9_loader_cmd_new(); - g_autoptr(GByteArray) spi_cmd = fu_struct_id9_spi_cmd_new(); - g_autoptr(GByteArray) unknown_cmd = fu_struct_id9_unknown_cmd_new(); + g_autoptr(FuStructId9LoaderCmd) st_loader = fu_struct_id9_loader_cmd_new(); + g_autoptr(FuStructId9SpiCmd) st_spi = fu_struct_id9_spi_cmd_new(); + g_autoptr(FuStructId9UnknownCmd) st_unknown = fu_struct_id9_unknown_cmd_new(); + g_autoptr(GBytes) blob = NULL; if (!fu_input_stream_size(stream, &streamsz, error)) return NULL; - fu_struct_id9_unknown_cmd_set_size(unknown_cmd, streamsz); + fu_struct_id9_unknown_cmd_set_size(st_unknown, streamsz); - fu_struct_id9_spi_cmd_set_size(spi_cmd, streamsz + FU_STRUCT_ID9_UNKNOWN_CMD_SIZE); - if (!fu_struct_id9_spi_cmd_set_data(spi_cmd, unknown_cmd, error)) + fu_struct_id9_spi_cmd_set_size(st_spi, streamsz + FU_STRUCT_ID9_UNKNOWN_CMD_SIZE); + if (!fu_struct_id9_spi_cmd_set_data(st_spi, st_unknown, error)) return NULL; - fu_struct_id9_loader_cmd_set_command(loader_cmd, command); - fu_struct_id9_loader_cmd_set_size(loader_cmd, streamsz + FU_STRUCT_ID9_SPI_CMD_SIZE); - if (!fu_wac_module_bluetooth_id9_calculate_crc32(spi_cmd, stream, &crc, error)) + fu_struct_id9_loader_cmd_set_command(st_loader, command); + fu_struct_id9_loader_cmd_set_size(st_loader, streamsz + FU_STRUCT_ID9_SPI_CMD_SIZE); + if (!fu_wac_module_bluetooth_id9_calculate_crc32(st_spi->buf, stream, &crc, error)) return NULL; - fu_struct_id9_loader_cmd_set_crc(loader_cmd, crc); - if (!fu_struct_id9_loader_cmd_set_data(loader_cmd, spi_cmd, error)) + fu_struct_id9_loader_cmd_set_crc(st_loader, crc); + if (!fu_struct_id9_loader_cmd_set_data(st_loader, st_spi, error)) return NULL; - if (!fu_struct_id9_loader_cmd_validate(loader_cmd->data, loader_cmd->len, 0, error)) + if (!fu_struct_id9_loader_cmd_validate(st_loader->buf->data, st_loader->buf->len, 0, error)) return NULL; - return fu_chunk_bytes_new(g_bytes_new(loader_cmd->data, loader_cmd->len)); + blob = fu_struct_id9_loader_cmd_to_bytes(st_loader); + return fu_chunk_bytes_new(blob); } static gboolean @@ -148,7 +150,7 @@ fu_wac_module_bluetooth_id9_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { const guint8 *blob; @@ -192,7 +194,8 @@ return NULL; loader_fw = fu_firmware_new_from_bytes(loader_bytes); fu_firmware_set_id(loader_fw, FU_FIRMWARE_ID_HEADER); - fu_firmware_add_image(firmware, loader_fw); + if (!fu_firmware_add_image(firmware, loader_fw, error)) + return NULL; payload_len = blob_len - 2 - loader_len; payload_bytes = fu_bytes_new_offset(fw, 2 + loader_len, payload_len, error); @@ -200,7 +203,8 @@ return NULL; payload_fw = fu_firmware_new_from_bytes(payload_bytes); fu_firmware_set_id(payload_fw, FU_FIRMWARE_ID_PAYLOAD); - fu_firmware_add_image(firmware, payload_fw); + if (!fu_firmware_add_image(firmware, payload_fw, error)) + return NULL; return g_steal_pointer(&firmware); } diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-bluetooth.c fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-bluetooth.c --- fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-bluetooth.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-bluetooth.c 2026-02-26 11:36:18.000000000 +0000 @@ -33,9 +33,9 @@ static void fu_wac_module_bluetooth_calculate_crc_byte(guint8 *crc, guint8 data) { - guint8 c[8]; - guint8 m[8]; - guint8 r[8]; + guint8 c[8] = {0}; + guint8 m[8] = {0}; + guint8 r[8] = {0}; /* find out what bits are set */ for (guint i = 0; i < 8; i++) { @@ -136,7 +136,7 @@ /* get default image */ fw = fu_firmware_get_bytes(firmware, error); if (fw == NULL) { - g_prefix_error(error, "wacom bluetooth module failed to get bytes: "); + g_prefix_error_literal(error, "failed to get bytes: "); return FALSE; } @@ -144,7 +144,7 @@ data = g_bytes_get_data(fw, &len); blocks = fu_wac_module_bluetooth_parse_blocks(data, len, TRUE, error); if (blocks == NULL) { - g_prefix_error(error, "wacom bluetooth module failed to parse: "); + g_prefix_error_literal(error, "failed to parse: "); return FALSE; } @@ -156,7 +156,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_START_TIMEOUT, error)) { - g_prefix_error(error, "wacom bluetooth module failed to erase: "); + g_prefix_error_literal(error, "failed to erase: "); return FALSE; } fu_progress_step_done(progress); @@ -164,7 +164,7 @@ /* data */ for (guint i = 0; i < blocks->len; i++) { FuWacModuleBluetoothBlockData *bd = g_ptr_array_index(blocks, i); - guint8 buf[256 + 11]; + guint8 buf[256 + 11]; /* nocheck:zero-init */ g_autoptr(GBytes) blob_chunk = NULL; /* build data packet */ @@ -181,7 +181,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_DATA_TIMEOUT, error)) { - g_prefix_error(error, "wacom bluetooth module failed to write: "); + g_prefix_error_literal(error, "failed to write: "); return FALSE; } @@ -200,7 +200,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_END_TIMEOUT, error)) { - g_prefix_error(error, "wacom bluetooth module failed to end: "); + g_prefix_error_literal(error, "failed to end: "); return FALSE; } fu_progress_step_done(progress); diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-scaler.c fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-scaler.c --- fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-scaler.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-scaler.c 2026-02-26 11:36:18.000000000 +0000 @@ -84,7 +84,7 @@ /* get default image */ fw = fu_firmware_get_bytes(firmware, error); if (fw == NULL) { - g_prefix_error(error, "wacom scaler module failed to get bytes: "); + g_prefix_error_literal(error, "wacom scaler module failed to get bytes: "); return FALSE; } @@ -95,7 +95,7 @@ blocks = fu_wac_module_scaler_parse_blocks(data, len, error); if (blocks == NULL) { - g_prefix_error(error, "wacom scaler module failed to parse blocks: "); + g_prefix_error_literal(error, "wacom scaler module failed to parse blocks: "); return FALSE; } @@ -107,7 +107,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_START_TIMEOUT, error)) { - g_prefix_error(error, "wacom scaler module failed to erase: "); + g_prefix_error_literal(error, "wacom scaler module failed to erase: "); return FALSE; } @@ -116,7 +116,7 @@ /* data */ for (guint i = 0; i < blocks->len; i++) { FuWacModuleScalerBlockData *bd = g_ptr_array_index(blocks, i); - guint8 buf[FU_WAC_MODULE_SCALER_PAYLOAD_SZ + 4]; + guint8 buf[FU_WAC_MODULE_SCALER_PAYLOAD_SZ + 4]; /* nocheck:zero-init */ g_autoptr(GBytes) blob_chunk = NULL; /* build data packet */ @@ -133,7 +133,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_DATA_TIMEOUT, error)) { - g_prefix_error(error, "wacom scaler module failed to write: "); + g_prefix_error_literal(error, "wacom scaler module failed to write: "); return FALSE; } /* update progress */ @@ -151,7 +151,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_END_TIMEOUT, error)) { - g_prefix_error(error, "wacom scaler module failed to end: "); + g_prefix_error_literal(error, "wacom scaler module failed to end: "); return FALSE; } fu_progress_step_done(progress); diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-sub-cpu.c fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-sub-cpu.c --- fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-sub-cpu.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-sub-cpu.c 2026-02-26 11:36:18.000000000 +0000 @@ -81,7 +81,7 @@ { GPtrArray *chunks = g_ptr_array_new_with_free_func(g_free); guint record_num = 0; - g_autoptr(GPtrArray) records = fu_srec_firmware_get_records(srec_firmware); + GPtrArray *records = fu_srec_firmware_get_records(srec_firmware); *data_len = 0; while (record_num < records->len) { @@ -100,7 +100,7 @@ static GBytes * fu_wac_module_sub_cpu_build_packet(FuChunk *chunk, GError **error) { - guint8 buf[FU_WAC_MODULE_SUB_CPU_PAYLOAD_SZ + 5]; + guint8 buf[FU_WAC_MODULE_SUB_CPU_PAYLOAD_SZ + 5]; /* nocheck:zero-init */ memset(buf, 0xff, sizeof(buf)); fu_memwrite_uint32(&buf[0], fu_chunk_get_address(chunk), G_BIG_ENDIAN); @@ -113,7 +113,7 @@ 0, /* src */ fu_chunk_get_data_sz(chunk), error)) { - g_prefix_error(error, "wacom sub_cpu module failed to build packet: "); + g_prefix_error_literal(error, "wacom sub_cpu module failed to build packet: "); return NULL; } @@ -157,7 +157,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_START_TIMEOUT, error)) { - g_prefix_error(error, "wacom sub_cpu module failed to erase: "); + g_prefix_error_literal(error, "wacom sub_cpu module failed to erase: "); return FALSE; } @@ -178,7 +178,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_DATA_TIMEOUT, error)) { - g_prefix_error(error, "wacom sub_cpu module failed to write: "); + g_prefix_error_literal(error, "wacom sub_cpu module failed to write: "); return FALSE; } /* update progress */ @@ -196,7 +196,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_END_TIMEOUT, error)) { - g_prefix_error(error, "wacom sub_cpu module failed to end: "); + g_prefix_error_literal(error, "wacom sub_cpu module failed to end: "); return FALSE; } fu_progress_step_done(progress); @@ -206,22 +206,22 @@ } static FuFirmware * -fu_wac_module_sub_cpu_prepare_firmware(FuDevice *self, +fu_wac_module_sub_cpu_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { - FuFirmware *firmware_srec = fu_srec_firmware_new(); - if (!fu_firmware_parse_stream(firmware_srec, + g_autoptr(FuFirmware) firmware = fu_srec_firmware_new(); + if (!fu_firmware_parse_stream(firmware, stream, 0, - flags | FWUPD_INSTALL_FLAG_NO_SEARCH, + flags | FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, error)) { - g_prefix_error(error, "wacom sub_cpu failed to parse firmware: "); + g_prefix_error_literal(error, "wacom sub_cpu failed to parse firmware: "); return NULL; } - return firmware_srec; + return g_steal_pointer(&firmware); } static void diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-touch-id7.c fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-touch-id7.c --- fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-touch-id7.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-touch-id7.c 2026-02-26 11:36:18.000000000 +0000 @@ -22,12 +22,12 @@ const guint8 *buf; gsize bufsz; gsize offset; -} WtaInfo; +} FuWacWtaInfo; typedef struct { guint32 header_size; guint16 firmware_number; -} WtaFileHeader; +} FuWacWtaFileHeader; typedef struct { guint32 file_name_length; @@ -35,7 +35,7 @@ guint8 ic_id; guint8 ma_id; guint32 block_count; -} WtaRecordHeader; +} FuWacWtaRecordHeader; G_DEFINE_TYPE(FuWacModuleTouchId7, fu_wac_module_touch_id7, FU_TYPE_WAC_MODULE) @@ -53,7 +53,9 @@ * */ static gboolean -fu_wac_module_touch_id7_read_file_header(WtaFileHeader *header, WtaInfo *info, GError **error) +fu_wac_module_touch_id7_read_file_header(FuWacWtaFileHeader *header, + FuWacWtaInfo *info, + GError **error) { info->offset += 4; @@ -99,7 +101,9 @@ * */ static gboolean -fu_wac_module_touch_id7_read_record_header(WtaRecordHeader *header, WtaInfo *info, GError **error) +fu_wac_module_touch_id7_read_record_header(FuWacWtaRecordHeader *header, + FuWacWtaInfo *info, + GError **error) { if (!fu_memread_uint32_safe(info->buf, info->bufsz, @@ -142,7 +146,7 @@ /** * fu_wac_module_touch_id7_generate_command: - * @header: A #WtaRecordHeader + * @header: A #FuWacWtaRecordHeader * @cmd: The type of command to be sent * @op_id: The operation serial number * @buf: The buffer to write the command into @@ -151,7 +155,7 @@ * */ static void -fu_wac_module_touch_id7_generate_command(const WtaRecordHeader *header, +fu_wac_module_touch_id7_generate_command(const FuWacWtaRecordHeader *header, guint8 cmd, guint16 op_id, guint8 *buf) @@ -171,13 +175,13 @@ */ static gboolean fu_wac_module_touch_id7_write_block(FuWacModule *self, - WtaInfo *info, + FuWacWtaInfo *info, FuProgress *progress, - WtaRecordHeader *record_hdr, + FuWacWtaRecordHeader *record_hdr, GError **error) { g_autoptr(GPtrArray) chunks = NULL; - g_autoptr(GByteArray) st_blk = NULL; + g_autoptr(FuStructWtaBlockHeader) st_blk = NULL; /* generate chunks off of the raw block data */ st_blk = fu_struct_wta_block_header_parse(info->buf, info->bufsz, info->offset, error); @@ -193,7 +197,7 @@ /* write data */ for (guint i = 0; i < chunks->len; i++) { FuChunk *chk = g_ptr_array_index(chunks, i); - guint8 buf[11 + FU_WAC_MODULE_CHUNK_SIZE]; + guint8 buf[11 + FU_WAC_MODULE_CHUNK_SIZE] = {0}; g_autoptr(GBytes) blob_chunk = NULL; buf[0] = FU_WAC_MODULE_COMMAND_DATA; @@ -243,14 +247,14 @@ */ static gboolean fu_wac_module_touch_id7_write_record(FuWacModule *self, - WtaInfo *info, + FuWacWtaInfo *info, FuProgress *progress, GError **error) { - WtaRecordHeader record_hdr = {0x0}; + FuWacWtaRecordHeader record_hdr = {0x0}; g_autoptr(GBytes) blob_start = NULL; g_autoptr(GBytes) blob_end = NULL; - guint8 command[11]; + guint8 command[11] = {0}; if (!fu_wac_module_touch_id7_read_record_header(&record_hdr, info, error)) return FALSE; @@ -316,8 +320,8 @@ { FuWacModule *self = FU_WAC_MODULE(device); g_autoptr(GBytes) blob = NULL; - WtaInfo info; - WtaFileHeader file_hdr; + FuWacWtaInfo info; + FuWacWtaFileHeader file_hdr; /* progress */ fu_progress_set_id(progress, G_STRLOC); diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-touch.c fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-touch.c --- fwupd-2.0.8/plugins/wacom-usb/fu-wac-module-touch.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac-module-touch.c 2026-02-26 11:36:18.000000000 +0000 @@ -41,7 +41,7 @@ /* build each data packet */ stream = fu_firmware_get_stream(firmware, error); if (stream == NULL) { - g_prefix_error(error, "wacom touch module failed to get stream: "); + g_prefix_error_literal(error, "wacom touch module failed to get stream: "); return FALSE; } chunks = fu_chunk_array_new_from_stream(stream, @@ -60,7 +60,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_START_TIMEOUT, error)) { - g_prefix_error(error, "wacom touch module failed to erase: "); + g_prefix_error_literal(error, "wacom touch module failed to erase: "); return FALSE; } fu_progress_step_done(progress); @@ -90,7 +90,7 @@ 0x0, /* src */ fu_chunk_get_data_sz(chk), error)) { - g_prefix_error(error, "wacom touch module failed to memcpy: "); + g_prefix_error_literal(error, "wacom touch module failed to memcpy: "); return FALSE; } blob_chunk = g_bytes_new(buf, sizeof(buf)); @@ -120,7 +120,7 @@ FU_WAC_MODULE_POLL_INTERVAL, FU_WAC_MODULE_END_TIMEOUT, error)) { - g_prefix_error(error, "wacom touch module failed to end: "); + g_prefix_error_literal(error, "wacom touch module failed to end: "); return FALSE; } fu_progress_step_done(progress); diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac-module.c fwupd-2.0.20/plugins/wacom-usb/fu-wac-module.c --- fwupd-2.0.8/plugins/wacom-usb/fu-wac-module.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac-module.c 2026-02-26 11:36:18.000000000 +0000 @@ -43,18 +43,21 @@ static gboolean fu_wac_module_refresh(FuWacModule *self, GError **error) { - FuWacDevice *parent_device = FU_WAC_DEVICE(fu_device_get_parent(FU_DEVICE(self))); + FuWacDevice *parent; FuWacModulePrivate *priv = GET_PRIVATE(self); guint8 buf[] = {[0] = FU_WAC_REPORT_ID_MODULE, [1 ... FU_WAC_PACKET_LEN - 1] = 0xff}; /* get from hardware */ - if (!fu_wac_device_get_feature_report(parent_device, + parent = FU_WAC_DEVICE(fu_device_get_parent(FU_DEVICE(self), error)); + if (parent == NULL) + return FALSE; + if (!fu_wac_device_get_feature_report(parent, buf, sizeof(buf), FU_HID_DEVICE_FLAG_ALLOW_TRUNC, error)) { - g_prefix_error(error, "failed to refresh status: "); - fu_error_convert(error); + g_prefix_error_literal(error, "failed to refresh status: "); + fwupd_error_convert(error); return FALSE; } @@ -124,22 +127,28 @@ guint busy_timeout, /* ms */ GError **error) { - FuWacDevice *parent_device = FU_WAC_DEVICE(fu_device_get_parent(FU_DEVICE(self))); + FuWacDevice *parent; FuWacModulePrivate *priv = GET_PRIVATE(self); const guint8 *data; gsize len = 0; - guint delay_ms = fu_device_has_flag(FU_DEVICE(parent_device), FWUPD_DEVICE_FLAG_EMULATED) - ? 10 - : poll_interval; - guint busy_poll_loops = busy_timeout / delay_ms; - guint8 buf[] = {[0] = FU_WAC_REPORT_ID_MODULE, - [1] = priv->fw_type, - [2] = command, - [3 ... FU_WAC_PACKET_LEN - 1] = 0xff}; + guint delay_ms; + guint busy_poll_loops; + guint8 buf[] = { + [0] = FU_WAC_REPORT_ID_MODULE, + [1] = priv->fw_type, + [2] = command, + [3 ... FU_WAC_PACKET_LEN - 1] = 0xff, + }; /* sanity check */ g_return_val_if_fail(FU_IS_WAC_MODULE(self), FALSE); - g_return_val_if_fail(FU_IS_WAC_DEVICE(parent_device), FALSE); + + parent = FU_WAC_DEVICE(fu_device_get_parent(FU_DEVICE(self), error)); + if (parent == NULL) + return FALSE; + delay_ms = + fu_device_has_flag(FU_DEVICE(parent), FWUPD_DEVICE_FLAG_EMULATED) ? 10 : poll_interval; + busy_poll_loops = busy_timeout / delay_ms; /* verify the size of the blob */ if (blob != NULL) { @@ -152,7 +161,7 @@ 0x0, /* src */ len, error)) { - g_prefix_error(error, "Submodule blob larger than buffer: "); + g_prefix_error_literal(error, "Submodule blob larger than buffer: "); return FALSE; } } @@ -173,12 +182,12 @@ } /* send to hardware */ - if (!fu_wac_device_set_feature_report(parent_device, + if (!fu_wac_device_set_feature_report(parent, buf, sizeof(buf), FU_HID_DEVICE_FLAG_ALLOW_TRUNC, error)) { - g_prefix_error(error, "failed to set module feature: "); + g_prefix_error_literal(error, "failed to set module feature: "); return FALSE; } @@ -217,8 +226,14 @@ FwupdInstallFlags flags, GError **error) { - FuDevice *parent = fu_device_get_parent(device); - g_autoptr(FuDeviceLocker) locker = fu_device_locker_new(parent, error); + FuDevice *parent; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* sanity check */ + parent = fu_device_get_parent(device, error); + if (parent == NULL) + return FALSE; + locker = fu_device_locker_new(parent, error); if (locker == NULL) return FALSE; return fu_device_cleanup(parent, progress, flags, error); @@ -269,8 +284,10 @@ fu_device_add_protocol(FU_DEVICE(self), "com.wacom.usb"); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_PARENT_NAME_PREFIX); fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_BCD); fu_device_set_remove_delay(FU_DEVICE(self), FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_WAC_DEVICE); } static void @@ -278,7 +295,7 @@ { FuWacModule *self = FU_WAC_MODULE(object); FuWacModulePrivate *priv = GET_PRIVATE(self); - FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self)); + FuDevice *proxy = fu_device_get_proxy(FU_DEVICE(self), NULL); /* not set in tests */ if (proxy != NULL) { @@ -308,7 +325,7 @@ } static void -fu_wac_module_set_progress(FuDevice *self, FuProgress *progress) +fu_wac_module_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac-plugin.c fwupd-2.0.20/plugins/wacom-usb/fu-wac-plugin.c --- fwupd-2.0.8/plugins/wacom-usb/fu-wac-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -32,7 +32,7 @@ FwupdInstallFlags flags, GError **error) { - FuDevice *parent = fu_device_get_parent(device); + FuDevice *parent = fu_device_get_parent(device, NULL); g_autoptr(FuDeviceLocker) locker = NULL; locker = fu_device_locker_new(parent != NULL ? parent : device, error); if (locker == NULL) @@ -70,14 +70,19 @@ break; } if (FU_IS_WAC_MODULE(device_tmp)) { - g_set_object(&main_device, FU_WAC_DEVICE(fu_device_get_proxy(device_tmp))); + FuWacDevice *proxy; + proxy = FU_WAC_DEVICE(fu_device_get_proxy(device_tmp, error)); + if (proxy == NULL) + return FALSE; + g_set_object(&main_device, proxy); break; } } /* reset */ if (main_device != NULL) { - g_autoptr(FuDeviceLocker) locker = fu_device_locker_new(main_device, error); + g_autoptr(FuDeviceLocker) locker = + fu_device_locker_new(FU_DEVICE(main_device), error); if (locker == NULL) return FALSE; g_info("resetting main device"); @@ -93,6 +98,7 @@ static void fu_wac_plugin_init(FuWacPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void @@ -106,6 +112,7 @@ fu_wac_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_set_device_gtype_default(plugin, FU_TYPE_WAC_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_WAC_ANDROID_DEVICE); fu_plugin_add_device_gtype(plugin, FU_TYPE_WAC_MODULE_BLUETOOTH); /* coverage */ diff -Nru fwupd-2.0.8/plugins/wacom-usb/fu-wac.rs fwupd-2.0.20/plugins/wacom-usb/fu-wac.rs --- fwupd-2.0.8/plugins/wacom-usb/fu-wac.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/fu-wac.rs 2026-02-26 11:36:18.000000000 +0000 @@ -77,7 +77,7 @@ ErrWrongImage, } -#[derive(ToBitString)] +#[derive(ToString)] enum FuWacDeviceStatus { Unknown = 0, Writing = 1 << 0, @@ -104,7 +104,7 @@ data: FuStructId9UnknownCmd, } -#[derive(New,Validate)] +#[derive(New, Validate, ToBytes)] #[repr(C, packed)] struct FuStructId9LoaderCmd { command: u8, diff -Nru fwupd-2.0.8/plugins/wacom-usb/meson.build fwupd-2.0.20/plugins/wacom-usb/meson.build --- fwupd-2.0.8/plugins/wacom-usb/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -58,6 +58,7 @@ ], install: true, install_rpath: libdir_pkg, + install_tag: 'tests', install_dir: installed_test_bindir, c_args: [ cargs, diff -Nru fwupd-2.0.8/plugins/wacom-usb/tests/wacom-intuos-bt-m.json fwupd-2.0.20/plugins/wacom-usb/tests/wacom-intuos-bt-m.json --- fwupd-2.0.8/plugins/wacom-usb/tests/wacom-intuos-bt-m.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/tests/wacom-intuos-bt-m.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/d16b682de56c42b134f1e5af7f9926c62dc246df851648e49aa1d1d0e5b38532-Wacom-Intuos_BT-M_MainFW-1.66.cab", - "emulation-url": "https://fwupd.org/downloads/48a669a65a69b999afdb172882959e30c25b61d3095f47b04d9207b019be74cf-Wacom-Intuos_BT-M_MainFW-1.66.zip", + "url": "d16b682de56c42b134f1e5af7f9926c62dc246df851648e49aa1d1d0e5b38532-Wacom-Intuos_BT-M_MainFW-1.66.cab", + "emulation-url": "48a669a65a69b999afdb172882959e30c25b61d3095f47b04d9207b019be74cf-Wacom-Intuos_BT-M_MainFW-1.66.zip", "components": [ { "name": "main", @@ -16,7 +16,7 @@ ] }, { - "url": "https://fwupd.org/downloads/1ee4f3dc9fd08acd4c6bc833b25e7061f85ddd40122148d66940cd3ddd748920-Wacom-Intuos_BT-M_BluetoothFW-1.12.cab", + "url": "1ee4f3dc9fd08acd4c6bc833b25e7061f85ddd40122148d66940cd3ddd748920-Wacom-Intuos_BT-M_BluetoothFW-1.12.cab", "components": [ { "name": "bluetooth", diff -Nru fwupd-2.0.8/plugins/wacom-usb/tests/wacom-intuos-bt-s.json fwupd-2.0.20/plugins/wacom-usb/tests/wacom-intuos-bt-s.json --- fwupd-2.0.8/plugins/wacom-usb/tests/wacom-intuos-bt-s.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wacom-usb/tests/wacom-intuos-bt-s.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,7 +3,7 @@ "interactive": false, "steps": [ { - "url": "https://fwupd.org/downloads/0d9314e86d28f78f5dc37d71c5cc5205b681334fe64b28f1ec95b5eedc6e1ea6-Wacom-CTL-4100WL-1.10.cab", + "url": "0d9314e86d28f78f5dc37d71c5cc5205b681334fe64b28f1ec95b5eedc6e1ea6-Wacom-CTL-4100WL-1.10.cab", "components": [ { "name": "bluetooth", @@ -22,7 +22,7 @@ ] }, { - "url": "https://fwupd.org/downloads/cfec6515263c0d358d40d7b8fb6472214015225210dfa2db8dd307e896f03dcf-Wacom-CTL-4100WL-1.11.cab", + "url": "cfec6515263c0d358d40d7b8fb6472214015225210dfa2db8dd307e896f03dcf-Wacom-CTL-4100WL-1.11.cab", "components": [ { "name": "bluetooth", diff -Nru fwupd-2.0.8/plugins/wch-ch341a/README.md fwupd-2.0.20/plugins/wch-ch341a/README.md --- fwupd-2.0.8/plugins/wch-ch341a/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch341a/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,61 @@ +--- +title: Plugin: CH341A +--- + +## Introduction + +The CH341A is an affordable SPI programmer by WinChipHead. + +The assumed map between UIO command bits, pins on CH341A chip and pins on SPI chip: + + UIO CH341A SPI CH341A + 0 D0/15 CS/1 CS0 + 1 D1/16 unused CS1 + 2 D2/17 unused CS2 + 3 D3/18 SCK/6 DCK + 4 D4/19 unused DOUT2 + 5 D5/20 SI/5 DOUBT + 6 D6/21 unused DIN2 + 7 D7/22 SO/2 DIN + +> [!WARNING] +> You must perform the 3.3V signal output modification if you are using the CH341A with 3.3V SPI +> chips. The CH341A has a design flaw that outputs 5V on the MISO and MOSI pins even when VCC is 3V +> which will almost certainly be out-of-specification for the device you are trying to program. + +![CH341A Signal Output Modification](wch-ch341a-vmod.png) + +See [this guide](https://www.chucknemeth.com/usb-devices/wch-ch341a/3v-wch-ch341a-mod) for more details. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob of unspecified format. + +This plugin supports the following protocol ID: + +- `org.jedec.cfi` + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +- `USB\VID_1A86&PID_5512` + +## Update Behavior + +The device programs devices in raw mode, and can best be used with `fwupdtool`. + +To write an image, use `sudo fwupdtool --plugins wch-ch341a install-blob firmware.bin` and to backup +the contents of a SPI device use `sudo fwupdtool --plugins wch-ch341a firmware-dump backup.bin` + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x1A86` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. + +## Version Considerations + +This plugin has been available since fwupd version `1.8.0`. diff -Nru fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a-cfi-device.c fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-cfi-device.c --- fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a-cfi-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-cfi-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,462 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-wch-ch341a-cfi-device.h" +#include "fu-wch-ch341a-device.h" + +struct _FuWchCh341aCfiDevice { + FuCfiDevice parent_instance; +}; + +G_DEFINE_TYPE(FuWchCh341aCfiDevice, fu_wch_ch341a_cfi_device, FU_TYPE_CFI_DEVICE) + +#define WCH_CH341A_PAYLOAD_SIZE 0x1A + +static gboolean +fu_wch_ch341a_cfi_device_chip_select(FuCfiDevice *self, gboolean value, GError **error) +{ + FuWchCh341aDevice *proxy; + proxy = FU_WCH_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + return fu_wch_ch341a_device_chip_select(proxy, value, error); +} + +typedef struct { + guint8 mask; + guint8 value; +} FuWchCh341aCfiDeviceHelper; + +static gboolean +fu_wch_ch341a_cfi_device_wait_for_status_cb(FuDevice *device, gpointer user_data, GError **error) +{ + FuWchCh341aCfiDeviceHelper *helper = (FuWchCh341aCfiDeviceHelper *)user_data; + FuWchCh341aCfiDevice *self = FU_WCH_CH341A_CFI_DEVICE(device); + FuWchCh341aDevice *proxy; + guint8 buf[2] = {0x0}; + g_autoptr(FuDeviceLocker) cslocker = NULL; + + /* enable chip */ + proxy = FU_WCH_CH341A_DEVICE(fu_device_get_proxy(device, error)); + if (proxy == NULL) + return FALSE; + cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); + if (cslocker == NULL) + return FALSE; + if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), + FU_CFI_DEVICE_CMD_READ_STATUS, + &buf[0], + error)) + return FALSE; + if (!fu_wch_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) { + g_prefix_error_literal(error, "failed to want to status: "); + return FALSE; + } + if ((buf[0x1] & helper->mask) != helper->value) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "wanted 0x%x, got 0x%x", + helper->value, + (guint)(buf[0x1] & helper->mask)); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wch_ch341a_cfi_device_wait_for_status(FuWchCh341aCfiDevice *self, + guint8 mask, + guint8 value, + guint count, + guint delay, + GError **error) +{ + FuWchCh341aCfiDeviceHelper helper = {.mask = mask, .value = value}; + return fu_device_retry_full(FU_DEVICE(self), + fu_wch_ch341a_cfi_device_wait_for_status_cb, + count, + delay, + &helper, + error); +} + +static gboolean +fu_wch_ch341a_cfi_device_read_jedec(FuCfiDevice *self, GError **error) +{ + FuWchCh341aDevice *proxy; + guint8 buf[WCH_CH341A_PAYLOAD_SIZE] = {0x9F}; + g_autoptr(FuDeviceLocker) cslocker = NULL; + g_autoptr(GString) flash_id = g_string_new(NULL); + + /* enable chip */ + cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); + if (cslocker == NULL) + return FALSE; + + /* read JEDEC ID */ + proxy = FU_WCH_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + if (!fu_wch_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) { + g_prefix_error_literal(error, "failed to request JEDEC ID: "); + return FALSE; + } + if (buf[1] == 0x0 && buf[2] == 0x0 && buf[3] == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "flash ID non-valid, got 0x000000"); + return FALSE; + } + if (buf[1] == 0xFF && buf[2] == 0xFF && buf[3] == 0xFF) { + fu_cfi_device_set_flash_id(FU_CFI_DEVICE(self), NULL); + return TRUE; + } + g_string_append_printf(flash_id, "%02X", buf[1]); + g_string_append_printf(flash_id, "%02X", buf[2]); + g_string_append_printf(flash_id, "%02X", buf[3]); + fu_cfi_device_set_flash_id(FU_CFI_DEVICE(self), flash_id->str); + + /* success */ + return TRUE; +} + +static gboolean +fu_wch_ch341a_cfi_device_write_enable(FuWchCh341aCfiDevice *self, GError **error) +{ + FuWchCh341aDevice *proxy; + guint8 buf[1] = {0x0}; + g_autoptr(FuDeviceLocker) cslocker = NULL; + + /* write enable */ + if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), FU_CFI_DEVICE_CMD_WRITE_EN, &buf[0], error)) + return FALSE; + cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); + if (cslocker == NULL) + return FALSE; + proxy = FU_WCH_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + if (!fu_wch_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) + return FALSE; + if (!fu_device_locker_close(cslocker, error)) + return FALSE; + + /* check that WEL is now set */ + return fu_wch_ch341a_cfi_device_wait_for_status(self, 0b10, 0b10, 10, 5, error); +} + +static gboolean +fu_wch_ch341a_cfi_device_chip_erase(FuWchCh341aCfiDevice *self, GError **error) +{ + FuWchCh341aDevice *proxy; + guint8 buf[] = {0x0}; + g_autoptr(FuDeviceLocker) cslocker = NULL; + + /* enable chip */ + cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); + if (cslocker == NULL) + return FALSE; + + /* erase */ + if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), + FU_CFI_DEVICE_CMD_CHIP_ERASE, + &buf[0], + error)) + return FALSE; + proxy = FU_WCH_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + if (!fu_wch_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) + return FALSE; + if (!fu_device_locker_close(cslocker, error)) + return FALSE; + + /* poll Read Status register BUSY */ + return fu_wch_ch341a_cfi_device_wait_for_status(self, 0b1, 0b0, 100, 500, error); +} + +static gboolean +fu_wch_ch341a_cfi_device_write_page(FuWchCh341aCfiDevice *self, FuChunk *page, GError **error) +{ + FuWchCh341aDevice *proxy; + guint8 buf[4] = {0x0}; + g_autoptr(FuChunkArray) chunks = NULL; + g_autoptr(FuDeviceLocker) cslocker = NULL; + g_autoptr(GBytes) page_blob = fu_chunk_get_bytes(page); + + if (!fu_wch_ch341a_cfi_device_write_enable(self, error)) + return FALSE; + + cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); + if (cslocker == NULL) + return FALSE; + + /* cmd, then 24 bit starting address */ + fu_memwrite_uint32(buf, fu_chunk_get_address(page), G_BIG_ENDIAN); + if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), + FU_CFI_DEVICE_CMD_PAGE_PROG, + &buf[0], + error)) + return FALSE; + proxy = FU_WCH_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + if (!fu_wch_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) + return FALSE; + + /* send data */ + chunks = fu_chunk_array_new_from_bytes(page_blob, + FU_CHUNK_ADDR_OFFSET_NONE, + FU_CHUNK_PAGESZ_NONE, + WCH_CH341A_PAYLOAD_SIZE); + for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { + g_autoptr(FuChunk) chk = NULL; + guint8 buf2[WCH_CH341A_PAYLOAD_SIZE] = {0x0}; + + /* prepare chunk */ + chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) + return FALSE; + if (!fu_memcpy_safe(buf2, + sizeof(buf2), + 0x0, /* dst */ + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + 0x0, /* src */ + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + if (!fu_wch_ch341a_device_spi_transfer(proxy, + buf2, + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + } + if (!fu_device_locker_close(cslocker, error)) + return FALSE; + + /* poll Read Status register BUSY */ + return fu_wch_ch341a_cfi_device_wait_for_status(self, 0b1, 0b0, 100, 50, error); +} + +static gboolean +fu_wch_ch341a_cfi_device_write_pages(FuWchCh341aCfiDevice *self, + FuChunkArray *pages, + FuProgress *progress, + GError **error) +{ + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, fu_chunk_array_length(pages)); + for (guint i = 0; i < fu_chunk_array_length(pages); i++) { + g_autoptr(FuChunk) page = fu_chunk_array_index(pages, i, error); + if (page == NULL) + return FALSE; + if (!fu_wch_ch341a_cfi_device_write_page(self, page, error)) + return FALSE; + fu_progress_step_done(progress); + } + /* success */ + return TRUE; +} + +static GBytes * +fu_wch_ch341a_cfi_device_read_firmware(FuWchCh341aCfiDevice *self, + gsize bufsz, + FuProgress *progress, + GError **error) +{ + FuWchCh341aDevice *proxy; + guint8 buf[WCH_CH341A_PAYLOAD_SIZE] = {0x0}; + g_autoptr(FuDeviceLocker) cslocker = NULL; + g_autoptr(GByteArray) blob = g_byte_array_new(); + g_autoptr(GPtrArray) chunks = NULL; + + /* enable chip */ + cslocker = fu_cfi_device_chip_select_locker_new(FU_CFI_DEVICE(self), error); + if (cslocker == NULL) + return NULL; + + /* read each block */ + chunks = fu_chunk_array_new(NULL, bufsz + 0x4, 0x0, 0x0, WCH_CH341A_PAYLOAD_SIZE); + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_steps(progress, chunks->len); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + + /* cmd, then 24 bit starting address */ + fu_memwrite_uint32(buf, 0x0, G_BIG_ENDIAN); + if (!fu_cfi_device_get_cmd(FU_CFI_DEVICE(self), + FU_CFI_DEVICE_CMD_READ_DATA, + &buf[0], + error)) + return NULL; + proxy = FU_WCH_CH341A_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + + /* the first package has cmd and address info */ + if (!fu_wch_ch341a_device_spi_transfer(proxy, buf, sizeof(buf), error)) + return NULL; + if (i == 0) { + g_byte_array_append(blob, buf + 0x4, fu_chunk_get_data_sz(chk) - 0x4); + } else { + g_byte_array_append(blob, buf + 0x0, fu_chunk_get_data_sz(chk)); + } + + /* done */ + fu_progress_step_done(progress); + } + + /* success */ + return g_bytes_new(blob->data, blob->len); +} + +static gboolean +fu_wch_ch341a_cfi_device_write_firmware(FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + FuWchCh341aCfiDevice *self = FU_WCH_CH341A_CFI_DEVICE(device); + FuDevice *proxy; + g_autoptr(GBytes) fw = NULL; + g_autoptr(GBytes) fw_verify = NULL; + g_autoptr(FuChunkArray) pages = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open programmer */ + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return FALSE; + locker = fu_device_locker_new(proxy, error); + if (locker == NULL) + return FALSE; + + /* progress */ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_flag(progress, FU_PROGRESS_FLAG_GUESSED); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_ERASE, 33, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 44, NULL); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_VERIFY, 35, NULL); + + /* get default image */ + fw = fu_firmware_get_bytes(firmware, error); + if (fw == NULL) + return FALSE; + + /* erase */ + if (!fu_wch_ch341a_cfi_device_write_enable(self, error)) { + g_prefix_error_literal(error, "failed to enable writes: "); + return FALSE; + } + if (!fu_wch_ch341a_cfi_device_chip_erase(self, error)) { + g_prefix_error_literal(error, "failed to erase: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* write each block */ + pages = fu_chunk_array_new_from_bytes(fw, + FU_CHUNK_ADDR_OFFSET_NONE, + FU_CHUNK_PAGESZ_NONE, + fu_cfi_device_get_page_size(FU_CFI_DEVICE(self))); + if (!fu_wch_ch341a_cfi_device_write_pages(self, + pages, + fu_progress_get_child(progress), + error)) { + g_prefix_error_literal(error, "failed to write pages: "); + return FALSE; + } + fu_progress_step_done(progress); + + /* verify each block */ + fw_verify = fu_wch_ch341a_cfi_device_read_firmware(self, + g_bytes_get_size(fw), + fu_progress_get_child(progress), + error); + if (fw_verify == NULL) { + g_prefix_error_literal(error, "failed to verify blocks: "); + return FALSE; + } + if (!fu_bytes_compare(fw, fw_verify, error)) + return FALSE; + fu_progress_step_done(progress); + + /* success! */ + return TRUE; +} + +static GBytes * +fu_wch_ch341a_cfi_device_dump_firmware(FuDevice *device, FuProgress *progress, GError **error) +{ + FuWchCh341aCfiDevice *self = FU_WCH_CH341A_CFI_DEVICE(device); + FuDevice *proxy; + gsize bufsz = fu_device_get_firmware_size_max(device); + g_autoptr(FuDeviceLocker) locker = NULL; + + /* open programmer */ + proxy = fu_device_get_proxy(FU_DEVICE(self), error); + if (proxy == NULL) + return NULL; + locker = fu_device_locker_new(proxy, error); + if (locker == NULL) + return NULL; + + /* sanity check */ + if (bufsz == 0x0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_READ, + "device firmware size not set"); + return NULL; + } + return fu_wch_ch341a_cfi_device_read_firmware(self, bufsz, progress, error); +} + +static void +fu_wch_ch341a_cfi_device_set_progress(FuDevice *device, FuProgress *progress) +{ + fu_progress_set_id(progress, G_STRLOC); + fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "detach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 100, "write"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_RESTART, 0, "attach"); + fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 0, "reload"); +} + +static void +fu_wch_ch341a_cfi_device_init(FuWchCh341aCfiDevice *self) +{ + fu_device_add_protocol(FU_DEVICE(self), "org.jedec.cfi"); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_NO_VERSION_EXPECTED); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_WCH_CH341A_DEVICE); +} + +static void +fu_wch_ch341a_cfi_device_class_init(FuWchCh341aCfiDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + FuCfiDeviceClass *cfi_class = FU_CFI_DEVICE_CLASS(klass); + + cfi_class->chip_select = fu_wch_ch341a_cfi_device_chip_select; + cfi_class->read_jedec = fu_wch_ch341a_cfi_device_read_jedec; + + device_class->write_firmware = fu_wch_ch341a_cfi_device_write_firmware; + device_class->dump_firmware = fu_wch_ch341a_cfi_device_dump_firmware; + device_class->set_progress = fu_wch_ch341a_cfi_device_set_progress; +} diff -Nru fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a-cfi-device.h fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-cfi-device.h --- fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a-cfi-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-cfi-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_WCH_CH341A_CFI_DEVICE (fu_wch_ch341a_cfi_device_get_type()) +G_DECLARE_FINAL_TYPE(FuWchCh341aCfiDevice, + fu_wch_ch341a_cfi_device, + FU, + WCH_CH341A_CFI_DEVICE, + FuCfiDevice) diff -Nru fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a-device.c fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-device.c --- fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,264 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-wch-ch341a-cfi-device.h" +#include "fu-wch-ch341a-device.h" +#include "fu-wch-ch341a-struct.h" + +struct _FuWchCh341aDevice { + FuUsbDevice parent_instance; + guint8 speed; +}; + +G_DEFINE_TYPE(FuWchCh341aDevice, fu_wch_ch341a_device, FU_TYPE_USB_DEVICE) + +#define WCH_CH341A_USB_TIMEOUT 1000 +#define WCH_CH341A_EP_OUT 0x02 /* host to device (write) */ +#define WCH_CH341A_EP_IN 0x82 /* device to host (read) */ +#define WCH_CH341A_EP_SIZE 0x20 + +#define FU_WCH_CH341A_STM_SPI_MODUS_STANDARD 0x00 +#define FU_WCH_CH341A_STM_SPI_MODUS_DOUBLE 0x04 + +#define FU_WCH_CH341A_STM_SPI_ENDIAN_BIG 0x0 +#define FU_WCH_CH341A_STM_SPI_ENDIAN_LITTLE 0x80 + +static const gchar * +fu_wch_ch341a_device_speed_to_string(guint8 speed) +{ + if (speed == FU_WCH_CH341A_STM_I2C_SPEED_LOW) + return "20kHz"; + if (speed == FU_WCH_CH341A_STM_I2C_SPEED_STANDARD) + return "100kHz"; + if (speed == FU_WCH_CH341A_STM_I2C_SPEED_FAST) + return "400kHz"; + if (speed == FU_WCH_CH341A_STM_I2C_SPEED_HIGH) + return "750kHz"; + if (speed == (FU_WCH_CH341A_STM_I2C_SPEED_LOW | FU_WCH_CH341A_STM_SPI_MODUS_DOUBLE)) + return "2*20kHz"; + if (speed == (FU_WCH_CH341A_STM_I2C_SPEED_STANDARD | FU_WCH_CH341A_STM_SPI_MODUS_DOUBLE)) + return "2*100kHz"; + if (speed == (FU_WCH_CH341A_STM_I2C_SPEED_FAST | FU_WCH_CH341A_STM_SPI_MODUS_DOUBLE)) + return "2*400kHz"; + if (speed == (FU_WCH_CH341A_STM_I2C_SPEED_HIGH | FU_WCH_CH341A_STM_SPI_MODUS_DOUBLE)) + return "2*750kHz"; + return NULL; +} + +static void +fu_wch_ch341a_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuWchCh341aDevice *self = FU_WCH_CH341A_DEVICE(device); + fwupd_codec_string_append(str, + idt, + "Speed", + fu_wch_ch341a_device_speed_to_string(self->speed)); +} + +static gboolean +fu_wch_ch341a_device_write(FuWchCh341aDevice *self, guint8 *buf, gsize bufsz, GError **error) +{ + gsize actual_length = 0; + + /* debug */ + fu_dump_raw(G_LOG_DOMAIN, "write", buf, bufsz); + if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), + WCH_CH341A_EP_OUT, + buf, + bufsz, + &actual_length, + WCH_CH341A_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to write 0x%x bytes: ", (guint)bufsz); + return FALSE; + } + if (bufsz != actual_length) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "only wrote 0x%x of 0x%x", + (guint)actual_length, + (guint)bufsz); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wch_ch341a_device_read(FuWchCh341aDevice *self, guint8 *buf, gsize bufsz, GError **error) +{ + gsize actual_length = 0; + + if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), + WCH_CH341A_EP_IN, + buf, + bufsz, + &actual_length, + WCH_CH341A_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to read 0x%x bytes: ", (guint)bufsz); + return FALSE; + } + if (bufsz != actual_length) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "only read 0x%x of 0x%x", + (guint)actual_length, + (guint)bufsz); + return FALSE; + } + + /* debug */ + fu_dump_raw(G_LOG_DOMAIN, "read", buf, bufsz); + + /* success */ + return TRUE; +} + +/** + * fu_wch_ch341a_device_reverse_uint8: + * @value: integer + * + * Calculates the reverse bit order for a single byte. + * + * Returns: the @value, reversed + **/ +static guint8 +fu_wch_ch341a_device_reverse_uint8(guint8 value) +{ + guint8 tmp = 0; + if (value & 0x01) + tmp = 0x80; + if (value & 0x02) + tmp |= 0x40; + if (value & 0x04) + tmp |= 0x20; + if (value & 0x08) + tmp |= 0x10; + if (value & 0x10) + tmp |= 0x08; + if (value & 0x20) + tmp |= 0x04; + if (value & 0x40) + tmp |= 0x02; + if (value & 0x80) + tmp |= 0x01; + return tmp; +} + +gboolean +fu_wch_ch341a_device_spi_transfer(FuWchCh341aDevice *self, guint8 *buf, gsize bufsz, GError **error) +{ + gsize buf2sz = bufsz + 1; + g_autofree guint8 *buf2 = g_malloc0(buf2sz); + + /* requires LSB first */ + buf2[0] = FU_WCH_CH341A_CMD_SPI_STREAM; + for (gsize i = 0; i < bufsz; i++) + buf2[i + 1] = fu_wch_ch341a_device_reverse_uint8(buf[i]); + + /* debug */ + fu_dump_raw(G_LOG_DOMAIN, "SPIwrite", buf, bufsz); + if (!fu_wch_ch341a_device_write(self, buf2, buf2sz, error)) + return FALSE; + + if (!fu_wch_ch341a_device_read(self, buf, bufsz, error)) + return FALSE; + + /* requires LSB first */ + for (gsize i = 0; i < bufsz; i++) + buf[i] = fu_wch_ch341a_device_reverse_uint8(buf[i]); + + /* debug */ + fu_dump_raw(G_LOG_DOMAIN, "SPIread", buf, bufsz); + + /* success */ + return TRUE; +} + +static gboolean +fu_wch_ch341a_device_configure_stream(FuWchCh341aDevice *self, GError **error) +{ + guint8 buf[] = {FU_WCH_CH341A_CMD_I2C_STREAM, + FU_WCH_CH341A_CMD_I2C_STM_SET | self->speed, + FU_WCH_CH341A_CMD_I2C_STM_END}; + if (!fu_wch_ch341a_device_write(self, buf, sizeof(buf), error)) { + g_prefix_error_literal(error, "failed to configure stream: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_wch_ch341a_device_chip_select(FuWchCh341aDevice *self, gboolean val, GError **error) +{ + guint8 buf[] = { + FU_WCH_CH341A_CMD_UIO_STREAM, + FU_WCH_CH341A_CMD_UIO_STM_OUT | (val ? 0x36 : 0x37), /* CS* high, SCK=0, DOUBT*=1 */ + FU_WCH_CH341A_CMD_UIO_STM_DIR | (val ? 0x3F : 0x00), /* pin direction */ + FU_WCH_CH341A_CMD_UIO_STM_END, + }; + return fu_wch_ch341a_device_write(self, buf, sizeof(buf), error); +} + +static gboolean +fu_wch_ch341a_device_probe(FuDevice *device, GError **error) +{ + g_autoptr(FuWchCh341aCfiDevice) cfi_device = NULL; + cfi_device = g_object_new(FU_TYPE_WCH_CH341A_CFI_DEVICE, + "context", + fu_device_get_context(device), + "proxy", + device, + "logical-id", + "SPI", + NULL); + fu_device_add_child(device, FU_DEVICE(cfi_device)); + return TRUE; +} + +static gboolean +fu_wch_ch341a_device_setup(FuDevice *device, GError **error) +{ + FuWchCh341aDevice *self = FU_WCH_CH341A_DEVICE(device); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_wch_ch341a_device_parent_class)->setup(device, error)) + return FALSE; + + /* set speed */ + if (!fu_wch_ch341a_device_configure_stream(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static void +fu_wch_ch341a_device_init(FuWchCh341aDevice *self) +{ + self->speed = FU_WCH_CH341A_STM_I2C_SPEED_STANDARD; + fu_usb_device_add_interface(FU_USB_DEVICE(self), 0x0); + fu_device_set_name(FU_DEVICE(self), "CH341A"); +} + +static void +fu_wch_ch341a_device_class_init(FuWchCh341aDeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->probe = fu_wch_ch341a_device_probe; + device_class->setup = fu_wch_ch341a_device_setup; + device_class->to_string = fu_wch_ch341a_device_to_string; +} diff -Nru fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a-device.h fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-device.h --- fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,20 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_WCH_CH341A_DEVICE (fu_wch_ch341a_device_get_type()) +G_DECLARE_FINAL_TYPE(FuWchCh341aDevice, fu_wch_ch341a_device, FU, WCH_CH341A_DEVICE, FuUsbDevice) + +gboolean +fu_wch_ch341a_device_chip_select(FuWchCh341aDevice *self, gboolean val, GError **error); +gboolean +fu_wch_ch341a_device_spi_transfer(FuWchCh341aDevice *self, + guint8 *buf, + gsize bufsz, + GError **error); diff -Nru fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a-plugin.c fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-plugin.c --- fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-wch-ch341a-device.h" +#include "fu-wch-ch341a-plugin.h" + +struct _FuWchCh341aPlugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuWchCh341aPlugin, fu_wch_ch341a_plugin, FU_TYPE_PLUGIN) + +static void +fu_wch_ch341a_plugin_init(FuWchCh341aPlugin *self) +{ +} + +static void +fu_wch_ch341a_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_WCH_CH341A_DEVICE); +} + +static void +fu_wch_ch341a_plugin_class_init(FuWchCh341aPluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->constructed = fu_wch_ch341a_plugin_constructed; +} diff -Nru fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a-plugin.h fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-plugin.h --- fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * Copyright 2022 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuWchCh341aPlugin, fu_wch_ch341a_plugin, FU, WCH_CH341A_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a.rs fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a.rs --- fwupd-2.0.8/plugins/wch-ch341a/fu-wch-ch341a.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch341a/fu-wch-ch341a.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,39 @@ +// Copyright 2025 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +enum FuWchCh341aCmd { + SetOutput = 0xA1, + IoAddr = 0xA2, + PrintOut = 0xA3, + SpiStream = 0xA8, + SioStream = 0xA9, + I2cStream = 0xAA, + UioStream = 0xAB, +} + +enum FuWchCh341aCmdI2c { + StmStart = 0x74, + StmStop = 0x75, + StmOut = 0x80, + StmIn = 0xC0, + StmSet = 0x60, + StmUs = 0x40, + StmMs = 0x50, + StmDly = 0x0F, + StmEnd = 0x00, +} + +enum FuWchCh341aCmdUio { + StmIn = 0x00, + StmDir = 0x40, + StmOut = 0x80, + StmUs = 0xC0, + StmEnd = 0x20, +} + +enum FuWchCh341aStmI2cSpeed { + Low = 0x00, + Standard = 0x01, + Fast = 0x02, + High = 0x03, +} diff -Nru fwupd-2.0.8/plugins/wch-ch341a/lsusb.txt fwupd-2.0.20/plugins/wch-ch341a/lsusb.txt --- fwupd-2.0.8/plugins/wch-ch341a/lsusb.txt 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch341a/lsusb.txt 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,68 @@ +Bus 001 Device 124: ID 1a86:5512 QinHeng Electronics CH341 in EPP/MEM/I2C mode, EPP/I2C adapter +Device Descriptor: + bLength 18 + bDescriptorType 1 + bcdUSB 1.10 + bDeviceClass 255 Vendor Specific Class + bDeviceSubClass 0 + bDeviceProtocol 2 + bMaxPacketSize0 8 + idVendor 0x1a86 QinHeng Electronics + idProduct 0x5512 CH341 in EPP/MEM/I2C mode, EPP/I2C adapter + bcdDevice 3.04 + iManufacturer 0 + iProduct 0 + iSerial 0 + bNumConfigurations 1 + Configuration Descriptor: + bLength 9 + bDescriptorType 2 + wTotalLength 0x0027 + bNumInterfaces 1 + bConfigurationValue 1 + iConfiguration 0 + bmAttributes 0x80 + (Bus Powered) + MaxPower 96mA + Interface Descriptor: + bLength 9 + bDescriptorType 4 + bInterfaceNumber 0 + bAlternateSetting 0 + bNumEndpoints 3 + bInterfaceClass 255 Vendor Specific Class + bInterfaceSubClass 1 + bInterfaceProtocol 2 + iInterface 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x82 EP 2 IN + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x02 EP 2 OUT + bmAttributes 2 + Transfer Type Bulk + Synch Type None + Usage Type Data + wMaxPacketSize 0x0020 1x 32 bytes + bInterval 0 + Endpoint Descriptor: + bLength 7 + bDescriptorType 5 + bEndpointAddress 0x81 EP 1 IN + bmAttributes 3 + Transfer Type Interrupt + Synch Type None + Usage Type Data + wMaxPacketSize 0x0008 1x 8 bytes + bInterval 1 +Device Status: 0x0000 + (Bus Powered) diff -Nru fwupd-2.0.8/plugins/wch-ch341a/meson.build fwupd-2.0.20/plugins/wch-ch341a/meson.build --- fwupd-2.0.8/plugins/wch-ch341a/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch341a/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,18 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginWchCh341a"'] +plugins += {meson.current_source_dir().split('/')[-1]: true} + +plugin_quirks += files('wch-ch341a.quirk') +plugin_builtins += static_library('fu_plugin_wch_ch341a', + rustgen.process('fu-wch-ch341a.rs'), + sources: [ + 'fu-wch-ch341a-cfi-device.c', + 'fu-wch-ch341a-device.c', + 'fu-wch-ch341a-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) + +device_tests += files('tests/wch-ch341a.json') diff -Nru fwupd-2.0.8/plugins/wch-ch341a/tests/wch-ch341a.json fwupd-2.0.20/plugins/wch-ch341a/tests/wch-ch341a.json --- fwupd-2.0.8/plugins/wch-ch341a/tests/wch-ch341a.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch341a/tests/wch-ch341a.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +{ + "name": "WinChipHead CH341a", + "interactive": false, + "steps": [ + { + "url": "31e4cf2ae3e5a3aac46eb12005f2dfd15dda85a5ccc4c93251e93bcf4c6035a1-firmware.cab", + "emulation-url": "dde0010267589e96126000bf191fce95fcdc30d795e486069b1820ce7e2aa37d-emulation.zip", + "components": [ + { + "guids": [ + "fdae557c-1f42-5e24-925f-4b6495c8b35c" + ] + } + ] + } + ] +} Binary files /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/plugins/wch-ch341a/wch-ch341a-vmod.png and /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/plugins/wch-ch341a/wch-ch341a-vmod.png differ diff -Nru fwupd-2.0.8/plugins/wch-ch341a/wch-ch341a.quirk fwupd-2.0.20/plugins/wch-ch341a/wch-ch341a.quirk --- fwupd-2.0.8/plugins/wch-ch341a/wch-ch341a.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch341a/wch-ch341a.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,2 @@ +[USB\VID_1A86&PID_5512] +Plugin = wch_ch341a diff -Nru fwupd-2.0.8/plugins/wch-ch347/README.md fwupd-2.0.20/plugins/wch-ch347/README.md --- fwupd-2.0.8/plugins/wch-ch347/README.md 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch347/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,40 @@ +--- +title: Plugin: CH347 +--- + +## Introduction + +The CH347 is an affordable SPI programmer by WinChipHead. + +## Firmware Format + +The daemon will decompress the cabinet archive and extract a firmware blob of unspecified format. + +This plugin supports the following protocol ID: + +- `org.jedec.cfi` + +## GUID Generation + +These devices use the standard USB DeviceInstanceId values, e.g. + +- `USB\VID_1A86&PID_55DB` + +## Update Behavior + +The device programs devices in raw mode, and can best be used with `fwupdtool`. + +To write an image, use `sudo fwupdtool --plugins wch-ch347 install-blob firmware.bin` and to backup +the contents of a SPI device use `sudo fwupdtool --plugins wch-ch347 firmware-dump backup.bin` + +## Vendor ID Security + +The vendor ID is set from the USB vendor, in this instance set to `USB:0x1A86` + +## External Interface Access + +This plugin requires read/write access to `/dev/bus/usb`. + +## Version Considerations + +This plugin has been available since fwupd version `1.8.14`. diff -Nru fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347-cfi-device.c fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-cfi-device.c --- fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347-cfi-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-cfi-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,56 @@ +/* + * Copyright 2023 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-wch-ch347-cfi-device.h" +#include "fu-wch-ch347-device.h" + +struct _FuWchCh347CfiDevice { + FuCfiDevice parent_instance; +}; + +G_DEFINE_TYPE(FuWchCh347CfiDevice, fu_wch_ch347_cfi_device, FU_TYPE_CFI_DEVICE) + +static gboolean +fu_wch_ch347_cfi_device_chip_select(FuCfiDevice *self, gboolean value, GError **error) +{ + FuWchCh347Device *proxy = FU_WCH_CH347_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + return fu_wch_ch347_device_chip_select(proxy, value, error); +} + +static gboolean +fu_wch_ch347_cfi_device_send_command(FuCfiDevice *self, + const guint8 *wbuf, + gsize wbufsz, + guint8 *rbuf, + gsize rbufsz, + FuProgress *progress, + GError **error) +{ + FuWchCh347Device *proxy = FU_WCH_CH347_DEVICE(fu_device_get_proxy(FU_DEVICE(self), error)); + if (proxy == NULL) + return FALSE; + return fu_wch_ch347_device_send_command(proxy, wbuf, wbufsz, rbuf, rbufsz, progress, error); +} + +static void +fu_wch_ch347_cfi_device_init(FuWchCh347CfiDevice *self) +{ + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_NO_VERSION_EXPECTED); + fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); + fu_device_set_proxy_gtype(FU_DEVICE(self), FU_TYPE_WCH_CH347_DEVICE); +} + +static void +fu_wch_ch347_cfi_device_class_init(FuWchCh347CfiDeviceClass *klass) +{ + FuCfiDeviceClass *cfi_class = FU_CFI_DEVICE_CLASS(klass); + cfi_class->chip_select = fu_wch_ch347_cfi_device_chip_select; + cfi_class->send_command = fu_wch_ch347_cfi_device_send_command; +} diff -Nru fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347-cfi-device.h fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-cfi-device.h --- fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347-cfi-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-cfi-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,16 @@ +/* + * Copyright 2023 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_WCH_CH347_CFI_DEVICE (fu_wch_ch347_cfi_device_get_type()) +G_DECLARE_FINAL_TYPE(FuWchCh347CfiDevice, + fu_wch_ch347_cfi_device, + FU, + WCH_CH347_CFI_DEVICE, + FuCfiDevice) diff -Nru fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347-device.c fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-device.c --- fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347-device.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,317 @@ +/* + * Copyright 2023 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-wch-ch347-cfi-device.h" +#include "fu-wch-ch347-device.h" +#include "fu-wch-ch347-struct.h" + +struct _FuWchCh347Device { + FuUsbDevice parent_instance; + guint8 divisor; +}; + +G_DEFINE_TYPE(FuWchCh347Device, fu_wch_ch347_device, FU_TYPE_USB_DEVICE) + +#define FU_WCH_CH347_USB_TIMEOUT 1000 + +#define FU_WCH_CH347_CS_ASSERT 0x00 +#define FU_WCH_CH347_CS_DEASSERT 0x40 +#define FU_WCH_CH347_CS_CHANGE 0x80 +#define FU_WCH_CH347_CS_IGNORE 0x00 + +#define FU_WCH_CH347_EP_OUT 0x06 +#define FU_WCH_CH347_EP_IN 0x86 + +#define FU_WCH_CH347_MODE1_IFACE 0x2 +#define FU_WCH_CH347_MODE2_IFACE 0x1 + +#define FU_WCH_CH347_PACKET_SIZE 510 +#define FU_WCH_CH347_PAYLOAD_SIZE (FU_WCH_CH347_PACKET_SIZE - 3) + +static void +fu_wch_ch347_device_to_string(FuDevice *device, guint idt, GString *str) +{ + FuWchCh347Device *self = FU_WCH_CH347_DEVICE(device); + fwupd_codec_string_append_hex(str, idt, "Divisor", self->divisor); +} + +static gboolean +fu_wch_ch347_device_write(FuWchCh347Device *self, + FuWchCh347CmdSpi cmd, + const guint8 *buf, + gsize bufsz, + GError **error) +{ + gsize actual_length = 0; + g_autoptr(FuStructWchCh347Req) st = fu_struct_wch_ch347_req_new(); + + /* pack */ + fu_struct_wch_ch347_req_set_cmd(st, cmd); + fu_struct_wch_ch347_req_set_payloadsz(st, bufsz); + if (bufsz > 0) + g_byte_array_append(st->buf, buf, bufsz); + + /* debug */ + fu_dump_raw(G_LOG_DOMAIN, "write", st->buf->data, st->buf->len); + if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), + FU_WCH_CH347_EP_OUT, + st->buf->data, + st->buf->len, + &actual_length, + FU_WCH_CH347_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to write 0x%x bytes: ", (guint)bufsz); + return FALSE; + } + if (st->buf->len != actual_length) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "only wrote 0x%x of 0x%x", + (guint)actual_length, + (guint)st->buf->len); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wch_ch347_device_read(FuWchCh347Device *self, + FuWchCh347CmdSpi cmd, + guint8 *buf, + gsize bufsz, + GError **error) +{ + gsize actual_length = 0; + guint16 size_rsp; + g_autoptr(FuStructWchCh347Req) st = fu_struct_wch_ch347_req_new(); + + /* pack */ + fu_struct_wch_ch347_req_set_cmd(st, cmd); + fu_struct_wch_ch347_req_set_payloadsz(st, sizeof(guint32)); + fu_byte_array_append_uint32(st->buf, bufsz, G_LITTLE_ENDIAN); + fu_byte_array_set_size(st->buf, FU_WCH_CH347_PACKET_SIZE, 0x0); + + if (!fu_usb_device_bulk_transfer(FU_USB_DEVICE(self), + FU_WCH_CH347_EP_IN, + st->buf->data, + st->buf->len, + &actual_length, + FU_WCH_CH347_USB_TIMEOUT, + NULL, + error)) { + g_prefix_error(error, "failed to read 0x%x bytes: ", (guint)bufsz); + return FALSE; + } + fu_dump_raw(G_LOG_DOMAIN, "read", st->buf->data, actual_length); + if (actual_length == 0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "returned 0 bytes"); + return FALSE; + } + + /* debug */ + if (fu_struct_wch_ch347_req_get_cmd(st) != cmd) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "invalid cmd, got 0x%02x, expected 0x%02x", + fu_struct_wch_ch347_req_get_cmd(st), + cmd); + return FALSE; + } + size_rsp = fu_struct_wch_ch347_req_get_payloadsz(st); + if (size_rsp != bufsz) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "size invalid, got 0x%04x, expected 0x04%x", + size_rsp, + (guint)bufsz); + return FALSE; + } + + /* success */ + return fu_memcpy_safe(buf, + bufsz, + 0x0, + st->buf->data, + st->buf->len, + FU_STRUCT_WCH_CH347_REQ_SIZE, + size_rsp, + error); +} + +gboolean +fu_wch_ch347_device_send_command(FuWchCh347Device *self, + const guint8 *wbuf, + gsize wbufsz, + guint8 *rbuf, + gsize rbufsz, + FuProgress *progress, + GError **error) +{ + /* write */ + if (wbufsz > 0) { + g_autoptr(GBytes) wblob = g_bytes_new_static(wbuf, wbufsz); + g_autoptr(FuChunkArray) chunks = + fu_chunk_array_new_from_bytes(wblob, + FU_CHUNK_ADDR_OFFSET_NONE, + FU_CHUNK_PAGESZ_NONE, + FU_WCH_CH347_PAYLOAD_SIZE); + for (guint i = 0; i < fu_chunk_array_length(chunks); i++) { + guint8 buf[1] = {0x0}; + g_autoptr(FuChunk) chk = NULL; + + /* prepare chunk */ + chk = fu_chunk_array_index(chunks, i, error); + if (chk == NULL) + return FALSE; + if (!fu_wch_ch347_device_write(self, + FU_WCH_CH347_CMD_SPI_OUT, + fu_chunk_get_data(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + if (!fu_wch_ch347_device_read(self, + FU_WCH_CH347_CMD_SPI_OUT, + buf, + sizeof(buf), + error)) + return FALSE; + } + } + + /* read */ + if (rbufsz > 0) { + g_autoptr(GPtrArray) chunks = + fu_chunk_array_mutable_new(rbuf, rbufsz, 0x0, 0x0, FU_WCH_CH347_PAYLOAD_SIZE); + g_autoptr(GByteArray) cmdbuf = g_byte_array_new(); + fu_byte_array_append_uint32(cmdbuf, rbufsz, G_LITTLE_ENDIAN); + if (!fu_wch_ch347_device_write(self, + FU_WCH_CH347_CMD_SPI_IN, + cmdbuf->data, + cmdbuf->len, + error)) + return FALSE; + fu_progress_set_id(progress, G_STRLOC); + fu_progress_set_status(progress, FWUPD_STATUS_DEVICE_READ); + fu_progress_set_steps(progress, chunks->len); + for (guint i = 0; i < chunks->len; i++) { + FuChunk *chk = g_ptr_array_index(chunks, i); + if (!fu_wch_ch347_device_read(self, + FU_WCH_CH347_CMD_SPI_IN, + fu_chunk_get_data_out(chk), + fu_chunk_get_data_sz(chk), + error)) + return FALSE; + fu_progress_step_done(progress); + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_wch_ch347_device_configure_stream(FuWchCh347Device *self, GError **error) +{ + guint8 data[26] = {[2] = 4, /* ?? */ + [3] = 1, /* ?? */ + [6] = 0, /* clock polarity: bit 1 */ + [8] = 0, /* clock phase: bit 0 */ + [11] = 2, /* ?? */ + [12] = (self->divisor & 0x7) << 3, /* clock divisor: bits 5:3 */ + [14] = 0, /* bit order: bit 7, 0=MSB */ + [16] = 7, /* ?? */ + [21] = 0}; /* CS polarity: bit 7 CS2, bit 6 CS1. 0 = active low */ + if (!fu_wch_ch347_device_write(self, + FU_WCH_CH347_CMD_SPI_SET_CFG, + data, + sizeof(data), + error)) { + g_prefix_error_literal(error, "failed to configure stream: "); + return FALSE; + } + if (!fu_wch_ch347_device_read(self, FU_WCH_CH347_CMD_SPI_SET_CFG, data, 1, error)) { + g_prefix_error_literal(error, "failed to confirm configure stream: "); + return FALSE; + } + + /* success */ + return TRUE; +} + +gboolean +fu_wch_ch347_device_chip_select(FuWchCh347Device *self, gboolean val, GError **error) +{ + guint8 buf[10] = { + [0] = val ? FU_WCH_CH347_CS_ASSERT | FU_WCH_CH347_CS_CHANGE + : FU_WCH_CH347_CS_DEASSERT | FU_WCH_CH347_CS_CHANGE, + [5] = FU_WCH_CH347_CS_IGNORE /* CS2 */ + }; + return fu_wch_ch347_device_write(self, + FU_WCH_CH347_CMD_SPI_CS_CTRL, + buf, + sizeof(buf), + error); +} + +static gboolean +fu_wch_ch347_device_probe(FuDevice *device, GError **error) +{ + g_autoptr(FuWchCh347CfiDevice) cfi_device = NULL; + cfi_device = g_object_new(FU_TYPE_WCH_CH347_CFI_DEVICE, + "context", + fu_device_get_context(device), + "proxy", + device, + "parent", + device, + "logical-id", + "SPI", + NULL); + fu_device_add_child(device, FU_DEVICE(cfi_device)); + return TRUE; +} + +static gboolean +fu_wch_ch347_device_setup(FuDevice *device, GError **error) +{ + FuWchCh347Device *self = FU_WCH_CH347_DEVICE(device); + + /* FuUsbDevice->setup */ + if (!FU_DEVICE_CLASS(fu_wch_ch347_device_parent_class)->setup(device, error)) + return FALSE; + + /* set divisor */ + if (!fu_wch_ch347_device_configure_stream(self, error)) + return FALSE; + + /* success */ + return TRUE; +} + +static void +fu_wch_ch347_device_init(FuWchCh347Device *self) +{ + self->divisor = 0b10; + fu_usb_device_add_interface(FU_USB_DEVICE(self), FU_WCH_CH347_MODE1_IFACE); + fu_device_set_name(FU_DEVICE(self), "CH347"); +} + +static void +fu_wch_ch347_device_class_init(FuWchCh347DeviceClass *klass) +{ + FuDeviceClass *device_class = FU_DEVICE_CLASS(klass); + device_class->probe = fu_wch_ch347_device_probe; + device_class->setup = fu_wch_ch347_device_setup; + device_class->to_string = fu_wch_ch347_device_to_string; +} diff -Nru fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347-device.h fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-device.h --- fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347-device.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-device.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,23 @@ +/* + * Copyright 2023 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +#define FU_TYPE_WCH_CH347_DEVICE (fu_wch_ch347_device_get_type()) +G_DECLARE_FINAL_TYPE(FuWchCh347Device, fu_wch_ch347_device, FU, WCH_CH347_DEVICE, FuUsbDevice) + +gboolean +fu_wch_ch347_device_chip_select(FuWchCh347Device *self, gboolean val, GError **error); +gboolean +fu_wch_ch347_device_send_command(FuWchCh347Device *self, + const guint8 *wbuf, + gsize wbufsz, + guint8 *rbuf, + gsize rbufsz, + FuProgress *progress, + GError **error); diff -Nru fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347-plugin.c fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-plugin.c --- fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347-plugin.c 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,36 @@ +/* + * Copyright 2023 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#include "config.h" + +#include "fu-wch-ch347-device.h" +#include "fu-wch-ch347-plugin.h" + +struct _FuWchCh347Plugin { + FuPlugin parent_instance; +}; + +G_DEFINE_TYPE(FuWchCh347Plugin, fu_wch_ch347_plugin, FU_TYPE_PLUGIN) + +static void +fu_wch_ch347_plugin_init(FuWchCh347Plugin *self) +{ +} + +static void +fu_wch_ch347_plugin_constructed(GObject *obj) +{ + FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); + fu_plugin_add_device_gtype(plugin, FU_TYPE_WCH_CH347_DEVICE); +} + +static void +fu_wch_ch347_plugin_class_init(FuWchCh347PluginClass *klass) +{ + FuPluginClass *plugin_class = FU_PLUGIN_CLASS(klass); + plugin_class->constructed = fu_wch_ch347_plugin_constructed; +} diff -Nru fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347-plugin.h fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-plugin.h --- fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347-plugin.h 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347-plugin.h 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,11 @@ +/* + * Copyright 2023 Richard Hughes + * + * SPDX-License-Identifier: LGPL-2.1-or-later + */ + +#pragma once + +#include + +G_DECLARE_FINAL_TYPE(FuWchCh347Plugin, fu_wch_ch347_plugin, FU, WCH_CH347_PLUGIN, FuPlugin) diff -Nru fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347.rs fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347.rs --- fwupd-2.0.8/plugins/wch-ch347/fu-wch-ch347.rs 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch347/fu-wch-ch347.rs 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +// Copyright 2024 Richard Hughes +// SPDX-License-Identifier: LGPL-2.1-or-later + +#[repr(u8)] +enum FuWchCh347CmdSpi { + SetCfg = 0xC0, + CsCtrl = 0xC1, + OutIn = 0xC2, + In = 0xC3, + Out = 0xC4, + GetCfg = 0xCA, +} + +#[derive(New, Getters)] +#[repr(C, packed)] +struct FuStructWchCh347Req { + cmd: FuWchCh347CmdSpi, + payloadsz: u16le, +} diff -Nru fwupd-2.0.8/plugins/wch-ch347/meson.build fwupd-2.0.20/plugins/wch-ch347/meson.build --- fwupd-2.0.8/plugins/wch-ch347/meson.build 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch347/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,19 @@ +cargs = ['-DG_LOG_DOMAIN="FuPluginWchCh347"'] +plugins += {meson.current_source_dir().split('/')[-1]: true} + +plugin_quirks += files('wch-ch347.quirk') +plugin_builtins += static_library('fu_plugin_wch_ch347', + rustgen.process('fu-wch-ch347.rs'), + sources: [ + 'fu-wch-ch347-cfi-device.c', + 'fu-wch-ch347-device.c', + 'fu-wch-ch347-plugin.c', + ], + include_directories: plugin_incdirs, + link_with: plugin_libs, + c_args: cargs, + dependencies: plugin_deps, +) + +enumeration_data += files('tests/wch-ch347-setup.json') +device_tests += files('tests/wch-ch347.json') diff -Nru fwupd-2.0.8/plugins/wch-ch347/tests/wch-ch347-setup.json fwupd-2.0.20/plugins/wch-ch347/tests/wch-ch347-setup.json --- fwupd-2.0.8/plugins/wch-ch347/tests/wch-ch347-setup.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch347/tests/wch-ch347-setup.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,124 @@ +{ + "UsbDevices": [ + { + "GType": "FuUsbDevice", + "PlatformId": "1-1", + "Created": "2024-10-24T17:45:42.490939Z", + "IdVendor": 6790, + "IdProduct": 21979, + "Device": 320, + "USB": 512, + "Manufacturer": 1, + "Product": 2, + "SerialNumber": 3, + "UsbConfigDescriptors": [ + { + "ConfigurationValue": 1 + } + ], + "UsbInterfaces": [ + { + "Length": 9, + "DescriptorType": 4, + "InterfaceClass": 2, + "InterfaceSubClass": 2, + "InterfaceProtocol": 1, + "UsbEndpoints": [ + { + "DescriptorType": 5, + "EndpointAddress": 131, + "Interval": 1, + "MaxPacketSize": 64 + } + ] + }, + { + "Length": 9, + "DescriptorType": 4, + "InterfaceNumber": 1, + "InterfaceClass": 10, + "UsbEndpoints": [ + { + "DescriptorType": 5, + "EndpointAddress": 4, + "MaxPacketSize": 512 + }, + { + "DescriptorType": 5, + "EndpointAddress": 132, + "MaxPacketSize": 512 + } + ] + }, + { + "Length": 9, + "DescriptorType": 4, + "InterfaceNumber": 2, + "InterfaceClass": 255, + "UsbEndpoints": [ + { + "DescriptorType": 5, + "EndpointAddress": 6, + "MaxPacketSize": 512 + }, + { + "DescriptorType": 5, + "EndpointAddress": 134, + "MaxPacketSize": 512 + } + ] + } + ], + "UsbEvents": [ + { + "Id": "#4693935e", + "Data": "001" + }, + { + "Id": "#bddbca22", + "Data": "MAJOR=189\nMINOR=75\nDEVNAME=bus/usb/001/076\nDEVTYPE=usb_device\nDRIVER=usb\nPRODUCT=1a86/55db/140\nTYPE=0/0/0\nBUSNUM=001\nDEVNUM=076" + }, + { + "Id": "#1ab3ae0a", + "Data": "076" + }, + { + "Id": "#028c3a0e", + "Data": "MDEyMzQ1Njc4OQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=" + }, + { + "Id": "#4f7f767b", + "Data": "wBoAAAAEAQAAAAAAAAACEAAAAAcAAAAAAAAAAAA=" + }, + { + "Id": "#e8e979a2", + "Data": "wAEAAA==" + }, + { + "Id": "#e62e60a8", + "Data": "wQoAgAAAAAAAAAAAAA==" + }, + { + "Id": "#e8f9929d", + "Data": "xAEAnw==" + }, + { + "Id": "#9074dd4e", + "Data": "xAEAAA==" + }, + { + "Id": "#c6abc2c8", + "Data": "wwQAAwAAAA==" + }, + { + "Id": "#43321f40", + "Data": "wwMAAAAA" + }, + { + "Id": "#279f56ea", + "Data": "wQoAwAAAAAAAAAAAAA==" + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/wch-ch347/tests/wch-ch347.json fwupd-2.0.20/plugins/wch-ch347/tests/wch-ch347.json --- fwupd-2.0.8/plugins/wch-ch347/tests/wch-ch347.json 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch347/tests/wch-ch347.json 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,17 @@ +{ + "name": "WinChipHead CH347", + "interactive": false, + "steps": [ + { + "emulation-file": "@enumeration_datadir@/wch-ch347-setup.json", + "components": [ + { + "version": "1.40", + "guids": [ + "114d0d9f-3a6a-55f1-9f67-4ba7ec653071" + ] + } + ] + } + ] +} diff -Nru fwupd-2.0.8/plugins/wch-ch347/wch-ch347.quirk fwupd-2.0.20/plugins/wch-ch347/wch-ch347.quirk --- fwupd-2.0.8/plugins/wch-ch347/wch-ch347.quirk 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/plugins/wch-ch347/wch-ch347.quirk 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,2 @@ +[USB\VID_1A86&PID_55DB] +Plugin = wch_ch347 diff -Nru fwupd-2.0.8/plugins/wistron-dock/README.md fwupd-2.0.20/plugins/wistron-dock/README.md --- fwupd-2.0.8/plugins/wistron-dock/README.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wistron-dock/README.md 2026-02-26 11:36:18.000000000 +0000 @@ -47,10 +47,3 @@ ## Version Considerations This plugin has been available since fwupd version `1.8.9`. - -## Owners - -Anyone can submit a pull request to modify this plugin, but the following people should be -consulted before making major or functional changes: - -* Felix Chen: @a999153 diff -Nru fwupd-2.0.8/plugins/wistron-dock/fu-wistron-dock-common.h fwupd-2.0.20/plugins/wistron-dock/fu-wistron-dock-common.h --- fwupd-2.0.8/plugins/wistron-dock/fu-wistron-dock-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wistron-dock/fu-wistron-dock-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -8,32 +8,6 @@ #include -#define FU_WISTRON_DOCK_CMD_ICP_ENTER 0x81 -#define FU_WISTRON_DOCK_CMD_ICP_EXIT 0x82 -#define FU_WISTRON_DOCK_CMD_ICP_ADDRESS 0x84 -#define FU_WISTRON_DOCK_CMD_ICP_READBLOCK 0x85 -#define FU_WISTRON_DOCK_CMD_ICP_WRITEBLOCK 0x86 -#define FU_WISTRON_DOCK_CMD_ICP_MCUID 0x87 -#define FU_WISTRON_DOCK_CMD_ICP_BBINFO 0x88 /* bb code information */ -#define FU_WISTRON_DOCK_CMD_ICP_USERINFO 0x89 /* user code information */ -#define FU_WISTRON_DOCK_CMD_ICP_DONE 0x5A -#define FU_WISTRON_DOCK_CMD_ICP_ERROR 0xFF -#define FU_WISTRON_DOCK_CMD_ICP_EXIT_WDRESET 0x01 /* exit ICP with watch dog reset */ - -#define FU_WISTRON_DOCK_CMD_DFU_ENTER 0x91 -#define FU_WISTRON_DOCK_CMD_DFU_EXIT 0x92 -#define FU_WISTRON_DOCK_CMD_DFU_ADDRESS 0x93 -#define FU_WISTRON_DOCK_CMD_DFU_READIMG_BLOCK 0x94 -#define FU_WISTRON_DOCK_CMD_DFU_WRITEIMG_BLOCK 0x95 -#define FU_WISTRON_DOCK_CMD_DFU_VERIFY 0x96 -#define FU_WISTRON_DOCK_CMD_DFU_COMPOSITE_VER 0x97 -#define FU_WISTRON_DOCK_CMD_DFU_WRITE_WDFL_SIG 0x98 -#define FU_WISTRON_DOCK_CMD_DFU_WRITE_WDFL_DATA 0x99 -#define FU_WISTRON_DOCK_CMD_DFU_VERIFY_WDFL 0x9A -#define FU_WISTRON_DOCK_CMD_DFU_SERINAL_NUMBER 0x9B -#define FU_WISTRON_DOCK_CMD_DFU_DONE 0x5A -#define FU_WISTRON_DOCK_CMD_DFU_ERROR 0xFF - #define FU_WISTRON_DOCK_WDIT_SIZE 512 /* bytes */ #define FU_WISTRON_DOCK_WDIT_TAG_ID 0x4954 /* 'IT' */ diff -Nru fwupd-2.0.8/plugins/wistron-dock/fu-wistron-dock-device.c fwupd-2.0.20/plugins/wistron-dock/fu-wistron-dock-device.c --- fwupd-2.0.8/plugins/wistron-dock/fu-wistron-dock-device.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wistron-dock/fu-wistron-dock-device.c 2026-02-26 11:36:18.000000000 +0000 @@ -305,7 +305,7 @@ fu_chunk_get_address(chk), error)) { g_prefix_error(error, - "failed to set img address 0x%x", + "failed to set img address 0x%x: ", (guint)fu_chunk_get_address(chk)); return FALSE; } @@ -316,7 +316,7 @@ fu_chunk_get_data_sz(chk), error)) { g_prefix_error(error, - "failed to write img data 0x%x", + "failed to write img data 0x%x: ", (guint)fu_chunk_get_address(chk)); return FALSE; } @@ -333,7 +333,7 @@ fu_wistron_dock_device_prepare_firmware(FuDevice *device, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuFirmware) firmware = fu_archive_firmware_new(); @@ -381,11 +381,14 @@ /* success */ fu_firmware_set_id(fw_wsig, FU_FIRMWARE_ID_SIGNATURE); - fu_firmware_add_image(fw_new, fw_wsig); + if (!fu_firmware_add_image(fw_new, fw_wsig, error)) + return NULL; fu_firmware_set_id(fw_wdfl, FU_FIRMWARE_ID_HEADER); - fu_firmware_add_image(fw_new, fw_wdfl); + if (!fu_firmware_add_image(fw_new, fw_wdfl, error)) + return NULL; fu_firmware_set_id(fw_cbin, FU_FIRMWARE_ID_PAYLOAD); - fu_firmware_add_image(fw_new, fw_cbin); + if (!fu_firmware_add_image(fw_new, fw_cbin, error)) + return NULL; return g_steal_pointer(&fw_new); } @@ -416,7 +419,7 @@ g_bytes_get_data(fw_wsig, NULL), g_bytes_get_size(fw_wsig), error)) { - g_prefix_error(error, "failed to write WDFL signature: "); + g_prefix_error_literal(error, "failed to write WDFL signature: "); return FALSE; } fu_progress_step_done(progress); @@ -429,7 +432,7 @@ g_bytes_get_data(fw_wdfl, NULL), g_bytes_get_size(fw_wdfl), error)) { - g_prefix_error(error, "failed to write WDFL data: "); + g_prefix_error_literal(error, "failed to write WDFL data: "); return FALSE; } fu_progress_step_done(progress); @@ -449,7 +452,7 @@ chunks, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to write payload: "); + g_prefix_error_literal(error, "failed to write payload: "); return FALSE; } fu_progress_step_done(progress); @@ -539,7 +542,7 @@ g_autofree gchar *version0 = NULL; g_autofree gchar *version1 = NULL; g_autofree gchar *version2 = NULL; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructWistronDockWditImg) st = NULL; /* parse */ st = fu_struct_wistron_dock_wdit_img_parse(buf, bufsz, offset, error); @@ -567,7 +570,7 @@ (guint)status & 0x0F, (guint)(status & 0xF0) >> 4); - offset += st->len; + offset += st->buf->len; } /* success */ @@ -579,7 +582,7 @@ { guint8 update_state = 0x0; guint8 buf[FU_WISTRON_DOCK_WDIT_SIZE + 1] = {FU_WISTRON_DOCK_ID_DOCK_WDIT}; - g_autoptr(GByteArray) st = NULL; + g_autoptr(FuStructWistronDockWdit) st = NULL; /* get WDIT */ if (!fu_hid_device_get_report(FU_HID_DEVICE(self), @@ -655,20 +658,20 @@ self, buf, sizeof(buf), - st->len + 0x1, + st->buf->len + 0x1, MIN(fu_struct_wistron_dock_wdit_get_device_cnt(st), 32), error)) { - g_prefix_error(error, "failed to parse imgs: "); + g_prefix_error_literal(error, "failed to parse imgs: "); return FALSE; } /* adding the MCU while flashing the device, ignore until it comes back in runtime mode */ if (self->update_phase == FU_WISTRON_DOCK_UPDATE_PHASE_DEPLOY && self->status_code == FU_WISTRON_DOCK_STATUS_CODE_UPDATING) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "ignoring device in MCU mode"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "ignoring device in MCU mode"); return FALSE; } @@ -686,19 +689,19 @@ return FALSE; if (!fu_wistron_dock_device_ensure_mcuid(self, error)) { - g_prefix_error(error, "failed to get MCUID: "); + g_prefix_error_literal(error, "failed to get MCUID: "); return FALSE; } if (!fu_wistron_dock_device_ensure_bbinfo(self, error)) { - g_prefix_error(error, "failed to get BBINFO: "); + g_prefix_error_literal(error, "failed to get BBINFO: "); return FALSE; } if (!fu_wistron_dock_device_ensure_userinfo(self, error)) { - g_prefix_error(error, "failed to get USERINFO: "); + g_prefix_error_literal(error, "failed to get USERINFO: "); return FALSE; } if (!fu_wistron_dock_device_ensure_wdit(self, error)) { - g_prefix_error(error, "failed to get WDIT: "); + g_prefix_error_literal(error, "failed to get WDIT: "); return FALSE; } @@ -792,7 +795,7 @@ } static void -fu_wistron_dock_device_set_progress(FuDevice *self, FuProgress *progress) +fu_wistron_dock_device_set_progress(FuDevice *device, FuProgress *progress) { fu_progress_set_id(progress, G_STRLOC); fu_progress_add_step(progress, FWUPD_STATUS_DECOMPRESSING, 0, "prepare-fw"); @@ -815,6 +818,7 @@ fu_device_set_version_format(FU_DEVICE(self), FWUPD_VERSION_FORMAT_QUAD); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_UPDATABLE); fu_device_add_flag(FU_DEVICE(self), FWUPD_DEVICE_FLAG_USABLE_DURING_UPDATE); + fu_device_add_private_flag(FU_DEVICE(self), FU_DEVICE_PRIVATE_FLAG_LAZY_VERFMT); fu_device_add_request_flag(FU_DEVICE(self), FWUPD_REQUEST_FLAG_ALLOW_GENERIC_MESSAGE); fu_device_set_remove_delay(FU_DEVICE(self), 5 * 60 * 1000); } diff -Nru fwupd-2.0.8/plugins/wistron-dock/fu-wistron-dock-plugin.c fwupd-2.0.20/plugins/wistron-dock/fu-wistron-dock-plugin.c --- fwupd-2.0.8/plugins/wistron-dock/fu-wistron-dock-plugin.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wistron-dock/fu-wistron-dock-plugin.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,12 +18,14 @@ static void fu_wistron_dock_plugin_init(FuWistronDockPlugin *self) { + fu_plugin_add_flag(FU_PLUGIN(self), FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); } static void fu_wistron_dock_plugin_constructed(GObject *obj) { FuPlugin *plugin = FU_PLUGIN(obj); + fu_plugin_add_udev_subsystem(plugin, "usb"); fu_plugin_add_device_gtype(plugin, FU_TYPE_WISTRON_DOCK_DEVICE); } diff -Nru fwupd-2.0.8/plugins/wistron-dock/fu-wistron-dock.rs fwupd-2.0.20/plugins/wistron-dock/fu-wistron-dock.rs --- fwupd-2.0.8/plugins/wistron-dock/fu-wistron-dock.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wistron-dock/fu-wistron-dock.rs 2026-02-26 11:36:18.000000000 +0000 @@ -55,3 +55,33 @@ Download = 0x1, Deploy = 0x2, } + +enum FuWistronDockCmdIcp { + Enter = 0x81, + Exit = 0x82, + Address = 0x84, + Readblock = 0x85, + Writeblock = 0x86, + Mcuid = 0x87, + Bbinfo = 0x88, // bb code information + Userinfo = 0x89, // user code information + Done = 0x5A, + Error = 0xFF, + ExitWdreset = 0x01, // exit ICP with watch dog reset +} + +enum FuWistronDockCmdDfu { + Enter = 0x91, + Exit = 0x92, + Address = 0x93, + ReadimgBlock = 0x94, + WriteimgBlock = 0x95, + Verify = 0x96, + CompositeVer = 0x97, + WriteWdflSig = 0x98, + WriteWdflData = 0x99, + VerifyWdfl = 0x9A, + SerinalNumber = 0x9B, + Done = 0x5A, + Error = 0xFF, +} diff -Nru fwupd-2.0.8/plugins/wistron-dock/tests/wistron-dock-40b7.json fwupd-2.0.20/plugins/wistron-dock/tests/wistron-dock-40b7.json --- fwupd-2.0.8/plugins/wistron-dock/tests/wistron-dock-40b7.json 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/plugins/wistron-dock/tests/wistron-dock-40b7.json 2026-02-26 11:36:18.000000000 +0000 @@ -3,8 +3,8 @@ "interactive": true, "steps": [ { - "url": "https://fwupd.org/downloads/f00838f10d6faf58ff57cbfcfca390ed63bd804e4c654f19ff89423b286dc5d2-lda_viking_r_composite.cab", - "emulation-url": "https://fwupd.org/downloads/08c477c340e51990fc5df84907149f513aef65ae7f828b469e0a14ac01579a34-lda_viking_r_composite.zip", + "url": "f00838f10d6faf58ff57cbfcfca390ed63bd804e4c654f19ff89423b286dc5d2-lda_viking_r_composite.cab", + "emulation-url": "08c477c340e51990fc5df84907149f513aef65ae7f828b469e0a14ac01579a34-lda_viking_r_composite.zip", "components": [ { "version": "1.0.1.4", @@ -15,8 +15,8 @@ ] }, { - "url": "https://fwupd.org/downloads/43bcfb3278ad2baf918af6abc972e0de9a0de66a25b012177b00d96664819010-lda_viking_r_composite.cab", - "emulation-url": "https://fwupd.org/downloads/330411c3eaaba8b7e8b51108fddc614963af5b5fcc0fd328683197491584f387-lda_viking_r_composite2.zip", + "url": "43bcfb3278ad2baf918af6abc972e0de9a0de66a25b012177b00d96664819010-lda_viking_r_composite.cab", + "emulation-url": "330411c3eaaba8b7e8b51108fddc614963af5b5fcc0fd328683197491584f387-lda_viking_r_composite2.zip", "components": [ { "version": "1.0.1.6", diff -Nru fwupd-2.0.8/po/.gitignore fwupd-2.0.20/po/.gitignore --- fwupd-2.0.8/po/.gitignore 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/.gitignore 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -*.pot diff -Nru fwupd-2.0.8/po/LINGUAS fwupd-2.0.20/po/LINGUAS --- fwupd-2.0.8/po/LINGUAS 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/LINGUAS 2026-02-26 11:36:18.000000000 +0000 @@ -1,6 +1,7 @@ af ar ast +bg ca cs da @@ -8,6 +9,7 @@ en_GB eo es +et eu fi fr diff -Nru fwupd-2.0.8/po/af.po fwupd-2.0.20/po/af.po --- fwupd-2.0.8/po/af.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/af.po 2026-02-26 11:36:18.000000000 +0000 @@ -8,12 +8,15 @@ msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Afrikaans (http://app.transifex.com/freedesktop/fwupd/language/af/)\n" +"PO-Revision-Date: 2025-10-24 09:52+0000\n" +"Last-Translator: Anonymous \n" +"Language-Team: Afrikaans \n" +"Language: af\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: af\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format diff -Nru fwupd-2.0.8/po/ar.po fwupd-2.0.20/po/ar.po --- fwupd-2.0.8/po/ar.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/ar.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,121 +4,1348 @@ # # Translators: # Omar TS , 2024-2025 +# ver nos, 2025 +# ver nos, 2025 +# zayed , 2025 +# jonnysemon , 2025. +# Zayed Al-Saidi , 2025. +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Arabic (http://app.transifex.com/freedesktop/fwupd/language/ar/)\n" +"PO-Revision-Date: 2025-10-24 09:53+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Arabic \n" +"Language: ar\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ar\n" -"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n" +"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 ? 4 : 5;\n" +"X-Generator: Weblate 5.14.1-dev\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f دقيقة متبقية" +msgstr[1] "%.0f دقيقة متبقية" +msgstr[2] "%.0f دقيقتان متبقية" +msgstr[3] "%.0f دقائق متبقية" +msgstr[4] "%.0f دقيقة متبقية" +msgstr[5] "%.0f دقيقة متبقية" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "تحديث متحكّم إدارة اللوحة الأساسية %s" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "تحديث بطارية %s" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system boot-up +#, c-format +msgid "%s CPU Microcode Update" +msgstr "تحديث الشيفرة المصغّرة لوحدة المعالجة المركزية %s" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "تحديث كاميرا %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "تحديث تشكيلة %s" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "تحديث محرّك الإدارة للمستهلكين %s" + +#. 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" + +#. 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" + +#. 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" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "تحديث شاشة عرض %s" + +#. TRANSLATORS: Dock refers to the port replicator hardware laptops are +#. * cradled in, or lowered onto +#, c-format +msgid "%s Dock Update" +msgstr "تحديث محطّة إرساء %s" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "تحديث قرص %s" + +#. 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" + +#. TRANSLATORS: a device that can read your fingerprint pattern +#, c-format +msgid "%s Fingerprint Reader Update" +msgstr "تحديث قارئ البصمات %s" + +#. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC +#, c-format +msgid "%s Flash Drive Update" +msgstr "تحديث قرص وميضي %s" + +#. TRANSLATORS: GPU refers to a Graphics Processing Unit, e.g. +#. * the "video card" +#, c-format +msgid "%s GPU Update" +msgstr "تحديث وحدة معالجة الرسوميات %s" + +#. TRANSLATORS: a large pressure-sensitive drawing area typically used +#. * by artists and digital artists +#, c-format +msgid "%s Graphics Tablet Update" +msgstr "تحديث لوح الرسم الرقمي %s" + +#. TRANSLATORS: two miniature speakers attached to your ears +#, c-format +msgid "%s Headphones Update" +msgstr "تحديث سمّاعات الرأس %s" + +#. TRANSLATORS: headphones with an integrated microphone +#, c-format +msgid "%s Headset Update" +msgstr "تحديث سمّاعة الرأس المدمجة %s" + +#. TRANSLATORS: an input device used by gamers, e.g. a joystick +#, c-format +msgid "%s Input Controller Update" +msgstr "تحديث متحكّم الإدخال %s" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "تحديث لوحة مفاتيح %s" + +#. 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" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "تحديث فأرة %s" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "تحديث واجهة شبكة %s" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "تحديث قرص صلب إلكتروني %s" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "تحديث متحكّم تخزين %s" + +#. 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" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM 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 "تحديث متحكّم ثندربولت %s" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "تحديث لوحة اللمس %s" + +#. TRANSLATORS: Dock refers to the port replicator device connected +#. * by plugging in a USB cable -- which may or may not also provide power +#, c-format +msgid "%s USB Dock Update" +msgstr "تحديث محطّة إرساء USB لـ %s" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "تحديث مستقبل USB لـ %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 +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "تحديث %s" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "قد لا يمكن استخدام %s وكل الأجهزة الموصولة أثناء التحديث." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "ظهر %s: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "تغير %s: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "اختفى %s: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "تعذّر تحديث %s حاليّاً" + +#. TRANSLATORS: %1 is a device name, e.g. "ThinkPad Universal +#. * ThunderBolt 4 Dock" and %2 is "fwupdmgr activate" +#, c-format +msgid "%s is pending activation; use %s to complete the update." +msgstr "تنتظر %s التفعيل؛ استخدِم %s لإكمال التحديث." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "وضع تصنيع %s" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "يجب أن يظل %s موصولًا طوال مدة التحديث لتجنب الضرر." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "يجب أن يظل %s موصولًا بمصدر طاقة طوال مدة التحديث لتجنب الضرر." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "تجاوز %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "نُسخة %s" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u يوم" +msgstr[1] "%u يومان" +msgstr[2] "%u أيام" +msgstr[3] "%u يوم" +msgstr[4] "%u يوم" +msgstr[5] "%u يوم" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "يتوفر تحديث مشغّل للجهاز %u." +msgstr[1] "يتوفر تحديث مشغّل للجهاز %u." +msgstr[2] "يتوفر تحديث مشغّل للجهازان %u." +msgstr[3] "يتوفر تحديث مشغّل للأجهزة %u." +msgstr[4] "يتوفر تحديث مشغّل للأجهزة %u." +msgstr[5] "يتوفر تحديث مشغّل للأجهزة %u." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "ليس الجهاز %u أفضل تشكيلة معروفة." +msgstr[1] "ليس الجهاز %u أفضل تشكيلة معروفة." +msgstr[2] "ليسا الجهازان %u أفضل تشكيلة معروفة." +msgstr[3] "ليست الأجهزة %u أفضل تشكيلة معروفة." +msgstr[4] "ليست الأجهزة %u أفضل تشكيلة معروفة." +msgstr[5] "ليست الأجهزة %u أفضل تشكيلة معروفة." + +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#, c-format +msgid "%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "%u devices are supported in the enabled remotes (an update has been published)" +msgstr[0] "جهاز %u مدعوم في الأطراف البعيدة المفعّلة (نُشِر تحديث)" +msgstr[1] "جهازان %u مدعومان في الأطراف البعيدة المفعّلة (نُشِر تحديث)" +msgstr[2] "أجهزة %u مدعومة في الأطراف البعيدة المفعّلة (نُشِر تحديث)" +msgstr[3] "أجهزة %u مدعومة في الأطراف البعيدة المفعّلة (نُشِر تحديث)" +msgstr[4] "أجهزة %u مدعومة في الأطراف البعيدة المفعّلة (نُشِر تحديث)" +msgstr[5] "أجهزة %u مدعومة في الأطراف البعيدة المفعّلة (نُشِر تحديث)" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "يمكن تحديث جهاز %u" +msgstr[1] "يمكن تحديث جهازان %u" +msgstr[2] "يمكن تحديث أجهزة %u" +msgstr[3] "يمكن تحديث أجهزة %u" +msgstr[4] "يمكن تحديث أجهزة %u" +msgstr[5] "يمكن تحديث أجهزة %u" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u ساعة" +msgstr[1] "%u ساعتان" +msgstr[2] "%u ساعات" +msgstr[3] "%u ساعة" +msgstr[4] "%u ساعة" +msgstr[5] "%u ساعة" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u دقيقة" +msgstr[1] "%u دقيقتان" +msgstr[2] "%u دقائق" +msgstr[3] "%u دقيقة" +msgstr[4] "%u دقيقة" +msgstr[5] "%u دقيقة" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u ثانية" +msgstr[1] "%u ثانيتان" +msgstr[2] "%u ثواني" +msgstr[3] "%u ثانية" +msgstr[4] "%u ثانية" +msgstr[5] "%u ثانية" + +#. TRANSLATORS: device tests can be specific to a CPU type +#, c-format +msgid "%u test was skipped" +msgid_plural "%u tests were skipped" +msgstr[0] "تُخطّي اختبار %u" +msgstr[1] "تُخطّي اختباران %u" +msgstr[2] "تُخطّت اختبارات %u" +msgstr[3] "تُخطّت اختبارات %u" +msgstr[4] "تُخطّت اختبارات %u" +msgstr[5] "تُخطّت اختبارات %u" + +#. TRANSLATORS: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (عتبة %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(أُلغِيَ)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "أصبح سجل TPM PCR قيمة غير صالحة" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "AMD Firmware Replay Protection" +msgstr "حماية مشغلات إي إم دي من الإعادة" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "حماية مشغلات إي إم دي من الكتابة" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "AMD Secure Processor Rollback Protection" +msgstr "حماية المعالج الآمن إي إم دي من الرجوع إلى إصدار أقدم" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "ARCHIVE FIRMWARE METAINFO [FIRMWARE] [METAINFO] [JCATFILE]" +msgstr "أَرْشِيف مُشغّل مَعْلُومَات وَصْفِيَّة [مُشغّل] [مَعْلُومَات وَصْفِيَّة] [مِلَفّ JCAT]" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "إجراء مطلوب:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "فعّل الأجهزة" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "فعّل الأجهزة المعلّقة" msgid "Activate the new firmware on the device" -msgstr "نشّط البرنامج الثابت الجديد على الجهاز" +msgstr "نشّط المشغّل الجديد على الجهاز" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "جارِ تفعيل تحديث المشغّل" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "جارِ تفعيل تحديث المشغّل لـ" + +#. TRANSLATORS: command description +msgid "Adds devices to watch for future emulation" +msgstr "أضف أجهزة للمراقبة للمحاكاة المستقبلية" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "العمر" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "وافق وفعّل المورد البعيد؟" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "اسم مستعار لـ %s" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "أصبحت جميع سجلات TPM PCR الآن صالحة" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "جميع سجلات TPM PCR صالحة" + +#. TRANSLATORS: an application is preventing system updates +msgid "All devices are prevented from update by system inhibit" +msgstr "كبح النظام يمنع تحديث كل الأجهزة" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "ستُحدَّث كل الأجهزة من نفس النوع في نفس الوقت" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "اسمح بتخفيض نُسَخ المشغّل" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "اسمح بإعادة تثبيت نُسَخ المشغّل الموجودة" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "اسمح بتبديل فرع المشغّل" + +#. TRANSLATORS: error message +msgid "Already exists, and no --force specified" +msgstr "موجود بالفعل، ولم يُحدّد --force" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "فرع بديل" + +#. TRANSLATORS: another application is updating the device already +msgid "An update is in progress" +msgstr "تحديث قيد التقدّم" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "يتطلّب تحديث إعادة إقلاع لإكماله." + +#. TRANSLATORS: explain why +msgid "An update requires the system to shutdown to complete." +msgstr "يتطلّب تحديث إغلاق النظام لإكماله." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "أجب بنعم على كل الأسئلة" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "طبّق التحديث حتى عندما لا يكون موصى به" #. TRANSLATORS: command line option msgid "Apply update files" msgstr "طبّق ملفات التحديث" +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "جارٍ تطبيق التحديث…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "المشغّل المُعتَمد:" +msgstr[1] "المشغّلان المُعتَمدان:" +msgstr[2] "المشغّلات المُعتَمدة:" +msgstr[3] "المشغّلات المُعتَمدة:" +msgstr[4] "المشغّلات المُعتَمدة:" +msgstr[5] "المشغّلات المُعتَمدة:" + +#. TRANSLATORS: command description +msgid "Asks the daemon to quit" +msgstr "اطلب من الخفيّ أن يقلع" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "اربط بوضع المشغّل" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "جارٍ الاستيثاق…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "تُتطلَّب تفاصيل الاستيثاق" + #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on a removable device" -msgstr "المصادقة مطلوبة للرجوع إلى إصدار أقدم من البرنامج الثابت على جهاز قابل للإزالة" +msgstr "الاستيثاق مطلوب للرجوع إلى إصدار أقدم من المشغّل على جهاز قابل للإزالة" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on this machine" -msgstr "المصادقة مطلوبة للرجوع إلى إصدار أقدم من البرنامج الثابت على هذا الجهاز" +msgstr "الاستيثاق مطلوب للرجوع إلى إصدار أقدم من المشغّل على هذا الجهاز" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to enable emulation data collection" -msgstr "المصادقة مطلوبة لتمكين جمع بيانات المحاكاة" +msgstr "الاستيثاق مطلوب لتمكين جمع بيانات المحاكاة" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to fix a host security issue" -msgstr "المصادقة مطلوبة لإصلاح مشكلة أمان المضيف" +msgstr "الاستيثاق مطلوب لإصلاح مشكلة أمان المضيف" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to load hardware emulation data" -msgstr "المصادقة مطلوبة لتحميل بيانات محاكاة الأجهزة" +msgstr "الاستيثاق مطلوب لتحميل بيانات محاكاة الأجهزة" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify BIOS settings" -msgstr "المصادقة مطلوبة لتعديل إعدادات البايوس" +msgstr "الاستيثاق مطلوب لتعديل إعدادات البايوس" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify a configured remote used for firmware updates" -msgstr "المصادقة مطلوبة لتعديل جهاز التحكم عن بعد المكوَّن والمستخدم لتحديثات البرامج الثابتة" +msgstr "الاستيثاق مطلوب لتعديل جهاز التحكم عن بعد المكوَّن والمستخدم لتحديثات المشغلات" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify daemon configuration" -msgstr "المصادقة مطلوبة لتعديل تكوين البرنامج الخفي" +msgstr "الاستيثاق مطلوب لتعديل ضبط الخدمة" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to read BIOS settings" -msgstr "المصادقة مطلوبة لقراءة إعدادات البايوس" +msgstr "الاستيثاق مطلوب لقراءة إعدادات البايوس" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to reset daemon configuration to defaults" -msgstr "المصادقة مطلوبة لإعادة تعيين تكوين البرنامج الخفي إلى الإعدادات الافتراضية" +msgstr "الاستيثاق مطلوب لإعادة ضبط الخدمة إلى الإعدادات المبدئية" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to save hardware emulation data" -msgstr "المصادقة مطلوبة لحفظ بيانات محاكاة الأجهزة" +msgstr "الاستيثاق مطلوب لحفظ بيانات محاكاة الأجهزة" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to set the list of approved firmware" -msgstr "المصادقة مطلوبة لتعيين قائمة البرامج الثابتة المعتمدة" +msgstr "الاستيثاق مطلوب لتعيين قائمة المشغلات المعتمدة" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to sign data using the client certificate" -msgstr "المصادقة مطلوبة لتوقيع البيانات باستخدام شهادة العميل" +msgstr "الاستيثاق مطلوب لتوقيع البيانات باستخدام شهادة العميل" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to stop the firmware update service" -msgstr "المصادقة مطلوبة لإيقاف خدمة تحديث البرامج الثابتة" +msgstr "الاستيثاق مطلوب لإيقاف خدمة تحديث المشغلات" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to switch to the new firmware version" -msgstr "المصادقة مطلوبة للتبديل إلى إصدار البرنامج الثابت الجديد" +msgstr "الاستيثاق مطلوب للتبديل إلى إصدار المشغّل الجديد" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to undo the fix for a host security issue" -msgstr "المصادقة مطلوبة للتراجع عن إصلاح مشكلة أمان المضيف" +msgstr "الاستيثاق مطلوب للتراجع عن إصلاح مشكلة أمان المضيف" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" -msgstr "المصادقة مطلوبة لفتح الجهاز" +msgstr "الاستيثاق مطلوب لفتح الجهاز" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on a removable device" -msgstr "المصادقة مطلوبة لتحديث البرنامج الثابت على جهاز قابل للإزالة" +msgstr "الاستيثاق مطلوب لتحديث المشغّل على جهاز قابل للإزالة" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on this machine" -msgstr "المصادقة مطلوبة لتحديث البرنامج الثابت على هذا الجهاز" +msgstr "الاستيثاق مطلوب لتحديث المشغّل على هذا الجهاز" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the stored checksums for the device" -msgstr "المصادقة مطلوبة لتحديث المجموع الاختباري المخزن للجهاز" +msgstr "الاستيثاق مطلوب لتحديث المجموع الاختباري المخزن للجهاز" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "إبلاغ آلي" + +#. TRANSLATORS: we can auto-uninhibit after a timeout +#, c-format +msgid "Automatically uninhibiting in %ums…" +msgstr "جارِ إلغاء التثبيط آلياً في %ums…" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "هل ترفع آلياً في كل مرة؟" + +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +msgid "BIOS Firmware Updates" +msgstr "تحديثات مشغلات BIOS" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "BIOS Rollback Protection" +msgstr "حماية BIOS من الرجوع إلى إصدار أقدم" + +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +msgid "BIOS firmware updates" +msgstr "تحديثات مشغلات BIOS" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "BIOS rollback protection" +msgstr "حماية BIOS من الرجوع إلى إصدار أقدم" #. TRANSLATORS: description of a BIOS setting msgid "BIOS updates delivered via LVFS or Windows Update" msgstr "تُسلّم تحديثات البايوس عبر LVFS أو Windows Update" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "BUILDER-XML اِسْم-الْمِلَفّ-وَجْهَة" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "بطارية" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "اربط مشغّل نواة جديد" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "ملفات المشغّل المحظورة:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "إصدار محظور" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "جارِ حظر المشغّل:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "احظر مشغّلًا معيّنًا من التثبيت" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "إصدار المُقلِع" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree +msgid "Branch" +msgstr "فرع" + +#. TRANSLATORS: command description +msgid "Build a cabinet archive from a firmware blob and XML metadata" +msgstr "ابنِ أرشيف cabinet من كائن مشغّل ومعلومات وصفيّة XML" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "ابنِ ملف مشغّل" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * Utilized by OS means the distribution enabled it +msgid "CET OS Support" +msgstr "دعم CET لنظام التشغيل" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "CET Platform" +msgstr "منصة CET" + +#. TRANSLATORS: longer description +msgid "CPU Microcode must be updated to mitigate against various information-disclosure security issues." +msgstr "يجب تحديث الكود المصغر لوحدة المعالجة المركزية (CPU) للتخفيف من المشكلات الأمنية المختلفة المتعلقة بكشف المعلومات." + +#. TRANSLATORS: we can save all device enumeration events for emulation +msgid "Can tag for emulation" +msgstr "يمكن وسمه للمحاكاة" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "إلغاء" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +#. TRANSLATORS: this is from ctrl+c +msgid "Cancelled" +msgstr "أُلغي" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "لا يمكن التطبيق كتحديث dbx مطبَّق سابقًا." + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "غُيّر" + +#. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "تحقّق إذا كانت أي أجهزة تنتظر إعادة إقلاع لإكمال التحديث" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "تحقّق من مطابقة التجزئة التشفيرية للمشغّل" + +#. TRANSLATORS: hash to that exact firmware archive +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "مجموع اختباري" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose branch" +msgstr "اختر فرع" + +#. TRANSLATORS: get interactive prompt +msgid "Choose device" +msgstr "اختر جهازًا" + +#. TRANSLATORS: get interactive prompt +msgid "Choose firmware" +msgstr "اختر مشغّل" + +#. TRANSLATORS: get interactive prompt +msgid "Choose release" +msgstr "اختر إصدار" + +#. TRANSLATORS: get interactive prompt +msgid "Choose volume" +msgstr "اختر مجلّد" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "امسح نتائج التحديث الأخير" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "الأمر غير موجود" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "مدعوم من المجتمع" + +#. TRANSLATORS: command description +msgid "Compares two versions for equality" +msgstr "قارن نسختين للتساوي" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +msgid "Configuration Change Suggested" +msgstr "اقتُرح تغيير في التشكيلة" + +#. TRANSLATORS: no peeking +msgid "Configuration is only readable by the system administrator" +msgstr "الضبط للقراءة فقط من قبل مدير النظام" + +#. TRANSLATORS: longer description +msgid "Control-Flow Enforcement Technology detects and prevents certain methods for running malicious software on the device." +msgstr "تكتشف تقنية فرض تدفق التحكم وتمنع بعض الطرق لتشغيل البرامج الضارة على الجهاز." + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Control-flow Enforcement Technology" +msgstr "تقنية فرض تدفق التحكم" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "حوّل ملف مشغّل" + +#. TRANSLATORS: command description +msgid "Create an EFI boot entry" +msgstr "أنشئ إدخال إقلاع EFI" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "أُنْشِئَ" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "حرج" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "تحقّق التجزئة التشفيرية متاح" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "القيمة الحالية" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "الإصدار الحالي" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "مُعرّف-الجهاز|GUID" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "خيارات التصحيح" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "جارٍ فك الضغط…" + +#. TRANSLATORS: command description +msgid "Delete an EFI boot entry" +msgstr "احذف إدخال إقلاع EFI" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "الوصف" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "افصل إلى وضع مُحمّل الإقلاع" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "التفاصيل" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works +#. * well together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "هل تنحرف عن أفضل تشكيلة معروفة؟" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "أعلام الجهاز" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "معرّف الجهاز" + +#. TRANSLATORS: description of the device requests +msgid "Device Requests" +msgstr "طلبات الجهاز" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "أُضيف جهاز:" + +#. TRANSLATORS: the device is already connected +msgid "Device already exists" +msgstr "الجهاز موجود بالفعل" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "طاقة بطارية الجهاز منخفضة جداً" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "طاقة بطارية الجهاز منخفضة جداً (%u%%، تتطلّب %u%%)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "يستطيع الجهاز استرداد فشل الوميض" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be updated while the lid is closed" +msgstr "لا يمكن تحديث الجهاز أثناء إغلاق الغطاء" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "غُيّر جهاز:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "يجب أن يتضمّن مشغّل الجهاز تحقّقًا من الإصدار" + +#. TRANSLATORS: emulated means we are pretending to be a different model +msgid "Device is emulated" +msgstr "الجهاز محاكًى" + +#. TRANSLATORS: device cannot be interrupted, for instance taking a phone call +msgid "Device is in use" +msgstr "الجهاز قيد الاستخدام" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "الجهاز مقفل" + +#. TRANSLATORS: we have two ways of communicating with the device, so we hide +#. one +msgid "Device is lower priority than an equivalent device" +msgstr "أولوية الجهاز أقل من جهاز مكافئ" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "يجب على الجهاز تثبيت كل الإصدارات المتوفّرة" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "الجهاز غير قابل للوصول" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "الجهاز غير قابل للوصول، أو خارج النطاق اللاسلكي" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "الجهاز قابل للاستخدام طوال مدة التحديث" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "الجهاز ينتظر تطبيق التحديث" + +#. TRANSLATORS: success, so say thank you to the user +msgid "Device list uploaded successfully, thanks!" +msgstr "رُفعت قائمة الأجهزة بنجاح، شكرا!" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "أُزيل جهاز:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "يتطلّب الجهاز توصيل طاقة التيار المتردّد" + +#. TRANSLATORS: device does not have a display connected +msgid "Device requires a display to be plugged in" +msgstr "يتطلّب الجهاز توصيل شاشة عرض" + +#. TRANSLATORS: The device cannot be updated due to missing vendor's license." +msgid "Device requires a software license to update" +msgstr "يتطلّب الجهاز رخصة برمجيات للتحديث" + +#. TRANSLATORS: longer description +msgid "Device software updates are provided for this device." +msgstr "تُوفَّر تحديثات برامج الجهاز لهذا الجهاز." + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "الجهاز يُمرّر التحديثات" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "يدعم الجهاز التبديل إلى فرع مشغّل مختلف" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "يتطلّب تحديث الجهاز تفعيلًا" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "سوف يدعم الجهاز المشغّل قبل التثبيت" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "لن يظهر الجهاز مرة أخرى بعد إكمال التحديث" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "الأجهزة التي حُدّثت بنجاح:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "الأجهزة التي لم تُحدّث بشكل صحيح:" + +#. TRANSLATORS: message letting the user there is an update +#. * waiting, but there is a reason it cannot be deployed +msgid "Devices with firmware updates that need user action: " +msgstr "أجهزة لديها تحديثات مشغّل تحتاج تدخّل المستخدِم: " + +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no +#. * device upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no +#. * device upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "الأجهزة التي لا تتوفّر لها تحديثات مشغّل: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device +#. * upgrade available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device +#. * upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "الأجهزة التي لديها أحدث نسخة مشغّل متوفّرة:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "لم يعثر على أي أجهزة بمُعرّفات GUID مطابقة" + +#. TRANSLATORS: Plugin is inactive and not used +#. TRANSLATORS: Suffix: the HSI result +msgid "Disabled" +msgstr "معطّل" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "عطّل طرفاً بعيداً محدّداً" + +#. TRANSLATORS: command description +msgid "Disables virtual testing devices" +msgstr "عطّل أجهزة الاختبار الافتراضية" + +#. TRANSLATORS: the OS the release was tested on +msgid "Distribution" +msgstr "توزيعة" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "لا تتحقّق من البيانات الوصفية القديمة" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "لا تتحقّق من الخط الزمني غير المبلّغ" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "لا تتحقّق إذا يجب تفعيل أطراف التنزيل" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "لا تتحقّق من أو تطلب إعادة الإقلاع بعد التحديث" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "لا تُضمّن بادئة نطاق السجل" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "لا تُضمّن بادئة الطابع الزمني" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "لا تُجرِ فحوصات أمان الجهاز" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "لا تطلب أجهزة" + +#. TRANSLATORS: command line option +msgid "Do not prompt to fix security issues" +msgstr "لا تطالب بإصلاح مشاكل الأمن" + +#. TRANSLATORS: command line option +msgid "Do not search the firmware when parsing" +msgstr "لا تبحث عن المشغّل عند التحليل" + +#. TRANSLATORS: warning message shown after update has been scheduled +msgid "Do not turn off your computer or remove the AC adaptor while the update is in progress." +msgstr "لا تطفئ حاسوبك أو تنزع محول التيار المتردد أثناء سير التحديث." + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "لا أكتب في قاعدة بيانات الخط الزمني" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "هل تفهم عواقب تغيير فرع المشغّل؟" + +#. TRANSLATORS: ask the user if it's okay to convert, +#. * "it" being the data contained in the EFI boot entry +msgid "Do you want to convert it now?" +msgstr "هل ترغب في تحويله الآن؟" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "هل ترغب في تعطيل هذه الميزة للتحديثات المستقبلية؟" + +#. TRANSLATORS: ask if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "هل ترغب في تحديث هذا الطرف البعيد الآن؟" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "هل ترغب في رفع التقارير آلياً للتحديثات المستقبلية؟" + +#. TRANSLATORS: command line option +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "لا تطالب بالاستيثاق (قد تُعرض تفاصيل أقل)" + +#. TRANSLATORS: success +msgid "Done!" +msgstr "تم!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "أخفّض إصدار %s من %s إلى %s؟" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "خفّض إصدار المشغّل على جهاز" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "جارِ تخفيض %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "نزّل ملفًا" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "جارٍ التنزيل…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "افرغ بيانات SMBIOS من ملف" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "المدة" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "EMULATION-FILE [ARCHIVE-FILE]" +msgstr "مِلَفّ-مُحَاكَاة [مِلَفّ-أَرْشِيف]" + +#. TRANSLATORS: longer description +msgid "Each system should have tests to ensure firmware security." +msgstr "يجب أن يحتوي كل نظام على اختبارات لضمان أمان المشغلات." + +#. TRANSLATORS: command description +msgid "Emulate a device using a JSON manifest" +msgstr "حاكِ جهازًا باستخدام بيان JSON" + +#. TRANSLATORS: this device is not actually real +msgid "Emulated" +msgstr "محاكًى" + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "مضيف محاكَى" + msgid "Enable" msgstr "يُمكَِن" msgid "Enable emulation data collection" msgstr "مكّن جمع بيانات المحاكاة" +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "فَعِّل المورد البعيد الجديد؟" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "هل تفعّل هذا الطرف البعيد؟" + #. TRANSLATORS: if the remote is enabled #. TRANSLATORS: Suffix: the HSI result msgid "Enabled" msgstr "ممكّن" +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "مفعّل إذا طابق الجهاز الصلب" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "فعّل طرفاً بعيداً محدّداً" + +#. TRANSLATORS: command description +msgid "Enables virtual testing devices" +msgstr "فعّل أجهزة الاختبار الافتراضية" + +#. TRANSLATORS: longer description +msgid "Enabling firmware updates for the BIOS allows fixing security issues." +msgstr "يسمح تفعيل تحديثات المشغلات لنظام BIOS بإصلاح المشكلات الأمنية." + msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." -msgstr "تُمكّن هذه الوظيفة على مسؤوليتك الخاصة، مما يعني أنه يتعين عليك الاتصال بالشركة المصنعة للمعدات الأصلية بخصوص أي مشاكل ناجمة عن هذه التحديثات. يجب فقط تقديم المشكلات المتعلقة بعملية التحديث نفسها إلى $OS_RELEASE:BUG_REPORT_URL$" +msgstr "تُمكّن هذه الوظيفة على مسؤوليتك الخاصة، مما يعني أنه يتعين عليك الاتصال بالشركة المصنعة للمعدات الأصلية بخصوص أي مشاكل ناجمة عن هذه التحديثات. يجب فقط تقديم المشكلات المتعلقة بعملية التحديث نفسها إلى $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "يُكَّن هذا الجهاز عن بعد على مسؤوليتك الخاصة." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "مُعَمَّى" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "ذاكرة وصول عشوائي (RAM) معمَّاة" + +msgid "Encrypted RAM makes it impossible for information that is stored in device memory to be read if the memory chip is removed and accessed." +msgstr "تجعل ذاكرة الوصول العشوائي (RAM) المعمَّاة من المستحيل قراءة المعلومات المخزَّنة في ذاكرة الجهاز إذا أُزيلت شريحة الذاكرة ووُصل إليها." + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "نهاية العمر" + +#. TRANSLATORS: The BIOS setting can only be changed to fixed values +msgid "Enumeration" +msgstr "تعداد" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "امحُ كل الخط الزمني لتحديثات المشغّل" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "جارٍ المسح…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "اخرج بعد تأخير صغير" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "اخرج بعد تحميل المحرك" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "صدّر بنية ملف مشغّل إلى XML" + +#. TRANSLATORS: command description +msgid "Export firmware history for manual upload" +msgstr "صدّر الخط الزمني للمشغّل لرفع يدوي" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "استخرج كائن مشغّل إلى صور" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "مِلَفّ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "مِلَفّ [مُعرّف-الجهاز|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME" +msgstr "اسم_الملف" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "اِسْم-الْمِلَفّ شَهَادَة مِفْتاح-خَاصّ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "اِسْم-الْمِلَفّ مُعَرّف-الجهاز [نُسْخَة]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "اِسْم-الْمِلَفّ إِزَاحَة بَيَانَات [نَوْعُ-المشغّل]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "اِسْم-الْمِلَفّ [مُعرّف-الجهاز|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "اِسْم-الْمِلَفّ [نَوْعُ-المشغّل]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "اِسْم-الْمِلَفّ-مَصْدَر اِسْم-الْمِلَفّ-وَجْهَة [نَوْعُ-المشغّل-مَصْدَر] [نَوْعُ-المشغّل-وَجْهَة]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "اسم_الملف|مجموع_اختباري1[,مجموع_اختباري2][,مجموع_اختباري3]" + +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "فشل" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "فشل تطبيق التحديث" + +#. TRANSLATORS: error message for Windows +msgid "Failed to connect to Windows service, please ensure it's running." +msgstr "فشل الاتصال بخدمة Windows، رجاء تأكّد من تشغيلها." + +#. TRANSLATORS: could not contact the fwupd service over D-Bus +msgid "Failed to connect to daemon" +msgstr "فشل الاتصال بالخفيّ" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "فشل تحميل dbx المحلي" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "فشل تحميل dbx للنظام" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "فشل القفل" #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" @@ -128,84 +1355,1438 @@ msgid "Failed to parse file" msgstr "فشل في تحليل الملف" +#. TRANSLATORS: the user didn't read the man page, %1 is '--filter' +#. TRANSLATORS: the user didn't read the man page, +#. * %1 is '--filter-release' +#. TRANSLATORS: the user didn't read the man page, %1 is '--filter' +#. TRANSLATORS: the user didn't read the man page, +#. * %1 is '--filter-release' +#, c-format +msgid "Failed to parse flags for %s" +msgstr "أخفق تحليل اللصائق لـ %s" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "فشل تحليل dbx المحلي" + +#. TRANSLATORS: a feature is something like "can show an image" +msgid "Failed to set front-end features" +msgstr "فشل ضبط ميزات الواجهة الأمامية" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "فشل التحقق من صحة محتويات ESP" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "خاطئ" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "اسم الملف" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "توقيع اسم الملف" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "مصدر اسم الملف" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +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: command line option +msgid "Filter with a set of release flags using a ~ prefix to exclude, e.g. 'trusted-release,~trusted-metadata'" +msgstr "صفِّ بمجموعة من لصائق الإصدار باستخدام بادئة ~ للاستثناء، مثل 'trusted-release،~trusted-metadata'" + +#. TRANSLATORS: command description +msgid "Finds firmware releases from the metadata" +msgstr "اعثر على إصدارات مشغّل من المعلومات الوصفيّة" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware Attestation" +msgstr "تصديق المشغلات" + +#. TRANSLATORS: longer description +msgid "Firmware Attestation checks device software using a reference copy, to make sure that it has not been changed." +msgstr "يتحقق تصديق المشغلات من برامج الجهاز باستخدام نُسخة مرجعية، للتأكد من أنها لم تتغير." + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware BIOS Descriptor" +msgstr "واصف BIOS للمشغلات" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Descriptor protects device firmware memory from being tampered with." +msgstr "يحمي واصف BIOS للمشغلات ذاكرة مشغلات الجهاز من التلاعب بها." + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "منطقة BIOS للمشغلات" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Region protects device firmware memory from being tampered with." +msgstr "تحمي منطقة BIOS للمشغلات ذاكرة مشغلات الجهاز من التلاعب بها." + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "URI مشغّل مبدئي" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "خدمة D-Bus لتحديث المشغلات" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "خدمة تحديث المشغلات" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "التحقق من محدّث المشغلات" + +msgid "Firmware Updater Verification checks that software used for updating has not been tampered with." +msgstr "يتحقق التحقق من محدّث المشغلات من عدم التلاعب بالبرامج المستخدمة للتحديث." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "تحديثات المشغلات" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "أداة المشغّل" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "حماية المشغلات من الكتابة" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "قفل حماية المشغلات من الكتابة" + +#. TRANSLATORS: longer description +msgid "Firmware Write Protection protects device firmware memory from being tampered with." +msgstr "تحمي حماية المشغلات من الكتابة ذاكرة مشغلات الجهاز من التلاعب بها." + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "تصديق المشغلات" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "المشغّل محظور بالفعل" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "المشغّل ليس محظوراً بالفعل" + +#. TRANSLATORS: the metadata is very out of date; %u is a number > 1 +#, c-format +msgid "Firmware metadata has not been updated for %u day and may not be up to date." +msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." +msgstr[0] "لم تُحدّث المعلومات الوصفيّة للمشغّل ليوم %u وربما تكون غير مُحدّثة." +msgstr[1] "لم تُحدّث المعلومات الوصفيّة للمشغّل ليومين %u وربما تكون غير مُحدّثة." +msgstr[2] "لم تُحدّث المعلومات الوصفيّة للمشغّل لأيام %u وربما تكون غير مُحدّثة." +msgstr[3] "لم تُحدّث المعلومات الوصفيّة للمشغّل لأيام %u وربما تكون غير مُحدّثة." +msgstr[4] "لم تُحدّث المعلومات الوصفيّة للمشغّل لأيام %u وربما تكون غير مُحدّثة." +msgstr[5] "لم تُحدّث المعلومات الوصفيّة للمشغّل لأيام %u وربما تكون غير مُحدّثة." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "تحديثات المشغلات" + +#. TRANSLATORS: user needs to run a command, %1 is 'fwupdmgr unlock' +#, c-format +msgid "Firmware updates disabled; run '%s' to enable" +msgstr "تحديثات المشغّل معطّلة؛ شغّل '%s' لتفعيلها" + +#. TRANSLATORS: command description +msgid "Fix a specific host security attribute" +msgstr "أصلح سمة أمن مُضيف محدّدة" + +#. TRANSLATORS: we've fixed a security problem on the machine +msgid "Fix reverted successfully" +msgstr "أُلغي الإصلاح بنجاح" + +#. TRANSLATORS: we've fixed a security problem on the machine +msgid "Fixed successfully" +msgstr "أُصلح بنجاح" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "أعلام" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "افرض الإجراء بتخفيف بعض فحوصات وقت التشغيل" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "عُثِرَ عليه" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "اكتشاف تعمية القرص الكاملة" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "قد تُبطَل أسرار التعمية الكاملة للقرص عند التحديث" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused Platform" +msgstr "منصة مصهورة" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "منصة مصهورة" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" +msgstr[1] "GUID" +msgstr[2] "GUIDs" +msgstr[3] "GUID" +msgstr[4] "GUID" +msgstr[5] "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "command-argument" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "GUID|DEVICE-ID" +msgstr "GUID|معرّف_الجهاز" + msgid "Get BIOS settings" msgstr "احصل على إعدادات البايوس" +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "احصل على كل لصائق الجهاز التي يدعمها fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "احصل على كل الأجهزة التي تدعم تحديثات المشغّل" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "احصل على كل الإضافات المفعّلة المسجّلة بالنظام" + +#. TRANSLATORS: command description +msgid "Get all known version formats" +msgstr "احصل على كل صيغ النسخ المعروفة" + +#. TRANSLATORS: command description +msgid "Get device report metadata" +msgstr "احصل على معلومات وصفيّة لتقرير الجهاز" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "احصل على تفاصيل ملف مشغّل" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "احصل على الأطراف البعيدة المضبوطة" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "احصل على سمات أمن المُضيف" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "اجلب قائمة المشغّل الموافق عليها" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "اجلب قائمة المشغّل المحظور" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "احصل على قائمة تحديثات كل الأجهزة المحدّدة، أو كل الأجهزة إن لم تُحدّد" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "اجلب إصدارات الجهاز" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "اجلب نتائج التحديث الأخير" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "مِلَفّ-HWIDS" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "الجهاز الصلب ينتظر إعادة التوصيل" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "عال" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "أحداث أمن المضيف" + +#. TRANSLATORS: error message for unsupported feature +msgid "Host Security ID (HSI) is not supported" +msgstr "معرّف أمن المُضيف (HSI) غير مدعوم" + +#. TRANSLATORS: success, so say thank you to the user +msgid "Host Security ID attributes uploaded successfully, thanks!" +msgstr "رُفِعت سمات معرّف أمن المُضيف بنجاح، شكراً!" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "معرّف أمن المُضيف:" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX" +msgstr "فِهْرِس" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX KEY [VALUE]" +msgstr "فِهْرِس مِفْتاح [قِيمَة]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX NAME TARGET [MOUNTPOINT]" +msgstr "فِهْرِس اسْم هَدَف [نُقْطَةُ ضم]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX1,INDEX2" +msgstr "فِهْرِس1،فِهْرِس2" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INHIBIT-ID" +msgstr "معرّف_الكبح" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "حماية IOMMU" + +#. TRANSLATORS: longer description +msgid "IOMMU Protection prevents connected devices from accessing unauthorized parts of system memory." +msgstr "تمنع حماية IOMMU الأجهزة المتصلة من الوصول إلى أجزاء غير مصرح بها من ذاكرة النظام." + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "حماية جهاز IOMMU مُعطَّلة" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "حماية جهاز IOMMU مُفَعَّلة" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "خمول…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "تجاهل فحوصات SSL الصارمة عند تنزيل الملفات" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "تجاهل إخفاقات التحقّق المشغّل" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "تجاهل إخفاقات عدم تطابق عتاد المشغّل" + +#. TRANSLATORS: command line option +msgid "Ignore non-critical firmware requirements" +msgstr "تجاهل متطلّبات المشغّل غير الحرجة" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "تجاهُل فحوصات SSL الصارمة، للقيام بهذا آلياً في المستقبل صدّر DISABLE_SSL_STRICT في بيئتك" + +#. TRANSLATORS: show the user a generic image that can be themed +msgid "Image" +msgstr "صورة" + +#. TRANSLATORS: show the user a random image from the internet +msgid "Image (custom)" +msgstr "صورة (مخصَّص)" + +#. TRANSLATORS: the inhibit ID is a short string like dbus-123456 +#, c-format +msgid "Inhibit ID is %s." +msgstr "مُعرّف التثبيط هو %s." + +#. TRANSLATORS: command description +msgid "Inhibit the system to prevent upgrades" +msgstr "اكبَح النظام لمنع الترقيات" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "مدة التثبيت" + +#. TRANSLATORS: command description +msgid "Install a firmware file in cabinet format on this hardware" +msgstr "ثبّت ملف مشغّل بصيغة 'cabinet' على هذا الجهاز الصلب" + +#. TRANSLATORS: command description +msgid "Install a raw firmware blob on a device" +msgstr "ثبّت كائن مشغّل أولي على جهاز" + +#. TRANSLATORS: command description +msgid "Install a specific firmware file on all devices that match" +msgstr "ثبّت ملف مشغّل معيّن على كل الأجهزة المطابقة" + +#. TRANSLATORS: command description +msgid "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" +msgstr "ثبّت مشغّل محدّد على جهاز، وستُثبّت كل الأجهزة الممكنة بمجرّد تطابق CAB" + msgid "Install old version of signed system firmware" -msgstr "ثبّت الإصدار القديم من البرامج الثابتة للنظام الموقَّعة" +msgstr "ثبّت الإصدار القديم من المشغلات للنظام الموقَّعة" msgid "Install old version of unsigned system firmware" -msgstr "ثبّت الإصدار القديم من البرامج الثابتة للنظام غير الموقَّعة" +msgstr "ثبّت الإصدار القديم من المشغلات للنظام غير الموقَّعة" msgid "Install signed device firmware" -msgstr "ثبّت البرامج الثابتة للجهاز الموقّعة" +msgstr "ثبّت المشغلات للجهاز الموقّعة" msgid "Install signed system firmware" -msgstr "تثبيت البرامج الثابتة للنظام الموقَّعة" +msgstr "تثبيت المشغلات للنظام الموقَّعة" msgid "Install unsigned device firmware" -msgstr "ثبّت البرامج الثابتة للجهاز غير الموقَّعة" +msgstr "ثبّت المشغلات للجهاز غير الموقَّعة" msgid "Install unsigned system firmware" -msgstr "تثبيت البرامج الثابتة للنظام غير الموقَّعة" +msgstr "تثبيت المشغلات للنظام غير الموقَّعة" + +#. TRANSLATORS: stay on one firmware version unless the new version is +#. explicitly +#. * specified +msgid "Installing a specific release is explicitly required" +msgstr "تثبيت إصدار معيّن مطلوب صراحةً" #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" -msgstr "جارٍ تثبيت تحديث البرنامج الثابت..." +msgstr "جارٍ تثبيت تحديث المشغّل…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "جارِ التثبيت على %s…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "تثبيت هذا التحديث قد يبطل أيضًا أي كفالة جهاز." + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "عدد صحيح" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "انتل BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM Protected" +msgstr "Intel BootGuard ACM محمي" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM محمي" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard Error Policy" +msgstr "سياسة خطأ Intel BootGuard" + +msgid "Intel BootGuard Error Policy ensures the device does not continue to start if its device software has been tampered with." +msgstr "تضمن سياسة خطأ Intel BootGuard عدم استمرار الجهاز في البدء إذا تعرض برنامج جهازه للتلاعب." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard Fuse" +msgstr "منصهر Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "منصهر Intel BootGuard OTP" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard Verified Boot" +msgstr "الإقلاع المتحقق منه Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "سياسة خطأ Intel BootGuard" + +#. TRANSLATORS: longer description +msgid "Intel BootGuard prevents unauthorized device software from operating when the device is started." +msgstr "يمنع Intel BootGuard برامج الجهاز غير المصرح بها من العمل عند بدء تشغيل الجهاز." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "الإقلاع المتحقق منه Intel BootGuard" + +#. TRANSLATORS: Title: GDS is where the CPU leaks information +msgid "Intel GDS Mitigation" +msgstr "تخفيف Intel GDS" + +#. TRANSLATORS: Title: GDS is where the CPU leaks information +msgid "Intel GDS mitigation" +msgstr "تخفيف Intel GDS" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Manufacturing Mode" +msgstr "وضع تصنيع محرك الإدارة انتل" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is +#. enabled +#. * with a jumper -- luckily it is probably not accessible to end users on +#. consumer +#. * boards +msgid "Intel Management Engine Override" +msgstr "تجاوز محرك الإدارة انتل" + +#. TRANSLATORS: longer description +msgid "Intel Management Engine Override disables checks for device software tampering." +msgstr "يعطّل تجاوز محرك الإدارة انتل فحوصات التلاعب ببرامج الجهاز." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Version" +msgstr "نُسخة محرك الإدارة انتل" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "جهاز داخلي" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "غير صالح" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "وسائط غير صالحة" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected GUID" +msgstr "وسائط غير صالحة، المُتوقّع GUID" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected INDEX KEY [VALUE]" +msgstr "وسائط غير صالحة، المُتوقّع فِهْرِس مِفْتاح [قِيمَة]" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected INDEX NAME TARGET [MOUNTPOINT]" +msgstr "وسائط غير صالحة، المُتوقّع فِهْرِس اسْم هَدَف [نُقْطَةُ ضم]" + +#. TRANSLATOR: This is the error message for +#. * incorrect parameter +msgid "Invalid arguments, expected an AppStream ID" +msgstr "وسائط غير صالحة، المُتوقّع مُعرّف AppStream" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected at least ARCHIVE FIRMWARE METAINFO" +msgstr "وسائط غير صالحة، المُتوقّع على الأقل أَرْشِيف مُشغّل مَعْلُومَات وَصْفِيَّة" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected base-16 integer" +msgstr "وسائط غير صالحة، المُتوقّع عدد صحيح أساسه 16" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "تخفيض إصدار" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "في نمط المُقلِع" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "ترقية" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "مشكلة" +msgstr[1] "مشكلة واحدة" +msgstr[2] "مشكلتين" +msgstr[3] "مشاكل" +msgstr[4] "مشكلة" +msgstr[5] "مشكلة" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "النوَاة لم تعد ملوثة" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "النواة ملوثة" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "قفل النواة مُعطَّل" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "قفل النواة مُفَعَّل" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "الموقع" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "آخر تعديل" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "أقل من دقيقة متبقية" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "الترخيص" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "إغلاق نواة لينكس" + +#. TRANSLATORS: longer description +msgid "Linux Kernel Lockdown mode prevents administrator (root) accounts from accessing and changing critical parts of system software." +msgstr "يمنع وضع إغلاق نواة لينكس حسابات المسؤول (الجذر) من الوصول إلى الأجزاء الهامة من برامج النظام وتغييرها." + +msgid "Linux Kernel Swap temporarily saves information to disk as you work. If the information is not protected, it could be accessed by someone if they obtained the disk." +msgstr "تحفظ مبادلة نواة لينكس المعلومات مؤقتًا على القرص أثناء عملك. إذا لم تكن المعلومات محمية، يمكن لشخص ما الوصول إليها إذا حصل على القرص." + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "التحقق من نواة لينكس" + +msgid "Linux Kernel Verification makes sure that critical system software has not been tampered with. Using device drivers which are not provided with the system can prevent this security feature from working correctly." +msgstr "يتأكد التحقق من نواة لينكس من عدم التلاعب ببرامج النظام الهامة. يمكن أن يمنع استخدام مشغلات الأجهزة غير الموفرة مع النظام عمل ميزة الأمان هذه بشكل صحيح." + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "مبادلة لينكس" msgid "Linux Vendor Firmware Service (stable firmware)" -msgstr "خدمة البرامج الثابتة لموردي Linux (البرامج الثابتة المستقرة)" +msgstr "خدمة المشغلات لموردي لينكس (المشغلات المستقرة)" msgid "Linux Vendor Firmware Service (testing firmware)" -msgstr "خدمة البرامج الثابتة لمورد Linux (اختبار البرامج الثابتة)" +msgstr "خدمة المشغلات لمورد لينكس (مشغلات تجريبية)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "نواة لينكس" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "إغلاق نواة لينكس" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "مبادلة لينكس" + +#. TRANSLATORS: command description +msgid "List EFI boot files" +msgstr "اسرد ملفات إقلاع EFI" + +#. TRANSLATORS: command description +msgid "List EFI boot parameters" +msgstr "اسرد معاملات إقلاع EFI" + +#. TRANSLATORS: command description +msgid "List EFI variables with a specific GUID" +msgstr "اسرد متغيّرات EFI بمُعرّف GUID محدّد" #. TRANSLATORS: command line option msgid "List entries in dbx" msgstr "قائمة الإدخالات في dbx" #. TRANSLATORS: command description +msgid "List the available firmware GTypes" +msgstr "اسرد أنواع G المشغّل المتوفّرة" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "اسرد أنواع المشغّل المتوفّرة" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "اسرد الملفات على ESP" + +#. TRANSLATORS: command description msgid "Load device emulation data" msgstr "حمّل بيانات محاكاة الجهاز" +#. TRANSLATORS: the plugin was created from a .so object, and was not built-in +msgid "Loaded from an external module" +msgstr "حُمِّل من وحدة خارجية" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "جارِ التحميل…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "مقفول" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "منخفض" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and key refers +#. * to the private/public key used to secure loading of firmware +msgid "MEI Key Manifest" +msgstr "بيان مفتاح MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and key refer +#. * to the private/public key used to secure loading of firmware +msgid "MEI key manifest" +msgstr "بيان مفتاح MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "وضع تصنيع MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "تجاوز MEI" + +msgid "MEI version" +msgstr "نُسخة MEI" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "فعّل الإضافات محدّدة يدوياً" + +#. TRANSLATORS: longer description +msgid "Manufacturing Mode is used when the device is manufactured and security features are not yet enabled." +msgstr "يُستخدم وضع التصنيع عند تصنيع الجهاز ولم تُكَّن ميزات الأمان بعد." + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "الطول الأقصى" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "القيمة القصوى" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "متوسط" + +#. TRANSLATORS: ask the user to do a simple task which should be translated +msgid "Message" +msgstr "رسالة" + +#. TRANSLATORS: ask the user a question, and it will not be translated +msgid "Message (custom)" +msgstr "رسالة (مخصَّص)" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "توقيع البيانات الوصفية" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "URI البيانات الوصفية" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "يمكن الحصول على المعلومات الوصفيّة من خدمة مشغّل بائع لينكس." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. * refresh recently -- %1 is '--force' +#, c-format +msgid "Metadata is up to date; use %s to refresh again." +msgstr "المعلومات الوصفيّة مُحدّثة؛ استخدِم %s للتحديث مجدّداً." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "الإصدار الأدنى" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "الطول الأدنى" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "القيمة الدنيا" + +#. TRANSLATORS: sets something in the daemon configuration file +msgid "Modifies a daemon configuration value" +msgstr "عدّل قيمة تشكيلة الـ daemon" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "عدّل طرفاً بعيداً محدّداً" + msgid "Modify a configured remote" msgstr "عدّل جهاز التحكم عن بعد المكوَّن" msgid "Modify daemon configuration" -msgstr "عدّل التكوين الخفي" +msgstr "عدّل ضبط الخدمة" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "راقب الـ daemon للأحداث" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "تضم ESP" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "يحتاج إلى إعادة إقلاع بعد التثبيت" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "يحتاج إلى إعادة إقلاع" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "يحتاج إلى إغلاق بعد التثبيت" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "الإصدار الجديد" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "لم تحدّد أي إجراء!" + +#. TRANSLATORS: no devices that can be upgraded with new firmware +msgid "No devices are updatable" +msgstr "لا توجد أجهزة يمكن تحديثها" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "لا تخفيضات لـ %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "لا مُعرّفات مشغّل مكتشفة" + +#. TRANSLATORS: nothing found +msgid "No firmware found" +msgstr "لا مشغّل مكتشف" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "لا أجهزة عتاد مكتشفة لديها قدرة تحديث المشغّل" + +#. TRANSLATORS: no repositories to download from +msgid "No matching releases for search token" +msgstr "لا إصدارات مطابقة لعنصر البحث" + +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +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: this is an error string +msgid "No updatable devices" +msgstr "لا أجهزة يمكن تحديثها" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "لا تحديثات متوفّرة" + +msgid "No updates available for remaining devices" +msgstr "لا تحديثات متوفّرة لبقية الأجهزة" + +#. TRANSLATORS: error message +#, c-format +msgid "No volume matched %s" +msgstr "لم يُطابق أي مجلّد %s" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "غير موافق عليه" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "لم يُعثَر عليه" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "غير مدعوم" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "حسنًا" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "حسن!" + +#. TRANSLATORS: the firmware old version +msgid "Old version" +msgstr "الإصدار القديم" + +#. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "ثبّت على الأجهزة المحاكية فقط" #. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "أظهر قيمة PCR واحدة فقط" +#. TRANSLATORS: command line option +msgid "Only use peer-to-peer networking when downloading files" +msgstr "استخدم شبكات الند للند فقط عند تنزيل الملفات" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "تُسمح ترقيات الإصدار فقط" + +#. TRANSLATORS: command line option +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "أخرج بصيغة JSON (عطّل كل المطالبات التفاعلية)" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "تجاوز مسار ESP المبدئي" + +#. TRANSLATORS: if we can get metadata from peer-to-peer clients +msgid "P2P Firmware" +msgstr "مشغّل P2P" + +#. TRANSLATORS: if we can get metadata from peer-to-peer clients +msgid "P2P Metadata" +msgstr "بيانات P2P وصفية" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "المسار" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "حلّل ملف مشغّل واعرض تفاصيله" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "جارٍ تحليل تحديث dbx…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "جارٍ تحليل dbx للنظام…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "كلمة السر" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "رقّع كائن مشغّل عند إزاحة معلومة" + +msgid "Payload" +msgstr "الحمولة" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "قيد الانتظار" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "هل تُجرى العملية؟" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "تصحيح أخطاء المنصة" + +#. TRANSLATORS: longer description +msgid "Platform Debugging allows device security features to be disabled. This should only be used by hardware manufacturers." +msgstr "يسمح تصحيح أخطاء المنصة بتعطيل ميزات أمان الجهاز. يجب أن يستخدمه مصنعو الأجهزة فقط." + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform debugging" +msgstr "تصحيح أخطاء المنصة" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "تأكد أنك تملك مفتاح استرداد الوحدة قبل المتابعة." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "أدخل رقمًا من 0 إلى %u: " + +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' +#, c-format +msgid "Please enter either %s or %s: " +msgstr "أدخل إما %s أو %s: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "تبعيات الملحق مفقودة" + +#. TRANSLATORS: The plugin enumeration might change the device current mode +msgid "Plugin enumeration may change device state" +msgstr "قد يغيّر تعداد الملحق حالة الجهاز الحالية" + +#. TRANSLATORS: The plugin is only for testing +msgid "Plugin is only for testing" +msgstr "الملحق للاختبار فقط" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "القيم الممكنة" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA Protection" +msgstr "حماية DMA قبل الإقلاع" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "حماية DMA قبل الإقلاع" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "حماية DMA قبل الإقلاع مُعطَّلة" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "حماية DMA قبل الإقلاع مُفَعَّلة" + +#. TRANSLATORS: longer description +msgid "Pre-boot DMA protection prevents devices from accessing system memory after being connected to the computer." +msgstr "تمنع حماية DMA قبل الإقلاع الأجهزة من الوصول إلى ذاكرة النظام بعد توصيلها بحاسوب." + +#. TRANSLATORS: warning message +msgid "Press unlock on the device to continue the update process." +msgstr "اضغط على فتح القفل على الجهاز لمواصلة عملية التحديث." + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "الإصدار السابق" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "الأولوية" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "مشاكل" + +msgid "Proceed with upload?" +msgstr "هل تشرع في الرفع؟" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "فحوصات أمان المعالج" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Processor rollback protection" +msgstr "حماية المعالج من الرجوع إلى إصدار أقدم" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "ملكيّ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "مُعرّف-طرف-بعيد" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "مُعرّف-طرف-بعيد مِفْتاح قِيمَة" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "قراءة فقط" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "اقرأ كائن مشغّل من جهاز" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "اقرأ مشغّلاً من جهاز" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "جارِ القراءة من %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "جارٍ القراءة…" + +#. TRANSLATORS: Plugin is active and in use +msgid "Ready" +msgstr "جاهز" + +#. TRANSLATORS: how often we should refresh the metadata +msgid "Refresh Interval" +msgstr "فاصل التحديث" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "حدّث المعلومات الوصفيّة من الخادوم البعيد" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "أأعيد تثبيت %s إلى %s؟" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "أعد تثبيت المشغّل الحالي على الجهاز" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "أعد تثبيت المشغّل على جهاز" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree +msgid "Release Branch" +msgstr "فرع الإصدار" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "أوسمة الإصدار" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "معرّف الإصدار" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "معرّف الطرف" + +#. TRANSLATORS: command description +msgid "Removes devices to watch for future emulation" +msgstr "يزيل الأجهزة من المراقبة للمحاكاة المستقبلية" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "URI التقرير" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "بُلّغ إلى خادوم طرفي" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "لم يُعثَر على نظام ملفات efivarfs المطلوب" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "الجهاز الصلب المطلوب غير موجود" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "يتطلّب مُقلِع" + +#. TRANSLATORS: metadata is downloaded +msgid "Requires internet connection" +msgstr "يتطلّب اتصال بالإنترنت" + msgid "Reset daemon configuration" -msgstr "أعد تعيين التكوين الخفي" +msgstr "أعد تعيين ضبط الخدمة" + +#. TRANSLATORS: sets something in the daemon configuration file +msgid "Resets a daemon configuration section" +msgstr "أعد ضبط قسم تشكيلة الـ daemon" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "أعد التشغيل الآن؟" + +#. TRANSLATORS: changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "هل تعيد إقلاع الـ daemon لجعل التغيير سارياً؟" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "جارٍ إعادة تشغيل الجهاز…" + +#. TRANSLATORS: command description +msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "استرجع إعدادات BIOS. أعد كل الإعدادات إن لم يُمرّر أي وسيط" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "أعد كل مُعرّفات العتاد لـ الحاسوب" + +#. TRANSLATORS: ask the user to upload +msgid "Review and upload report now?" +msgstr "هل تراجع التقرير وترفعه الآن؟" + +#. TRANSLATORS: longer description +msgid "Rollback Protection prevents device software from being downgraded to an older version that has security problems." +msgstr "تمنع حماية الرجوع إلى إصدار أقدم من برامج الجهاز الرجوع إلى نُسخة أقدم بها مشكلات أمنية." + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr get-upgrades` +#, c-format +msgid "Run `%s` for more information." +msgstr "شغّل `%s` لمزيد من المعلومات." + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr sync` +#, c-format +msgid "Run `%s` to complete this action." +msgstr "شغّل `%s` لإكمال هذا الإجراء." + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "شغّل روتين تنظيف مكوّن الإضافة عند استخدام install-blob" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "شغّل روتين تحضير مكوّن الإضافة عند استخدام install-blob" + +#. TRANSLATORS: command description +msgid "Run the post-reboot cleanup action" +msgstr "شغّل إجراء التنظيف بعد إعادة الإقلاع" + +#. TRANSLATORS: tell a user how to get information +#, c-format +msgid "Run without '%s' to see" +msgstr "شغّل بدون '%s' لترى" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "النواة قيد التشغيل قديمة جداً" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "لاحقة زمن التشغيل" + +#. TRANSLATORS: Software Bill of Materials link +msgid "SBOM" +msgstr "قائمة مكونات البرمجية" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SECTION" +msgstr "قِسْم" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING VALUE" +msgstr "إِعْدَادٌ قِيمَة" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "إعداد1 قيمة1 [إعداد2] [قيمة2]" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "SMAP" +msgstr "SMAP" + +#. TRANSLATORS: Title: Whether firmware is locked down +msgid "SMM locked down" +msgstr "SMM مقفل" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "واصف SPI BIOS" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "منطقة SPI BIOS" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "قفل SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "حماية SPI من الإعادة" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "كتابة SPI" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "حماية SPI من الكتابة" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "نِظَامٌ فَرْعِيّ مُشَغّل [مُعرّف-الجهاز|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "احفظ ملفاً يسمح بتوليد مُعرّفات العتاد" #. TRANSLATORS: command description msgid "Save device emulation data" msgstr "حفظ بيانات محاكاة الجهاز" +#. TRANSLATORS: key for a offline report filename +msgid "Saved report" +msgstr "حُفِظ التقرير" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +msgid "Scalar Increment" +msgstr "الزيادة العددية" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "جارٍ الجدولة…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "الإقلاع الآمن مُعطَّل" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "الإقلاع الآمن مُفَعَّل" + msgid "Security hardening for HSI" -msgstr "التشديد الأمني ​​لـ HSI" +msgstr "التشديد الأمني ل HSI" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "اطلع على %s لمزيد من التفاصيل." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "راجع %s لمزيد من المعلومات." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "جهاز مختار" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "اُختير المجلّد" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "الرقم التسلسلي" + +#. TRANSLATORS: Configured a BIOS setting to a value +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "اضبط إعداد BIOS ‏'%s' باستخدام '%s'." + +#. TRANSLATORS: command description +msgid "Set a BIOS setting" +msgstr "اضبط إعداد BIOS" msgid "Set one or more BIOS settings" msgstr "عيّن واحدًا أو أكثر من إعدادات البايوس" +#. TRANSLATORS: command description +msgid "Set or remove an EFI boot hive entry" +msgstr "اضبط أو أزل إدخال خلية إقلاع EFI" + +#. TRANSLATORS: command description +msgid "Set the EFI boot next" +msgstr "اضبط التالي لإقلاع EFI" + +#. TRANSLATORS: command description +msgid "Set the EFI boot order" +msgstr "اضبط تسلسل إقلاع EFI" + +#. TRANSLATORS: command line option +msgid "Set the download retries for transient errors" +msgstr "اضبط محاولات إعادة التنزيل للأخطاء العابرة" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "اضبط إعداد أو أكثر من إعدادات BIOS" + #. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" -msgstr "يضبط قائمة البرامج الثابتة المعتمدة" +msgstr "يضبط قائمة المشغلات المعتمدة" + +#. TRANSLATORS: type of BIOS setting +msgid "Setting type" +msgstr "نوع الإعداد" #. TRANSLATORS: description of a BIOS setting msgid "Settings will apply after system reboots" msgstr "ستُطبّق الإعدادات بعد إعادة تشغيل النظام" +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "شارك الخط الزمني للمشغّل مع المطورين" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "اعرض كل النتائج" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "اعرض نُسَخ العميل والـ daemon" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "اعرض معلومات الخدمة المفصلة لنطاق معين" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "اعرض معلومات تصحيح الأخطاء لجميع النطاقات" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "اعرض خيارات التصحيح" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "اعرض الأجهزة التي لا يمكن تحديثها" + #. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "عرض معلومات التصحيح الإضافية" +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "اعرض الخط الزمني لتحديثات المشغّل" + #. TRANSLATORS: command line option msgid "Show the calculated version of the dbx" msgstr "عرض النسخة المحسوبة من dbx" +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "أغلق الآن؟" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "سجّل مشغّلاً بمفتاح جديد" + msgid "Sign data using the client certificate" msgstr "سجّل البيانات باستخدام شهادة العميل" @@ -215,33 +2796,878 @@ msgstr "سجّل البيانات باستخدام شهادة العميل" #. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "وقّع البيانات المرفوعة بشهادة العميل" + +msgid "Signature" +msgstr "التوقيع" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "حمولة موقّعة" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "الحجم" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "قد تصبح بعض أسرار المنصة غير صالحة عند تحديث هذا المشغّل." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "المصدر" + +#. TRANSLATORS: command line option msgid "Specify the dbx database file" msgstr "حدد ملف قاعدة بيانات dbx" msgid "Stop the fwupd service" msgstr "أوقف خدمة fwupd" +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "سلسلة" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "نجاح" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "فُعِّلت كل الأجهزة بنجاح" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "عطِّل الطرف البعيد بنجاح" + +#. TRANSLATORS: comment explaining result of command +msgid "Successfully disabled test devices" +msgstr "عُطِّل أجهزة الاختبار بنجاح" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata:" +msgstr "نُزّلت معلومات وصفيّة جديدة بنجاح:" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "فُعِّل وُحُدّث الطرف البعيد بنجاح" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "فُعِّل الطرف البعيد بنجاح" + +#. TRANSLATORS: comment explaining result of command +msgid "Successfully enabled test devices" +msgstr "فُعِّل أجهزة الاختبار بنجاح" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "ثبّت المشغّل بنجاح" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "عدِّل قيمة الإعداد بنجاح" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "عدِّل الطرف البعيد بنجاح" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "حُدّثت المعلومات الوصفيّة يدوياً بنجاح" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully reset configuration section" +msgstr "أُعِيد ضبط قسم التشكيلة بنجاح" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully reset configuration values" +msgstr "أُعِيد ضبط قيم التشكيلة بنجاح" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "حُدّثت تحقّقات الجهاز بنجاح" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "رُفِع تقرير %u بنجاح" +msgstr[1] "رُفِع تقريران %u بنجاح" +msgstr[2] "رُفِعت تقارير %u بنجاح" +msgstr[3] "رُفِعت تقارير %u بنجاح" +msgstr[4] "رُفِعت تقارير %u بنجاح" +msgstr[5] "رُفِعت تقارير %u بنجاح" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "تُحقّقت تحقّقات الجهاز بنجاح" + +#. TRANSLATORS: the device showed up in time +#, c-format +msgid "Successfully waited %.0fms for device" +msgstr "اُنتُظِر الجهاز بنجاح لـ %.0fms" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "ملخص" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Supervisor Mode Access Prevention" +msgstr "منع الوصول لوضع المشرف" + +#. TRANSLATORS: longer description +msgid "Supervisor Mode Access Prevention ensures critical parts of device memory are not accessed by less secure programs." +msgstr "يضمن منع الوصول لوضع المشرف عدم وصول البرامج الأقل أمانًا إلى الأجزاء الهامة من ذاكرة الجهاز." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "مدعوم" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "وحدة معالجة مركزية (CPU) مدعومة" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "مدعوم على الخادوم الطرفي" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend To Idle" +msgstr "تعليق إلى وضع الخمول" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend To RAM" +msgstr "تعليق إلى ذاكرة الوصول العشوائي (RAM)" + +#. TRANSLATORS: longer description +msgid "Suspend to Idle allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "يسمح التعليق إلى وضع الخمول للجهاز بالانتقال بسرعة إلى وضع السكون لتوفير الطاقة. عندما يكون الجهاز معلَّقًا، يمكن إزالة ذاكرته ماديًا والوصول إلى معلوماته." + +#. TRANSLATORS: longer description +msgid "Suspend to RAM allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "يسمح التعليق إلى ذاكرة الوصول العشوائي (RAM) للجهاز بالانتقال بسرعة إلى وضع السكون لتوفير الطاقة. عندما يكون الجهاز معلَّقًا، يمكن إزالة ذاكرته ماديًا والوصول إلى معلوماته." + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "تعليق إلى وضع الخمول" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "تعليق إلى ذاكرة الوصول العشوائي (RAM)" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "حوّل الفرع من %s إلى %s؟" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "بدّل فرع المشغّل على الجهاز" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the chosen configuration" +msgstr "زامِن إصدارات المشغّل مع التشكيلة المختارة" + +#. TRANSLATORS: Title: Whether firmware is locked down +msgid "System Management Mode" +msgstr "وضع إدارة النظام" + +#. TRANSLATORS: this CLI tool is now preventing system updates +msgid "System Update Inhibited" +msgstr "تحديث النظام مثبّط" + +#. TRANSLATORS: longer description +msgid "System management mode is used by the firmware to access resident BIOS code and data." +msgstr "تستخدم المشغلات وضع إدارة النظام للوصول إلى رمز وبيانات BIOS المقيمين." + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low" +msgstr "طاقة النظام منخفضة جداً" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low (%u%%, requires %u%%)" +msgstr "طاقة النظام منخفضة جداً (%u%%، تتطلّب %u%%)" + +#. TRANSLATORS: Must be plugged into an outlet +msgid "System requires external power source" +msgstr "يتطلّب النظام مصدر طاقة خارجي" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "نَصّ" + +#. TRANSLATORS: longer description +msgid "TPM (Trusted Platform Module) is a computer chip that detects when hardware components have been tampered with." +msgstr "TPM (وحدة النظام الأساسي الموثوقة) هي شريحة حاسوب تكتشف متى عُبث بمكونات الأجهزة." + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "إعادة بناء TPM PCR0" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "إعادة بناء TPM PCR0 غير صالحة" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "إعادة بناء TPM PCR0 صالحة الآن" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "تشكيلة منصة TPM" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM Reconstruction" +msgstr "إعادة بناء TPM" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "سجلات تكوين المنصة (PCRs) فارغة TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "وسم" +msgstr[1] "وسمان" +msgstr[2] "وسوم" +msgstr[3] "أوسمة" +msgstr[4] "أوسمة" +msgstr[5] "أوسمة" + +#. TRANSLATORS: we're saving all USB events for emulation +msgid "Tagged for emulation" +msgstr "وُسِمَ للمحاكاة" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "ملوث" + +#. show the user the entire data blob +msgid "Target" +msgstr "الهدف" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "اختبر جهازًا باستخدام بيان JSON" + +#. TRANSLATORS: when the release was tested +msgid "Tested" +msgstr "اُختُبِرَ" + +#. TRANSLATORS: the %s is a vendor name, e.g. Lenovo +#, c-format +msgid "Tested by %s" +msgstr "اُختُبِرَ من قبل %s" + +#. TRANSLATORS: someone we trust has tested this +msgid "Tested by trusted vendor" +msgstr "اُختُبِرَ من قبل بائع موثوق" + +#. TRANSLATORS: the boot entry was in a legacy format +msgid "The EFI boot entry is not in hive format, and shim may not be new enough to read it." +msgstr "إدخال إقلاع EFI ليس بصيغة خلية، وربما لا يكون shim جديداً بما فيه الكفاية لقراءته." + +#. TRANSLATORS: try to treat the legacy format as a hive +msgid "The EFI boot entry was not in hive format, falling back" +msgstr "إدخال إقلاع EFI ليس بصيغة خلية، التراجع للوراء" + +#. TRANSLATORS: longer description +msgid "The Intel Management Engine Key Manifest must be valid so that the device firmware can be trusted by the CPU." +msgstr "يجب أن يكون بيان مفتاح محرك الإدارة انتل صالحًا حتى تثق وحدة المعالجة المركزية (CPU) بمشغلات الجهاز." + +#. TRANSLATORS: longer description +msgid "The Intel Management Engine controls device components and needs to have a recent version to avoid security issues." +msgstr "يتحكم محرك الإدارة انتل في مكونات الجهاز ويحتاج إلى نُسخة حديثة لتجنب المشكلات الأمنية." + #. TRANSLATORS: do not translate the variables marked using $ 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 هي خدمة مجانية تعمل ككيان قانوني مستقل وليس لها أي اتصال بـ $OS_RELEASE:NAME$. ربما لم يتحقق موزّعك من أيّ من تحديثات البرامج الثابتة للتأكد من توافقها مع نظامك أو الأجهزة المتصلة. تُوفّر كافة البرامج الثابتة فقط من قبل الشركة المصنعة للمعدات الأصلية." +msgstr "LVFS هي خدمة مجانية تعمل ككيان قانوني مستقل وليس لها أي اتصال بـ $OS_RELEASE:NAME$. ربما لم يتحقق موزّعك من أيّ من تحديثات المشغلات للتأكد من توافقها مع نظامك أو الأجهزة المتصلة. تُوفّر كافة المشغلات فقط من قبل الشركة المصنعة للمعدات الأصلية." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Platform Configuration is used to check whether the device start process has been modified." +msgstr "تُستخدم تشكيلة منصة TPM (وحدة النظام الأساسي الموثوقة) للتحقق مما إذا كانت عملية بدء تشغيل الجهاز عُدّلت." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Reconstruction is used to check whether the device start process has been modified." +msgstr "تُستخدم إعادة بناء TPM (وحدة النظام الأساسي الموثوقة) للتحقق مما إذا كانت عملية بدء تشغيل الجهاز عُدّلت." + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "يختلف TPM PCR0 عن إعادة البناء." + +#. TRANSLATORS: longer description +msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." +msgstr "يُستخدم مفتاح منصة UEFI لتحديد ما إذا كان برنامج الجهاز يأتي من مصدر موثوق." + +#. TRANSLATORS: HSI event title +msgid "The UEFI certificate store is now up to date" +msgstr "مخزن شهادات UEFI مُحدَّث الآن" + +#. TRANSLATORS: longer description +msgid "The UEFI db contains the list of valid certificates that can be used to authorize what EFI binaries are allowed to run." +msgstr "تحتوي قاعدة بيانات UEFI على قائمة الشهادات الصالحة التي يمكن استخدامها للتصريح لثنائيات EFI المسموح تشغيلها." + +#. TRANSLATORS: longer description +msgid "The UEFI system can set up memory attributes at boot which prevent common exploits from running." +msgstr "يمكن لنظام UEFI ضبط سمات الذاكرة عند الإقلاع التي تمنع تشغيل عمليات الاستغلال الشائعة." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "حمّل الخفيّ شيفرة طرف ثالث ولم يعد مدعومًا من قبل المطورين الأصليين!" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "المشغّل من %s لا يوفره %s، بائع العتاد." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "لم يُضبَط ساعة النظام صحيحًا وقد يفشل تنزيل الملفات." + +#. TRANSLATORS: warning message shown after update has been scheduled +msgid "The update will continue when the device USB cable has been re-inserted." +msgstr "سيكمل التحديث عند إعادة إدخال كابل USB الخاص بالجهاز." + +#. TRANSLATORS: warning message shown after update has been scheduled +msgid "The update will continue when the device USB cable has been unplugged and then re-inserted." +msgstr "سيكمل التحديث عند فصل كابل USB الخاص بالجهاز ثم إعادة إدخاله." + +#. TRANSLATORS: warning message shown after update has been scheduled +msgid "The update will continue when the device USB cable has been unplugged." +msgstr "سيكمل التحديث عند فصل كابل USB الخاص بالجهاز." + +#. TRANSLATORS: warning message +msgid "The update will continue when the device power cable has been removed and re-inserted." +msgstr "سيكمل التحديث عند نزع كابل طاقة الجهاز ثم إعادة إدخاله." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "لم يوفّر البائع أي ملاحظات إصدار." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "توجد أجهزة بها مشكلات:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "لا توجد ملفات مشغّل محظورة" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "لا مشغّل مُعتَمد." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "سيعاد هذا الجهاز إلى %s عند تنفيذ الأمر %s." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "وفّر أعضاء مجتمع LVFS هذا المشغّل ولم يوفّره (أو يدعمه) باعه الأصلي." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "لم يُصَدَّق على هذه الحزمة، وقد لا تعمل بشكل صحيح." #. TRANSLATORS: we're poking around as a power user msgid "This program may only work correctly as root" msgstr "قد يعمل هذا البرنامج بشكل صحيح فقط كجذر" 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 "يحتوي جهاز التحكم عن بعد هذا على برامج ثابتة غير محظورة، ولكنها لا تزال قيد الاختبار من قبل بائع الأجهزة. يجب عليك التأكد من أن لديك طريقة للرجوع إلى إصدار أقدم يدويًا في حالة فشل تحديث البرنامج الثابت." +msgstr "يحتوي جهاز التحكم عن بعد هذا على مشغلات غير محظورة، ولكنها لا تزال قيد الاختبار من قبل بائع الأجهزة. يجب عليك التأكد من أن لديك طريقة للرجوع إلى إصدار أقدم يدويًا في حالة فشل تحديث المشغّل." + +#. TRANSLATORS: error message +msgid "This system doesn't support firmware settings" +msgstr "هذا النظام لا يدعم إعدادات المشغّل" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "يملك هذا النظام مشكلات HSI في زمن التشغيل." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "يملك هذا النظام مستوى أمان HSI منخفضًا." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "تسمح هذه الأداة للمشرف بتطبيق تحديثات UEFI dbx." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "تتيح هذه الأداة للمدير استعلام الخفيّ fwupd والتحكم به، مما يتيح له أداء إجراءات مثل تثبيت مشغّل أو تخفيض إصداره." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "هذه الأداة تسمح للمُدير باستخدام إضافات fwupd دون تثبيتها على نظام المُضيف." + +#. TRANSLATORS: the %1 is a kernel command line key=value +#, c-format +msgid "This tool can add a kernel argument of '%s', but it will only be active after restarting the computer." +msgstr "يمكن لهذه الأداة إضافة وسيط نواة '%s'، لكن لن يصبح سارياً إلا بعد إعادة إقلاع الحاسوب." + +#. TRANSLATORS: the %1 is a BIOS setting name. +#. * %2 and %3 are the values, e.g. "True" or "Windows10" +#, c-format +msgid "This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, but it will only be active after restarting the computer." +msgstr "يمكن لهذه الأداة تغيير إعداد BIOS ‏'%s' من '%s' إلى '%s' آلياً، لكن لن يصبح سارياً إلا بعد إعادة إقلاع الحاسوب." + +#. TRANSLATORS: the %1 is a kernel command line key=value +#, c-format +msgid "This tool can change the kernel argument from '%s' to '%s', but it will only be active after restarting the computer." +msgstr "يمكن لهذه الأداة تغيير وسيط النواة من '%s' إلى '%s'، لكن لن يصبح سارياً إلا بعد إعادة إقلاع الحاسوب." #. TRANSLATORS: CLI description msgid "This tool will read and parse the TPM event log from the system firmware." -msgstr "ستقوم هذه الأداة بقراءة وتحليل سجل أحداث TPM من البرنامج الثابت للنظام." +msgstr "ستقوم هذه الأداة بقراءة وتحليل سجل أحداث TPM من المشغّل للنظام." + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "فشل عابر" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "صحيح" + +#. TRANSLATORS: We verified the metadata against the server +msgid "Trusted metadata" +msgstr "بيانات وصفية موثوقة" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "حمولة موثوقة" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "النوع" + +#. TRANSLATORS: Title: Bootservice is when only readable from early-boot +msgid "UEFI Bootservice Variables" +msgstr "متغيرات خدمة إقلاع UEFI" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition may not be set up correctly" +msgstr "قد لا يكون قسم UEFI ESP مُعدًّا صحيحًا" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "لم يُكتَشَف أو يُضبَط قسم UEFI ESP" + +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI Memory Protection" +msgstr "حماية ذاكرة UEFI" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "مفتاح منصة UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI Secure Boot" +msgstr "إقلاع UEFI الآمن" + +#. TRANSLATORS: longer description +msgid "UEFI Secure Boot prevents malicious software from being loaded when the device starts." +msgstr "يمنع الإقلاع الآمن UEFI تحميل البرامج الضارة عند بدء تشغيل الجهاز." + +#. TRANSLATORS: longer description +msgid "UEFI boot service variables should not be readable from runtime mode." +msgstr "لا يجب أن تكون متغيرات خدمة إقلاع UEFI قابلة للقراءة من وضع التشغيل." + +#. TRANSLATORS: Title: Bootservice is when only readable from early-boot +msgid "UEFI bootservice variables" +msgstr "متغيرات خدمة إقلاع UEFI" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "تحديثات كبسولة UEFI غير متاحة أو غير مفعّلة في إعداد المشغّل" + +#. TRANSLATORS: Title: is UEFI db up-to-date +msgid "UEFI db" +msgstr "قاعدة بيانات UEFI" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "أداة UEFI dbx المساعدة" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "لا يمكن تحديث مشغّل UEFI في نمط BIOS القديم" + +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI memory protection" +msgstr "حماية ذاكرة UEFI" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled and locked" +msgstr "حماية ذاكرة UEFI مُفَعَّلة ومقفولة" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled but not locked" +msgstr "حماية ذاكرة UEFI مُفَعَّلة ولكنها غير مقفولة" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now locked" +msgstr "حماية ذاكرة UEFI مقفولة الآن" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now unlocked" +msgstr "حماية ذاكرة UEFI مفتوحة الآن" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "مفتاح منصة UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "إقلاع UEFI الآمن" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "تعذّر الاتصال بالخدمة" + +#. TRANSLATORS: error message +msgid "Unable to find attribute" +msgstr "تعذّر العثور على السمة" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "فكّ ربط المشغّل الحالي" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "جارِ إلغاء حظر المشغّل:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "ارفع الحظر عن مشغّل معيّن من التثبيت" + +#. TRANSLATORS: command description +msgid "Undo the host security attribute fix" +msgstr "تراجع عن إصلاح سمة أمن المُضيف" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "غير مُعَمَّى" + +#. TRANSLATORS: command description +msgid "Uninhibit the system to allow upgrades" +msgstr "ارفع الكبح عن النظام للسماح بالترقيات" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Unknown" +msgstr "غير معروف" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "جهاز غير معروف" msgid "Unlock the device to allow access" msgstr "الغ قفل الجهاز للسماح بالوصول" +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "مفتوح" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "افتح قفل الجهاز لوصول المشغّل" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "تزيل ضم ESP" + +#. TRANSLATORS: message shown after device has been marked for emulation +msgid "Unplug and replug the device to continue the update process." +msgstr "افصل الجهاز ثم أعد توصيله لمواصلة عملية التحديث." + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "حمولة غير موقّعة" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "نسخة daemon %s غير مدعومة، نسخة العميل هي %s" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "غير ملوث" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "قابل للتحديث" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "خطأ التحديث" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "صورة التحديث" + +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "رسالة التحديث" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +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 هذا للمزيد من المعلومات:" + +#. TRANSLATORS: ask if we can update metadata +msgid "Update now?" +msgstr "هل تحدّث الآن؟" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "حدّث التجزئة التشفيرية المخزّنة بمحتويات ذاكرة القراءة فقط الحالية" + msgid "Update the stored device verification information" msgstr "حدّث معلومات التحقق من الجهاز المخزن" +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "حدّث المعلومات الوصفيّة المخزّنة بالمحتويات الحالية" + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "حدّث كل الأجهزة المحدّدة إلى أحدث نسخة مشغّل، أو كل الأجهزة إن لم تُحدّد" + +msgid "Updating" +msgstr "جارِ التحديث" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "جارِ تحديث %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "أرقّي %s من %s إلى %s؟" + +#. TRANSLATORS: ask the user to upload +msgid "Upload data now?" +msgstr "أرفع البيانات الآن؟" + +#. TRANSLATORS: command description +msgid "Upload the list of updatable devices to a remote server" +msgstr "ارفع قائمة الأجهزة القابلة للتحديث إلى خادوم طرفي" + +#. TRANSLATORS: ask the user to share, %s is something +#. * like: "Linux Vendor Firmware Service" +#, c-format +msgid "Upload these anonymous results to the %s to help other users?" +msgstr "هل ترفع هذه النتائج المجهولة إلى %s لمساعدة مستخدِمين آخرين؟" + +#. TRANSLATORS: explain why we want to upload +#, c-format +msgid "Uploading a device list allows the %s team to know what hardware exists, and allows us to put pressure on vendors that do not upload firmware updates for their hardware." +msgstr "رفع قائمة الأجهزة يتيح لفريق %s معرفة الأجهزة الصلبة الموجودة، ويتيح لنا الضغط على الباعة الذين لا يرفعون تحديثات مشغّل لأجهزتهم الصلبة." + +#. TRANSLATORS: explain why we want to upload +msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." +msgstr "رفع تقارير المشغّل يساعد بائعي العتاد على تحديد التحديثات الفاشلة والناجحة على الأجهزة الحقيقية بسرعة." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "الإلحاح" + +#. TRANSLATORS: explain how to get help, %1 is +#. * 'fwupdtool --help' +#. TRANSLATORS: explain how to get help, +#. * where $1 is something like 'fwupdmgr --help' +#, c-format +msgid "Use %s for help" +msgstr "استخدِم %s للمساعدة" + +#. TRANSLATORS: CTRL^C [holding control, and then pressing C] will exit the +#. program +msgid "Use CTRL^C to cancel." +msgstr "استخدِم CTRL^C للإلغاء." + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "أُشعِرَ المستخدم" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "اسم المستخدم" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "VERSION1 VERSION2 [FORMAT]" +msgstr "نُسْخَة1 نُسْخَة2 [صِيغَة]" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "صالح" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "جارٍ التحقق من صحة محتويات ESP…" + +#. TRANSLATORS: one line variant of release (e.g. 'China') +msgid "Variant" +msgstr "المتغير" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "البائع" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "جارٍ التحقق…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "النُسخة" + +#. TRANSLATORS: the fwupd version the release was tested on +msgid "Version[fwupd]" +msgstr "إصدار[fwupd]" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING" +msgstr "تحذير" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "WORD" +msgstr "كَلِمَة" + +#. TRANSLATORS: command description +msgid "Wait for a device to appear" +msgstr "انتظر لظهور جهاز" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "ينتظر…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "راقب تغييرات العتاد" + +#. TRANSLATORS: check various UEFI and ACPI tables are unchanged after the +#. update +msgid "Will measure elements of system integrity around an update" +msgstr "سيقيس عناصر سلامة النظام حول التحديث" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "جارِ كتابة الملف:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "جارِ الكتابة…" + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from a recovery or installation disk, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "اضمن ارتياحك لاستعادة الإعداد من قرص استعادة أو تثبيت، لأن هذا التغيير قد يمنع النظام من الإقلاع إلى لينكس أو يسبّب عدم استقرار آخر للنظام." + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from the system firmware setup, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "اضمن ارتياحك لاستعادة الإعداد من إعداد مشغّل النظام، لأن هذا التغيير قد يمنع النظام من الإقلاع إلى لينكس أو يسبّب عدم استقرار آخر للنظام." + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "ربما لم يتحقق موزّعك من أيّ من تحديثات المشغلات للتأكد من توافقها مع نظامك أو الأجهزة المتصلة." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "قد يتلف عتادك باستخدام هذا المشغّل، وقد يبطل تثبيت هذا الإصدار أي ضمان مع %s." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "نظامك مضبوط على BKC %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[APPSTREAM_ID]" +msgstr "[مُعرّف_APPSTREAM]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[مجموع_اختباري]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[مُعرّف-الجهاز|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[مُعرّف-الجهاز|GUID] [فَرْع]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[معرّف_الجهاز|GUID] [الإصدار]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE]" +msgstr "[جَهَاز]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[ملف توقيع_الملف معرّف_الطرف]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[اسم_الملف1] [اسم_الملف2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FWUPD-VERSION]" +msgstr "[نُسْخَةُ-FWUPD]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[REASON] [TIMEOUT]" +msgstr "[السبب] [مهلة]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SECTION] KEY VALUE" +msgstr "[قِسْم] مِفْتاح قِيمَة" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "[إعداد1] [إعداد2] [--بدون-استيثاق]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2]..." +msgstr "[إِعْدَادٌ1] [إِعْدَادٌ2]..." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[مِلَفّ-SMBIOS|مِلَفّ-HWIDS]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "مبدئي" + #. TRANSLATORS: program name msgid "fwupd TPM event log utility" msgstr "الأداة المساعدة لسجل أحداث Fwupd TPM" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "ملحقات fwupd" diff -Nru fwupd-2.0.8/po/ast.po fwupd-2.0.20/po/ast.po --- fwupd-2.0.8/po/ast.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/ast.po 2026-02-26 11:36:18.000000000 +0000 @@ -8,12 +8,15 @@ msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Asturian (http://www.transifex.com/freedesktop/fwupd/language/ast/)\n" +"PO-Revision-Date: 2025-10-24 09:53+0000\n" +"Last-Translator: Anonymous \n" +"Language-Team: Asturian \n" +"Language: ast\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ast\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: this is when a device is hotplugged msgid "Added" diff -Nru fwupd-2.0.8/po/bg.po fwupd-2.0.20/po/bg.po --- fwupd-2.0.8/po/bg.po 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/po/bg.po 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,3442 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# +# Translators: +# twlvnn kraftwerk , 2025. +# Richard Hughes , 2025. +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"PO-Revision-Date: 2025-10-24 10:45+0000\n" +"Last-Translator: twlvnn kraftwerk \n" +"Language-Team: Bulgarian \n" +"Language: bg\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "остава %.0f минута" +msgstr[1] "остават %.0f минути" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "Обновление на контролера за управление (BMC) за %s" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "Обновление на батерия за %s" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system boot-up +#, c-format +msgid "%s CPU Microcode Update" +msgstr "Обновление на микрокод на ЦП за %s" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "Обновление на камера за %s" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "Обновление на настройки за %s" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "Обновление на модул за управление (ME) за потребители за %s" + +#. 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" + +#. 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 "Обновление на модул за управление (ME) за корпорации за %s" + +#. 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" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "Обновление на видео драйверите за %s" + +#. TRANSLATORS: Dock refers to the port replicator hardware laptops are +#. * cradled in, or lowered onto +#, c-format +msgid "%s Dock Update" +msgstr "Обновление на докинг станция %s" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "Обновление на устройство %s" + +#. 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" + +#. TRANSLATORS: a device that can read your fingerprint pattern +#, c-format +msgid "%s Fingerprint Reader Update" +msgstr "Обновление на четеца за пръстови отпечатъци %s" + +#. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC +#, c-format +msgid "%s Flash Drive Update" +msgstr "Обновление на устройство за флаш %s" + +#. TRANSLATORS: GPU refers to a Graphics Processing Unit, e.g. +#. * the "video card" +#, c-format +msgid "%s GPU Update" +msgstr "Обновление на GPU %s" + +#. TRANSLATORS: a large pressure-sensitive drawing area typically used +#. * by artists and digital artists +#, c-format +msgid "%s Graphics Tablet Update" +msgstr "Обновление на графичен таблет за %s" + +#. TRANSLATORS: two miniature speakers attached to your ears +#, c-format +msgid "%s Headphones Update" +msgstr "Обновление на слушалките за %s" + +#. TRANSLATORS: headphones with an integrated microphone +#, c-format +msgid "%s Headset Update" +msgstr "Обновление на слушалките с микрофон за %s" + +#. TRANSLATORS: an input device used by gamers, e.g. a joystick +#, c-format +msgid "%s Input Controller Update" +msgstr "Обновление на контролер за вход за %s" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "Обновление на клавиатура за %s" + +#. 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 "Обновление на модул за управление (ME) за %s" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "Обновление на мишка за %s" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "Обновление на мрежова карта на %s" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "Обновление на SSD %s" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "Обновление на контролер за съхранение на %s" + +#. 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" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "Обновление на доверен модул (TPM) за %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: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "Обновление на сензорен панел за %s" + +#. TRANSLATORS: Dock refers to the port replicator device connected +#. * by plugging in a USB cable -- which may or may not also provide power +#, c-format +msgid "%s USB Dock Update" +msgstr "Обновление на докинг станция по USB %s" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "Обновление на приемник по USB „%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 +#. -- +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Update" +msgstr "Обновление %s" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "%s и всички свързани устройства може да не могат да се използват по време на обновяването." + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s се появи: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s в променен: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s изчезна: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s в момента не може да се обновява" + +#. TRANSLATORS: %1 is a device name, e.g. "ThinkPad Universal +#. * ThunderBolt 4 Dock" and %2 is "fwupdmgr activate" +#, c-format +msgid "%s is pending activation; use %s to complete the update." +msgstr "%s чака да бъде активиран; използвайте %s, за да завършите обновлението." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s режим на производство" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "%s трябва да остане свързан по време на обновяването, за да няма повреда." + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "%s трябва да остане включен в тока по време на обновяването, за да няма повреда." + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "Принудително включване на %s" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s версия %s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s версия" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u ден" +msgstr[1] "%u дена" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u устройство има налично обновление на фърмуера." +msgstr[1] "%u устройства имат налични обновления на фърмуера." + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u устройство не е с най-добрите известни настройки." +msgstr[1] "%u устройства не са с най-известните настройки." + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u час" +msgstr[1] "%u часа" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u минута" +msgstr[1] "%u минути" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u секунда" +msgstr[1] "%u секунди" + +#. TRANSLATORS: device tests can be specific to a CPU type +#, c-format +msgid "%u test was skipped" +msgid_plural "%u tests were skipped" +msgstr[0] "%u тест е пропуснат" +msgstr[1] "%u теста са пропуснати" + +#. TRANSLATORS: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (праг %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(остарял)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "TPM PCR вече е с невалидна стойност" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "AMD защита на фърмуера от запис" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "ARCHIVE FIRMWARE METAINFO [FIRMWARE] [METAINFO] [JCATFILE]" +msgstr "АРХИВ ФЪРМУЕР МЕТАИНФОРМАЦИЯ [ФЪРМУЕР] [МЕТАИНФОРМАЦИЯ] [JCAT-ФАЙЛ]" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "Необходимо е действие:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "Включване на устройства" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "Включване на чакащите устройства" + +msgid "Activate the new firmware on the device" +msgstr "Включване на новия фърмуер на устройството" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "Включване на обновлението на фърмуера" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "Включване на обновлението на фърмуера за" + +#. TRANSLATORS: command description +msgid "Adds devices to watch for future emulation" +msgstr "Добавяне на устройства за наблюдаване за бъдещо емулиране" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "Възраст" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "Съгласяване и включване на отдалечения сървър?" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#, c-format +msgid "Alias to %s" +msgstr "Псевдоним на %s" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "Всички TPM PCR сега са валидни" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "Всички TPM PCR са валидни" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "Всички устройства от един и същи тип ще бъдат обновени едновременно" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "Позволяване на понижаване на версиите на фърмуер" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "Позволяване на преинсталиране на съществуващи версии на фърмуер" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "Позволяване на сменяне на потока на фърмуер" + +#. TRANSLATORS: error message +msgid "Already exists, and no --force specified" +msgstr "Вече съществува и не е посочено —force" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "Алтернативен поток" + +#. TRANSLATORS: another application is updating the device already +msgid "An update is in progress" +msgstr "Обновяване е в ход" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "Необходимо е рестартиране, за да завърши обновлението." + +#. TRANSLATORS: explain why +msgid "An update requires the system to shutdown to complete." +msgstr "Необходимо е изключване на системата, за да завърши обновлението." + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "Отговаряне с „да“ на всички въпроси" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "Прилагане на обновлението, дори когато не е препоръчано" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "Прилагане на файловете за обновление" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "Прилагане на обновлението…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "Одобрен фърмуер:" +msgstr[1] "Одобрен фърмуер:" + +#. TRANSLATORS: command description +msgid "Asks the daemon to quit" +msgstr "Попитване на демона да спре" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "Закачване в режим на фърмуер" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "Удостоверяване…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "Необходими са данни за идентифициране" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on a removable device" +msgstr "За понижаване на фърмуера на преносимо устройство е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "За понижаване на фърмуера на тази машина е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to enable emulation data collection" +msgstr "За включване на събирането на данни за емулация е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to fix a host security issue" +msgstr "За отстраняване на проблем със сигурността на хоста е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to load hardware emulation data" +msgstr "За зареждане на данни за хардуерна емулация е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify BIOS settings" +msgstr "За променяне на BIOS настройките е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify a configured remote used for firmware updates" +msgstr "За променяне на настроен отдалечен сървър за обновяване на фърмуер е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify daemon configuration" +msgstr "За променяне на настройките на демон е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to read BIOS settings" +msgstr "За четене на BIOS настройките е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to reset daemon configuration to defaults" +msgstr "За връщане на стандартните настройките на демон е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to save hardware emulation data" +msgstr "За запазване на данни за хардуерна емулация е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to set the list of approved firmware" +msgstr "За задаване на списъка с одобрен фърмуер е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to sign data using the client certificate" +msgstr "За подписване на данни с клиентския сертификат е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to stop the firmware update service" +msgstr "За спиране на услугата за обновяване на фърмуера е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to switch to the new firmware version" +msgstr "За сменяне към новата версия на фърмуера е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to undo the fix for a host security issue" +msgstr "За отменяне на проблем със сигурността на хоста е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to unlock a device" +msgstr "За отключване на устройство е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on a removable device" +msgstr "За обновяване на фърмуера на преносимо устройство е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the firmware on this machine" +msgstr "За обновяване на фърмуера на тази машина е необходима идентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to update the stored checksums for the device" +msgstr "За обновяване на съхранените контролни суми за устройството е необходима идентификация" + +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "Автоматично докладване" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "Автоматично качване всеки път?" + +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +msgid "BIOS Firmware Updates" +msgstr "Обновления на фърмуера на BIOS" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "BIOS Rollback Protection" +msgstr "Защита от връщане на BIOS" + +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +msgid "BIOS firmware updates" +msgstr "Обновления на фърмуера на BIOS" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "Обновления на BIOS, предоставяни чрез LVFS или Windows Update" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "XML-ФАЙЛ-ЗА-ИЗГРАЖДАНЕ ЦЕЛ-ИМЕ-НА-ФАЙЛ" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "Батерия" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "Свързване на нов драйвер на ядро" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "Блокирани файлове с фърмуер:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "Блокирана версия" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "Блокиране на фърмуера:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "Блокиране на инсталирането на конкретен фърмуер" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "Версия на първоначалната зареждаща програма" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree +msgid "Branch" +msgstr "Клон" + +#. TRANSLATORS: command description +msgid "Build a cabinet archive from a firmware blob and XML metadata" +msgstr "Изграждане на архив на cabinet от blob на фърмуер и XML метаданни" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "Изграждане на файл с фърмуер" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * Utilized by OS means the distribution enabled it +msgid "CET OS Support" +msgstr "Поддръжка на CET OS" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "CET Platform" +msgstr "Платформа CET" + +#. TRANSLATORS: longer description +msgid "CPU Microcode must be updated to mitigate against various information-disclosure security issues." +msgstr "Микрокодът на ЦП трябва да бъде обновен, за да се намалят проблемите със сигурността, свързани с разкриването на информация." + +#. TRANSLATORS: we can save all device enumeration events for emulation +msgid "Can tag for emulation" +msgstr "Може да бъде маркирано за емулация" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "Отказване" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +#. TRANSLATORS: this is from ctrl+c +msgid "Cancelled" +msgstr "Отменено" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "Обновлението на dbx не може да бъде приложено, защото вече е приложено." + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "Променено" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "Проверяване на съответствието на криптографския хеш с фърмуера" + +#. TRANSLATORS: hash to that exact firmware archive +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "Контролна сума" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose branch" +msgstr "Изберете поток" + +#. TRANSLATORS: get interactive prompt +msgid "Choose device" +msgstr "Изберете устройство" + +#. TRANSLATORS: get interactive prompt +msgid "Choose firmware" +msgstr "Изберете фърмуер" + +#. TRANSLATORS: get interactive prompt +msgid "Choose release" +msgstr "Изберете издание" + +#. TRANSLATORS: get interactive prompt +msgid "Choose volume" +msgstr "Изберете том" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "Изчистване на резултатите от последното обновление" + +#. TRANSLATORS: error message +msgid "Command not found" +msgstr "Командата не е намерена" + +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "Подкрепянo от общността" + +#. TRANSLATORS: command description +msgid "Compares two versions for equality" +msgstr "Сравняване на две версии за еднаквост" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +msgid "Configuration Change Suggested" +msgstr "Предложена е промяна на настройките" + +#. TRANSLATORS: no peeking +msgid "Configuration is only readable by the system administrator" +msgstr "Настройките са достъпни за четене само от системния администратор" + +#. TRANSLATORS: longer description +msgid "Control-Flow Enforcement Technology detects and prevents certain methods for running malicious software on the device." +msgstr "Технологията за налагане на контрол на потока открива и предотвратява определени методи за изпълняване на злонамерен софтуер на устройството." + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Control-flow Enforcement Technology" +msgstr "Технология за налагане на контрол на потока (CET)" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "Преобразуване на файл с фърмуер" + +#. TRANSLATORS: command description +msgid "Create an EFI boot entry" +msgstr "Създаване на EFI запис за зареждане" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "Дата на създаване" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "Критично" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "Налична е криптографска проверка на хеша" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "Текуща стойност" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "Текуща версия" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "ИД-УСТРОЙСТВО|ГЛ-УН-ИД" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "Опции за отстраняване на грешки" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "Декомпресиране…" + +#. TRANSLATORS: command description +msgid "Delete an EFI boot entry" +msgstr "Изтриване на EFI запис за зареждане" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "Описание" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "Откачване в режим на първоначална зареждаща програма" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "Подробности" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works +#. * well together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "Отклоняване от най-добрите известни настройки?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "Флагове на устройството" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "Ид. на устройството" + +#. TRANSLATORS: description of the device requests +msgid "Device Requests" +msgstr "Заявки на устройството" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "Устройството е добавено:" + +#. TRANSLATORS: the device is already connected +msgid "Device already exists" +msgstr "Устройството вече съществува" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "Твърде нисък заряд на батерия на устройство" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "Твърде нисък заряд на батерия на устройство (%u%%, трябва %u%%)" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be updated while the lid is closed" +msgstr "Устройството не може да се използва, когато капакът е затворен" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "Устройството е променено:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "За фърмуера на устройството е необходима проверка на версията" + +#. TRANSLATORS: emulated means we are pretending to be a different model +msgid "Device is emulated" +msgstr "Устройството е емулирано" + +#. TRANSLATORS: device cannot be interrupted, for instance taking a phone call +msgid "Device is in use" +msgstr "Устройството се използва в момента" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "Устройството е заключено" + +#. TRANSLATORS: we have two ways of communicating with the device, so we hide +#. one +msgid "Device is lower priority than an equivalent device" +msgstr "Устройството е с по-нисък приоритет от еквивалентно устройство" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "За устройството е необходимо да бъдат инсталирани всички предоставени издания" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "Устройството е недостъпно" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "Устройството е недостъпно или е извън обхват на безжичната мрежа" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "Устройството не може да се използва за времето на обновлението" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "Устройството изчаква за прилагането на обновлението" + +#. TRANSLATORS: success, so say thank you to the user +msgid "Device list uploaded successfully, thanks!" +msgstr "Списъкът с устройства е успешно качен, благодаря!" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "Устройството е премахнато:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "Устройството изисква захранване от мрежата" + +#. TRANSLATORS: device does not have a display connected +msgid "Device requires a display to be plugged in" +msgstr "За устройството е необходимо да бъде свързан екран" + +#. TRANSLATORS: The device cannot be updated due to missing vendor's license." +msgid "Device requires a software license to update" +msgstr "За устройството е необходим лиценз за софтуер, за да бъде обновен" + +#. TRANSLATORS: longer description +msgid "Device software updates are provided for this device." +msgstr "За това устройство се предоставят обновления на софтуера на устройството." + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "Устройството поддържа превключване към различен поток от фърмуер" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "Обновлението на устройството се нуждае от активиране" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "Устройството ще направи резервно копие на фърмуера преди инсталиране" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "Устройството няма да се появи отново след завършване на обновлението" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "Устройства, които са обновени правилно:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "Устройства, които не са обновени правилно:" + +#. TRANSLATORS: message letting the user there is an update +#. * waiting, but there is a reason it cannot be deployed +msgid "Devices with firmware updates that need user action: " +msgstr "Устройства с обновления на фърмуера, които изискват потребителска намеса: " + +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no +#. * device upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no +#. * device upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "Устройства без налични обновления на фърмуера: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device +#. * upgrade available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device +#. * upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "Устройства с най-новата налична версия на фърмуера:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "Няма намерени устройства със съвпадащи глобално уникални идентификатори" + +#. TRANSLATORS: Plugin is inactive and not used +#. TRANSLATORS: Suffix: the HSI result +msgid "Disabled" +msgstr "Изключено" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "Изключване на даден отдалечен сървър" + +#. TRANSLATORS: command description +msgid "Disables virtual testing devices" +msgstr "Изключване на виртуални устройства за изпробване" + +#. TRANSLATORS: the OS the release was tested on +msgid "Distribution" +msgstr "Дистрибуция" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "Без проверяване за стари метаданни" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "Без проверяване за неподадени доклади в историята" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "Без проверяване дали отдалечените сървъри за изтегляне трябва да бъдат включени" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "Без проверяване или запитване за рестартиране след обновяване" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "Без включване на журнала за представката на домейна" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "Без включване на представката на датата" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "Без изпълняване на проверки за сигурността на устройството" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "Без запитване за устройства" + +#. TRANSLATORS: command line option +msgid "Do not prompt to fix security issues" +msgstr "Без запитване за отстраняване на проблеми със сигурността" + +#. TRANSLATORS: command line option +msgid "Do not search the firmware when parsing" +msgstr "Без претърсване на фърмуера при анализиране" + +#. TRANSLATORS: warning message shown after update has been scheduled +msgid "Do not turn off your computer or remove the AC adaptor while the update is in progress." +msgstr "Не изключвайте компютъра си и не изваждайте кабела на адаптера за електрическата мрежа по време на обновяването." + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "Без записване в базата от данни на историята" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "Разбирате ли последствията от промяната на потока на фърмуера?" + +#. TRANSLATORS: ask the user if it's okay to convert, +#. * "it" being the data contained in the EFI boot entry +msgid "Do you want to convert it now?" +msgstr "Искате ли да го преобразувате сега?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "Искате ли да изключите тази функция за бъдещи обновления?" + +#. TRANSLATORS: ask if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "Искате ли да опресните този отдалечен сървър сега?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "Искате ли автоматично да качвате доклади за бъдещи обновления?" + +#. TRANSLATORS: command line option +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "Без запитване за удостоверяване (може да се покаже по-малко информация)" + +#. TRANSLATORS: success +msgid "Done!" +msgstr "Готово!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "Понижаване на %s от %s до %s?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "Понижаване на фърмуера на устройство" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "Понижаване на %s…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "Изтегляне на файл" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "Изтегляне…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "Извличане на SMBIOS данни от файл" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "Продължителност" + +#. TRANSLATORS: longer description +msgid "Each system should have tests to ensure firmware security." +msgstr "Всяка система трябва да има тестове, за да се гарантира сигурността на фърмуера." + +#. TRANSLATORS: command description +msgid "Emulate a device using a JSON manifest" +msgstr "Емулиране на устройство с JSON манифест" + +#. TRANSLATORS: this device is not actually real +msgid "Emulated" +msgstr "Емулирано" + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "Емулиран хост" + +msgid "Enable" +msgstr "Включване" + +msgid "Enable emulation data collection" +msgstr "Включване на събирането на данни за емулация" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "Включване на новия отдалечен сървър?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "Включване на този отдалечен сървър?" + +#. TRANSLATORS: if the remote is enabled +#. TRANSLATORS: Suffix: the HSI result +msgid "Enabled" +msgstr "Включено" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "Включено, ако хардуерът съвпада" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "Включване на даден отдалечен сървър" + +#. TRANSLATORS: command description +msgid "Enables virtual testing devices" +msgstr "Включване на виртуални устройства за изпробване" + +#. TRANSLATORS: longer description +msgid "Enabling firmware updates for the BIOS allows fixing security issues." +msgstr "Включването на обновленията на BIOS на фърмуера позволява отстраняването на проблеми със сигурността." + +msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." +msgstr "Включването на тази функционалност се извършва на собствен риск, което означава, че трябва да се свържете с производителя на оригиналното си оборудване относно евентуални проблеми, причинени от тези обновления. Само проблеми със самия процес на обновяването трябва да се подават на адрес $OS_RELEASE:BUG_REPORT_URL$." + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "Включването на този отдалечен сървър се извършва на ваш собствен риск." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "Шифриран" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "Шифриран RAM" + +msgid "Encrypted RAM makes it impossible for information that is stored in device memory to be read if the memory chip is removed and accessed." +msgstr "Шифрираната RAM памет не позволява информацията, която се съхранява в паметта на устройството, да бъде прочетена, ако чипът на паметта бъде изваден и се получи достъп до него." + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "Не получава обновления" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "Изтриване на цялата история на обновленията на фърмуера" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "Изтриване…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "Изход след кратко изчакване" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "Изход след зареждането на ядрото" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "Изнасяне на файловата структура на фърмуер в XML" + +#. TRANSLATORS: command description +msgid "Export firmware history for manual upload" +msgstr "Изнасяне на историята на фърмуера за качване на ръка" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "Извличане на blob от фърмуер в изображения" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "ФАЙЛ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "ФАЙЛ [ИД-УСТРОЙСТВО|ГЛ-УН-ИД]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME" +msgstr "ИМЕ-НА-ФАЙЛ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "ИМЕ-НА-ФАЙЛ СЕРТИФИКАТ ЧАСТЕН-КЛЮЧ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "ИМЕ-НА-ФАЙЛ ОТСТОЯНИЕ ДАННИ [ВИД-ФЪРМУЕР]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "ИМЕ-НА-ФАЙЛ [ИД-УСТРОЙСТВО|ГЛ-УН-ИД]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "ИМЕ-НА-ФАЙЛ [ВИД-ФЪРМУЕР]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "ИЗТОЧНИК-ИМЕ-НА-ФАЙЛ ЦЕЛ-ИМЕ-НА-ФАЙЛ [ИЗТОЧНИК-ВИД-ФЪРМУЕР] [ЦЕЛ-ВИД-ФЪРМУЕР]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "ИМЕ-НА-ФАЙЛ|КОНТРОЛНА-СУМА1[,КОНТРОЛНА-СУМА2][,КОНТРОЛНА-СУМА3]" + +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "Неуспешно" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "Обновлението не може да бъде приложено" + +#. TRANSLATORS: error message for Windows +msgid "Failed to connect to Windows service, please ensure it's running." +msgstr "Свързването с Windows услугата е неуспешно, уверете се, че тя работи." + +#. TRANSLATORS: could not contact the fwupd service over D-Bus +msgid "Failed to connect to daemon" +msgstr "Свързването с демона е неуспешно" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "Локалните dbx данни не може да бъдат заредени" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "Системните dbx данни не може да бъдат заредени" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "Заключването е неуспешно" + +#. TRANSLATORS: the user didn't read the man page +msgid "Failed to parse arguments" +msgstr "Аргументите не може да бъдат разчетени" + +#. TRANSLATORS: failed to read measurements file +msgid "Failed to parse file" +msgstr "Файлът не може да бъде разчетен" + +#. TRANSLATORS: the user didn't read the man page, %1 is '--filter' +#. TRANSLATORS: the user didn't read the man page, +#. * %1 is '--filter-release' +#. TRANSLATORS: the user didn't read the man page, %1 is '--filter' +#. TRANSLATORS: the user didn't read the man page, +#. * %1 is '--filter-release' +#, c-format +msgid "Failed to parse flags for %s" +msgstr "Флаговете за %s не може да бъдат разчетени" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "Локалните dbx данни не може да бъдат разчетени" + +#. TRANSLATORS: a feature is something like "can show an image" +msgid "Failed to set front-end features" +msgstr "Функциите на предния слой не може да бъдат зададени" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "ESP съдържанието не може да бъде проверено" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "Лъжа" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "Име на файл" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "Име на файл" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "Местоположение на файл" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +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: command line option +msgid "Filter with a set of release flags using a ~ prefix to exclude, e.g. 'trusted-release,~trusted-metadata'" +msgstr "Филтриране с набор от флагове на устройството, като се използва представката ~ за изключване, напр. „trusted-release,~trusted-metadata“" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware Attestation" +msgstr "Атестиране на фърмуера" + +#. TRANSLATORS: longer description +msgid "Firmware Attestation checks device software using a reference copy, to make sure that it has not been changed." +msgstr "Атестирането на фърмуера проверява софтуера на устройството, като използва копие, за да се увери, че не е бил променян." + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware BIOS Descriptor" +msgstr "Дескриптор на BIOS за фърмуера" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Descriptor protects device firmware memory from being tampered with." +msgstr "Фърмуер дескриптор на BIOS предпазва паметта на фърмуера на устройството от подправяне." + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "Регион на BIOS за фърмуера" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Region protects device firmware memory from being tampered with." +msgstr "Регион на BIOS за фърмуера предпазва паметта на фърмуера на устройството от подправяне." + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "Адрес на отдалечения сървър на фърмуера" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "D-Bus услуга за обновяване на фърмуер" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "Демон за обновяване на фърмуер" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "Проверяване на инструмента за обновяване на фърмуера" + +msgid "Firmware Updater Verification checks that software used for updating has not been tampered with." +msgstr "Проверяване на инструмента за обновяване на фърмуера проверява дали софтуерът, използван за обновяване, не е бил подправен." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "Фърмуер обновления" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "Помощна програма за фърмуер" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "Защита на фърмуера от запис" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "Заключване на защитата на фърмуера от запис" + +#. TRANSLATORS: longer description +msgid "Firmware Write Protection protects device firmware memory from being tampered with." +msgstr "Защитата на фърмуера от запис предпазва паметта на фърмуера на устройството от подправяне." + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "Атестиране на фърмуера" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "Фърмуерът вече е блокиран" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "Фърмуерът все още не е блокиран" + +#. TRANSLATORS: the metadata is very out of date; %u is a number > 1 +#, c-format +msgid "Firmware metadata has not been updated for %u day and may not be up to date." +msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." +msgstr[0] "Метаданните на фърмуера не са обновявани от %u ден и може да не са актуални." +msgstr[1] "Метаданните на фърмуера не са обновявани от %u дена и може да не са актуални." + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "Фърмуер обновления" + +#. TRANSLATORS: user needs to run a command, %1 is 'fwupdmgr unlock' +#, c-format +msgid "Firmware updates disabled; run '%s' to enable" +msgstr "Обновлението на фърмуера е изключено; изпълнете „%s“ за включване" + +#. TRANSLATORS: command description +msgid "Fix a specific host security attribute" +msgstr "Поправяне на конкретен атрибут за сигурност на хост" + +#. TRANSLATORS: we've fixed a security problem on the machine +msgid "Fix reverted successfully" +msgstr "Поправката е успешно отменена" + +#. TRANSLATORS: we've fixed a security problem on the machine +msgid "Fixed successfully" +msgstr "Успешно поправено" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "Флагове" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "Принуждаване на действието чрез игнориране на някои проверки на средата за изпълнение" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "Намерено" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "Открито е цялостно шифриране на диска" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "Пълното шифриране на тайните на диска може да бъде нулирано при обновяване" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused Platform" +msgstr "Слята платформа" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "Слята платформа" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "Глобално уникален идентификатор" +msgstr[1] "Глобално уникални идентификатори" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "command-argument" +msgid "GUID" +msgstr "ГЛ-УН-ИД" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "GUID|DEVICE-ID" +msgstr "ГЛ-УН-ИД|ИД-УСТРОЙСТВО" + +msgid "Get BIOS settings" +msgstr "Получаване на BIOS настройките" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "Получаване на всички флагове на устройствата, поддържани от fwupd" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "Получаване на всички устройства, които поддържат обновяване на фърмуера" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "Получаване на всички включени приставки, регистрирани в системата" + +#. TRANSLATORS: command description +msgid "Get all known version formats" +msgstr "Получаване на всички известни формати на версии" + +#. TRANSLATORS: command description +msgid "Get device report metadata" +msgstr "Получаване на метаданните за доклада на устройството" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "Получаване на подробностите за файл с фърмуер" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "Получаване на зададените отдалечени сървъри" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "Получаване на атрибутите за сигурност на хост" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "Получаване на списъка с одобрен фърмуер" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "Получаване на списъка с блокиран фърмуер" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "Получаване на изданията за дадено устройство" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "Получаване на резултатите от последното обновление" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS-ФАЙЛ" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "Хардуерът чака да бъде включен отново" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "Голяма" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "Събития за сигурност на хоста" + +#. TRANSLATORS: error message for unsupported feature +msgid "Host Security ID (HSI) is not supported" +msgstr "Ид. за сигурност на хоста (HSI) не се поддържа" + +#. TRANSLATORS: success, so say thank you to the user +msgid "Host Security ID attributes uploaded successfully, thanks!" +msgstr "Атрибутите на идентификатора за сигурност на хоста са качени успешно, благодаря!" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "Ид. за сигурност на хоста:" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX" +msgstr "УКАЗАТЕЛ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX KEY [VALUE]" +msgstr "УКАЗАТЕЛ КЛЮЧ [СТОЙНОСТ]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX NAME TARGET [MOUNTPOINT]" +msgstr "УКАЗАТЕЛ ИМЕ ЦЕЛ [ТОЧКА-НА-МОНТИРАНЕ]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX1,INDEX2" +msgstr "УКАЗАТЕЛ1,УКАЗАТЕЛ2" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INHIBIT-ID" +msgstr "ПРЕДОТВРАТЯВАНЕ-ИД" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "Защита на IOMMU" + +#. TRANSLATORS: longer description +msgid "IOMMU Protection prevents connected devices from accessing unauthorized parts of system memory." +msgstr "Защита на IOMMU не позволява достъпа на свързани устройства до неодобрени части от системната памет." + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "Изключена е IOMMU защитата на устройството" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "Включена е IOMMU защитата на устройството" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "Бездейства…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "Игнориране на строгите проверки на SSL при изтегляне на файлове" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "Игнориране на грешки при контролната сума на фърмуера" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "Игнориране на грешки за хардуерно несъответствие на фърмуера" + +#. TRANSLATORS: command line option +msgid "Ignore non-critical firmware requirements" +msgstr "Игнориране на некритични изисквания за фърмуера" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "Игнориране на стриктните проверки на SSL, за да правите това автоматично в бъдеще, изнесете DISABLE_SSL_STRICT във вашата среда" + +#. TRANSLATORS: show the user a generic image that can be themed +msgid "Image" +msgstr "Изображение" + +#. TRANSLATORS: show the user a random image from the internet +msgid "Image (custom)" +msgstr "Изображение (друго)" + +#. TRANSLATORS: command description +msgid "Inhibit the system to prevent upgrades" +msgstr "Предотвратяване на системата да спира обновления" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "Време за инсталиране" + +#. TRANSLATORS: command description +msgid "Install a firmware file in cabinet format on this hardware" +msgstr "Инсталиране на файл с фърмуер във формат cabinet на този хардуер" + +#. TRANSLATORS: command description +msgid "Install a raw firmware blob on a device" +msgstr "Инсталиране на необработен фърмуер blob на устройство" + +#. TRANSLATORS: command description +msgid "Install a specific firmware file on all devices that match" +msgstr "Инсталиране на конкретен файл с фърмуер на всички устройства, които съвпадат" + +#. TRANSLATORS: command description +msgid "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" +msgstr "Инсталиране на конкретен фърмуер на устройство, като всички възможни устройства също ще бъдат инсталирани, след като CAB съвпадне" + +msgid "Install old version of signed system firmware" +msgstr "Инсталиране на стара версия на подписан системен фърмуер" + +msgid "Install old version of unsigned system firmware" +msgstr "Инсталиране на стара версия на неподписан системен фърмуер" + +msgid "Install signed device firmware" +msgstr "Инсталиране на подписан фърмуер на устройство" + +msgid "Install signed system firmware" +msgstr "Инсталиране на подписан системен фърмуер" + +msgid "Install unsigned device firmware" +msgstr "Инсталиране на неподписан фърмуер на устройство" + +msgid "Install unsigned system firmware" +msgstr "Инсталиране на неподписан системен фърмуер" + +#. TRANSLATORS: stay on one firmware version unless the new version is +#. explicitly +#. * specified +msgid "Installing a specific release is explicitly required" +msgstr "Необходимо е изрично инсталиране на конкретна версия" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +msgid "Installing firmware update…" +msgstr "Инсталиране на обновлението на фърмуера…" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "Инсталиране в %s…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "Инсталирането на това обновление може да доведе до отпадане на гаранцията на устройството." + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "Цяло число" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM Protected" +msgstr "Intel BootGuard защитен ACM" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard защитен ACM" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard Error Policy" +msgstr "Политика за грешки на Intel BootGuard" + +msgid "Intel BootGuard Error Policy ensures the device does not continue to start if its device software has been tampered with." +msgstr "Политиката за грешки на Intel BootGuard гарантира, че устройството няма да продължи да се стартира, ако софтуерът на устройството е бил подправен." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard Fuse" +msgstr "Intel BootGuard Fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard Verified Boot" +msgstr "Потвърдено зареждане с Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Политика за грешки на Intel BootGuard" + +#. TRANSLATORS: longer description +msgid "Intel BootGuard prevents unauthorized device software from operating when the device is started." +msgstr "Intel BootGuard не позволява софтуер от неодобрени устройства да работи при стартирането им." + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Потвърдено зареждане с Intel BootGuard" + +#. TRANSLATORS: Title: GDS is where the CPU leaks information +msgid "Intel GDS Mitigation" +msgstr "Intel GDS заобикаляне" + +#. TRANSLATORS: Title: GDS is where the CPU leaks information +msgid "Intel GDS mitigation" +msgstr "Intel GDS заобикаляне" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Manufacturing Mode" +msgstr "Режим на производство на модула за управление на Intel" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is +#. enabled +#. * with a jumper -- luckily it is probably not accessible to end users on +#. consumer +#. * boards +msgid "Intel Management Engine Override" +msgstr "Принудително включване на модула за управление на Intel" + +#. TRANSLATORS: longer description +msgid "Intel Management Engine Override disables checks for device software tampering." +msgstr "Принудително включване на модула за управление на Intel изключва проверките за подправяне на устройството." + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Version" +msgstr "Версия на модула за управление на Intel" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "Вътрешно устройство" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "Невалиден" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "Невалидни аргументи" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected GUID" +msgstr "Невалидни аргументи, очакваше се ГЛ-УН-ИД" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected INDEX KEY [VALUE]" +msgstr "Невалидни аргументи, очакваше се УКАЗАТЕЛ КЛЮЧ [СТОЙНОСТ]" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected INDEX NAME TARGET [MOUNTPOINT]" +msgstr "Невалидни аргументи, очакваше се УКАЗАТЕЛ ИМЕ ЦЕЛ [ТОЧКА-НА-МОНТИРАНЕ]" + +#. TRANSLATOR: This is the error message for +#. * incorrect parameter +msgid "Invalid arguments, expected an AppStream ID" +msgstr "Невалидни аргументи, очакваше се ид. на AppStream" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected at least ARCHIVE FIRMWARE METAINFO" +msgstr "Невалидни аргументи, очакваше се поне АРХИВ ФЪРМУЕР МЕТАИНФОРМАЦИЯ" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected base-16 integer" +msgstr "Невалидни аргументи, очаква се цяло число в шестнадесетична бройна система" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "Понижение" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "В режим на първоначална зареждаща програма" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "Надграждане" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "Проблем" +msgstr[1] "Проблеми" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "Ядрото вече не е замърсено" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "Ядрото е замърсено" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "Заключването на Linux ядрото е изключено" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "Заключването на Linux ядрото е включено" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "МЕСТОПОЛОЖЕНИЕ" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "Последна промяна" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "Остава по-малко от минута" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "Лицензия" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "Заключване на Linux ядрото" + +#. TRANSLATORS: longer description +msgid "Linux Kernel Lockdown mode prevents administrator (root) accounts from accessing and changing critical parts of system software." +msgstr "Режимът на заключване на Linux ядрото предотвратява достъпа на администратори (root) до критични части от системния софтуер и тяхната промяна." + +msgid "Linux Kernel Swap temporarily saves information to disk as you work. If the information is not protected, it could be accessed by someone if they obtained the disk." +msgstr "Пейджинг паметта на Linux ядрото временно запазва информация на диска, докато работите. Ако информацията не е защитена, някой може да получи достъп до нея, ако има достъп до диска." + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "Потвърждение на Linux ядрото" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "Linux пейджинг" + +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "Услуга за фърмуера на производителя на Linux (стабилен фърмуер)" + +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "Услуга за фърмуера на производителя на Linux (фърмуер за изпробване)" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux ядро" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Заключване на Linux ядрото" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux пейджинг" + +#. TRANSLATORS: command description +msgid "List EFI boot files" +msgstr "Изброяване на EFI файловете за зареждане" + +#. TRANSLATORS: command description +msgid "List EFI boot parameters" +msgstr "Изброяване на параметрите за зареждане на EFI" + +#. TRANSLATORS: command description +msgid "List EFI variables with a specific GUID" +msgstr "Изброяване на EFI променливи с конкретен глобально уникален идентификатор" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "Изброяване на записите в dbx" + +#. TRANSLATORS: command description +msgid "List the available firmware GTypes" +msgstr "Изброяване на наличния GTypes фърмуер" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "Изброяване на наличните видове фърмуер" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "Изброяване на файловете в ESP" + +#. TRANSLATORS: command description +msgid "Load device emulation data" +msgstr "Зареждане на данните за емулация на устройство" + +#. TRANSLATORS: the plugin was created from a .so object, and was not built-in +msgid "Loaded from an external module" +msgstr "Зареден от външен модул" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "Зареждане…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "Заключен" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "Ниска" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and key refers +#. * to the private/public key used to secure loading of firmware +msgid "MEI Key Manifest" +msgstr "Манифест на ключа на MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and key refer +#. * to the private/public key used to secure loading of firmware +msgid "MEI key manifest" +msgstr "Манифест на ключа на MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "Режим на производство на MEI" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "Принудително включване на MEI" + +msgid "MEI version" +msgstr "MEI версия" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "Разрешаване на конкретни приставки на ръка" + +#. TRANSLATORS: longer description +msgid "Manufacturing Mode is used when the device is manufactured and security features are not yet enabled." +msgstr "Режим на производство се използва, когато устройството е произведено и функциите за сигурност все още не са включени." + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "Максимална дължина" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "Максимална стойност" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "Средна" + +#. TRANSLATORS: ask the user to do a simple task which should be translated +msgid "Message" +msgstr "Съобщение" + +#. TRANSLATORS: ask the user a question, and it will not be translated +msgid "Message (custom)" +msgstr "Съобщение (друго)" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "Подпис на метаданните" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "Адрес на метаданните" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "Метаданните могат да бъдат получени от услугата за фърмуера на производителя на Linux." + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. * refresh recently -- %1 is '--force' +#, c-format +msgid "Metadata is up to date; use %s to refresh again." +msgstr "Метаданните са актуални; използвайте %s, за да ги опресните отново." + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "Минимална версия" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "Минимална дължина" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "Минимална стойност" + +#. TRANSLATORS: sets something in the daemon configuration file +msgid "Modifies a daemon configuration value" +msgstr "Променяне на стойността на настройка на демон" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "Променяне на даден отдалечен сървър" + +msgid "Modify a configured remote" +msgstr "Променяне на настроен отдалечен сървър" + +msgid "Modify daemon configuration" +msgstr "Променяне на настройките на фърмуера" + +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "Наблюдаване на демона за събития" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "Монтиране на ESP" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "Необходимо е рестартиране след инсталиране" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "Необходимо е рестартиране" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "Необходимо е изключване след инсталиране" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "Нова версия" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "Няма зададено действие!" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "Няма понижения за %s" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "Няма намерени идентификатори на фърмуер" + +#. TRANSLATORS: nothing found +msgid "No firmware found" +msgstr "Няма намерен фърмуер" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +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: this is an error string +msgid "No updatable devices" +msgstr "Няма обновяеми устройства" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "Няма налични обновления" + +msgid "No updates available for remaining devices" +msgstr "Няма налични обновления за останалите устройства" + +#. TRANSLATORS: error message +#, c-format +msgid "No volume matched %s" +msgstr "Няма съответстващ том за %s" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "Не е одобрено" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "Не е намерено" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "Неподдържано" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "Добре" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "Добре!" + +#. TRANSLATORS: the firmware old version +msgid "Old version" +msgstr "Стара версия" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "Показване само на една стойност на PCR" + +#. TRANSLATORS: command line option +msgid "Only use peer-to-peer networking when downloading files" +msgstr "При изтегляне на файлове използвайте само равноправна мрежа" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "Само надграждания на версиите са позволеи" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "Предефиниране на стандартното местоположение на ESP" + +#. TRANSLATORS: if we can get metadata from peer-to-peer clients +msgid "P2P Firmware" +msgstr "P2P фърмуер" + +#. TRANSLATORS: if we can get metadata from peer-to-peer clients +msgid "P2P Metadata" +msgstr "P2P метаданни" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "МЕСТОПОЛОЖЕНИЕ" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "Анализиране и показване на подробности за файл с фърмуер" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "Разчитане на dbx обновлението…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "Разчитане на системните dbx данни…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "Парола" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "Поправяне на blob на фърмуер с известно отместване" + +msgid "Payload" +msgstr "Тяло на заявката" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "В опашката" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "Изпълняване на операцията?" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "Отстраняване на грешки в платформата" + +#. TRANSLATORS: longer description +msgid "Platform Debugging allows device security features to be disabled. This should only be used by hardware manufacturers." +msgstr "Отстраняване на грешки в платформата позволява да се изключат функциите за сигурност на устройството. Това трябва да се използва само от производителите на хардуер." + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform debugging" +msgstr "Отстраняване на грешки в платформата" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "Уверете се, че разполагате с ключа за възстановяване на тома, преди да продължите." + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "Въведете число от 0 до %u: " + +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' +#, c-format +msgid "Please enter either %s or %s: " +msgstr "Въведете %s или %s: " + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "Липсват зависимости от приставката" + +#. TRANSLATORS: The plugin is only for testing +msgid "Plugin is only for testing" +msgstr "Приставката е само за изпробване" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "Възможни стойности" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA Protection" +msgstr "Защита на DMA преди зареждане" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "Защита на DMA преди зареждане" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "Защитата на DMA преди зареждане е изключена" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "Защитата на DMA преди зареждане е включена" + +#. TRANSLATORS: longer description +msgid "Pre-boot DMA protection prevents devices from accessing system memory after being connected to the computer." +msgstr "Защитата на DMA преди зареждане предотвратява достъпа на устройства до системната памет, след като са свързани към компютъра." + +#. TRANSLATORS: warning message +msgid "Press unlock on the device to continue the update process." +msgstr "Отключете устройството, за да продължите процеса на обновяване." + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "Предишна версия" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "Приоритет" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "Проблеми" + +msgid "Proceed with upload?" +msgstr "Продължаване с качването?" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "Проверки за сигурността на процесора" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "Собственическо" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "ИД-НА-ОТДАЛЕЧЕН-СЪРВЪР" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "ИД-НА-ОТДАЛЕЧЕН-СЪРВЪР КЛЮЧ СТОЙНОСТ" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "Само за четене" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "Четене на фърмуер blob от устройство" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "Четене на фърмуер от устройство" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "Четене от %s…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "Четене…" + +#. TRANSLATORS: Plugin is active and in use +msgid "Ready" +msgstr "Активен" + +#. TRANSLATORS: how often we should refresh the metadata +msgid "Refresh Interval" +msgstr "Интервал за опресняване" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "Опресняване на метаданните от отдалечен сървър" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "Преинсталиране на %s като %s?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "Преинсталиране на текущия фърмуер на устройството" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "Преинсталиране на фърмуера на устройството" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree +msgid "Release Branch" +msgstr "Поток на издаване" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "Флагове за изданието" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "Ид. на изданието" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "Ид. на отдалечения сървър" + +#. TRANSLATORS: command description +msgid "Removes devices to watch for future emulation" +msgstr "Премахване на устройства за наблюдаване за бъдещо емулиране" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "Адрес за докладване" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "Докладвано на отдалечения сървър" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "Необходимата файлова система efivarfs не е намерена" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "Необходимият хардуер не е намерен" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "Необходима е първоначална зареждаща програма" + +#. TRANSLATORS: metadata is downloaded +msgid "Requires internet connection" +msgstr "Необходима е връзка с Интернет" + +msgid "Reset daemon configuration" +msgstr "Връщане на настройките на демона" + +#. TRANSLATORS: sets something in the daemon configuration file +msgid "Resets a daemon configuration section" +msgstr "Връщане на настройките от раздел на демона" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "Рестартиране сега?" + +#. TRANSLATORS: changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "Рестартиране на демона, за да влезе в сила промяната?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "Рестартиране на устройството…" + +#. TRANSLATORS: command description +msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "Извличане на настройките на BIOS. Ако не са подадени аргументи, се връщат всички настройки" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "Връщане на всички идентификатори на хардуера за машината" + +#. TRANSLATORS: ask the user to upload +msgid "Review and upload report now?" +msgstr "Преглед и качване на доклада сега?" + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr get-upgrades` +#, c-format +msgid "Run `%s` for more information." +msgstr "Изпълнете „%s“ за повече информация." + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr sync` +#, c-format +msgid "Run `%s` to complete this action." +msgstr "Изпълнете „%s“, за да завършите това действие." + +#. TRANSLATORS: command description +msgid "Run the post-reboot cleanup action" +msgstr "Изпълняване на действието за изчистване след рестартиране" + +#. TRANSLATORS: tell a user how to get information +#, c-format +msgid "Run without '%s' to see" +msgstr "Изпълнете без „%s“, за да видите" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "Работещото ядро е твърде старо" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "Наставка на средата за изпълнение" + +#. TRANSLATORS: Software Bill of Materials link +msgid "SBOM" +msgstr "SBOM" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SECTION" +msgstr "РАЗДЕЛ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING VALUE" +msgstr "СТОЙНОСТ-НА-НАСТРОЙКАТА" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "НАСТРОЙКА1 СТОЙНОСТ1 [НАСТРОЙКА2] [СТОЙНОСТ2]" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "SMAP" +msgstr "SMAP" + +#. TRANSLATORS: Title: Whether firmware is locked down +msgid "SMM locked down" +msgstr "Режимът на управление на системата е заключен" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "Дескриптор на SPI BIOS" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI регион на BIOS" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "Блокиране на SPI" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "Записване на SPI" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "ПОДСИСТЕМА ДРАЙВЕР [ИД-УСТРОЙСТВО|ГЛ-УН-ИД]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "Записване на файл, който позволява генериране на идентификатори на хардуер" + +#. TRANSLATORS: command description +msgid "Save device emulation data" +msgstr "Запазване на данните за емулация на устройство" + +#. TRANSLATORS: key for a offline report filename +msgid "Saved report" +msgstr "Докладът е запазен" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +msgid "Scalar Increment" +msgstr "Скаларно увеличение" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "Насрочване…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "Атестираното стартиране е изключено" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "Атестираното стартиране е включено" + +msgid "Security hardening for HSI" +msgstr "Затвърдяване на сигурността на HSI" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "Вижте %s за повече подробности." + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "Вижте %s за повече информация." + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "Избрано устройство" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "Избран том" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "Сериен номер" + +#. TRANSLATORS: Configured a BIOS setting to a value +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "Задаване на настройката „%s“ на BIOS с „%s“." + +#. TRANSLATORS: command description +msgid "Set a BIOS setting" +msgstr "Задаване на BIOS настройка" + +msgid "Set one or more BIOS settings" +msgstr "Задаване на една или повече настройки на BIOS" + +#. TRANSLATORS: command description +msgid "Set or remove an EFI boot hive entry" +msgstr "Задаване или премахване на EFI hive запис за зареждане" + +#. TRANSLATORS: command description +msgid "Set the EFI boot order" +msgstr "Задаване на EFI реда на зареждане" + +#. TRANSLATORS: command line option +msgid "Set the download retries for transient errors" +msgstr "Задаване на опитите за изтегляне за грешки при изпращане" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "Задаване на една или повече настройки на BIOS" + +#. TRANSLATORS: firmware approved by the admin +msgid "Sets the list of approved firmware" +msgstr "Задаване на списъка с одобрен фърмуер" + +#. TRANSLATORS: type of BIOS setting +msgid "Setting type" +msgstr "Вид настройка" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "Настройките ще бъдат приложени след рестартиране на системата" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "Споделяне на историята на фърмуера с разработчиците" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "Показване на всички резултати" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "Показване на версиите на клиента и демона" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "Показване на допълнителна информация за демона за конкретен домейн" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "Показване на информация за отстраняване на грешки за всички домейни" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "Показване на опциите за отстраняване на грешки" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "Показване на устройства, които не могат да бъдат обновени" + +#. TRANSLATORS: command line option +msgid "Show extra debugging information" +msgstr "Показване на допълнителна информация за отстраняване на грешки" + +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "Показване на историята на обновленията на фърмуера" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "Показване на изчислената версия на dbx" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "Изключване сега?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "Подписване на фърмуер с нов ключ" + +msgid "Sign data using the client certificate" +msgstr "Подписване на данни с клиентския сертификат" + +#. TRANSLATORS: command description +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "Подписване на данни чрез клиентския сертификат" + +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "Подписване на качените данни с клиентския сертификат" + +msgid "Signature" +msgstr "Подпис" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "Подписано тяло на заявка" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "Размер" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "Някои от тайните на платформата могат да бъдат анулирани при обновяване на този фърмуер." + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "Изходен код" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "Посочете файла с база от данни dbx" + +msgid "Stop the fwupd service" +msgstr "Спиране на fwupd услугата" + +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "Низ" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "Успешно" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "Устройствата бяха успешно включени" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "Отдалеченият сървър е успешно изключен" + +#. TRANSLATORS: comment explaining result of command +msgid "Successfully disabled test devices" +msgstr "Устройствата за изпробване бяха успешно изключени" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "Включването и опресняването на отдалечения сървър е успешно" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "Отдалеченият сървър е успешно включен" + +#. TRANSLATORS: comment explaining result of command +msgid "Successfully enabled test devices" +msgstr "Устройствата за изпробване бяха успешно включени" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "Фърмуерът е успешно инсталиран" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "Стойността на настройката е успешно променена" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "Отдалеченият сървър е успешно променен" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "Метаданните бяха успешно опреснени на ръка" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully reset configuration section" +msgstr "Разделът с настройките е успешно върнат" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully reset configuration values" +msgstr "Стойностите на настройките бяха успешно върнати" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "Контролните суми на устройството бяха успешно обновени" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "%u доклад е успешно качен" +msgstr[1] "%u доклада са успешно качени" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "Контролните суми на устройството бяха успешно потвърдени" + +#. TRANSLATORS: the device showed up in time +#, c-format +msgid "Successfully waited %.0fms for device" +msgstr "Успешно изчакване на устройството за %.0fмс" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "Обобщение" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Supervisor Mode Access Prevention" +msgstr "Предотвратяване на достъпа в режим на надзирател (SMAP)" + +#. TRANSLATORS: longer description +msgid "Supervisor Mode Access Prevention ensures critical parts of device memory are not accessed by less secure programs." +msgstr "Предотвратяване на достъпа в режим на надзирател гарантира, че критичните части от паметта на устройството не се достъпват от несигурни програми." + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "Поддържано" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "Поддържан ЦП" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "Поддържано на отдалечения сървър" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend To Idle" +msgstr "Приспиване към бездействие" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend To RAM" +msgstr "Приспиване към RAM" + +#. TRANSLATORS: longer description +msgid "Suspend to Idle allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Приспиването към бездействие позволява на устройството бързо да премине в режим на приспиване, за да пести енергия. Докато устройството е спряно, паметта му може да бъде извадена и да се получи достъп до информацията в нея." + +#. TRANSLATORS: longer description +msgid "Suspend to RAM allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Приспиването към RAM позволява на устройството бързо да премине в режим на приспиване, за да пести енергия. Докато устройството е спряно, паметта му може да бъде извадена и да се получи достъп до информацията в нея." + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Приспиване-към-бездействие" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Приспиване-към-RAM" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "Сменяне на потока от %s към %s?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "Сменяне на потока на фърмуера на устройството" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the chosen configuration" +msgstr "Синхронизиране на версиите на фърмуера с избраните настройки" + +#. TRANSLATORS: Title: Whether firmware is locked down +msgid "System Management Mode" +msgstr "Режим на управление на системата (SSM)" + +#. TRANSLATORS: longer description +msgid "System management mode is used by the firmware to access resident BIOS code and data." +msgstr "Режимът за управление на системата се използва от фърмуера за достъп до кода и данните на BIOS." + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low" +msgstr "Твърде нисък заряд на батерия на устройството" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low (%u%%, requires %u%%)" +msgstr "Твърде нисък заряд на батерия на устройството (%u%%, трябва %u%%)" + +#. TRANSLATORS: Must be plugged into an outlet +msgid "System requires external power source" +msgstr "Системата изисква външен източник на захранване" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "ТЕКСТ" + +#. TRANSLATORS: longer description +msgid "TPM (Trusted Platform Module) is a computer chip that detects when hardware components have been tampered with." +msgstr "TPM (доверен модул) е компютърен чип, който разпознава, когато хардуерните компоненти са били подправени." + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0 възстановяване" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "TPM PCR0 възстановяването е невалидно" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "TPM PCR0 възстановяването сега е валидно" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "Настройки на платформата на TPM" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM Reconstruction" +msgstr "Възстановяване на TPM" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "Празни PCR регистри на TPM" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM версия 2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "Етикет" +msgstr[1] "Етикети" + +#. TRANSLATORS: we're saving all USB events for emulation +msgid "Tagged for emulation" +msgstr "Маркирано за емулация" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "Замърсено" + +#. show the user the entire data blob +msgid "Target" +msgstr "Цел" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "Изпробване на устройство с JSON манифест" + +#. TRANSLATORS: when the release was tested +msgid "Tested" +msgstr "Изпробвано" + +#. TRANSLATORS: the %s is a vendor name, e.g. Lenovo +#, c-format +msgid "Tested by %s" +msgstr "Изпробвано от %s" + +#. TRANSLATORS: someone we trust has tested this +msgid "Tested by trusted vendor" +msgstr "Тестван от надежден доставчик" + +#. TRANSLATORS: the boot entry was in a legacy format +msgid "The EFI boot entry is not in hive format, and shim may not be new enough to read it." +msgstr "EFI записът за зареждане не е в hive формат и shim може да не е достатъчно нов, за да го прочете." + +#. TRANSLATORS: try to treat the legacy format as a hive +msgid "The EFI boot entry was not in hive format, falling back" +msgstr "EFI записът за зареждане не беше в hive формат, връщане към стандартната стойност" + +#. TRANSLATORS: longer description +msgid "The Intel Management Engine Key Manifest must be valid so that the device firmware can be trusted by the CPU." +msgstr "Манифестът на ключа на модула за управление на Intel трябва да бъде валиден, за да може процесорът да се довери на фърмуера на устройството." + +#. TRANSLATORS: longer description +msgid "The Intel Management Engine controls device components and needs to have a recent version to avoid security issues." +msgstr "Модулът за управление на Intel контролира компонентите на устройството и трябва да има актуална версия, за да се избегнат проблеми със сигурността." + +#. TRANSLATORS: do not translate the variables marked using $ +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 е безплатна услуга, която функционира като независимо юридическо лице и няма връзка с $OS_RELEASE:NAME$. Възможно е вашият дистрибутор да не е проверил някоя от обновленията на фърмуера за съвместимост с вашата система или свързани устройства. Целият фърмуер се предоставя само от производителя на оригиналното оборудване." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Platform Configuration is used to check whether the device start process has been modified." +msgstr "Настройките на платформата TPM (доверен модул) се използват за проверяване дали процесът на стартиране на устройството е бил променен." + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Reconstruction is used to check whether the device start process has been modified." +msgstr "Възстановяване на TPM (доверен модул) се използва за проверяване дали процесът на стартиране на устройството е бил променен." + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0 се различава от възстановяването." + +#. TRANSLATORS: longer description +msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." +msgstr "Платформеният ключ на UEFI се използва, за да се определи дали софтуерът на устройството идва от надежден източник." + +#. TRANSLATORS: longer description +msgid "The UEFI system can set up memory attributes at boot which prevent common exploits from running." +msgstr "UEFI системата може да зададе атрибути на паметта при зареждане, които предотвратяват изпълнението на често срещани пробиви." + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "Демонът е заредил код на трето лице и вече не се поддържа от разработчиците от следния клон!" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "Фърмуерът от %s не се доставя от %s, производителя на хардуера." + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "Системният часовник не е настроен правилно и изтеглянето на файлове може да бъде неуспешно." + +#. TRANSLATORS: warning message shown after update has been scheduled +msgid "The update will continue when the device USB cable has been re-inserted." +msgstr "Обновяването ще продължи, когато USB кабелът на устройството бъде свързан наново." + +#. TRANSLATORS: warning message shown after update has been scheduled +msgid "The update will continue when the device USB cable has been unplugged and then re-inserted." +msgstr "Обновяването ще продължи, когато USB кабелът на устройството бъде изваден и след това отново свързан." + +#. TRANSLATORS: warning message shown after update has been scheduled +msgid "The update will continue when the device USB cable has been unplugged." +msgstr "Обновяването ще продължи, когато USB кабелът на устройството бъде изваден." + +#. TRANSLATORS: warning message +msgid "The update will continue when the device power cable has been removed and re-inserted." +msgstr "ОБновяването ще продължи, когато захранващият кабел на устройството бъде изваден и свързан отново." + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "Производителят не е предоставил бележки за изданието." + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "Има устройства с проблеми:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "Няма блокирани файлове с фърмуер" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "Няма одобрен фърмуер." + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "Това устройство ще бъде върнато към %s, когато се изпълни командата %s." + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "Този фърмуер е предоставен от членове на общността на LVFS и не се предоставя (или поддържа) от оригиналния производител на хардуера." + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "Този пакет не е потвърден и може да не работи правилно." + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +msgstr "Тази програма може да работи правилно само като основен потребител" + +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: error message +msgid "This system doesn't support firmware settings" +msgstr "Тази система не поддържа настройки на фърмуера" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "Тази система има проблеми със средата за изпълнение на HSI." + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "Тази система е с ниско ниво на сигурност на HSI." + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "Този инструмент позволява на администратор да прилага обновления на UEFI dbx." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "Този инструмент позволява на администратора да прави заявки и да контролира fwupd демона, което му позволява да извършва действия като инсталиране или понижаване на фърмуера." + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "Този инструмент позволява на администратора да използва приставките на fwupd, без да бъдат инсталирани на хост системата." + +#. TRANSLATORS: the %1 is a kernel command line key=value +#, c-format +msgid "This tool can add a kernel argument of '%s', but it will only be active after restarting the computer." +msgstr "Този инструмент може да добваи аргумента „%s“ на ядрото, но ще бъде включен само след рестартиране на компютъра." + +#. TRANSLATORS: the %1 is a BIOS setting name. +#. * %2 and %3 are the values, e.g. "True" or "Windows10" +#, c-format +msgid "This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, but it will only be active after restarting the computer." +msgstr "Този инструмент може автоматично да промени настройката на BIOS „%s“ от „%s“ на „%s“, но тя ще бъде включена само след рестартиране на компютъра." + +#. TRANSLATORS: the %1 is a kernel command line key=value +#, c-format +msgid "This tool can change the kernel argument from '%s' to '%s', but it will only be active after restarting the computer." +msgstr "Този инструмент може да промени аргумента на ядрото от „%s“ на „%s“, но ще бъде включен само след рестартиране на компютъра." + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "Този инструмент ще прочете и анализира журналите на събитията на TPM от системния фърмуер." + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "Грешка при изпращане" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "Истина" + +#. TRANSLATORS: We verified the metadata against the server +msgid "Trusted metadata" +msgstr "Надеждни метаданни" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "Надеждно тяло на заявка" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "Вид" + +#. TRANSLATORS: Title: Bootservice is when only readable from early-boot +msgid "UEFI Bootservice Variables" +msgstr "Променливи на услугата за стартиране на UEFI" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition may not be set up correctly" +msgstr "Възможно е UEFI ESP дялът да не е създаден правилно" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP дялът не е открит или настроен" + +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI Memory Protection" +msgstr "Защита на паметта на UEFI" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "Платформен ключ на UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI Secure Boot" +msgstr "Атестирано стартиране на UEFI" + +#. TRANSLATORS: longer description +msgid "UEFI Secure Boot prevents malicious software from being loaded when the device starts." +msgstr "Атестираното стартиране на UEFI предотвратява зареждането на злонамерен софтуер при стартиране на устройството." + +#. TRANSLATORS: longer description +msgid "UEFI boot service variables should not be readable from runtime mode." +msgstr "Променливите на UEFI услугата за зареждане не трябва да бъдат четими по време на изпълнение." + +#. TRANSLATORS: Title: Bootservice is when only readable from early-boot +msgid "UEFI bootservice variables" +msgstr "Променливи на услугата за стартиране на UEFI" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "Обновленията на UEFI капсулата не са налични или включени в настройките на фърмуера" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "Инструмент за UEFI dbx" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "Фърмуерът на UEFI не може да бъде обновен в режим на остарял BIOS" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled and locked" +msgstr "Включена е UEFI защитата на паметта" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled but not locked" +msgstr "Защитата на паметта на UEFI е включена, но не е заключена" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now locked" +msgstr "Защитата на паметта на UEFI вече е заключена" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now unlocked" +msgstr "Защитата на паметта на UEFI е отключена" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "Платформен ключ на UEFI" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "Атестирано стартиране на UEFI" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "Свързването с услугата е неуспешно" + +#. TRANSLATORS: error message +msgid "Unable to find attribute" +msgstr "Атрибутът не може да бъде намерен" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "Освобождаване на текущия драйвер" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "Отблокиране на фърмуера:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "Отблокиране на инсталирането на конкретен фърмуер" + +#. TRANSLATORS: command description +msgid "Undo the host security attribute fix" +msgstr "Отменяне на поправката на конкретния атрибут за сигурност на хоста" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "Нешифрирано" + +#. TRANSLATORS: command description +msgid "Uninhibit the system to allow upgrades" +msgstr "Освобождаване на системата да позволява надграждания" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Unknown" +msgstr "Неизвестно" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "Неизвестно устройство" + +msgid "Unlock the device to allow access" +msgstr "Отключване на устройството за позволяване на достъп" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "Отключен" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "Отключване на устройството за достъп до фърмуера" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "Демонтиране на ESP" + +#. TRANSLATORS: message shown after device has been marked for emulation +msgid "Unplug and replug the device to continue the update process." +msgstr "Извадете и свържете наново устройството, за да продължите процеса на обновяване." + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "Неподписано тяло на заявка" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "Неподдържана версия на демона %s, версията на клиента е %s" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "Незамърсено" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "Може да се обнови" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "Грешка при обновяването" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "Изображение за обновлението" + +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "Съобщение за обновлението" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +msgstr "Състояние на обновлението" + +#. TRANSLATORS: the server sent the user a small message +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "Неуспешните обновления са известен проблем, посетете този адрес за повече информация:" + +#. TRANSLATORS: ask if we can update metadata +msgid "Update now?" +msgstr "Обновяване сега?" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "Обновяване на съхранения криптографски хеш с текущото съдържание на ROM" + +msgid "Update the stored device verification information" +msgstr "Обновяване на съхранената информация за потвърждаване на устройството" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "Обновяване на съхранените метаданни с текущото им съдържание" + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "Обновяване на всички посочени устройства до най-новата версия на фърмуера или всички устройства, ако не е посочено" + +msgid "Updating" +msgstr "Обновяване" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "Обновяване %s…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "Надграждане на %s от %s до %s?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload data now?" +msgstr "Качване на данните сега?" + +#. TRANSLATORS: command description +msgid "Upload the list of updatable devices to a remote server" +msgstr "Качване на списъка с обновяемите устройства на отдалечен сървър" + +#. TRANSLATORS: ask the user to share, %s is something +#. * like: "Linux Vendor Firmware Service" +#, c-format +msgid "Upload these anonymous results to the %s to help other users?" +msgstr "Искате ли да качите тези анонимни резултати в %s, за да помогнете на други потребители?" + +#. TRANSLATORS: explain why we want to upload +#, c-format +msgid "Uploading a device list allows the %s team to know what hardware exists, and allows us to put pressure on vendors that do not upload firmware updates for their hardware." +msgstr "Качването на списък с устройства позволява на екипа на %s да знае какъв хардуер съществува и ни позволява да оказваме натиск върху производителите, които не качват обновления на фърмуера за своя хардуер." + +#. TRANSLATORS: explain why we want to upload +msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." +msgstr "Качването на доклади за фърмуера помага на производителите на хардуер бързо да идентифицират неуспешните и успешните обновления на истински устройства." + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "Спешност" + +#. TRANSLATORS: explain how to get help, %1 is +#. * 'fwupdtool --help' +#. TRANSLATORS: explain how to get help, +#. * where $1 is something like 'fwupdmgr --help' +#, c-format +msgid "Use %s for help" +msgstr "Използвайте %s за помощ" + +#. TRANSLATORS: CTRL^C [holding control, and then pressing C] will exit the +#. program +msgid "Use CTRL^C to cancel." +msgstr "Използвайте CTRL^C за отказване." + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "Потребителят е уведомен" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "Потребителско име" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "VERSION1 VERSION2 [FORMAT]" +msgstr "ВЕРСИЯ1 ВЕРСИЯ2 [ФОРМАТ]" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "Валиден" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "Проверяване на ESP съдържанието…" + +#. TRANSLATORS: one line variant of release (e.g. 'China') +msgid "Variant" +msgstr "Вариант" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "Производител" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "Потвърждаване…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "Версия" + +#. TRANSLATORS: the fwupd version the release was tested on +msgid "Version[fwupd]" +msgstr "Версия[fwupd]" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING" +msgstr "ВНИМАНИЕ" + +#. TRANSLATORS: command description +msgid "Wait for a device to appear" +msgstr "Изчакване да се появи устройство" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "Изчакване…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "Следене за промени в хардуера" + +#. TRANSLATORS: check various UEFI and ACPI tables are unchanged after the +#. update +msgid "Will measure elements of system integrity around an update" +msgstr "Измерване на елементите на целостта на системата след обновяване" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "Запис на файл:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "Записване…" + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from a recovery or installation disk, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "Трябва да се уверите, че сте сигурни при възстановяването на настройката от диск за възстановяване или инсталационен диск, тъй като тази промяна може да доведе до отказ на системата да стартира в Linux или да причини друга нестабилност на системата." + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from the system firmware setup, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "Трябва да се уверите, че сте сигурни при възстановяването на настройката на системния фърмуер, тъй като тази промяна може да доведе до отказ на системата да стартира в Linux или да причини друга нестабилност на системата." + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "Възможно е вашият дистрибутор да не е проверил някое от обновленията на фърмуера за съвместимост с вашата система или свързани устройства." + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "Вашият хардуер може да бъде повреден при използването на този фърмуер и инсталирането на тази версия може да доведе до отпадане на гаранцията с %s." + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "Вашата система е настроена на най-добрите известни настройки на %s." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[APPSTREAM_ID]" +msgstr "[APPSTREAM_ИД]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[КОНТРОЛНА-СУМА]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[ИД-УСТРОЙСТВО|ГЛ-УН-ИД]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[ИД-УСТРОЙСТВО|ГЛ-УН-ИД] [КЛОН]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "ИД-УСТРОЙСТВО|ГЛ-УН-ИД [ВЕРСИЯ]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE]" +msgstr "[УСТРОЙСТВО]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[ФАЙЛ ПОДПИС-НА-ФАЙЛ ИД-НА-ОТДАЛЕЧЕН-СЪРВЪР]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[ИМЕ-НА-ФАЙЛ1] [ИМЕ-НА-ФАЙЛ2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[REASON] [TIMEOUT]" +msgstr "[ПРИЧИНА] [ИЗЧАКВАНЕ]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SECTION] KEY VALUE" +msgstr "[РАЗДЕЛ] КЛЮЧ СТОЙНОСТ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "[НАСТРОЙКА1] [НАСТРОЙКА2] [—no-authenticate]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2]..." +msgstr "[НАСТРОЙКА1] [НАСТРОЙКА2]…" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SMBIOS-ФАЙЛ|HWIDS-ФАЙЛ]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "стандартно" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd инструмент за журналите на събитията в TPM" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd приставки" diff -Nru fwupd-2.0.8/po/ca.po fwupd-2.0.20/po/ca.po --- fwupd-2.0.8/po/ca.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/ca.po 2026-02-26 11:36:18.000000000 +0000 @@ -5,16 +5,21 @@ # Translators: # Antoni Bella Pérez , 2017-2025 # Robert Antoni Buj i Gelonch , 2017 +# Richard Hughes , 2025. +# Xusi Fons , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Catalan (http://app.transifex.com/freedesktop/fwupd/language/ca/)\n" +"PO-Revision-Date: 2025-12-02 23:02+0000\n" +"Last-Translator: Xusi Fons \n" +"Language-Team: Catalan \n" +"Language: ca\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ca\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.15-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -35,7 +40,7 @@ msgstr "Actualització de la bateria %s" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "Actualització del microcodi de la CPU %s" @@ -293,7 +298,7 @@ #, c-format msgid "%u device has a firmware upgrade available." msgid_plural "%u devices have a firmware upgrade available." -msgstr[0] " %u dispositiu té una actualització de microprogramari disponible." +msgstr[0] "%u dispositiu té una actualització de microprogramari disponible." msgstr[1] "%u dispositius tenen una actualització de microprogramari disponible." #. TRANSLATORS: this is shown in the MOTD @@ -367,11 +372,11 @@ #. TRANSLATORS: command description msgid "Activate devices" -msgstr "Activa els dispositius." +msgstr "Activa els dispositius" #. TRANSLATORS: command description msgid "Activate pending devices" -msgstr "Activa els dispositius pendents." +msgstr "Activa els dispositius pendents" msgid "Activate the new firmware on the device" msgstr "Activa el microprogramari nou al dispositiu" @@ -457,11 +462,11 @@ #. TRANSLATORS: command line option msgid "Apply update even when not advised" -msgstr "Aplica una actualització fins i tot quan no se us aconselli." +msgstr "Aplica una actualització fins i tot quan no se us aconselli" #. TRANSLATORS: command line option msgid "Apply update files" -msgstr "Aplica els fitxers d'actualització." +msgstr "Aplica els fitxers d'actualització" #. TRANSLATORS: actually sending the update to the hardware msgid "Applying update…" @@ -480,11 +485,11 @@ #. TRANSLATORS: command description msgid "Attach to firmware mode" -msgstr "Adjunta al mode microprogramari." +msgstr "Adjunta al mode microprogramari" #. TRANSLATORS: waiting for user to authenticate msgid "Authenticating…" -msgstr "S'està autenticant..." +msgstr "S'està autenticant…" #. TRANSLATORS: user needs to run a command msgid "Authentication details are required" @@ -613,7 +618,7 @@ #. TRANSLATORS: command description msgid "Bind new kernel driver" -msgstr "Vincula el controlador actual." +msgstr "Vincula el controlador actual" #. TRANSLATORS: there follows a list of hashes msgid "Blocked firmware files:" @@ -629,7 +634,7 @@ #. TRANSLATORS: command description msgid "Blocks a specific firmware from being installed" -msgstr "Bloqueja la instal·lació d'un microprogramari específic." +msgstr "Bloqueja la instal·lació d'un microprogramari específic" #. TRANSLATORS: firmware version of bootloader msgid "Bootloader Version" @@ -645,7 +650,7 @@ #. TRANSLATORS: command description msgid "Build a firmware file" -msgstr "Construeix un fitxer de microprogramari." +msgstr "Compila un fitxer de microprogramari" #. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, #. * Utilized by OS means the distribution enabled it @@ -683,8 +688,12 @@ msgstr "S'ha canviat" #. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "Comprova si hi ha dispositius pendents de reiniciar per a completar l'actualització" + +#. TRANSLATORS: command description msgid "Checks cryptographic hash matches firmware" -msgstr "Comprova que la suma criptogràfica coincideix amb el microprogramari." +msgstr "Comprova que la suma criptogràfica es idèntica a la del microprogramari" #. TRANSLATORS: hash to that exact firmware archive #. TRANSLATORS: remote checksum @@ -714,7 +723,7 @@ #. TRANSLATORS: command description msgid "Clears the results from the last update" -msgstr "Esborra els resultats de l'última actualització." +msgstr "Esborra els resultats de l'última actualització" #. TRANSLATORS: error message msgid "Command not found" @@ -746,7 +755,7 @@ #. TRANSLATORS: command description msgid "Convert a firmware file" -msgstr "Converteix un fitxer de microprogramari." +msgstr "Converteix un fitxer de microprogramari" #. TRANSLATORS: command description msgid "Create an EFI boot entry" @@ -782,7 +791,7 @@ #. TRANSLATORS: decompressing the firmware file msgid "Decompressing…" -msgstr "S'està descomprimint..." +msgstr "S'està descomprimint…" #. TRANSLATORS: command description msgid "Delete an EFI boot entry" @@ -795,7 +804,7 @@ #. TRANSLATORS: command description msgid "Detach to bootloader mode" -msgstr "Separa del mode carregador d'arrencada." +msgstr "Separa del mode carregador d'arrencada" #. TRANSLATORS: more details about the update link msgid "Details" @@ -979,7 +988,7 @@ #. TRANSLATORS: command description msgid "Disables a given remote" -msgstr "Inhabilita un remot indicat." +msgstr "Inhabilita un remot indicat" #. TRANSLATORS: command description msgid "Disables virtual testing devices" @@ -1074,7 +1083,7 @@ #. TRANSLATORS: command description msgid "Downgrades the firmware on a device" -msgstr "Desactualitza el microprogramari en un dispositiu." +msgstr "Degrada el microprogramari d'un dispositiu" #. TRANSLATORS: %1 is a device name #, c-format @@ -1087,16 +1096,20 @@ #. TRANSLATORS: downloading from a remote server msgid "Downloading…" -msgstr "S'està descarregant..." +msgstr "S'està descarregant…" #. TRANSLATORS: command description msgid "Dump SMBIOS data from a file" -msgstr "Aboca les dades al SMBIOS des d'un fitxer." +msgstr "Aboca les dades del SMBIOS des d'un fitxer" #. TRANSLATORS: length of time the update takes to apply msgid "Duration" msgstr "Durada" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "EMULATION-FILE [ARCHIVE-FILE]" +msgstr "FITXER-DEMULACIÓ [ARXIU-FITXER]" + #. TRANSLATORS: longer description msgid "Each system should have tests to ensure firmware security." msgstr "Cada sistema haurà de tenir proves per a assegurar la seguretat del microprogramari." @@ -1138,7 +1151,7 @@ #. TRANSLATORS: command description msgid "Enables a given remote" -msgstr "Habilita un remot indicat." +msgstr "Habilita un remot indicat" #. TRANSLATORS: command description msgid "Enables virtual testing devices" @@ -1176,11 +1189,11 @@ #. TRANSLATORS: command description msgid "Erase all firmware update history" -msgstr "Esborra tot l'historial de les actualitzacions de microprogramari." +msgstr "Esborra tot l'historial de les actualitzacions de microprogramari" #. TRANSLATORS: erasing contents of the flash chips msgid "Erasing…" -msgstr "S'està esborrant..." +msgstr "S'està esborrant…" #. TRANSLATORS: exit after we've started up, used for user profiling msgid "Exit after a small delay" @@ -1200,7 +1213,7 @@ #. TRANSLATORS: command description msgid "Extract a firmware blob to images" -msgstr "Extreu un blob de microprogramari en imatges." +msgstr "Extreu un blob de microprogramari en imatges" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILE" @@ -1219,10 +1232,6 @@ msgstr "NOM_FITXER CERTIFICAT CLAU_PRIVADA" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "NOM_FITXER ID_DISPOSITIU" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" msgstr "NOM_FITXER DESPLAÇAMENT DADES [TIPUS_MICROPROGRAMARI]" @@ -1482,15 +1491,15 @@ #. TRANSLATORS: command description msgid "Get all device flags supported by fwupd" -msgstr "Obté totes les etiquetes admeses per «fwupd»." +msgstr "Obtén totes les etiquetes admeses per «fwupd»" #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" -msgstr "Obté tots els dispositius que admeten actualitzacions de microprogramari." +msgstr "Obtén tots els dispositius que admeten actualitzacions de microprogramari" #. TRANSLATORS: command description msgid "Get all enabled plugins registered with the system" -msgstr "Obté tots els connectors habilitats registrats amb el sistema." +msgstr "Obtén tots els connectors habilitats registrats amb el sistema" #. TRANSLATORS: command description msgid "Get all known version formats" @@ -1502,15 +1511,15 @@ #. TRANSLATORS: command description msgid "Gets details about a firmware file" -msgstr "Obté la informació sobre un fitxer de microprogramari." +msgstr "Obtén detalls sobre un fitxer de microprogramari" #. TRANSLATORS: command description msgid "Gets the configured remotes" -msgstr "Obté els remots configurats." +msgstr "Obtén els remots configurats" #. TRANSLATORS: command description msgid "Gets the host security attributes" -msgstr "Obté els atributs de seguretat de l'amfitrió." +msgstr "Obtén els atributs de seguretat de l'amfitrió" #. TRANSLATORS: firmware approved by the admin msgid "Gets the list of approved firmware" @@ -1518,19 +1527,19 @@ #. TRANSLATORS: command description msgid "Gets the list of blocked firmware" -msgstr "Obté la llista del microprogramari bloquejat." +msgstr "Obtén la llista del microprogramari bloquejat" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Obté la llista d'actualitzacions per al maquinari connectat." +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "Obtén la llista d'actualitzacions per a tots els dispositius especificats, o de tots els dispositius si no s'especifica" #. TRANSLATORS: command description msgid "Gets the releases for a device" -msgstr "Obté els alliberaments per a un dispositiu." +msgstr "Obtén els alliberaments per a un dispositiu" #. TRANSLATORS: command description msgid "Gets the results from the last update" -msgstr "Obté els resultats de l'última actualització." +msgstr "Obtén els resultats de l'última actualització" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "HWIDS-FILE" @@ -1549,7 +1558,6 @@ msgstr "Esdeveniments de seguretat a l'amfitrió" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "L'ID de seguretat de l'amfitrió (HSI) no està admès" @@ -1605,7 +1613,7 @@ #. TRANSLATORS: daemon is inactive msgid "Idle…" -msgstr "Està ociós..." +msgstr "Està ociós…" #. TRANSLATORS: command line option msgid "Ignore SSL strict checks when downloading files" @@ -1690,7 +1698,7 @@ #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" -msgstr "S'està instal·lant l'actualització de microprogramari..." +msgstr "S'està instal·lant l'actualització de microprogramari…" #. TRANSLATORS: %1 is a device name #, c-format @@ -1924,7 +1932,7 @@ #. TRANSLATORS: command line option msgid "List entries in dbx" -msgstr "Llista les entrades a la dbx." +msgstr "Llista les entrades a la dbx" #. TRANSLATORS: command description msgid "List the available firmware GTypes" @@ -1932,11 +1940,11 @@ #. TRANSLATORS: command description msgid "List the available firmware types" -msgstr "Llista els tipus de microprogramari disponibles." +msgstr "Llista els tipus de microprogramari disponibles" #. TRANSLATORS: command description msgid "Lists files on the ESP" -msgstr "Llista els fitxers en l'ESP." +msgstr "Llista els fitxers en l'ESP" #. TRANSLATORS: command description msgid "Load device emulation data" @@ -1948,7 +1956,7 @@ #. TRANSLATORS: parsing the firmware information msgid "Loading…" -msgstr "S'està carregant..." +msgstr "S'està carregant…" #. TRANSLATORS: Suffix: the HSI result msgid "Locked" @@ -2046,7 +2054,7 @@ #. TRANSLATORS: command description msgid "Modifies a given remote" -msgstr "Modifica un remot indicat." +msgstr "Modifica un remot indicat" msgid "Modify a configured remote" msgstr "Modifica un remot configurat" @@ -2056,11 +2064,11 @@ #. TRANSLATORS: command description msgid "Monitor the daemon for events" -msgstr "Monitora el dimoni per als esdeveniments." +msgstr "Monitora el dimoni per als esdeveniments" #. TRANSLATORS: command description msgid "Mounts the ESP" -msgstr "Munta l'ESP." +msgstr "Munta l'ESP" #. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware msgid "Needs a reboot after installation" @@ -2100,6 +2108,10 @@ msgid "No hardware detected with firmware update capability" msgstr "No s'ha detectat cap maquinari amb capacitat per a l'actualització del microprogramari" +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +msgstr "No cal reiniciar" + #. TRANSLATORS: no repositories to download from msgid "No releases available" msgstr "No hi ha cap llançament disponible" @@ -2153,6 +2165,10 @@ msgstr "Versió antiga" #. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Instal·la només en els dispositius emulats" + +#. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Mostra només un únic valor de PCR" @@ -2166,8 +2182,8 @@ msgstr "Només es permeten actualitzacions de la versió" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Sortida en el format JSON" +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Sortida en el format JSON (desactiva totes les indicacions interactives)" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -2187,15 +2203,15 @@ #. TRANSLATORS: command description msgid "Parse and show details about a firmware file" -msgstr "Analitza i mostra els detalls sobre un fitxer de microprogramari." +msgstr "Analitza i mostra els detalls sobre un fitxer de microprogramari" #. TRANSLATORS: reading new dbx from the update msgid "Parsing dbx update…" -msgstr "S'està analitzant l'actualització de la dbx..." +msgstr "S'està analitzant l'actualització de la dbx…" #. TRANSLATORS: reading existing dbx from the system msgid "Parsing system dbx…" -msgstr "S'està analitzant la dbx del sistema..." +msgstr "S'està analitzant la dbx del sistema…" #. TRANSLATORS: remote filename base msgid "Password" @@ -2238,7 +2254,8 @@ msgid "Please enter a number from 0 to %u: " msgstr "Introduïu un número del 0 al %u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " msgstr "Introduïu %s o %s:" @@ -2320,7 +2337,7 @@ #. TRANSLATORS: command description msgid "Read a firmware blob from a device" -msgstr "Llegeix un blob de microprogramari des d'un dispositiu." +msgstr "Llegeix un blob de microprogramari des d'un dispositiu" #. TRANSLATORS: command description msgid "Read a firmware from a device" @@ -2333,7 +2350,7 @@ #. TRANSLATORS: reading from the flash chips msgid "Reading…" -msgstr "S'està llegint..." +msgstr "S'està llegint…" #. TRANSLATORS: Plugin is active and in use msgid "Ready" @@ -2345,7 +2362,7 @@ #. TRANSLATORS: command description msgid "Refresh metadata from remote server" -msgstr "Refresca les metadades des del servidor remot." +msgstr "Refresca les metadades des del servidor remot" #. TRANSLATORS: message letting the user know an upgrade is available #. * %1 is the device name and %2 is a version string @@ -2355,11 +2372,11 @@ #. TRANSLATORS: command description msgid "Reinstall current firmware on the device" -msgstr "Torna a instal·lar el microprogramari actual en el dispositiu." +msgstr "Torna a instal·lar el microprogramari actual al dispositiu" #. TRANSLATORS: command description msgid "Reinstall firmware on a device" -msgstr "Torna a instal·lar el microprogramari a un dispositiu." +msgstr "Torna a instal·lar el microprogramari a un dispositiu" #. TRANSLATORS: the stream of firmware, e.g. nonfree msgid "Release Branch" @@ -2423,7 +2440,7 @@ #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" -msgstr "S'està reiniciant el dispositiu..." +msgstr "S'està reiniciant el dispositiu…" #. TRANSLATORS: command description msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" @@ -2431,7 +2448,7 @@ #. TRANSLATORS: command description msgid "Return all the hardware IDs for the machine" -msgstr "Retorna tots els ID del maquinari de la màquina." +msgstr "Retorna tots els identificadors del maquinari de la màquina" #. TRANSLATORS: ask the user to upload msgid "Review and upload report now?" @@ -2548,7 +2565,7 @@ #. TRANSLATORS: scheduling an update to be done on the next boot msgid "Scheduling…" -msgstr "Planificació..." +msgstr "Planificació…" #. TRANSLATORS: HSI event title msgid "Secure Boot disabled" @@ -2629,7 +2646,7 @@ #. TRANSLATORS: command description msgid "Share firmware history with the developers" -msgstr "Comparteix l'historial de microprogramari amb els desenvolupadors." +msgstr "Comparteix l'historial de microprogramari amb els desenvolupadors" #. TRANSLATORS: command line option msgid "Show all results" @@ -2657,15 +2674,15 @@ #. TRANSLATORS: command line option msgid "Show extra debugging information" -msgstr "Mostra informació de depuració addicional." +msgstr "Mostra informació de depuració addicional" #. TRANSLATORS: command description msgid "Show history of firmware updates" -msgstr "Mostra l'historial de les actualitzacions de microprogramari." +msgstr "Mostra l'historial de les actualitzacions de microprogramari" #. TRANSLATORS: command line option msgid "Show the calculated version of the dbx" -msgstr "Mostra la versió calculada de la dbx." +msgstr "Mostra la versió calculada de la dbx" #. TRANSLATORS: shutdown to apply the update msgid "Shutdown now?" @@ -2708,7 +2725,7 @@ #. TRANSLATORS: command line option msgid "Specify the dbx database file" -msgstr "Especifica el fitxer de base de dades dbx." +msgstr "Especifica el fitxer de base de dades dbx" msgid "Stop the fwupd service" msgstr "Atura el servei del «fwupd»" @@ -2734,11 +2751,6 @@ msgid "Successfully disabled test devices" msgstr "Els dispositius de prova s'han inhabilitat amb èxit" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "S'han baixat les metadades noves amb èxit:" - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "S'ha habilitat i refrescat el remot amb èxit" @@ -2852,7 +2864,7 @@ #. TRANSLATORS: command description msgid "Switch the firmware branch on the device" -msgstr "Canvia la branca de microprogramari en el dispositiu." +msgstr "Canvia la branca del microprogramari al dispositiu" #. TRANSLATORS: command description msgid "Sync firmware versions to the chosen configuration" @@ -2992,6 +3004,14 @@ msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." msgstr "La clau de la plataforma UEFI s'empra per a determinar si el programari del dispositiu prové d'una font de confiança." +#. TRANSLATORS: HSI event title +msgid "The UEFI certificate store is now up to date" +msgstr "El magatzem de certificats UEFI ja està actualitzat" + +#. TRANSLATORS: longer description +msgid "The UEFI db contains the list of valid certificates that can be used to authorize what EFI binaries are allowed to run." +msgstr "La base de dades UEFI conté la llista dels certificats vàlids que es poden usar per a autoritzar quins binaris EFI es poden executar." + #. TRANSLATORS: longer description msgid "The UEFI system can set up memory attributes at boot which prevent common exploits from running." msgstr "El sistema UEFI pot configurar atributs de memòria a l'arrencada que impedeixen que s’executin els explotadors habituals." @@ -3035,7 +3055,7 @@ #. TRANSLATORS: nothing to show msgid "There are no blocked firmware files" -msgstr "No hi ha cap fitxer de microprogramari bloquejat." +msgstr "No hi ha cap fitxer de microprogramari bloquejat" #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator @@ -3167,6 +3187,10 @@ msgid "UEFI capsule updates not available or enabled in firmware setup" msgstr "Les actualitzacions de la càpsula UEFI no estan disponibles o habilitades a la configuració del microprogramari" +#. TRANSLATORS: Title: is UEFI db up-to-date +msgid "UEFI db" +msgstr "Bd UEFI" + #. TRANSLATORS: program name msgid "UEFI dbx Utility" msgstr "Utilitat dbx UEFI" @@ -3213,7 +3237,7 @@ #. TRANSLATORS: command description msgid "Unbind current driver" -msgstr "Desvincula el controlador actual." +msgstr "Desvincula el controlador actual" #. TRANSLATORS: we will now offer this firmware to the user msgid "Unblocking firmware:" @@ -3221,7 +3245,7 @@ #. TRANSLATORS: command description msgid "Unblocks a specific firmware from being installed" -msgstr "Desbloqueja la instal·lació d'un microprogramari específic." +msgstr "Desbloqueja la instal·lació d'un microprogramari específic" #. TRANSLATORS: command description msgid "Undo the host security attribute fix" @@ -3255,7 +3279,7 @@ #. TRANSLATORS: command description msgid "Unlocks the device for firmware access" -msgstr "Desbloqueja el dispositiu per a accedir al microprogramari." +msgstr "Desbloqueja el dispositiu per a accedir al microprogramari" #. TRANSLATORS: command description msgid "Unmounts the ESP" @@ -3308,26 +3332,19 @@ #. TRANSLATORS: command description msgid "Update the stored cryptographic hash with current ROM contents" -msgstr "Actualitza la suma criptogràfica emmagatzemada amb el contingut actual de la ROM." +msgstr "Actualitza la suma criptogràfica emmagatzemada amb el contingut actual de la ROM" msgid "Update the stored device verification information" msgstr "Actualitza la informació de verificació dels dispositius emmagatzemats" #. TRANSLATORS: command description msgid "Update the stored metadata with current contents" -msgstr "Actualitza les metadades emmagatzemades amb el contingut actual." +msgstr "Actualitza les metadades emmagatzemades amb el contingut actual" #. TRANSLATORS: command description msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" msgstr "Actualitza tots els dispositius especificats a la versió més recent del microprogramari o tots els dispositius sense especificar" -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "S'han publicat les actualitzacions per a %u dispositiu local" -msgstr[1] "S'han publicat les actualitzacions de %u per a %u dispositius locals" - msgid "Updating" msgstr "S'està actualitzant" @@ -3400,7 +3417,7 @@ #. TRANSLATORS: ESP refers to the EFI System Partition msgid "Validating ESP contents…" -msgstr "S'està validant el contingut ESP..." +msgstr "S'està validant el contingut ESP…" #. TRANSLATORS: one line variant of release (e.g. 'China') msgid "Variant" @@ -3412,7 +3429,7 @@ #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" -msgstr "S'està verificant..." +msgstr "S'està verificant…" #. TRANSLATORS: the detected version number of the dbx msgid "Version" @@ -3436,7 +3453,7 @@ #. TRANSLATORS: command description msgid "Watch for hardware changes" -msgstr "Mira per a canvis al maquinari." +msgstr "Vigila els canvis al maquinari" #. TRANSLATORS: check various UEFI and ACPI tables are unchanged after the #. update @@ -3449,7 +3466,7 @@ #. TRANSLATORS: writing to the flash chips msgid "Writing…" -msgstr "S'està escrivint..." +msgstr "S'està escrivint…" #. TRANSLATORS: the user has to manually recover; we can't do it msgid "You should ensure you are comfortable restoring the setting from a recovery or installation disk, as this change may cause the system to not boot into Linux or cause other system instability." diff -Nru fwupd-2.0.8/po/cs.po fwupd-2.0.20/po/cs.po --- fwupd-2.0.8/po/cs.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/cs.po 2026-02-26 11:36:18.000000000 +0000 @@ -7,16 +7,20 @@ # Ascii Wolf , 2017 # Marek Černocký , 2016,2018 # Pavel Borecki , 2020-2023,2025 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Czech (http://app.transifex.com/freedesktop/fwupd/language/cs/)\n" +"PO-Revision-Date: 2025-10-24 09:58+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Czech \n" +"Language: cs\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: cs\n" "Plural-Forms: nplurals=4; plural=(n == 1 && n % 1 == 0) ? 0 : (n >= 2 && n <= 4 && n % 1 == 0) ? 1: (n % 1 != 0 ) ? 2 : 3;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -31,7 +35,7 @@ #. * is the device that updates all the other firmware on the system #, c-format msgid "%s BMC Update" -msgstr "Aktualizace zařízení pro vestavěnou zprávu (BMC) %s" +msgstr "Aktualizace zařízení pro vestavěnou správu (BMC) %s" #. TRANSLATORS: battery refers to the system power source #, c-format @@ -39,7 +43,7 @@ msgstr "Aktualizace pro akumulátor %s" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "Aktualizace mikrokódu pro procesor %s" @@ -112,7 +116,7 @@ #. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC #, c-format msgid "%s Flash Drive Update" -msgstr "%s aktualizace pro flash disk" +msgstr "%s aktualizace pro flash úložiště" #. TRANSLATORS: GPU refers to a Graphics Processing Unit, e.g. #. * the "video card" @@ -172,7 +176,7 @@ #. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter #, c-format msgid "%s Storage Controller Update" -msgstr "Aktualizace pro %s řadič úložiště" +msgstr "Aktualizace pro řadič úložiště %s" #. TRANSLATORS: the entire system, e.g. all internal devices, #. * the first %s is the device name, e.g. 'ThinkPad P50` @@ -220,7 +224,7 @@ #. TRANSLATORS: warn the user before updating, %1 is a device name #, c-format msgid "%s and all connected devices may not be usable while updating." -msgstr "%s a k němu připojená zařízení nemusí být v průběhu aktualizace použitelná." +msgstr "Po dobu aktualizace se může se stát, že %s a všechna k němu připojená zařízení nebude možné přechodně používat." #. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". #. %2 refers to a result value, e.g. "Invalid" @@ -252,24 +256,24 @@ #. * ThunderBolt 4 Dock" and %2 is "fwupdmgr activate" #, c-format msgid "%s is pending activation; use %s to complete the update." -msgstr "%s čeká na aktivaci; dokončete aktualizaci spuštěním %s." +msgstr "%s čeká na aktivaci – dokončete aktualizaci spuštěním %s." #. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT #, c-format msgid "%s manufacturing mode" -msgstr "režim pro výrobce v %s" +msgstr "režim %s pro výrobce" #. TRANSLATORS: warn the user before #. * updating, %1 is a device name #, c-format msgid "%s must remain connected for the duration of the update to avoid damage." -msgstr "Je třeba, aby %s zůstalo v průběhu aktualizace připojené, jinak hrozí poškození." +msgstr "Je třeba, aby %s zůstalo v průběhu aktualizace připojené – jinak hrozí poškození." #. TRANSLATORS: warn the user before updating, %1 is a machine #. * name #, c-format msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." -msgstr "Je třeba, aby %s bylo po dobu aktualizace připojené ke zdroji napájení z elektrické sítě, jinak hrozí poškození." +msgstr "Je třeba, aby %s bylo po dobu aktualizace připojené ke zdroji napájení z elektrické sítě – jinak hrozí poškození." #. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT #, c-format @@ -300,18 +304,38 @@ msgid "%u device has a firmware upgrade available." msgid_plural "%u devices have a firmware upgrade available." msgstr[0] "Je k dispozici novější firmware pro %u zařízení." -msgstr[1] "Jsou k dispozici novější firmware pro %u zařízení." -msgstr[2] "Jsou k dispozici novější firmware pro %u zařízení." -msgstr[3] "Jsou k dispozici novější firmware pro %u zařízení." +msgstr[1] "Jsou k dispozici novější firmwary pro %u zařízení." +msgstr[2] "Jsou k dispozici novější firmwary pro %u zařízení." +msgstr[3] "Jsou k dispozici novější firmwary pro %u zařízení." #. TRANSLATORS: this is shown in the MOTD #, c-format msgid "%u device is not the best known configuration." msgid_plural "%u devices are not the best known configuration." msgstr[0] "Firmware na %u zařízení není na verzi, která by byla osvědčená v kombinaci s verzemi firmwarů ostatních zařízení." -msgstr[1] "Firmware na %u zařízeních není na verzi, která by byla osvědčená v kombinaci s verzemi firmwarů ostatních zařízení." -msgstr[2] "Firmware na %u zařízeních není na verzi, která by byla osvědčená v kombinaci s verzemi firmwarů ostatních zařízení." -msgstr[3] "Firmware na %u zařízeních není na verzi, která by byla osvědčená v kombinaci s verzemi firmwarů ostatních zařízení." +msgstr[1] "Firmwary na %u zařízeních nejsou na verzích, které by byly osvědčené v kombinaci s verzemi firmwarů ostatních zařízení." +msgstr[2] "Firmwary na %u zařízeních nejsou na verzích, které by byly osvědčené v kombinaci s verzemi firmwarů ostatních zařízení." +msgstr[3] "Firmwary na %u zařízeních nejsou na verzích, které by byly osvědčené v kombinaci s verzemi firmwarů ostatních zařízení." + +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#, c-format +msgid "%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "%u devices are supported in the enabled remotes (an update has been published)" +msgstr[0] "%u zařízení je podporováno v zapnutých repozitářích (byla vydána aktualizace)" +msgstr[1] "%u zařízení jsou podporována v zapnutých repozitářích (byla vydána aktualizace)" +msgstr[2] "%u zařízení je podporováno v zapnutých repozitářích (byla vydána aktualizace)" +msgstr[3] "%u zařízení jsou podporována v zapnutých repozitářích (byla vydána aktualizace)" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "%u zařízení je aktualizovatelné" +msgstr[1] "%u zařízení jsou aktualizovatelná" +msgstr[2] "%u zařízení je aktualizovatelných" +msgstr[3] "%u zařízení jsou aktualizovatelná" #. TRANSLATORS: duration in minutes #, c-format @@ -369,7 +393,7 @@ #. TRANSLATORS: Title: if hardware enforces control of SPI writes msgid "AMD Firmware Write Protection" -msgstr "AMD – zamezení zápisu firmware" +msgstr "AMD – ochrana zápisu firmware" #. TRANSLATORS: Title: if firmware enforces rollback protection msgid "AMD Secure Processor Rollback Protection" @@ -396,7 +420,7 @@ #. TRANSLATORS: shown when shutting down to switch to the new version msgid "Activating firmware update" -msgstr "Aktivace aktualizovaného firmwaru" +msgstr "Aktivuje se aktualizovaný firmware" #. TRANSLATORS: shown when shutting down to switch to the new version msgid "Activating firmware update for" @@ -429,7 +453,7 @@ #. TRANSLATORS: an application is preventing system updates msgid "All devices are prevented from update by system inhibit" -msgstr "Aktualizaci veškerých zařízení je bráněno systémovým inhibitorem" +msgstr "Aktualizaci veškerých zařízení je bráněno inhibitorem v systému" #. TRANSLATORS: on some systems certain devices have to have matching #. versions, @@ -459,7 +483,7 @@ #. TRANSLATORS: another application is updating the device already msgid "An update is in progress" -msgstr "Probíhá aktualizace" +msgstr "Aktualizace už probíhá" #. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." @@ -496,7 +520,7 @@ #. TRANSLATORS: command description msgid "Asks the daemon to quit" -msgstr "Požádá proces služby aby se ukončila" +msgstr "Požádá proces služby aby se ukončil" #. TRANSLATORS: command description msgid "Attach to firmware mode" @@ -520,7 +544,7 @@ #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to enable emulation data collection" -msgstr "Pro zapnutí shromažďování dat emulace je vyžadováno ověření" +msgstr "Pro zapnutí shromažďování dat emulace je vyžadováno ověření se" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to fix a host security issue" @@ -679,7 +703,7 @@ #. TRANSLATORS: longer description msgid "CPU Microcode must be updated to mitigate against various information-disclosure security issues." -msgstr "Aby bylo možné zmírnit dopad různých problémů s úniky různých informací z procesoru, je třeba zaktualizovat jeho mikrokód" +msgstr "Aby bylo možné zmírnit dopad různých problémů s úniky různých informací z procesoru, je třeba zaktualizovat jeho mikrokód." #. TRANSLATORS: we can save all device enumeration events for emulation msgid "Can tag for emulation" @@ -703,6 +727,10 @@ msgstr "Změněno" #. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "Zkontrolovat zda dokončení aktualizace nějakých zařízení nečeká na restart" + +#. TRANSLATORS: command description msgid "Checks cryptographic hash matches firmware" msgstr "Zkontrolovat zda se kryptografický otisk shoduje s firmware" @@ -966,7 +994,7 @@ #. TRANSLATORS: message letting the user there is an update #. * waiting, but there is a reason it cannot be deployed msgid "Devices with firmware updates that need user action: " -msgstr "Zařízení u kterých aktualizace firmware vyžaduje akci uživatele:" +msgstr "Zařízení u kterých aktualizace firmware vyžaduje akci uživatele: " #. TRANSLATORS: message letting the user know no device #. * upgrade available due to missing on LVFS @@ -977,7 +1005,7 @@ #. TRANSLATORS: message letting the user know no #. * device upgrade available due to missing on LVFS msgid "Devices with no available firmware updates: " -msgstr "Zařízení, pro která nejsou k dispozici aktualizace firmware:" +msgstr "Zařízení, pro která nejsou k dispozici aktualizace firmware: " #. TRANSLATORS: message letting the user know no device upgrade available #. TRANSLATORS: message letting the user know no device @@ -1243,8 +1271,8 @@ msgstr "SOUBOR CERTIFIKÁT SOUKROMÝ-KLÍČ" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "SOUBOR IDENTIFIKÁTOR-ZAŘÍZENÍ" +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "SOUBOR IDENTIFIKATOR-ZARIZENI [VERZE]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" @@ -1364,7 +1392,7 @@ #. TRANSLATORS: Title: firmware refers to the flash chip in the computer msgid "Firmware BIOS Descriptor" -msgstr "BIOS popisovač firmware" +msgstr "Popisovač ve firmware pro BIOS" #. TRANSLATORS: longer description msgid "Firmware BIOS Descriptor protects device firmware memory from being tampered with." @@ -1372,7 +1400,7 @@ #. TRANSLATORS: Title: SPI refers to the flash chip in the computer msgid "Firmware BIOS Region" -msgstr "BIOS oblast firmware" +msgstr "Oblast ve firmware pro BIOS" #. TRANSLATORS: longer description msgid "Firmware BIOS Region protects device firmware memory from being tampered with." @@ -1384,7 +1412,7 @@ #. TRANSLATORS: program summary msgid "Firmware Update D-Bus Service" -msgstr "Služba D-Bus pro aktualizaci firmwaru" +msgstr "D-Bus služba pro aktualizaci firmwaru" #. TRANSLATORS: program name msgid "Firmware Update Daemon" @@ -1392,7 +1420,7 @@ #. TRANSLATORS: Title: if the fwupd plugins are all present and correct msgid "Firmware Updater Verification" -msgstr "Ověřování nástroje pro aktualizaci firmware" +msgstr "Ověřování nástrojů pro aktualizaci firmware" msgid "Firmware Updater Verification checks that software used for updating has not been tampered with." msgstr "Ověřování nástroje pro aktualizaci firmware kontroluje, že se software, použitým pro aktualizaci, nebylo manipulováno." @@ -1549,8 +1577,8 @@ msgstr "Získat seznam blokovaných firmwarů" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Vypsat seznam aktualizací pro připojený hardware" +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "Získá seznam aktualizací pro určená zařízení (nebo pro všechna, pokud specifikována nejsou)" #. TRANSLATORS: command description msgid "Gets the releases for a device" @@ -1577,7 +1605,6 @@ msgstr "Události zabezpečení na hostiteli" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "Identif. zabezpečení hostitele (HSI) není podporován" @@ -1750,10 +1777,10 @@ #. TRANSLATORS: Title: BootGuard is a trademark from Intel, #. * error policy is what to do on failure msgid "Intel BootGuard Error Policy" -msgstr "Co Intel BootGuard udělá v případě chyb" +msgstr "Co má Intel BootGuard udělat v případě chyb" msgid "Intel BootGuard Error Policy ensures the device does not continue to start if its device software has been tampered with." -msgstr "Zásada chyb Intel BootGuard zajišťuje, že zařízení nebude pokračovat ve startu, pokud se software zařízení bylo manipulováno." +msgstr "Zásada pro případ výskytu chyb Intel BootGuard zajišťuje, že zařízení nebude pokračovat ve startu, pokud se software zařízení bylo manipulováno." #. TRANSLATORS: Title: BootGuard is a trademark from Intel msgid "Intel BootGuard Fuse" @@ -1772,7 +1799,7 @@ #. TRANSLATORS: Title: BootGuard is a trademark from Intel, #. * error policy is what to do on failure msgid "Intel BootGuard error policy" -msgstr "Co Intel BootGuard udělá v případě chyb" +msgstr "Co má Intel BootGuard udělat v případě chyb" #. TRANSLATORS: longer description msgid "Intel BootGuard prevents unauthorized device software from operating when the device is started." @@ -1801,7 +1828,7 @@ #. consumer #. * boards msgid "Intel Management Engine Override" -msgstr "Intel Management Engine – přebití" +msgstr "Intel Management Engine – přebití" #. TRANSLATORS: longer description msgid "Intel Management Engine Override disables checks for device software tampering." @@ -2000,7 +2027,7 @@ #. TRANSLATORS: Title: MEI = Intel Management Engine msgid "MEI manufacturing mode" -msgstr "režim pro výrobce v MEI" +msgstr "režim MEI pro výrobce" #. TRANSLATORS: Title: MEI = Intel Management Engine, and the #. * "override" is the physical PIN that can be driven to @@ -2112,6 +2139,10 @@ msgid "No action specified!" msgstr "Není zadána žádné akce!" +#. TRANSLATORS: no devices that can be upgraded with new firmware +msgid "No devices are updatable" +msgstr "Nejsou aktualizovatelná žádná zařízení" + #. TRANSLATORS: message letting the user know no device downgrade available #. * %1 is the device name #, c-format @@ -2130,6 +2161,10 @@ msgid "No hardware detected with firmware update capability" msgstr "Nezjištěn žádný hardware vybavený pro aktualizaci firmware v něm." +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +msgstr "Není třeba restart" + #. TRANSLATORS: no repositories to download from msgid "No releases available" msgstr "Nejsou k dispozici žádná vydání" @@ -2183,6 +2218,10 @@ msgstr "Stará verze" #. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Instalovat pouze na emulovaná zařízení" + +#. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Zobrazit pouze jedinou PCR hodnotu" @@ -2196,8 +2235,8 @@ msgstr "Zařízení umožňuje pouze aktualizace na novější verze" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Výstup ve formátu JSON" +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Výstup v JSON formátu (vypne veškeré interaktivní výzvy)" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -2244,7 +2283,7 @@ #. TRANSLATORS: prompt to apply the update msgid "Perform operation?" -msgstr "Provést operaci" +msgstr "Provést operaci?" #. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware msgid "Platform Debugging" @@ -2266,9 +2305,10 @@ #. TRANSLATORS: the user isn't reading the question #, c-format msgid "Please enter a number from 0 to %u: " -msgstr "Zadejte prosím číslo od 0 do %u:" +msgstr "Zadejte číslo od 0 do %u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " msgstr "Zadejte buď %s nebo %s: " @@ -2277,6 +2317,10 @@ msgid "Plugin dependencies missing" msgstr "Chybí komponenty, na kterých zásuvný modul závisí" +#. TRANSLATORS: The plugin enumeration might change the device current mode +msgid "Plugin enumeration may change device state" +msgstr "Může se stát, že vyčíslení zásuvného modulu změní stav zařízení" + #. TRANSLATORS: The plugin is only for testing msgid "Plugin is only for testing" msgstr "Zásuvný modul slouží pouze pro testování" @@ -2303,7 +2347,7 @@ #. TRANSLATORS: longer description msgid "Pre-boot DMA protection prevents devices from accessing system memory after being connected to the computer." -msgstr "Ochrana přímého přístupu do paměti (DMA) brání zařízení přistupovat k systémové paměti poté co jsou připojena k počítači." +msgstr "Ochrana přímého přístupu do paměti (DMA) (před zavedením systému) brání zařízení přistupovat k systémové paměti poté co jsou připojena k počítači." #. TRANSLATORS: warning message msgid "Press unlock on the device to continue the update process." @@ -2475,7 +2519,7 @@ #. * command name, e.g. `fwupdmgr get-upgrades` #, c-format msgid "Run `%s` for more information." -msgstr "Podrobnosti získáte spuštěním „%s“." +msgstr "Podrobnosti získáte spuštěním `%s`." #. TRANSLATORS: this is shown in the MOTD -- %1 is the #. * command name, e.g. `fwupdmgr sync` @@ -2534,15 +2578,15 @@ #. TRANSLATORS: Title: SPI refers to the flash chip in the computer msgid "SPI BIOS Descriptor" -msgstr "Popisovač SPI BIOS" +msgstr "Popisovač v SPI pro BIOS" #. TRANSLATORS: Title: SPI refers to the flash chip in the computer msgid "SPI BIOS region" -msgstr "Oblast SPI BIOS" +msgstr "Oblast v SPI pro BIOS" #. TRANSLATORS: Title: SPI refers to the flash chip in the computer msgid "SPI lock" -msgstr "SPI uzamčení" +msgstr "uzamčení SPI" #. TRANSLATORS: Title: if hardware enforces control of SPI replays msgid "SPI replay protection" @@ -2550,7 +2594,7 @@ #. TRANSLATORS: Title: SPI refers to the flash chip in the computer msgid "SPI write" -msgstr "SPI zápis" +msgstr "zápis do SPI" #. TRANSLATORS: Title: if hardware enforces control of SPI writes msgid "SPI write protection" @@ -2766,8 +2810,8 @@ #. TRANSLATORS: success message -- where 'metadata' is information #. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Nová metadata úspěšně stažena:" +msgid "Successfully downloaded new metadata:" +msgstr "Úspěšně stažena nová metadata:" #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" @@ -2834,7 +2878,7 @@ #. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention msgid "Supervisor Mode Access Prevention" -msgstr "Zabránění v přístupu k režimu supervizora" +msgstr "Zabránění v přístupu k režimu supervizoru" #. TRANSLATORS: longer description msgid "Supervisor Mode Access Prevention ensures critical parts of device memory are not accessed by less secure programs." @@ -3036,7 +3080,7 @@ #. TRANSLATORS: longer description msgid "The UEFI system can set up memory attributes at boot which prevent common exploits from running." -msgstr "UEFI systém může nastavovat atributy paměti při startu, které brání spouštění běžných exploitů" +msgstr "UEFI systém může nastavovat atributy paměti při startu, které brání spouštění běžných zneužití zranitelností." #. TRANSLATORS: the user is SOL for support... msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" @@ -3171,7 +3215,7 @@ #. TRANSLATORS: Title: Bootservice is when only readable from early-boot msgid "UEFI Bootservice Variables" -msgstr "UEFI Bootservice Variables" +msgstr "Proměnné UEFI Bootservice" #. TRANSLATORS: partition refers to something on disk, again, hey Arch users msgid "UEFI ESP partition may not be set up correctly" @@ -3367,15 +3411,6 @@ msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" msgstr "Aktualizuje firmware veškerých zadaných zařízení na nejnovější verzi, případně na všech zařízeních, pokud nejsou žádná vyjmenována" -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "Pro %u lokální zařízení byly vydány aktualizace" -msgstr[1] "Pro %u z %u lokálních zařízení byly vydány aktualizace" -msgstr[2] "Pro %u z %u lokálních zařízení byly vydány aktualizace" -msgstr[3] "Pro %u z %u lokálních zařízení byly vydány aktualizace" - msgid "Updating" msgstr "Aktualizuje se" @@ -3509,7 +3544,7 @@ #. TRANSLATORS: show the user a warning msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." -msgstr "Kompatibilita aktualizací firmware s připojenými zařízeními, či vámi používaným systémem, nemusí být poskytovatelem vámi používané distribuce ověřována. " +msgstr "Může být, že kompatibilita aktualizací firmware s připojenými zařízeními, či vámi používaným systémem, není poskytovatelem vámi používané distribuce ověřována." #. TRANSLATORS: %1 is the device vendor name #, c-format diff -Nru fwupd-2.0.8/po/da.po fwupd-2.0.20/po/da.po --- fwupd-2.0.8/po/da.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/da.po 2026-02-26 11:36:18.000000000 +0000 @@ -5,16 +5,20 @@ # Translators: # scootergrisen, 2019 # scootergrisen, 2019-2021 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Danish (http://app.transifex.com/freedesktop/fwupd/language/da/)\n" +"PO-Revision-Date: 2025-10-24 09:59+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Danish \n" +"Language: da\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: da\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -29,7 +33,7 @@ msgstr "%s Opdatering for batteri" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "%s opdatering af CPU-mikrokode" @@ -730,10 +734,6 @@ msgstr "FILNAVN CERTIFIKAT PRIVAT-NØGLE" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "FILNAVN ENHEDS-ID" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME [DEVICE-ID|GUID]" msgstr "FILNAVN [ENHEDS-ID|GUID]" @@ -902,10 +902,6 @@ msgstr "Henter listen over blokerede firmware" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Henter listen over opdateringer for tilsluttet hardware" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "Henter resultaterne fra en enhed" @@ -1210,10 +1206,6 @@ msgstr "Kun versionsopgraderinger er tilladte" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Output i JSON-format" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Tilsidesæt standard-ESP-stien" @@ -1517,11 +1509,6 @@ msgid "Successfully disabled remote" msgstr "Det lykkedes at deaktivere fjern" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Det lykkedes at downloade ny metadata: " - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "Det lykkedes at aktivere og opdatere fjernen" diff -Nru fwupd-2.0.8/po/de.po fwupd-2.0.20/po/de.po --- fwupd-2.0.8/po/de.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/de.po 2026-02-26 11:36:18.000000000 +0000 @@ -10,16 +10,21 @@ # Marco Tedaldi , 2015 # Mario Blättermann , 2025 # Wolfgang Stöggl , 2015 +# Chris Hofstaedtler , 2025. +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: German (http://app.transifex.com/freedesktop/fwupd/language/de/)\n" +"PO-Revision-Date: 2025-10-24 10:02+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: German \n" +"Language: de\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: de\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -40,7 +45,7 @@ msgstr "%s Akku-Aktualisierung" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "%s CPU-Microcode-Aktualisierung" @@ -308,6 +313,22 @@ msgstr[0] "%u Gerät ist nicht die beste bekannte Konfiguration." msgstr[1] "%u Geräte sind nicht die beste bekannte Konfiguration." +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#, c-format +msgid "%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "%u devices are supported in the enabled remotes (an update has been published)" +msgstr[0] "%u Gerät wird in den aktivierten Gegenstellen unterstützt (eine Aktualisierung wurde veröffentlicht)" +msgstr[1] "%u Geräte werden in den aktivierten Gegenstellen unterstützt (eine Aktualisierung wurde veröffentlicht)" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "%u Gerät ist aktualisierbar" +msgstr[1] "%u Geräte sind aktualisierbar" + #. TRANSLATORS: duration in minutes #, c-format msgid "%u hour" @@ -505,7 +526,7 @@ #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to enable emulation data collection" -msgstr "Zum Aktivieren der Emulationsdatenerfassung ist eine Authentifizierung erforderlich." +msgstr "Zum Aktivieren der Emulationsdatenerfassung ist eine Authentifizierung erforderlich" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to fix a host security issue" @@ -582,7 +603,7 @@ #. TRANSLATORS: we can auto-uninhibit after a timeout #, c-format msgid "Automatically uninhibiting in %ums…" -msgstr "Wird in %u ms automatisch entriegelt..." +msgstr "Wird in %u ms automatisch entriegelt…" #. TRANSLATORS: can we JFDI? msgid "Automatically upload every time?" @@ -688,6 +709,10 @@ msgstr "Geändert" #. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "Prüfen, ob für den Abschluss der Aktualisierung ein Neustart der Geräte aussteht" + +#. TRANSLATORS: command description msgid "Checks cryptographic hash matches firmware" msgstr "Überprüft, ob der kryptografische Hash mit der Firmware übereinstimmt" @@ -876,7 +901,7 @@ #. TRANSLATORS: the device cannot update from A->C and has to go A->B->C msgid "Device is required to install all provided releases" -msgstr "Das Gerät muss alle bereitgestellten Versionen nacheinander installieren." +msgstr "Das Gerät muss alle bereitgestellten Versionen nacheinander installieren" #. TRANSLATORS: currently unreachable, perhaps because it is in a lower power #. state @@ -951,7 +976,7 @@ #. TRANSLATORS: message letting the user there is an update #. * waiting, but there is a reason it cannot be deployed msgid "Devices with firmware updates that need user action: " -msgstr "Geräte mit Firmware-Aktualisierungen, die eine Benutzeraktion erfordern:" +msgstr "Geräte mit Firmware-Aktualisierungen, die eine Benutzeraktion erfordern: " #. TRANSLATORS: message letting the user know no device #. * upgrade available due to missing on LVFS @@ -1102,6 +1127,10 @@ msgid "Duration" msgstr "Dauer" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "EMULATION-FILE [ARCHIVE-FILE]" +msgstr "EMULATIONSDATEI [ARCHIVDATEI]" + #. TRANSLATORS: longer description msgid "Each system should have tests to ensure firmware security." msgstr "Jedes System sollte Tests haben, um die Sicherheit der Firmware zu gewährleisten." @@ -1224,8 +1253,8 @@ msgstr "DATEINAME ZERTIFIKAT PRIVATER-SCHLÜSSEL" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "DATEINAME GERÄTEKENNUNG" +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "DATEINAME GERÄTEKENNUNG [VERSION]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" @@ -1329,11 +1358,15 @@ #. TRANSLATORS: command line option msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" -msgstr "Mit einer Reihe von Geräte-Bitschaltern filtern, wobei ein ~-Präfix zum Ausschluss verwendet wird, z. B. 'internal,~needs-reboot'." +msgstr "Mit einer Reihe von Geräte-Bitschaltern filtern, wobei ein ~-Präfix zum Ausschluss verwendet wird, z. B. 'internal,~needs-reboot'" #. TRANSLATORS: command line option msgid "Filter with a set of release flags using a ~ prefix to exclude, e.g. 'trusted-release,~trusted-metadata'" -msgstr "Mit einer Reihe von Veröffentlichungsbitschaltern filtern, wobei ein ~-Präfix zum Ausschluss verwendet wird, z. B. 'trusted-release,~trusted-metadata'." +msgstr "Mit einer Reihe von Veröffentlichungsbitschaltern filtern, wobei ein ~-Präfix zum Ausschluss verwendet wird, z. B. 'trusted-release,~trusted-metadata'" + +#. TRANSLATORS: command description +msgid "Finds firmware releases from the metadata" +msgstr "Findet Firmware-Versionen aus den Metadaten" #. TRANSLATORS: Title: if we can verify the firmware checksums msgid "Firmware Attestation" @@ -1408,7 +1441,7 @@ #. TRANSLATORS: user selected something not possible msgid "Firmware is not already blocked" -msgstr "Firmware ist nicht bereits blockiert." +msgstr "Firmware ist nicht bereits blockiert" #. TRANSLATORS: the metadata is very out of date; %u is a number > 1 #, c-format @@ -1424,7 +1457,7 @@ #. TRANSLATORS: user needs to run a command, %1 is 'fwupdmgr unlock' #, c-format msgid "Firmware updates disabled; run '%s' to enable" -msgstr "Firmware-Aktualisierungen deaktiviert; rufe '%s' auf, um sie zu aktivieren." +msgstr "Firmware-Aktualisierungen deaktiviert; rufe '%s' auf, um sie zu aktivieren" #. TRANSLATORS: command description msgid "Fix a specific host security attribute" @@ -1526,8 +1559,8 @@ msgstr "Holt die Liste der blockierten Firmware" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Ermittelt die Liste der Aktualisierungen für angeschlossene Hardware" +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "Ruft die Liste der Aktualisierungen für alle angegebenen Geräte ab, oder alle Geräte, wenn nicht angegeben" #. TRANSLATORS: command description msgid "Gets the releases for a device" @@ -1554,7 +1587,6 @@ msgstr "Host-Sicherheitsereignisse" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "Host-Sicherheitskennung (HSI) wird nicht unterstützt" @@ -1815,7 +1847,7 @@ #. TRANSLATOR: This is the error message for #. * incorrect parameter msgid "Invalid arguments, expected an AppStream ID" -msgstr "Ungültige Argumente. Es wurde eine AppStream-Kennung erwartet." +msgstr "Ungültige Argumente. Es wurde eine AppStream-Kennung erwartet" #. TRANSLATORS: error message msgid "Invalid arguments, expected at least ARCHIVE FIRMWARE METAINFO" @@ -1840,8 +1872,8 @@ #. TRANSLATORS: issue fixed with the release, e.g. CVE msgid "Issue" msgid_plural "Issues" -msgstr[0] "Anliegen" -msgstr[1] "Anliegen" +msgstr[0] "Problem" +msgstr[1] "Probleme" #. TRANSLATORS: HSI event title msgid "Kernel is no longer tainted" @@ -2087,6 +2119,10 @@ msgid "No action specified!" msgstr "Keine Aktion angegeben!" +#. TRANSLATORS: no devices that can be upgraded with new firmware +msgid "No devices are updatable" +msgstr "Keine Geräte sind aktualisierbar" + #. TRANSLATORS: message letting the user know no device downgrade available #. * %1 is the device name #, c-format @@ -2106,6 +2142,14 @@ msgstr "Es wurde keine Hardware erkannt, deren Firmware aktualisiert werden kann" #. TRANSLATORS: no repositories to download from +msgid "No matching releases for search token" +msgstr "Keine passenden Veröffentlichungen für Suchbegriff" + +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +msgstr "Es ist kein Neustart erforderlich" + +#. TRANSLATORS: no repositories to download from msgid "No releases available" msgstr "Keine Freigaben verfügbar" @@ -2158,6 +2202,10 @@ msgstr "Alte Version" #. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Nur auf emulierten Geräten installieren" + +#. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Nur einzelnen PCR-Wert anzeigen" @@ -2171,8 +2219,8 @@ msgstr "Nur Versionsaktualisierungen sind erlaubt" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Ausgabe im JSON-Format" +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Ausgabe im JSON-Format (deaktiviert alle interaktiven Eingabeaufforderungen)" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -2243,7 +2291,8 @@ msgid "Please enter a number from 0 to %u: " msgstr "Bitte geben Sie eine Zahl von 0 bis %u ein: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " msgstr "Bitte geben Sie entweder %s oder %s ein: " @@ -2252,6 +2301,10 @@ msgid "Plugin dependencies missing" msgstr "Fehlende Plugin-Abhängigkeiten" +#. TRANSLATORS: The plugin enumeration might change the device current mode +msgid "Plugin enumeration may change device state" +msgstr "Plugin-Aufzählung kann den Gerätestatus ändern" + #. TRANSLATORS: The plugin is only for testing msgid "Plugin is only for testing" msgstr "Plugin ist nur zum Testen" @@ -2741,8 +2794,8 @@ #. TRANSLATORS: success message -- where 'metadata' is information #. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Neue Metadaten wurden erfolgreich heruntergeladen: " +msgid "Successfully downloaded new metadata:" +msgstr "Neue Metadaten erfolgreich heruntergeladen:" #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" @@ -2750,7 +2803,7 @@ #. TRANSLATORS: success message msgid "Successfully enabled remote" -msgstr "Erfolgreich aktivierte Gegenstelle " +msgstr "Erfolgreich aktivierte Gegenstelle" #. TRANSLATORS: comment explaining result of command msgid "Successfully enabled test devices" @@ -2997,9 +3050,17 @@ msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." msgstr "Der UEFI-Plattformschlüssel wird verwendet, um festzustellen, ob die Gerätesoftware aus einer vertrauenswürdigen Quelle stammt." +#. TRANSLATORS: HSI event title +msgid "The UEFI certificate store is now up to date" +msgstr "Der UEFI-Zertifikatspeicher ist jetzt auf dem neuesten Stand" + +#. TRANSLATORS: longer description +msgid "The UEFI db contains the list of valid certificates that can be used to authorize what EFI binaries are allowed to run." +msgstr "Die UEFI-DB enthält die Liste der gültigen Zertifikate, die zur Autorisierung der EFI-Binärdateien verwendet werden können." + #. TRANSLATORS: longer description msgid "The UEFI system can set up memory attributes at boot which prevent common exploits from running." -msgstr "Das UEFI-System kann beim Booten Speicherattribute einrichten, die die Ausführung gängiger Exploits verhindern." +msgstr "Das UEFI-System kann beim Booten Speicherattribute zur Verhinderung gängiger Exploits einrichten." #. TRANSLATORS: the user is SOL for support... msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" @@ -3172,6 +3233,10 @@ msgid "UEFI capsule updates not available or enabled in firmware setup" msgstr "Aktualisierung der UEFI-Kapsel nicht verfügbar oder in der Firmware-Einrichtung aktiviert" +#. TRANSLATORS: Title: is UEFI db up-to-date +msgid "UEFI db" +msgstr "UEFI-DB" + #. TRANSLATORS: program name msgid "UEFI dbx Utility" msgstr "UEFI dbx-Dienstprogramm" @@ -3326,13 +3391,6 @@ msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" msgstr "Aktualisiert alle angegebenen Geräte auf die neueste Firmware-Version, oder alle Geräte, wenn nicht angegeben" -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "Es wurden Aktualisierungen für %u lokales Gerät veröffentlicht" -msgstr[1] "Es wurden Aktualisierungen für %u von %u der lokalen Geräte veröffentlicht" - msgid "Updating" msgstr "Wird aktualisiert" @@ -3431,6 +3489,10 @@ msgid "WARNING" msgstr "WARNUNG" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "WORD" +msgstr "WORT" + #. TRANSLATORS: command description msgid "Wait for a device to appear" msgstr "Warten, bis ein Gerät erscheint" diff -Nru fwupd-2.0.8/po/en_GB.po fwupd-2.0.20/po/en_GB.po --- fwupd-2.0.8/po/en_GB.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/en_GB.po 2026-02-26 11:36:18.000000000 +0000 @@ -6,16 +6,20 @@ # Andi Chandler , 2019-2020,2022 # Bruce Cowan , 2024 # Richard Hughes , 2015,2017-2025 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: English (United Kingdom) (http://app.transifex.com/freedesktop/fwupd/language/en_GB/)\n" +"PO-Revision-Date: 2025-10-24 10:02+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: English (United Kingdom) \n" +"Language: en_GB\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: en_GB\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -36,7 +40,7 @@ msgstr "%s Battery Update" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "%s CPU Microcode Update" @@ -304,6 +308,22 @@ msgstr[0] "%u device is not the best known configuration." msgstr[1] "%u devices are not the best known configuration." +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#, c-format +msgid "%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "%u devices are supported in the enabled remotes (an update has been published)" +msgstr[0] "%u device is supported in the enabled remotes (an update has been published)" +msgstr[1] "%u devices are supported in the enabled remotes (an update has been published)" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "%u device is updatable" +msgstr[1] "%u devices are updatable" + #. TRANSLATORS: duration in minutes #, c-format msgid "%u hour" @@ -684,6 +704,10 @@ msgstr "Changed" #. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "Check if any devices are pending a reboot to complete update" + +#. TRANSLATORS: command description msgid "Checks cryptographic hash matches firmware" msgstr "Checks cryptographic hash matches firmware" @@ -1224,8 +1248,8 @@ msgstr "FILENAME CERTIFICATE PRIVATE-KEY" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "FILENAME DEVICE-ID" +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "FILENAME DEVICE-ID [VERSION]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" @@ -1335,6 +1359,10 @@ msgid "Filter with a set of release flags using a ~ prefix to exclude, e.g. 'trusted-release,~trusted-metadata'" msgstr "Filter with a set of release flags using a ~ prefix to exclude, e.g. 'trusted-release,~trusted-metadata'" +#. TRANSLATORS: command description +msgid "Finds firmware releases from the metadata" +msgstr "Finds firmware releases from the metadata" + #. TRANSLATORS: Title: if we can verify the firmware checksums msgid "Firmware Attestation" msgstr "Firmware Attestation" @@ -1526,8 +1554,8 @@ msgstr "Gets the list of blocked firmware" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Gets the list of updates for connected hardware" +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "Gets the list of updates for all specified devices, or all devices if unspecified" #. TRANSLATORS: command description msgid "Gets the releases for a device" @@ -1554,7 +1582,6 @@ msgstr "Host Security Events" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "Host Security ID (HSI) is not supported" @@ -2087,6 +2114,10 @@ msgid "No action specified!" msgstr "No action specified!" +#. TRANSLATORS: no devices that can be upgraded with new firmware +msgid "No devices are updatable" +msgstr "No devices are updatable" + #. TRANSLATORS: message letting the user know no device downgrade available #. * %1 is the device name #, c-format @@ -2106,6 +2137,14 @@ msgstr "No hardware detected with firmware update capability" #. TRANSLATORS: no repositories to download from +msgid "No matching releases for search token" +msgstr "No matching releases for search token" + +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +msgstr "No reboot is necessary" + +#. TRANSLATORS: no repositories to download from msgid "No releases available" msgstr "No releases available" @@ -2158,6 +2197,10 @@ msgstr "Old version" #. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Only install onto emulated devices" + +#. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Only show single PCR value" @@ -2171,8 +2214,8 @@ msgstr "Only version upgrades are allowed" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Output in JSON format" +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Output in JSON format (disables all interactive prompts)" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -2243,7 +2286,8 @@ msgid "Please enter a number from 0 to %u: " msgstr "Please enter a number from 0 to %u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " msgstr "Please enter either %s or %s: " @@ -2252,6 +2296,10 @@ msgid "Plugin dependencies missing" msgstr "Plug-in dependencies missing" +#. TRANSLATORS: The plugin enumeration might change the device current mode +msgid "Plugin enumeration may change device state" +msgstr "Plugin enumeration may change device state" + #. TRANSLATORS: The plugin is only for testing msgid "Plugin is only for testing" msgstr "Plug-in is only for testing" @@ -2741,8 +2789,8 @@ #. TRANSLATORS: success message -- where 'metadata' is information #. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Successfully downloaded new metadata: " +msgid "Successfully downloaded new metadata:" +msgstr "Successfully downloaded new metadata:" #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" @@ -3338,13 +3386,6 @@ msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" msgstr "Updates all specified devices to latest firmware version, or all devices if unspecified" -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "Updates have been published for %u local device" -msgstr[1] "Updates have been published for %u of %u local devices" - msgid "Updating" msgstr "Updating" @@ -3443,6 +3484,10 @@ msgid "WARNING" msgstr "WARNING" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "WORD" +msgstr "WORD" + #. TRANSLATORS: command description msgid "Wait for a device to appear" msgstr "Wait for a device to appear" diff -Nru fwupd-2.0.8/po/eo.po fwupd-2.0.20/po/eo.po --- fwupd-2.0.8/po/eo.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/eo.po 2026-02-26 11:36:18.000000000 +0000 @@ -8,12 +8,15 @@ msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Esperanto (http://www.transifex.com/freedesktop/fwupd/language/eo/)\n" +"PO-Revision-Date: 2025-10-24 10:02+0000\n" +"Last-Translator: Anonymous \n" +"Language-Team: Esperanto \n" +"Language: eo\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: eo\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: duration in seconds #, c-format diff -Nru fwupd-2.0.8/po/es.po fwupd-2.0.20/po/es.po --- fwupd-2.0.8/po/es.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/es.po 2026-02-26 11:36:18.000000000 +0000 @@ -11,16 +11,21 @@ # Francisco Serrador , 2022 # Giovanni Alfredo Garciliano Diaz , 2024 # Santiago FN, 2024 +# Richard Hughes , 2025. +# Francisco Serrador , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Spanish (http://app.transifex.com/freedesktop/fwupd/language/es/)\n" +"PO-Revision-Date: 2025-11-18 20:51+0000\n" +"Last-Translator: Francisco Serrador \n" +"Language-Team: Spanish \n" +"Language: es\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: es\n" -"Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" +"Plural-Forms: nplurals=3; plural=(n == 1) ? 0 : ((n != 0 && n % 1000000 == 0) ? 1 : 2);\n" +"X-Generator: Weblate 5.15-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -42,7 +47,7 @@ msgstr "Actualización %s de Batería" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "Actualización %s de Mmicrocódigo CPU" @@ -310,7 +315,7 @@ msgid "%u device is not the best known configuration." msgid_plural "%u devices are not the best known configuration." msgstr[0] "%u dispositivo no es la mejor configuración conocida." -msgstr[1] "%u dispositivos no son la mejor configuración conocida" +msgstr[1] "%u dispositivos no son la mejor configuración conocida." msgstr[2] "%u dispositivos no son la mejor configuración conocida." #. TRANSLATORS: duration in minutes @@ -400,7 +405,7 @@ #. TRANSLATORS: command description msgid "Adds devices to watch for future emulation" -msgstr "Añade dispositivos que vigilen para emulación futura." +msgstr "Añade dispositivos que vigilen para emulación futura" #. TRANSLATORS: the age of the metadata msgid "Age" @@ -485,9 +490,9 @@ #. * the domain administrator msgid "Approved firmware:" msgid_plural "Approved firmware:" -msgstr[0] "Firmware aprobado" -msgstr[1] "Firmware aprobado" -msgstr[2] "Firmware aprobado" +msgstr[0] "Firmware aprobado:" +msgstr[1] "Firmware aprobados:" +msgstr[2] "Firmware aprobados:" #. TRANSLATORS: command description msgid "Asks the daemon to quit" @@ -589,6 +594,11 @@ msgid "Automatic Reporting" msgstr "Informes Automáticos" +#. TRANSLATORS: we can auto-uninhibit after a timeout +#, c-format +msgid "Automatically uninhibiting in %ums…" +msgstr "Inhibido automáticamente en %u ms…" + #. TRANSLATORS: can we JFDI? msgid "Automatically upload every time?" msgstr "¿Subo automáticamente cada vez?" @@ -1107,6 +1117,10 @@ msgid "Duration" msgstr "Duración" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "EMULATION-FILE [ARCHIVE-FILE]" +msgstr "EMULACIÓN-ARCHIVO [ARCHIVADOR-ARCHIVO]" + #. TRANSLATORS: longer description msgid "Each system should have tests to ensure firmware security." msgstr "Cada sistema tendría pruebas para verificar la seguridad del firmware." @@ -1229,10 +1243,6 @@ msgstr "ID-ARCHIVO CERTIFICA LLAVE-PRIVADA" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "ID-NOMBRE ID-DISPOSITIVO" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" msgstr "NOMBRE-ARCHIVO DESPLAZAMIENTO [TIPO-FIRMWARE]" @@ -1279,7 +1289,7 @@ #. TRANSLATORS: another fwupdtool instance is already running msgid "Failed to lock" -msgstr "Error al bloquear" +msgstr "Incorrecto al bloquear" #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" @@ -1301,7 +1311,7 @@ #. TRANSLATORS: could not parse file msgid "Failed to parse local dbx" -msgstr "Intérprete erróneo del dbx local" +msgstr "Intérprete incorrecto del dbx local" #. TRANSLATORS: a feature is something like "can show an image" msgid "Failed to set front-end features" @@ -1310,7 +1320,7 @@ #. TRANSLATORS: something with a blocked hash exists #. * in the users ESP -- which would be bad! msgid "Failed to validate ESP contents" -msgstr "Error al validar el contenido de ESP" +msgstr "Incorrecto al validar el contenido de ESP" #. TRANSLATORS: item is FALSE msgid "False" @@ -1401,7 +1411,7 @@ #. TRANSLATORS: longer description msgid "Firmware Write Protection protects device firmware memory from being tampered with." -msgstr "Protección de Escritura del Firmware del de la memoria del firmware del dispositivo con el que es condonado. " +msgstr "Protección de Escritura del Firmware del de la memoria del firmware del dispositivo con el que es condonado." #. TRANSLATORS: Title: if we can verify the firmware checksums msgid "Firmware attestation" @@ -1533,10 +1543,6 @@ msgstr "Obtiene el listado de firmware bloqueado" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Obtiene el listado de actualizaciones para hardware conectado" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "Obtiene las publicaciones para un dispositivo" @@ -1561,7 +1567,6 @@ msgstr "Eventos de Seguridad de Hospedaje" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "ID de Seguridad de Hospedaje (HSI) no está mantenido" @@ -1941,7 +1946,7 @@ #. TRANSLATORS: command description msgid "List the available firmware GTypes" -msgstr "Enumera los tipos GType del firmware que estén disponibles." +msgstr "Enumera los tipos GType del firmware que estén disponibles" #. TRANSLATORS: command description msgid "List the available firmware types" @@ -2155,7 +2160,7 @@ #. TRANSLATORS: Suffix: the HSI result msgid "OK" -msgstr "APROBADO" +msgstr "Aceptar" #. TRANSLATORS: this is for the device tests msgid "OK!" @@ -2166,6 +2171,10 @@ msgstr "Versión anterior" #. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Solamente instalar en dispositivos emulados" + +#. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Solamente muestra el único valor de PCR" @@ -2179,8 +2188,8 @@ msgstr "Solamente las modernizaciones de versión están permitidas" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Formato de salida en JSON" +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Salida en formato JSON (desactiva todos los intérpretes interactivos)" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -2251,7 +2260,8 @@ msgid "Please enter a number from 0 to %u: " msgstr "Introduzca un número desde 0 hasta %u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " msgstr "Introduzca o bien %s o bien %s:" @@ -2393,7 +2403,7 @@ #. TRANSLATORS: command description msgid "Removes devices to watch for future emulation" -msgstr "Extrae dispositivos para vigilar en emulación futura." +msgstr "Extrae dispositivos para vigilar en emulación futura" #. TRANSLATORS: URI to send success/failure reports msgid "Report URI" @@ -2440,7 +2450,7 @@ #. TRANSLATORS: command description msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" -msgstr "Obtiene la configuración de la BIOS. Si no se proporciona ningún argumente se devuelve toda la configuración." +msgstr "Obtiene la configuración de la BIOS. Si no se proporciona ningún argumente se devuelve toda la configuración" #. TRANSLATORS: command description msgid "Return all the hardware IDs for the machine" @@ -2491,6 +2501,10 @@ msgid "Runtime Suffix" msgstr "Sufijo de tiempo de ejecución" +#. TRANSLATORS: Software Bill of Materials link +msgid "SBOM" +msgstr "SBOM" + #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "SECTION" msgstr "SECCIÓN" @@ -2573,7 +2587,7 @@ #. TRANSLATORS: the %1 is a URL to a wiki page #, c-format msgid "See %s for more details." -msgstr "Vea %s para más detalles." +msgstr "Consulte %s para más detalles." #. TRANSLATORS: %s is a link to a website #, c-format @@ -2602,7 +2616,7 @@ msgstr "Fije un parámetro de la BIOS" msgid "Set one or more BIOS settings" -msgstr "Fije uno o más parámetros de BIOS" +msgstr "Establece uno o más parámetros de BIOS" #. TRANSLATORS: command description msgid "Set or remove an EFI boot hive entry" @@ -2743,11 +2757,6 @@ msgid "Successfully disabled test devices" msgstr "Se desactivaron correctamente los dispositivos de prueba" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Descarga correctamente metadatos nuevos: " - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "Remoto activado y recargado correctamente" @@ -3003,6 +3012,18 @@ msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." msgstr "La Clave de Plataforma UEFI es utilizada para determinar si el software del dispositivo viene desde un origen confiado." +#. TRANSLATORS: HSI event title +msgid "The UEFI certificate store is now up to date" +msgstr "El almacenaje de certificado UEFI está ahora actualizado" + +#. TRANSLATORS: longer description +msgid "The UEFI db contains the list of valid certificates that can be used to authorize what EFI binaries are allowed to run." +msgstr "La BdD UEFI contiene el listado de certificados válidos que pueden ser utilizados para autorizar que binarios EFI están permitidos a ejecutar." + +#. TRANSLATORS: longer description +msgid "The UEFI system can set up memory attributes at boot which prevent common exploits from running." +msgstr "El sistema UEFI puede configurar atributos de memoria al arrancar lo cual previene exploits comunes desde ejecuciones." + #. TRANSLATORS: the user is SOL for support... msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" msgstr "¡El demonio ha cargado código de 3ª parte y ya no está mantenido por los desarrolladores actuales!" @@ -3144,7 +3165,11 @@ #. TRANSLATORS: partition refers to something on disk, again, hey Arch users msgid "UEFI ESP partition not detected or configured" -msgstr "Partición ESP UEFI no detectada o configurada" +msgstr "Partición ESP de la UEFI no detectada o configurada" + +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI Memory Protection" +msgstr "Protección de Memoria UEFI" #. TRANSLATORS: Title: PK is the 'platform key' for the machine msgid "UEFI Platform Key" @@ -3170,6 +3195,10 @@ msgid "UEFI capsule updates not available or enabled in firmware setup" msgstr "Actualizaciones de cápsulas UEFI no disponible o activada en configuración de firmware" +#. TRANSLATORS: Title: is UEFI db up-to-date +msgid "UEFI db" +msgstr "BdD UEFI" + #. TRANSLATORS: program name msgid "UEFI dbx Utility" msgstr "Utilidad dbx UEFI" @@ -3178,6 +3207,26 @@ msgid "UEFI firmware can not be updated in legacy BIOS mode" msgstr "Firmware UEFI no puede ser actualizado en modo de BIOS heredada" +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI memory protection" +msgstr "Protección de memoria UEFI" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled and locked" +msgstr "Protección de memoria UEFI activada y bloqueada" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled but not locked" +msgstr "Protección de memoria UEFI activada pero no bloqueada" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now locked" +msgstr "Ahora la protección de memoria UEFI está bloqueada" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now unlocked" +msgstr "Ahora la protección de memoria UEFI está desbloqueada" + #. TRANSLATORS: Title: PK is the 'platform key' for the machine msgid "UEFI platform key" msgstr "Clave UEFI de la plataforma" @@ -3304,14 +3353,6 @@ msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" msgstr "Actualiza todos los dispositivos especificados a la última versión del firmware, o todos los dispositivos si no especificados" -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "Ha sido publicada %u actualización de dispositivo local" -msgstr[1] "Han sido publicadas %u actualizaciones de %u dispositivos locales" -msgstr[2] "Han sido publicadas %u actualizaciones de %u dispositivos locales" - msgid "Updating" msgstr "Actualizando" @@ -3384,7 +3425,7 @@ #. TRANSLATORS: ESP refers to the EFI System Partition msgid "Validating ESP contents…" -msgstr "Validación de contenidos ESP…" +msgstr "Validación de contenido ESP…" #. TRANSLATORS: one line variant of release (e.g. 'China') msgid "Variant" @@ -3408,7 +3449,7 @@ #. TRANSLATORS: this is a prefix on the console msgid "WARNING" -msgstr "AVISO:" +msgstr "AVISO" #. TRANSLATORS: command description msgid "Wait for a device to appear" @@ -3492,6 +3533,14 @@ msgstr "[ARCHIVO-NOMBRE1] [ARCHIVO-NOMBRE2]" #. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FWUPD-VERSION]" +msgstr "[VERSIÓN-FWUPD]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[REASON] [TIMEOUT]" +msgstr "[RAZÓN] [T_AGOTADO]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "[SECTION] KEY VALUE" msgstr "[SECCIÓN] CLAVE VALOR" diff -Nru fwupd-2.0.8/po/et.po fwupd-2.0.20/po/et.po --- fwupd-2.0.8/po/et.po 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/po/et.po 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,4605 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# Priit Jõerüüt , 2025. +# Richard Hughes , 2025. +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-10-08 22:17+0100\n" +"PO-Revision-Date: 2025-10-30 04:26+0000\n" +"Last-Translator: Priit Jõerüüt \n" +"Language-Team: Estonian \n" +"Language: et\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" + +#: data/remotes.d/lvfs.metainfo.xml:6 +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "" + +#. TRANSLATORS: do not translate the variables marked using $ +#: data/remotes.d/lvfs.metainfo.xml:12 +#: data/remotes.d/lvfs-testing.metainfo.xml:12 +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 "" + +#: data/remotes.d/lvfs.metainfo.xml:19 +#: data/remotes.d/lvfs-testing.metainfo.xml:25 +msgid "" +"Enabling this functionality is done at your own risk, which means you have " +"to contact your original equipment manufacturer regarding any problems " +"caused by these updates. Only problems with the update process itself should " +"be filed at $OS_RELEASE:BUG_REPORT_URL$." +msgstr "" + +#: data/remotes.d/lvfs-testing.metainfo.xml:6 +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "" + +#: data/remotes.d/lvfs-testing.metainfo.xml:19 +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: description of a BIOS setting +#: libfwupdplugin/fu-bios-settings.c:325 +msgid "Settings will apply after system reboots" +msgstr "" + +#. TRANSLATORS: description of a BIOS setting +#: libfwupdplugin/fu-bios-settings.c:329 +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "" + +#: libfwupdplugin/fu-bios-settings.c:339 +msgid "Enable" +msgstr "Võta kasutusele" + +#. TRANSLATORS: if the remote is enabled +#. TRANSLATORS: Suffix: the HSI result +#: libfwupdplugin/fu-bios-settings.c:342 src/fu-util-common.c:2041 +#: src/fu-util-common.c:2191 +msgid "Enabled" +msgstr "Kasutusel" + +#: policy/org.freedesktop.fwupd.policy.in:17 +msgid "Stop the fwupd service" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:19 +msgid "Authentication is required to stop the firmware update service" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:28 +msgid "Install signed system firmware" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:30 +#: policy/org.freedesktop.fwupd.policy.in:41 +msgid "Authentication is required to update the firmware on this machine" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:39 +msgid "Install unsigned system firmware" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:51 +msgid "Install old version of signed system firmware" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:53 +#: policy/org.freedesktop.fwupd.policy.in:65 +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:63 +msgid "Install old version of unsigned system firmware" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:75 +#: policy/org.freedesktop.fwupd.policy.in:98 +msgid "Install signed device firmware" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:77 +#: policy/org.freedesktop.fwupd.policy.in:88 +msgid "Authentication is required to update the firmware on a removable device" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:86 +#: policy/org.freedesktop.fwupd.policy.in:109 +msgid "Install unsigned device firmware" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:100 +#: policy/org.freedesktop.fwupd.policy.in:111 +msgid "" +"Authentication is required to downgrade the firmware on a removable device" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:121 +msgid "Unlock the device to allow access" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:123 +msgid "Authentication is required to unlock a device" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:132 +msgid "Modify daemon configuration" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:134 +msgid "Authentication is required to modify daemon configuration" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:144 +msgid "Reset daemon configuration" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:146 +msgid "Authentication is required to reset daemon configuration to defaults" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:156 +msgid "Activate the new firmware on the device" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:158 +msgid "Authentication is required to switch to the new firmware version" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:167 +msgid "Update the stored device verification information" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:169 +msgid "" +"Authentication is required to update the stored checksums for the device" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:178 +msgid "Modify a configured remote" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:180 +msgid "" +"Authentication is required to modify a configured remote used for firmware " +"updates" +msgstr "" + +#. TRANSLATORS: firmware approved by the admin +#: policy/org.freedesktop.fwupd.policy.in:189 src/fu-util.c:5639 +msgid "Sets the list of approved firmware" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:191 +msgid "Authentication is required to set the list of approved firmware" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:200 +msgid "Sign data using the client certificate" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:202 +msgid "Authentication is required to sign data using the client certificate" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:211 +msgid "Get BIOS settings" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:213 +msgid "Authentication is required to read BIOS settings" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:222 +msgid "Set one or more BIOS settings" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:224 +msgid "Authentication is required to modify BIOS settings" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:234 +#: policy/org.freedesktop.fwupd.policy.in:245 +msgid "Security hardening for HSI" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:236 +msgid "Authentication is required to fix a host security issue" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:247 +msgid "Authentication is required to undo the fix for a host security issue" +msgstr "" + +#. TRANSLATORS: command description +#: policy/org.freedesktop.fwupd.policy.in:256 src/fu-tool.c:5639 +#: src/fu-util.c:5775 +msgid "Load device emulation data" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:258 +msgid "Authentication is required to load hardware emulation data" +msgstr "" + +#. TRANSLATORS: command description +#: policy/org.freedesktop.fwupd.policy.in:267 src/fu-util.c:5782 +msgid "Save device emulation data" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:269 +msgid "Authentication is required to save hardware emulation data" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:279 +msgid "Enable emulation data collection" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:281 +msgid "Authentication is required to enable emulation data collection" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/tpm/fu-tpm-eventlog.c:102 plugins/uefi-dbx/fu-dbxtool.c:112 +#: src/fu-util.c:5231 +msgid "Show extra debugging information" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/tpm/fu-tpm-eventlog.c:110 +msgid "Only show single PCR value" +msgstr "" + +#. TRANSLATORS: we're poking around as a power user +#: plugins/tpm/fu-tpm-eventlog.c:123 plugins/uefi-dbx/fu-dbxtool.c:248 +#: src/fu-tool.c:6023 +msgid "This program may only work correctly as root" +msgstr "" + +#. TRANSLATORS: program name +#: plugins/tpm/fu-tpm-eventlog.c:127 +msgid "fwupd TPM event log utility" +msgstr "" + +#. TRANSLATORS: CLI description +#: plugins/tpm/fu-tpm-eventlog.c:131 +msgid "" +"This tool will read and parse the TPM event log from the system firmware." +msgstr "" + +#. TRANSLATORS: the user didn't read the man page +#: plugins/tpm/fu-tpm-eventlog.c:135 plugins/uefi-dbx/fu-dbxtool.c:184 +#: src/fu-tool.c:5888 src/fu-util.c:5870 +msgid "Failed to parse arguments" +msgstr "" + +#. TRANSLATORS: failed to read measurements file +#: plugins/tpm/fu-tpm-eventlog.c:149 +msgid "Failed to parse file" +msgstr "" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +#: plugins/uefi-capsule/fu-uefi-capsule-plugin.c:589 +msgid "Installing firmware update…" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/uefi-dbx/fu-dbxtool.c:120 +msgid "Show the calculated version of the dbx" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/uefi-dbx/fu-dbxtool.c:128 +msgid "List entries in dbx" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/uefi-dbx/fu-dbxtool.c:136 +msgid "Apply update files" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/uefi-dbx/fu-dbxtool.c:144 +msgid "Specify the dbx database file" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: plugins/uefi-dbx/fu-dbxtool.c:146 src/fu-util.c:5773 src/fu-util.c:5780 +msgid "FILENAME" +msgstr "FAILINIMI" + +#. TRANSLATORS: command line option +#: plugins/uefi-dbx/fu-dbxtool.c:153 +msgid "Override the default ESP path" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: plugins/uefi-dbx/fu-dbxtool.c:155 +msgid "PATH" +msgstr "ASUKOHT" + +#. TRANSLATORS: command line option +#: plugins/uefi-dbx/fu-dbxtool.c:162 +msgid "Apply update even when not advised" +msgstr "" + +#. TRANSLATORS: description of dbxtool +#: plugins/uefi-dbx/fu-dbxtool.c:177 +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "" + +#. TRANSLATORS: program name +#: plugins/uefi-dbx/fu-dbxtool.c:180 +msgid "UEFI dbx Utility" +msgstr "" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +#: plugins/uefi-dbx/fu-dbxtool.c:209 plugins/uefi-dbx/fu-dbxtool.c:279 +msgid "Failed to load local dbx" +msgstr "" + +#. TRANSLATORS: could not read existing system data +#: plugins/uefi-dbx/fu-dbxtool.c:218 plugins/uefi-dbx/fu-dbxtool.c:270 +msgid "Failed to load system dbx" +msgstr "" + +#. TRANSLATORS: the detected version number of the dbx +#: plugins/uefi-dbx/fu-dbxtool.c:225 +msgid "Version" +msgstr "Versioon" + +#. TRANSLATORS: user did not include a filename parameter +#: plugins/uefi-dbx/fu-dbxtool.c:261 +msgid "Filename required" +msgstr "" + +#. TRANSLATORS: reading existing dbx from the system +#: plugins/uefi-dbx/fu-dbxtool.c:266 +msgid "Parsing system dbx…" +msgstr "" + +#. TRANSLATORS: reading new dbx from the update +#: plugins/uefi-dbx/fu-dbxtool.c:275 +msgid "Parsing dbx update…" +msgstr "" + +#. TRANSLATORS: could not parse file +#: plugins/uefi-dbx/fu-dbxtool.c:288 +msgid "Failed to parse local dbx" +msgstr "" + +#. TRANSLATORS: same or newer update already applied +#: plugins/uefi-dbx/fu-dbxtool.c:296 +msgid "Cannot apply as dbx update has already been applied." +msgstr "" + +#. TRANSLATORS: ESP refers to the EFI System Partition +#: plugins/uefi-dbx/fu-dbxtool.c:303 +msgid "Validating ESP contents…" +msgstr "" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +#: plugins/uefi-dbx/fu-dbxtool.c:311 +msgid "Failed to validate ESP contents" +msgstr "" + +#. TRANSLATORS: actually sending the update to the hardware +#: plugins/uefi-dbx/fu-dbxtool.c:318 +msgid "Applying update…" +msgstr "" + +#. TRANSLATORS: dbx file failed to be applied as an update +#: plugins/uefi-dbx/fu-dbxtool.c:330 +msgid "Failed to apply update" +msgstr "" + +#. TRANSLATORS: success +#: plugins/uefi-dbx/fu-dbxtool.c:335 +msgid "Done!" +msgstr "Tehtud!" + +#. TRANSLATORS: user did not tell the tool what to do +#: plugins/uefi-dbx/fu-dbxtool.c:342 +msgid "No action specified!" +msgstr "" + +#. TRANSLATORS: the user isn't reading the question +#: src/fu-console.c:194 +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "" + +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' +#: src/fu-console.c:231 +#, c-format +msgid "Please enter either %s or %s: " +msgstr "" + +#. TRANSLATORS: daemon is inactive +#: src/fu-console.c:363 +msgid "Idle…" +msgstr "Jõude…" + +#. TRANSLATORS: decompressing the firmware file +#: src/fu-console.c:367 +msgid "Decompressing…" +msgstr "Lahtipakkimisel…" + +#. TRANSLATORS: parsing the firmware information +#: src/fu-console.c:371 +msgid "Loading…" +msgstr "Laadin andmeid…" + +#. TRANSLATORS: restarting the device to pick up new F/W +#: src/fu-console.c:375 +msgid "Restarting device…" +msgstr "" + +#. TRANSLATORS: reading from the flash chips +#: src/fu-console.c:379 +msgid "Reading…" +msgstr "Loen andmeid…" + +#. TRANSLATORS: writing to the flash chips +#: src/fu-console.c:383 +msgid "Writing…" +msgstr "Kirjutan andmeid…" + +#. TRANSLATORS: erasing contents of the flash chips +#: src/fu-console.c:387 +msgid "Erasing…" +msgstr "Kustutan andmeid…" + +#. TRANSLATORS: verifying we wrote the firmware correctly +#: src/fu-console.c:391 +msgid "Verifying…" +msgstr "Kontrollin salvestatu korrektsust…" + +#. TRANSLATORS: scheduling an update to be done on the next boot +#: src/fu-console.c:395 +msgid "Scheduling…" +msgstr "Ajastan järgmiseks käivituseks…" + +#. TRANSLATORS: downloading from a remote server +#: src/fu-console.c:399 +msgid "Downloading…" +msgstr "Laadin alla…" + +#. TRANSLATORS: waiting for user to authenticate +#: src/fu-console.c:403 +msgid "Authenticating…" +msgstr "" + +#. TRANSLATORS: waiting for device to do something +#: src/fu-console.c:408 +msgid "Waiting…" +msgstr "" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +#. TRANSLATORS: Suffix: the fallback HSI result +#: src/fu-console.c:415 src/fu-util-common.c:1729 src/fu-util-common.c:1769 +#: src/fu-util-common.c:2257 +msgid "Unknown" +msgstr "" + +#. TRANSLATORS: time remaining for completing firmware flash +#: src/fu-console.c:469 +msgid "Less than one minute remaining" +msgstr "" + +#. TRANSLATORS: more than a minute +#: src/fu-console.c:474 +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: this is a prefix on the console +#: src/fu-console.c:555 +msgid "WARNING" +msgstr "" + +#. TRANSLATORS: turn on all debugging +#: src/fu-debug.c:241 +msgid "Show debugging information for all domains" +msgstr "" + +#. TRANSLATORS: turn on all debugging +#: src/fu-debug.c:249 +msgid "Do not include timestamp prefix" +msgstr "" + +#. TRANSLATORS: turn on all debugging +#: src/fu-debug.c:257 +msgid "Do not include log domain prefix" +msgstr "" + +#. TRANSLATORS: this is for daemon development +#: src/fu-debug.c:265 +msgid "Show daemon verbose information for a particular domain" +msgstr "" + +#. TRANSLATORS: for the --verbose arg +#: src/fu-debug.c:345 +msgid "Debugging Options" +msgstr "" + +#. TRANSLATORS: for the --verbose arg +#: src/fu-debug.c:347 +msgid "Show debugging options" +msgstr "" + +#. TRANSLATORS: this is shown in the MOTD +#: src/fu-engine-helper.c:179 +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr sync` +#: src/fu-engine-helper.c:187 +#, c-format +msgid "Run `%s` to complete this action." +msgstr "" + +#. TRANSLATORS: this is shown in the MOTD +#: src/fu-engine-helper.c:194 +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr get-upgrades` +#: src/fu-engine-helper.c:202 +#, c-format +msgid "Run `%s` for more information." +msgstr "" + +#. TRANSLATORS: exit after we've started up, used for user profiling +#: src/fu-main.c:117 +msgid "Exit after a small delay" +msgstr "" + +#. TRANSLATORS: exit straight away, used for automatic profiling +#: src/fu-main.c:125 +msgid "Exit after the engine has loaded" +msgstr "" + +#. TRANSLATORS: program name +#: src/fu-main.c:143 +msgid "Firmware Update Daemon" +msgstr "" + +#. TRANSLATORS: program summary +#: src/fu-main.c:148 +msgid "Firmware Update D-Bus Service" +msgstr "" + +#. TRANSLATORS: show the user a warning +#: src/fu-remote-list.c:149 +msgid "" +"Your distributor may not have verified any of the firmware updates for " +"compatibility with your system or connected devices." +msgstr "" + +#. TRANSLATORS: show the user a warning +#: src/fu-remote-list.c:155 +msgid "Enabling this remote is done at your own risk." +msgstr "" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +#: src/fu-security-attr-common.c:22 +msgid "SPI write" +msgstr "" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +#: src/fu-security-attr-common.c:26 +msgid "SPI lock" +msgstr "" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +#: src/fu-security-attr-common.c:30 +msgid "SPI BIOS region" +msgstr "" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +#: src/fu-security-attr-common.c:34 +msgid "SPI BIOS Descriptor" +msgstr "" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +#: src/fu-security-attr-common.c:38 +msgid "Pre-boot DMA protection" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +#: src/fu-security-attr-common.c:42 src/fu-security-attr-common.c:267 +msgid "Intel BootGuard" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +#: src/fu-security-attr-common.c:47 +msgid "Intel BootGuard verified boot" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +#: src/fu-security-attr-common.c:52 +msgid "Intel BootGuard ACM protected" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +#: src/fu-security-attr-common.c:57 +msgid "Intel BootGuard error policy" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +#: src/fu-security-attr-common.c:62 +msgid "Intel BootGuard OTP fuse" +msgstr "" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +#: src/fu-security-attr-common.c:67 +msgid "CET Platform" +msgstr "" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * Utilized by OS means the distribution enabled it +#: src/fu-security-attr-common.c:72 +msgid "CET OS Support" +msgstr "" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +#: src/fu-security-attr-common.c:76 +msgid "SMAP" +msgstr "" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +#: src/fu-security-attr-common.c:80 src/fu-security-attr-common.c:299 +msgid "Encrypted RAM" +msgstr "" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +#: src/fu-security-attr-common.c:85 +msgid "IOMMU" +msgstr "" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +#: src/fu-security-attr-common.c:89 +msgid "Linux kernel lockdown" +msgstr "" + +#. TRANSLATORS: Title: if it's tainted or not +#: src/fu-security-attr-common.c:93 +msgid "Linux kernel" +msgstr "" + +#. TRANSLATORS: Title: swap space or swap partition +#: src/fu-security-attr-common.c:97 +msgid "Linux swap" +msgstr "" + +#. TRANSLATORS: Title: sleep state +#: src/fu-security-attr-common.c:101 +msgid "Suspend-to-ram" +msgstr "" + +#. TRANSLATORS: Title: a better sleep state +#: src/fu-security-attr-common.c:105 +msgid "Suspend-to-idle" +msgstr "" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +#: src/fu-security-attr-common.c:109 +msgid "UEFI platform key" +msgstr "" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +#: src/fu-security-attr-common.c:113 +msgid "UEFI secure boot" +msgstr "" + +#. TRANSLATORS: Title: Bootservice is when only readable from early-boot +#: src/fu-security-attr-common.c:117 +msgid "UEFI bootservice variables" +msgstr "" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be empty +#: src/fu-security-attr-common.c:121 +msgid "TPM empty PCRs" +msgstr "" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +#: src/fu-security-attr-common.c:125 +msgid "TPM PCR0 reconstruction" +msgstr "" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +#: src/fu-security-attr-common.c:129 src/fu-security-attr-common.c:348 +msgid "TPM v2.0" +msgstr "" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#: src/fu-security-attr-common.c:135 +#, c-format +msgid "%s manufacturing mode" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +#: src/fu-security-attr-common.c:138 +msgid "MEI manufacturing mode" +msgstr "" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#: src/fu-security-attr-common.c:144 +#, c-format +msgid "%s override" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +#: src/fu-security-attr-common.c:150 +msgid "MEI override" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and key refer +#. * to the private/public key used to secure loading of firmware +#: src/fu-security-attr-common.c:155 +msgid "MEI key manifest" +msgstr "" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#. +#: src/fu-security-attr-common.c:164 +#, c-format +msgid "%s v%s" +msgstr "" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#: src/fu-security-attr-common.c:168 +#, c-format +msgid "%s version" +msgstr "" + +#: src/fu-security-attr-common.c:170 +msgid "MEI version" +msgstr "" + +#. TRANSLATORS: Title: if firmware updates are available +#: src/fu-security-attr-common.c:174 +msgid "Firmware updates" +msgstr "" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +#: src/fu-security-attr-common.c:178 +msgid "Firmware attestation" +msgstr "" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +#: src/fu-security-attr-common.c:182 +msgid "fwupd plugins" +msgstr "" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +#: src/fu-security-attr-common.c:187 +msgid "Platform debugging" +msgstr "" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +#: src/fu-security-attr-common.c:191 +msgid "Supported CPU" +msgstr "" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +#: src/fu-security-attr-common.c:195 +msgid "Processor rollback protection" +msgstr "" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +#: src/fu-security-attr-common.c:199 +msgid "SPI replay protection" +msgstr "" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +#: src/fu-security-attr-common.c:203 +msgid "SPI write protection" +msgstr "" + +#. TRANSLATORS: Title: if the part has been fused +#: src/fu-security-attr-common.c:207 +msgid "Fused platform" +msgstr "" + +#. TRANSLATORS: Title: if we are emulating a different host +#: src/fu-security-attr-common.c:211 +msgid "Emulated host" +msgstr "" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +#: src/fu-security-attr-common.c:215 +msgid "BIOS rollback protection" +msgstr "" + +#. TRANSLATORS: Title: GDS is where the CPU leaks information +#: src/fu-security-attr-common.c:219 +msgid "Intel GDS mitigation" +msgstr "" + +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +#: src/fu-security-attr-common.c:223 +msgid "BIOS firmware updates" +msgstr "" + +#. TRANSLATORS: Title: Whether firmware is locked down +#: src/fu-security-attr-common.c:227 +msgid "SMM locked down" +msgstr "" + +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +#: src/fu-security-attr-common.c:231 +msgid "UEFI memory protection" +msgstr "" + +#. TRANSLATORS: Title: is UEFI db up-to-date +#: src/fu-security-attr-common.c:235 src/fu-security-attr-common.c:428 +msgid "UEFI db" +msgstr "" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +#: src/fu-security-attr-common.c:247 +msgid "Firmware Write Protection" +msgstr "" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +#: src/fu-security-attr-common.c:251 +msgid "Firmware Write Protection Lock" +msgstr "" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +#: src/fu-security-attr-common.c:255 +msgid "Firmware BIOS Region" +msgstr "" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +#: src/fu-security-attr-common.c:259 +msgid "Firmware BIOS Descriptor" +msgstr "" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +#: src/fu-security-attr-common.c:263 +msgid "Pre-boot DMA Protection" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +#: src/fu-security-attr-common.c:272 +msgid "Intel BootGuard Verified Boot" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +#: src/fu-security-attr-common.c:277 +msgid "Intel BootGuard ACM Protected" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +#: src/fu-security-attr-common.c:282 +msgid "Intel BootGuard Error Policy" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +#: src/fu-security-attr-common.c:286 +msgid "Intel BootGuard Fuse" +msgstr "" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +#: src/fu-security-attr-common.c:291 +msgid "Control-flow Enforcement Technology" +msgstr "" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +#: src/fu-security-attr-common.c:295 +msgid "Supervisor Mode Access Prevention" +msgstr "" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +#: src/fu-security-attr-common.c:304 +msgid "IOMMU Protection" +msgstr "" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +#: src/fu-security-attr-common.c:308 +msgid "Linux Kernel Lockdown" +msgstr "" + +#. TRANSLATORS: Title: if it's tainted or not +#: src/fu-security-attr-common.c:312 +msgid "Linux Kernel Verification" +msgstr "" + +#. TRANSLATORS: Title: swap space or swap partition +#: src/fu-security-attr-common.c:316 +msgid "Linux Swap" +msgstr "" + +#. TRANSLATORS: Title: sleep state +#: src/fu-security-attr-common.c:320 +msgid "Suspend To RAM" +msgstr "" + +#. TRANSLATORS: Title: a better sleep state +#: src/fu-security-attr-common.c:324 +msgid "Suspend To Idle" +msgstr "" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +#: src/fu-security-attr-common.c:328 +msgid "UEFI Platform Key" +msgstr "" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +#: src/fu-security-attr-common.c:332 +msgid "UEFI Secure Boot" +msgstr "" + +#. TRANSLATORS: Title: Bootservice is when only readable from early-boot +#: src/fu-security-attr-common.c:336 +msgid "UEFI Bootservice Variables" +msgstr "" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be empty +#: src/fu-security-attr-common.c:340 +msgid "TPM Platform Configuration" +msgstr "" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +#: src/fu-security-attr-common.c:344 +msgid "TPM Reconstruction" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +#: src/fu-security-attr-common.c:352 +msgid "Intel Management Engine Manufacturing Mode" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is enabled +#. * with a jumper -- luckily it is probably not accessible to end users on consumer +#. * boards +#: src/fu-security-attr-common.c:358 +msgid "Intel Management Engine Override" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and key refers +#. * to the private/public key used to secure loading of firmware +#: src/fu-security-attr-common.c:363 +msgid "MEI Key Manifest" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +#: src/fu-security-attr-common.c:367 +msgid "Intel Management Engine Version" +msgstr "" + +#. TRANSLATORS: Title: if firmware updates are available +#: src/fu-security-attr-common.c:371 +msgid "Firmware Updates" +msgstr "" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +#: src/fu-security-attr-common.c:375 +msgid "Firmware Attestation" +msgstr "" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +#: src/fu-security-attr-common.c:379 +msgid "Firmware Updater Verification" +msgstr "" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +#: src/fu-security-attr-common.c:384 +msgid "Platform Debugging" +msgstr "" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +#: src/fu-security-attr-common.c:388 +msgid "Processor Security Checks" +msgstr "" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +#: src/fu-security-attr-common.c:392 +msgid "AMD Secure Processor Rollback Protection" +msgstr "" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +#: src/fu-security-attr-common.c:396 +msgid "AMD Firmware Replay Protection" +msgstr "" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +#: src/fu-security-attr-common.c:400 +msgid "AMD Firmware Write Protection" +msgstr "" + +#. TRANSLATORS: Title: if the part has been fused +#: src/fu-security-attr-common.c:404 +msgid "Fused Platform" +msgstr "" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +#: src/fu-security-attr-common.c:408 +msgid "BIOS Rollback Protection" +msgstr "" + +#. TRANSLATORS: Title: GDS is where the CPU leaks information +#: src/fu-security-attr-common.c:412 +msgid "Intel GDS Mitigation" +msgstr "" + +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +#: src/fu-security-attr-common.c:416 +msgid "BIOS Firmware Updates" +msgstr "" + +#. TRANSLATORS: Title: Whether firmware is locked down +#: src/fu-security-attr-common.c:420 +msgid "System Management Mode" +msgstr "" + +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +#: src/fu-security-attr-common.c:424 +msgid "UEFI Memory Protection" +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:442 +msgid "" +"Firmware Write Protection protects device firmware memory from being " +"tampered with." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:447 +msgid "" +"Firmware BIOS Region protects device firmware memory from being tampered " +"with." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:452 +msgid "" +"Firmware BIOS Descriptor protects device firmware memory from being tampered " +"with." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:457 +msgid "" +"Pre-boot DMA protection prevents devices from accessing system memory after " +"being connected to the computer." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:465 +msgid "" +"Intel BootGuard prevents unauthorized device software from operating when " +"the device is started." +msgstr "" + +#: src/fu-security-attr-common.c:471 +msgid "" +"Intel BootGuard Error Policy ensures the device does not continue to start " +"if its device software has been tampered with." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:477 +msgid "" +"Control-Flow Enforcement Technology detects and prevents certain methods for " +"running malicious software on the device." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:482 +msgid "" +"Supervisor Mode Access Prevention ensures critical parts of device memory " +"are not accessed by less secure programs." +msgstr "" + +#: src/fu-security-attr-common.c:488 +msgid "" +"Encrypted RAM makes it impossible for information that is stored in device " +"memory to be read if the memory chip is removed and accessed." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:493 +msgid "" +"IOMMU Protection prevents connected devices from accessing unauthorized " +"parts of system memory." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:498 +msgid "" +"Linux Kernel Lockdown mode prevents administrator (root) accounts from " +"accessing and changing critical parts of system software." +msgstr "" + +#: src/fu-security-attr-common.c:504 +msgid "" +"Linux Kernel Verification makes sure that critical system software has not " +"been tampered with. Using device drivers which are not provided with the " +"system can prevent this security feature from working correctly." +msgstr "" + +#: src/fu-security-attr-common.c:511 +msgid "" +"Linux Kernel Swap temporarily saves information to disk as you work. If the " +"information is not protected, it could be accessed by someone if they " +"obtained the disk." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:517 +msgid "" +"Suspend to RAM allows the device to quickly go to sleep in order to save " +"power. While the device has been suspended, its memory could be physically " +"removed and its information accessed." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:523 +msgid "" +"Suspend to Idle allows the device to quickly go to sleep in order to save " +"power. While the device has been suspended, its memory could be physically " +"removed and its information accessed." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:529 +msgid "" +"The UEFI Platform Key is used to determine if device software comes from a " +"trusted source." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:534 +msgid "" +"UEFI Secure Boot prevents malicious software from being loaded when the " +"device starts." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:539 +msgid "UEFI boot service variables should not be readable from runtime mode." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:543 +msgid "" +"The TPM (Trusted Platform Module) Platform Configuration is used to check " +"whether the device start process has been modified." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:548 +msgid "" +"The TPM (Trusted Platform Module) Reconstruction is used to check whether " +"the device start process has been modified." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:553 +msgid "" +"TPM (Trusted Platform Module) is a computer chip that detects when hardware " +"components have been tampered with." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:559 +msgid "" +"Manufacturing Mode is used when the device is manufactured and security " +"features are not yet enabled." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:564 +msgid "" +"Intel Management Engine Override disables checks for device software " +"tampering." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:569 +msgid "" +"The Intel Management Engine Key Manifest must be valid so that the device " +"firmware can be trusted by the CPU." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:574 +msgid "" +"The Intel Management Engine controls device components and needs to have a " +"recent version to avoid security issues." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:579 +msgid "Device software updates are provided for this device." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:583 +msgid "" +"Firmware Attestation checks device software using a reference copy, to make " +"sure that it has not been changed." +msgstr "" + +#: src/fu-security-attr-common.c:589 +msgid "" +"Firmware Updater Verification checks that software used for updating has not " +"been tampered with." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:595 +msgid "" +"Platform Debugging allows device security features to be disabled. This " +"should only be used by hardware manufacturers." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:600 +msgid "Each system should have tests to ensure firmware security." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:606 +msgid "" +"Rollback Protection prevents device software from being downgraded to an " +"older version that has security problems." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:611 +msgid "" +"CPU Microcode must be updated to mitigate against various information-" +"disclosure security issues." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:616 +msgid "Enabling firmware updates for the BIOS allows fixing security issues." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:620 +msgid "" +"System management mode is used by the firmware to access resident BIOS code " +"and data." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:625 +msgid "" +"The UEFI system can set up memory attributes at boot which prevent common " +"exploits from running." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:630 +msgid "" +"The UEFI db contains the list of valid certificates that can be used to " +"authorize what EFI binaries are allowed to run." +msgstr "" + +#. TRANSLATORS: %s is a link to a website +#: src/fu-tool.c:143 src/fu-util.c:4796 +#, c-format +msgid "See %s for more information." +msgstr "" + +#. TRANSLATORS: another fwupdtool instance is already running +#: src/fu-tool.c:228 +msgid "Failed to lock" +msgstr "" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +#. TRANSLATORS: this is from ctrl+c +#: src/fu-tool.c:286 src/fu-util.c:5183 +msgid "Cancelled" +msgstr "" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +#: src/fu-tool.c:384 src/fu-util.c:102 +msgid "Action Required:" +msgstr "" + +#. TRANSLATORS: device has been chosen by the daemon for the user +#: src/fu-tool.c:558 src/fu-util.c:187 +msgid "Selected device" +msgstr "" + +#. TRANSLATORS: this is to abort the interactive prompt +#: src/fu-tool.c:574 src/fu-tool.c:2948 src/fu-tool.c:4052 src/fu-tool.c:4310 +#: src/fu-util.c:203 src/fu-util.c:2554 src/fu-util.c:3697 +msgid "Cancel" +msgstr "" + +#. TRANSLATORS: get interactive prompt +#: src/fu-tool.c:582 src/fu-util.c:213 +msgid "Choose device" +msgstr "" + +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no +#. * device upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no +#. * device upgrade available due to missing on LVFS +#: src/fu-tool.c:751 src/fu-tool.c:1753 src/fu-util.c:2797 src/fu-util.c:3281 +msgid "Devices with no available firmware updates: " +msgstr "" + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device +#. * upgrade available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device +#. * upgrade available +#: src/fu-tool.c:761 src/fu-tool.c:1743 src/fu-util.c:2807 src/fu-util.c:3271 +msgid "Devices with the latest available firmware version:" +msgstr "" + +#: src/fu-tool.c:773 +msgid "No updates available for remaining devices" +msgstr "" + +#. TRANSLATORS: nothing attached that can be upgraded +#: src/fu-tool.c:960 src/fu-util.c:605 +msgid "No hardware detected with firmware update capability" +msgstr "" + +#. TRANSLATORS: %1 is a device name +#: src/fu-tool.c:999 src/fu-util.c:148 +#, c-format +msgid "Updating %s…" +msgstr "" + +#. TRANSLATORS: %1 is a device name +#: src/fu-tool.c:1003 src/fu-util.c:156 +#, c-format +msgid "Installing on %s…" +msgstr "" + +#. TRANSLATORS: %1 is a device name +#: src/fu-tool.c:1007 +#, c-format +msgid "Reading from %s…" +msgstr "" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid closed +#: src/fu-tool.c:1567 src/fu-util.c:3077 +#, c-format +msgid "%s is not currently updatable" +msgstr "" + +#. TRANSLATORS: message letting the user there is an update +#. * waiting, but there is a reason it cannot be deployed +#: src/fu-tool.c:1764 src/fu-util.c:3292 +msgid "Devices with firmware updates that need user action: " +msgstr "" + +#. TRANSLATORS: success message -- a per-system setting value +#: src/fu-tool.c:2223 src/fu-util.c:3985 +msgid "Successfully modified configuration value" +msgstr "" + +#. TRANSLATORS: success message -- a per-system setting value +#: src/fu-tool.c:2250 +msgid "Successfully reset configuration section" +msgstr "" + +#. TRANSLATORS: success message for a per-remote setting change +#: src/fu-tool.c:2287 src/fu-util.c:3355 +msgid "Successfully modified remote" +msgstr "" + +#. TRANSLATORS: success message +#: src/fu-tool.c:2321 src/fu-util.c:3446 +msgid "Successfully disabled remote" +msgstr "" + +#. TRANSLATORS: success message +#: src/fu-tool.c:2445 src/fu-util.c:3389 src/fu-util.c:3401 +msgid "Successfully enabled remote" +msgstr "" + +#. TRANSLATORS: comment explaining result of command +#: src/fu-tool.c:2472 +msgid "Successfully disabled test devices" +msgstr "" + +#. TRANSLATORS: comment explaining result of command +#: src/fu-tool.c:2519 +msgid "Successfully enabled test devices" +msgstr "" + +#. TRANSLATORS: shown when shutting down to switch to the new version +#: src/fu-tool.c:2615 +msgid "Activating firmware update" +msgstr "" + +#. TRANSLATORS: this is when a device is hotplugged +#: src/fu-tool.c:2790 +msgid "Device added:" +msgstr "Sead on lisatud:" + +#. TRANSLATORS: this is when a device is hotplugged +#: src/fu-tool.c:2805 +msgid "Device removed:" +msgstr "Seade on eemaldatud:" + +#. TRANSLATORS: this is when a device has been updated +#: src/fu-tool.c:2820 +msgid "Device changed:" +msgstr "Seade on muudetud:" + +#. TRANSLATORS: this is when the daemon state changes +#: src/fu-tool.c:2832 +msgid "Changed" +msgstr "Olek on muutunud" + +#. TRANSLATORS: nothing found +#: src/fu-tool.c:2890 +msgid "No firmware IDs found" +msgstr "Ühtegi püsivara tunnust ei leidunud" + +#. TRANSLATORS: nothing found +#: src/fu-tool.c:2920 +msgid "No firmware found" +msgstr "Mitte mingit püsivara ei leidunud" + +#. TRANSLATORS: get interactive prompt +#: src/fu-tool.c:2954 +msgid "Choose firmware" +msgstr "Vali püsivara" + +#. TRANSLATORS: decompressing images from a container firmware +#: src/fu-tool.c:3181 +msgid "Writing file:" +msgstr "" + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#: src/fu-tool.c:3846 +msgid "Metadata is already up to date" +msgstr "" + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. * refresh recently -- %1 is '--force' +#: src/fu-tool.c:3854 src/fu-util.c:2291 +#, c-format +msgid "Metadata is up to date; use %s to refresh again." +msgstr "" + +#. TRANSLATORS: error message for unsupported feature +#: src/fu-tool.c:3959 src/fu-tool.c:4436 src/fu-tool.c:4473 src/fu-util.c:4379 +#: src/fu-util.c:4889 src/fu-util.c:5054 +msgid "Host Security ID (HSI) is not supported" +msgstr "" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +#: src/fu-tool.c:3996 src/fu-util.c:4418 +msgid "Host Security ID:" +msgstr "" + +#. TRANSLATORS: Volume has been chosen by the user +#: src/fu-tool.c:4045 +msgid "Selected volume" +msgstr "" + +#. TRANSLATORS: get interactive prompt +#: src/fu-tool.c:4058 +msgid "Choose volume" +msgstr "" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +#: src/fu-tool.c:4320 src/fu-util.c:3707 +msgid "Choose branch" +msgstr "" + +#. TRANSLATORS: Configured a BIOS setting to a value +#: src/fu-tool.c:4412 src/fu-util.c:4826 +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "" + +#. TRANSLATOR: This is the error message for +#. * incorrect parameter +#: src/fu-tool.c:4446 src/fu-tool.c:4483 src/fu-util.c:4899 src/fu-util.c:5064 +msgid "Invalid arguments, expected an AppStream ID" +msgstr "" + +#. TRANSLATORS: we've fixed a security problem on the machine +#: src/fu-tool.c:4461 src/fu-util.c:4909 +msgid "Fixed successfully" +msgstr "" + +#. TRANSLATORS: we've fixed a security problem on the machine +#: src/fu-tool.c:4498 src/fu-util.c:5077 +msgid "Fix reverted successfully" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4535 src/fu-util.c:4867 +msgid "This system doesn't support firmware settings" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4544 src/fu-util.c:4875 +msgid "Unable to find attribute" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4681 +msgid "Invalid arguments, expected INDEX NAME TARGET [MOUNTPOINT]" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4695 +msgid "Already exists, and no --force specified" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4723 +#, c-format +msgid "No volume matched %s" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4747 +msgid "Invalid arguments, expected base-16 integer" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4808 +msgid "Invalid arguments, expected INDEX KEY [VALUE]" +msgstr "" + +#. TRANSLATORS: try to treat the legacy format as a hive +#: src/fu-tool.c:4826 +msgid "The EFI boot entry was not in hive format, falling back" +msgstr "" + +#. TRANSLATORS: the boot entry was in a legacy format +#: src/fu-tool.c:4844 +msgid "" +"The EFI boot entry is not in hive format, and shim may not be new enough to " +"read it." +msgstr "" + +#. TRANSLATORS: ask the user if it's okay to convert, +#. * "it" being the data contained in the EFI boot entry +#: src/fu-tool.c:4852 +msgid "Do you want to convert it now?" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4987 +msgid "Invalid arguments, expected GUID" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:5015 +msgid "Invalid arguments, expected at least ARCHIVE FIRMWARE METAINFO" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5146 src/fu-util.c:5239 +msgid "Show client and daemon versions" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5154 src/fu-util.c:5255 +msgid "Allow reinstalling existing firmware versions" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5162 src/fu-util.c:5263 +msgid "Allow downgrading firmware versions" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5170 src/fu-util.c:5271 +msgid "Allow switching firmware branch" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5178 src/fu-util.c:5287 +msgid "Force the action by relaxing some runtime checks" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5186 +msgid "Ignore firmware checksum failures" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5194 +msgid "Ignore firmware hardware mismatch failures" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5202 +msgid "Ignore non-critical firmware requirements" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5210 src/fu-util.c:5335 +msgid "Do not check or prompt for reboot after update" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5218 +msgid "Do not search the firmware when parsing" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5226 src/fu-util.c:5343 +msgid "Do not perform device safety checks" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5234 src/fu-util.c:5351 +msgid "Do not prompt for devices" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5242 src/fu-util.c:5367 +msgid "Show all results" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5250 src/fu-util.c:5375 +msgid "Show devices that are not updatable" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5258 src/fu-tool.c:5266 +msgid "Manually enable specific plugins" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5274 +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5282 +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5290 src/fu-util.c:5383 +msgid "Ignore SSL strict checks when downloading files" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5298 src/fu-util.c:5399 +msgid "" +"Filter with a set of device flags using a ~ prefix to exclude, e.g. " +"'internal,~needs-reboot'" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5307 src/fu-util.c:5408 +msgid "" +"Filter with a set of release flags using a ~ prefix to exclude, e.g. " +"'trusted-release,~trusted-metadata'" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5316 src/fu-util.c:5417 +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5370 src/fu-tool.c:5383 src/fu-util.c:5511 +msgid "FILE" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5372 +msgid "Dump SMBIOS data from a file" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5378 src/fu-util.c:5705 +msgid "Get all enabled plugins registered with the system" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5385 src/fu-util.c:5513 +msgid "Gets details about a firmware file" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5391 src/fu-util.c:5480 +msgid "Show history of firmware updates" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5396 src/fu-tool.c:5464 src/fu-tool.c:5478 src/fu-tool.c:5505 +#: src/fu-tool.c:5520 src/fu-tool.c:5623 src/fu-tool.c:5630 src/fu-util.c:5466 +#: src/fu-util.c:5518 src/fu-util.c:5526 src/fu-util.c:5534 src/fu-util.c:5562 +#: src/fu-util.c:5582 src/fu-util.c:5596 src/fu-util.c:5624 src/fu-util.c:5658 +#: src/fu-util.c:5787 src/fu-util.c:5794 +msgid "[DEVICE-ID|GUID]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5398 src/fu-util.c:5520 +msgid "" +"Gets the list of updates for all specified devices, or all devices if " +"unspecified" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5405 src/fu-util.c:5474 +msgid "Get all devices that support firmware updates" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5411 +msgid "Get all device flags supported by fwupd" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5417 +msgid "Watch for hardware changes" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5422 +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5424 +msgid "Install a raw firmware blob on a device" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5429 src/fu-util.c:5504 +msgid "FILE [DEVICE-ID|GUID]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5431 +msgid "" +"Install a specific firmware on a device, all possible devices will also be " +"installed once the CAB matches" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5437 src/fu-tool.c:5444 src/fu-tool.c:5457 src/fu-util.c:5541 +#: src/fu-util.c:5548 src/fu-util.c:5555 +msgid "DEVICE-ID|GUID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5439 +msgid "Reinstall firmware on a device" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5446 +msgid "Attach to firmware mode" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5452 +msgid "Get device report metadata" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5459 +msgid "Detach to bootloader mode" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5466 +msgid "Unbind current driver" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5471 +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5473 +msgid "Bind new kernel driver" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5480 +msgid "Activate pending devices" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5485 +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5487 src/fu-util.c:5822 +msgid "Return all the hardware IDs for the machine" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5492 +msgid "HWIDS-FILE" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5494 +msgid "Save a file that allows generation of hardware IDs" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5500 +msgid "Monitor the daemon for events" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5507 src/fu-util.c:5528 +msgid "" +"Updates all specified devices to latest firmware version, or all devices if " +"unspecified" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5513 +msgid "TEXT" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5515 +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5522 +msgid "Update the stored metadata with current contents" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5527 +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5529 +msgid "Sign a firmware with a new key" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5534 src/fu-tool.c:5541 +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5536 +msgid "Read a firmware blob from a device" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5543 +msgid "Read a firmware from a device" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5548 +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5550 +msgid "Patch a firmware blob at a known offset" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5556 +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5558 +msgid "Convert a firmware file" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5563 +msgid "BUILDER-XML FILENAME-DST" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5565 +msgid "Build a firmware file" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5570 src/fu-tool.c:5577 src/fu-tool.c:5584 +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5572 +msgid "Parse and show details about a firmware file" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5579 +msgid "Export a firmware file structure to XML" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5586 +msgid "Extract a firmware blob to images" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5592 +msgid "List the available firmware types" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5598 +msgid "List the available firmware GTypes" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5604 src/fu-util.c:5577 +msgid "Gets the configured remotes" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5609 src/fu-util.c:5589 +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5611 src/fu-util.c:5591 +msgid "Refresh metadata from remote server" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5616 +msgid "[FWUPD-VERSION]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5618 src/fu-util.c:5673 +msgid "Gets the host security attributes" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5625 src/fu-util.c:5789 +msgid "Adds devices to watch for future emulation" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5632 src/fu-util.c:5796 +msgid "Removes devices to watch for future emulation" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5637 +msgid "EMULATION-FILE [ARCHIVE-FILE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5645 +msgid "Mounts the ESP" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5651 +msgid "Unmounts the ESP" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5657 +msgid "Lists files on the ESP" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5662 src/fu-util.c:5665 +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5664 src/fu-util.c:5667 +msgid "Switch the firmware branch on the device" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5670 +msgid "Erase all firmware update history" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5676 +msgid "[SETTING1] [SETTING2]..." +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5678 src/fu-util.c:5761 +msgid "" +"Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5683 +msgid "SETTING VALUE" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5685 +msgid "Set a BIOS setting" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5690 +msgid "ARCHIVE FIRMWARE METAINFO [FIRMWARE] [METAINFO] [JCATFILE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5692 +msgid "Build a cabinet archive from a firmware blob and XML metadata" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5697 +msgctxt "command-argument" +msgid "GUID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5699 +msgid "List EFI variables with a specific GUID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5707 +msgid "List EFI boot parameters" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5712 src/fu-tool.c:5726 +msgid "INDEX" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5714 +msgid "Set the EFI boot next" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5719 +msgid "INDEX1,INDEX2" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5721 +msgid "Set the EFI boot order" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5728 +msgid "Delete an EFI boot entry" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5733 +msgid "INDEX NAME TARGET [MOUNTPOINT]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5735 +msgid "Create an EFI boot entry" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5740 +msgid "INDEX KEY [VALUE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5742 +msgid "Set or remove an EFI boot hive entry" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5749 +msgid "List EFI boot files" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5754 src/fu-tool.c:5761 src/fu-util.c:5801 src/fu-util.c:5808 +msgid "[APPSTREAM_ID]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5756 src/fu-util.c:5803 +msgid "Fix a specific host security attribute" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5763 src/fu-util.c:5810 +msgid "Undo the host security attribute fix" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5768 +msgid "[DEVICE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5770 +msgid "Run the post-reboot cleanup action" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5775 src/fu-util.c:5644 +msgid "[SECTION] KEY VALUE" +msgstr "" + +#. TRANSLATORS: sets something in the daemon configuration file +#: src/fu-tool.c:5777 src/fu-util.c:5646 +msgid "Modifies a daemon configuration value" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5782 src/fu-util.c:5651 +msgid "SECTION" +msgstr "" + +#. TRANSLATORS: sets something in the daemon configuration file +#: src/fu-tool.c:5784 src/fu-util.c:5653 +msgid "Resets a daemon configuration section" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5789 src/fu-util.c:5603 +msgid "REMOTE-ID KEY VALUE" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5791 src/fu-util.c:5605 +msgid "Modifies a given remote" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5796 src/fu-tool.c:5803 src/fu-util.c:5610 src/fu-util.c:5617 +msgid "REMOTE-ID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5798 src/fu-util.c:5612 +msgid "Enables a given remote" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5805 src/fu-util.c:5619 +msgid "Disables a given remote" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5811 +msgid "Enables virtual testing devices" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5817 +msgid "Disables virtual testing devices" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5823 +msgid "Get all known version formats" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5828 +msgid "VERSION1 VERSION2 [FORMAT]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5830 +msgid "Compares two versions for equality" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5835 src/fu-util.c:5569 +msgid "WORD" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5837 src/fu-util.c:5571 +msgid "Finds firmware releases from the metadata" +msgstr "" + +#. TRANSLATORS: CLI description +#: src/fu-tool.c:5876 +msgid "" +"This tool allows an administrator to use the fwupd plugins without being " +"installed on the host system." +msgstr "" + +#. TRANSLATORS: program name +#: src/fu-tool.c:5880 src/fu-util.c:5863 +msgid "Firmware Utility" +msgstr "" + +#. TRANSLATORS: try to help +#: src/fu-tool.c:5900 src/fu-util.c:5881 +msgid "" +"Ignoring SSL strict checks, to do this automatically in the future export " +"DISABLE_SSL_STRICT in your environment" +msgstr "" + +#. TRANSLATORS: the user didn't read the man page, %1 is '--filter' +#. TRANSLATORS: the user didn't read the man page, +#. * %1 is '--filter-release' +#. TRANSLATORS: the user didn't read the man page, %1 is '--filter' +#. TRANSLATORS: the user didn't read the man page, +#. * %1 is '--filter-release' +#: src/fu-tool.c:5914 src/fu-tool.c:5928 src/fu-util.c:5907 src/fu-util.c:5921 +#, c-format +msgid "Failed to parse flags for %s" +msgstr "" + +#. TRANSLATORS: explain how to get help, %1 is +#. * 'fwupdtool --help' +#. TRANSLATORS: explain how to get help, +#. * where $1 is something like 'fwupdmgr --help' +#: src/fu-tool.c:6003 src/fu-util.c:6084 +#, c-format +msgid "Use %s for help" +msgstr "" + +#. TRANSLATORS: %1 is a device name +#: src/fu-util.c:152 +#, c-format +msgid "Downgrading %s…" +msgstr "" + +#. TRANSLATORS: a list of failed updates +#: src/fu-util.c:336 +msgid "Devices that were not updated correctly:" +msgstr "" + +#. TRANSLATORS: a list of successful updates +#: src/fu-util.c:352 +msgid "Devices that have been updated successfully:" +msgstr "" + +#. TRANSLATORS: explain why we want to upload +#: src/fu-util.c:367 +msgid "" +"Uploading firmware reports helps hardware vendors to quickly identify " +"failing and successful updates on real devices." +msgstr "" + +#. TRANSLATORS: ask the user to upload +#: src/fu-util.c:374 +msgid "Review and upload report now?" +msgstr "" + +#. TRANSLATORS: metadata is downloaded +#: src/fu-util.c:376 src/fu-util.c:2657 src/fu-util.c:3399 src/fu-util.c:5016 +msgid "Requires internet connection" +msgstr "" + +#. TRANSLATORS: offer to disable this nag +#: src/fu-util.c:381 +msgid "Do you want to disable this feature for future updates?" +msgstr "" + +#. TRANSLATORS: offer to stop asking the question +#: src/fu-util.c:415 +msgid "Do you want to upload reports automatically for future updates?" +msgstr "" + +#. TRANSLATORS: no rebooting needed +#: src/fu-util.c:562 +msgid "No reboot is necessary" +msgstr "" + +#. TRANSLATORS: success message +#: src/fu-util.c:685 +msgid "Successfully installed firmware" +msgstr "" + +#. TRANSLATORS: this is for the device tests +#: src/fu-util.c:817 +msgid "Did not find any devices with matching GUIDs" +msgstr "" + +#. TRANSLATORS: this is for the device tests +#: src/fu-util.c:872 +msgid "OK!" +msgstr "" + +#. TRANSLATORS: the inhibit ID is a short string like dbus-123456 +#: src/fu-util.c:1260 +#, c-format +msgid "Inhibit ID is %s." +msgstr "" + +#. TRANSLATORS: we can auto-uninhibit after a timeout +#: src/fu-util.c:1265 +#, c-format +msgid "Automatically uninhibiting in %ums…" +msgstr "" + +#. TRANSLATORS: CTRL^C [holding control, and then pressing C] will exit the program +#: src/fu-util.c:1270 +msgid "Use CTRL^C to cancel." +msgstr "" + +#. TRANSLATORS: this CLI tool is now preventing system updates +#: src/fu-util.c:1272 +msgid "System Update Inhibited" +msgstr "" + +#. TRANSLATORS: the device is already connected +#: src/fu-util.c:1341 src/fu-util.c:1347 +msgid "Device already exists" +msgstr "" + +#. TRANSLATORS: the device showed up in time +#: src/fu-util.c:1375 +#, c-format +msgid "Successfully waited %.0fms for device" +msgstr "" + +#. TRANSLATORS: device tests can be specific to a CPU type +#: src/fu-util.c:1438 +#, c-format +msgid "%u test was skipped" +msgid_plural "%u tests were skipped" +msgstr[0] "" +msgstr[1] "" + +#. show the user the entire data blob +#: src/fu-util.c:1662 src/fu-util.c:4105 src/fu-util.c:5001 +msgid "Target" +msgstr "" + +#: src/fu-util.c:1663 src/fu-util.c:4107 src/fu-util.c:5002 +msgid "Payload" +msgstr "" + +#: src/fu-util.c:1665 src/fu-util.c:4109 +msgid "Signature" +msgstr "" + +#: src/fu-util.c:1666 src/fu-util.c:4110 +msgid "Proceed with upload?" +msgstr "" + +#. TRANSLATORS: the server sent the user a small message +#: src/fu-util.c:1692 +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#: src/fu-util.c:1735 +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: key for a offline report filename +#: src/fu-util.c:1866 +msgid "Saved report" +msgstr "" + +#. TRANSLATORS: %1 is a device name, e.g. "ThinkPad Universal +#. * ThunderBolt 4 Dock" and %2 is "fwupdmgr activate" +#: src/fu-util.c:1904 +#, c-format +msgid "%s is pending activation; use %s to complete the update." +msgstr "" + +#. TRANSLATORS: success message when user refreshes device checksums +#: src/fu-util.c:2149 +msgid "Successfully updated device checksums" +msgstr "" + +#. TRANSLATORS: explain why no metadata available +#: src/fu-util.c:2166 +msgid "No remotes are currently enabled so no metadata is available." +msgstr "" + +#. TRANSLATORS: explain why no metadata available +#: src/fu-util.c:2170 +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "" + +#. TRANSLATORS: Turn on the remote +#: src/fu-util.c:2173 +msgid "Enable this remote?" +msgstr "" + +#: src/fu-util.c:2261 +msgid "Updating" +msgstr "" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +#: src/fu-util.c:2315 +msgid "Successfully downloaded new metadata:" +msgstr "" + +#. TRANSLATORS: no devices that can be upgraded with new firmware +#: src/fu-util.c:2323 +msgid "No devices are updatable" +msgstr "" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#: src/fu-util.c:2330 +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#: src/fu-util.c:2339 +#, c-format +msgid "" +"%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "" +"%u devices are supported in the enabled remotes (an update has been " +"published)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: success message -- the user can do this by-hand too +#: src/fu-util.c:2386 +msgid "Successfully refreshed metadata manually" +msgstr "" + +#. TRANSLATORS: no repositories to download from +#: src/fu-util.c:2444 +msgid "No releases available" +msgstr "" + +#. TRANSLATORS: no repositories to download from +#: src/fu-util.c:2502 +msgid "No matching releases for search token" +msgstr "" + +#. TRANSLATORS: get interactive prompt +#: src/fu-util.c:2563 +msgid "Choose release" +msgstr "" + +#. TRANSLATORS: success message when user verified device checksums +#: src/fu-util.c:2594 +msgid "Successfully verified device checksums" +msgstr "" + +#. TRANSLATORS: the metadata is very out of date; %u is a number > 1 +#: src/fu-util.c:2645 +#, c-format +msgid "" +"Firmware metadata has not been updated for %u day and may not be up to date." +msgid_plural "" +"Firmware metadata has not been updated for %u days and may not be up to date." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: ask if we can update metadata +#: src/fu-util.c:2655 +msgid "Update now?" +msgstr "" + +#. TRANSLATORS: this is an error string +#: src/fu-util.c:2824 +msgid "No updatable devices" +msgstr "" + +#. TRANSLATORS: this is an error string +#: src/fu-util.c:2833 +msgid "No updates available" +msgstr "" + +#. TRANSLATORS: no repositories to download from +#: src/fu-util.c:2862 +msgid "No remotes available" +msgstr "" + +#. TRANSLATORS: BKC is the industry name for the best known configuration and is a set +#. * of firmware that works together +#: src/fu-util.c:2969 +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "" + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync` +#: src/fu-util.c:2975 +#, c-format +msgid "" +"This device will be reverted back to %s when the %s command is performed." +msgstr "" + +#. TRANSLATORS: the best known configuration is a set of software that we know works +#. * well together. In the OEM and ODM industries it is often called a BKC +#: src/fu-util.c:2983 +msgid "Deviate from the best known configuration?" +msgstr "" + +#. TRANSLATORS: prompt to apply the update +#: src/fu-util.c:2988 src/fu-util.c:4351 src/fu-util-common.c:380 +#: src/fu-util-common.c:2784 +msgid "Perform operation?" +msgstr "" + +#. TRANSLATORS: ask if we can update the metadata +#: src/fu-util.c:3397 +msgid "Do you want to refresh this remote now?" +msgstr "" + +#. TRANSLATORS: success message +#: src/fu-util.c:3413 +msgid "Successfully enabled and refreshed remote" +msgstr "" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#: src/fu-util.c:3481 +#, c-format +msgid "No downgrades for %s" +msgstr "" + +#. TRANSLATORS: shown when shutting down to switch to the new version +#: src/fu-util.c:3840 +msgid "Activating firmware update for" +msgstr "" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +#: src/fu-util.c:3854 +msgid "Successfully activated all devices" +msgstr "" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +#: src/fu-util.c:3926 +msgid "There is no approved firmware." +msgstr "" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +#: src/fu-util.c:3932 +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: changes only take effect on restart +#: src/fu-util.c:3975 src/fu-util.c:4011 +msgid "Restart the daemon to make the change effective?" +msgstr "" + +#. TRANSLATORS: success message -- a per-system setting value +#: src/fu-util.c:4020 +msgid "Successfully reset configuration values" +msgstr "" + +#. TRANSLATORS: ask the user to share, %s is something +#. * like: "Linux Vendor Firmware Service" +#: src/fu-util.c:4083 +#, c-format +msgid "Upload these anonymous results to the %s to help other users?" +msgstr "" + +#. TRANSLATORS: success, so say thank you to the user +#: src/fu-util.c:4134 +msgid "Host Security ID attributes uploaded successfully, thanks!" +msgstr "" + +#. TRANSLATORS: can we JFDI? +#: src/fu-util.c:4142 +msgid "Automatically upload every time?" +msgstr "" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +#: src/fu-util.c:4296 +msgid "Configuration Change Suggested" +msgstr "" + +#. TRANSLATORS: the %1 is a BIOS setting name. +#. * %2 and %3 are the values, e.g. "True" or "Windows10" +#: src/fu-util.c:4308 +#, c-format +msgid "" +"This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, " +"but it will only be active after restarting the computer." +msgstr "" + +#. TRANSLATORS: the user has to manually recover; we can't do it +#: src/fu-util.c:4317 +msgid "" +"You should ensure you are comfortable restoring the setting from the system " +"firmware setup, as this change may cause the system to not boot into Linux " +"or cause other system instability." +msgstr "" + +#. TRANSLATORS: the %1 is a kernel command line key=value +#: src/fu-util.c:4327 +#, c-format +msgid "" +"This tool can change the kernel argument from '%s' to '%s', but it will only " +"be active after restarting the computer." +msgstr "" + +#. TRANSLATORS: the %1 is a kernel command line key=value +#: src/fu-util.c:4335 +#, c-format +msgid "" +"This tool can add a kernel argument of '%s', but it will only be active " +"after restarting the computer." +msgstr "" + +#. TRANSLATORS: the user has to manually recover; we can't do it +#: src/fu-util.c:4342 +msgid "" +"You should ensure you are comfortable restoring the setting from a recovery " +"or installation disk, as this change may cause the system to not boot into " +"Linux or cause other system instability." +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-util.c:4545 +msgid "Unable to connect to service" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-util.c:4554 +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "" + +#. TRANSLATORS: user selected something not possible +#: src/fu-util.c:4650 +msgid "Firmware is already blocked" +msgstr "" + +#. TRANSLATORS: we will not offer this firmware to the user +#: src/fu-util.c:4655 +msgid "Blocking firmware:" +msgstr "" + +#. TRANSLATORS: nothing to show +#: src/fu-util.c:4686 src/fu-util.c:4736 +msgid "There are no blocked firmware files" +msgstr "" + +#. TRANSLATORS: user selected something not possible +#: src/fu-util.c:4705 +msgid "Firmware is not already blocked" +msgstr "" + +#. TRANSLATORS: we will now offer this firmware to the user +#: src/fu-util.c:4710 +msgid "Unblocking firmware:" +msgstr "" + +#. TRANSLATORS: there follows a list of hashes +#: src/fu-util.c:4741 +msgid "Blocked firmware files:" +msgstr "" + +#. TRANSLATORS: explain why we want to upload +#: src/fu-util.c:5006 +#, c-format +msgid "" +"Uploading a device list allows the %s team to know what hardware exists, and " +"allows us to put pressure on vendors that do not upload firmware updates for " +"their hardware." +msgstr "" + +#. TRANSLATORS: ask the user to upload +#: src/fu-util.c:5014 +msgid "Upload data now?" +msgstr "" + +#. TRANSLATORS: success, so say thank you to the user +#: src/fu-util.c:5040 +msgid "Device list uploaded successfully, thanks!" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5247 +msgid "Set the download retries for transient errors" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5279 +msgid "Only install onto emulated devices" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5295 +msgid "Answer yes to all questions" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5303 +msgid "Sign the uploaded data with the client certificate" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5311 +msgid "Do not check for unreported history" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5319 +msgid "Do not check for old metadata" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5327 +msgid "Do not check if download remotes should be enabled" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5359 +msgid "Do not write to the history database" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5391 +msgid "Only use peer-to-peer networking when downloading files" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5425 +msgid "Do not prompt to fix security issues" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5433 +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5468 +msgid "Check if any devices are pending a reboot to complete update" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5486 +msgid "Share firmware history with the developers" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5492 +msgid "Export firmware history for manual upload" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5497 +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5499 +msgid "Install a specific firmware file on all devices that match" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5506 +msgid "Install a firmware file in cabinet format on this hardware" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5536 +msgid "Checks cryptographic hash matches firmware" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5543 +msgid "Unlocks the device for firmware access" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5550 +msgid "Clears the results from the last update" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5557 +msgid "Gets the results from the last update" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5564 +msgid "Gets the releases for a device" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5584 +msgid "Downgrades the firmware on a device" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5598 +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5626 +msgid "Activate devices" +msgstr "" + +#. TRANSLATORS: firmware approved by the admin +#: src/fu-util.c:5632 +msgid "Gets the list of approved firmware" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5637 +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5660 +msgid "Reinstall current firmware on the device" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5679 +msgid "Sync firmware versions to the chosen configuration" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5684 src/fu-util.c:5691 +msgid "[CHECKSUM]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5686 +msgid "Blocks a specific firmware from being installed" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5693 +msgid "Unblocks a specific firmware from being installed" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5699 +msgid "Gets the list of blocked firmware" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5710 +msgid "LOCATION" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5712 +msgid "Download a file" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5717 src/fu-util.c:5724 +msgid "[FILENAME1] [FILENAME2]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5719 +msgid "Test a device using a JSON manifest" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5726 +msgid "Emulate a device using a JSON manifest" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5731 +msgid "[REASON] [TIMEOUT]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5733 +msgid "Inhibit the system to prevent upgrades" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5738 +msgid "INHIBIT-ID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5740 +msgid "Uninhibit the system to allow upgrades" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5745 +msgid "GUID|DEVICE-ID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5747 +msgid "Wait for a device to appear" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5753 +msgid "Asks the daemon to quit" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5759 +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5766 +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5768 +msgid "Sets one or more BIOS settings" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5816 +msgid "Upload the list of updatable devices to a remote server" +msgstr "" + +#. TRANSLATORS: CLI description +#: src/fu-util.c:5858 +msgid "" +"This tool allows an administrator to query and control the fwupd daemon, " +"allowing them to perform actions such as installing or downgrading firmware." +msgstr "" + +#. TRANSLATORS: try to help +#: src/fu-util.c:5895 +msgid "" +"The system clock has not been set correctly and downloading files may fail." +msgstr "" + +#. TRANSLATORS: error message for Windows +#: src/fu-util.c:5998 +msgid "Failed to connect to Windows service, please ensure it's running." +msgstr "" + +#. TRANSLATORS: could not contact the fwupd service over D-Bus +#: src/fu-util.c:6002 +msgid "Failed to connect to daemon" +msgstr "" + +#. TRANSLATORS: the user is SOL for support... +#: src/fu-util.c:6012 +msgid "" +"The daemon has loaded 3rd party code and is no longer supported by the " +"upstream developers!" +msgstr "" + +#. TRANSLATORS: a feature is something like "can show an image" +#: src/fu-util.c:6062 +msgid "Failed to set front-end features" +msgstr "" + +#. TRANSLATORS: The BIOS setting can only be changed to fixed values +#: src/fu-util-bios-setting.c:33 +msgid "Enumeration" +msgstr "" + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +#: src/fu-util-bios-setting.c:37 +msgid "Integer" +msgstr "" + +#. TRANSLATORS: The BIOS setting accepts strings +#: src/fu-util-bios-setting.c:41 +msgid "String" +msgstr "" + +#. TRANSLATORS: type of BIOS setting +#: src/fu-util-bios-setting.c:107 +msgid "Setting type" +msgstr "" + +#. TRANSLATORS: tell a user how to get information +#: src/fu-util-bios-setting.c:115 +#, c-format +msgid "Run without '%s' to see" +msgstr "" + +#. TRANSLATORS: current value of a BIOS setting +#: src/fu-util-bios-setting.c:118 +msgid "Current Value" +msgstr "" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +#: src/fu-util-bios-setting.c:124 src/fu-util-common.c:1980 +msgid "Description" +msgstr "" + +#. TRANSLATORS: item is TRUE +#: src/fu-util-bios-setting.c:129 +msgid "True" +msgstr "" + +#. TRANSLATORS: item is FALSE +#: src/fu-util-bios-setting.c:132 +msgid "False" +msgstr "" + +#. TRANSLATORS: BIOS setting is read only +#: src/fu-util-bios-setting.c:135 +msgid "Read Only" +msgstr "" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +#: src/fu-util-bios-setting.c:149 +msgid "Minimum value" +msgstr "" + +#. TRANSLATORS: Highest valid integer for BIOS setting +#: src/fu-util-bios-setting.c:151 +msgid "Maximum value" +msgstr "" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +#: src/fu-util-bios-setting.c:156 +msgid "Scalar Increment" +msgstr "" + +#. TRANSLATORS: Shortest valid string for BIOS setting +#: src/fu-util-bios-setting.c:160 +msgid "Minimum length" +msgstr "" + +#. TRANSLATORS: Longest valid string for BIOS setting +#: src/fu-util-bios-setting.c:162 +msgid "Maximum length" +msgstr "" + +#. TRANSLATORS: Possible values for a bios setting +#: src/fu-util-bios-setting.c:168 +msgid "Possible Values" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-util-bios-setting.c:200 +msgid "Invalid arguments" +msgstr "" + +#. TRANSLATORS: the vendor did not upload this +#: src/fu-util-common.c:268 +msgid "" +"This firmware is provided by LVFS community members and is not provided (or " +"supported) by the original hardware vendor." +msgstr "" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +#: src/fu-util-common.c:274 +msgid "Installing this update may also void any device warranty." +msgstr "" + +#. TRANSLATORS: naughty vendor +#: src/fu-util-common.c:282 +msgid "The vendor did not supply any release notes." +msgstr "" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#: src/fu-util-common.c:310 +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#: src/fu-util-common.c:319 +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#: src/fu-util-common.c:328 +#, c-format +msgid "Reinstall %s to %s?" +msgstr "" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#: src/fu-util-common.c:350 +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#. +#: src/fu-util-common.c:362 +#, c-format +msgid "" +"%s must remain connected for the duration of the update to avoid damage." +msgstr "" + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#. +#: src/fu-util-common.c:371 +#, c-format +msgid "" +"%s must remain plugged into a power source for the duration of the update to " +"avoid damage." +msgstr "" + +#. TRANSLATORS: explain why +#: src/fu-util-common.c:401 +msgid "An update requires the system to shutdown to complete." +msgstr "" + +#. TRANSLATORS: shutdown to apply the update +#: src/fu-util-common.c:404 +msgid "Shutdown now?" +msgstr "" + +#. TRANSLATORS: explain why we want to reboot +#: src/fu-util-common.c:415 +msgid "An update requires a reboot to complete." +msgstr "" + +#. TRANSLATORS: reboot to apply the update +#: src/fu-util-common.c:417 +msgid "Restart now?" +msgstr "" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#: src/fu-util-common.c:475 +#, c-format +msgid "Alias to %s" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-util-common.c:523 +msgid "Command not found" +msgstr "" + +#. TRANSLATORS: this is the default branch name when unset +#: src/fu-util-common.c:572 +msgid "default" +msgstr "" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#: src/fu-util-common.c:588 +#, c-format +msgid "%s Device Update" +msgstr "" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#: src/fu-util-common.c:593 +#, c-format +msgid "%s Configuration Update" +msgstr "" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#: src/fu-util-common.c:598 +#, c-format +msgid "%s System Update" +msgstr "" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#: src/fu-util-common.c:603 +#, c-format +msgid "%s Embedded Controller Update" +msgstr "" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#: src/fu-util-common.c:608 +#, c-format +msgid "%s ME Update" +msgstr "" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#: src/fu-util-common.c:613 +#, c-format +msgid "%s Corporate ME Update" +msgstr "" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#: src/fu-util-common.c:618 +#, c-format +msgid "%s Consumer ME Update" +msgstr "" + +#. 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` +#: src/fu-util-common.c:624 +#, c-format +msgid "%s Controller Update" +msgstr "" + +#. 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` +#: src/fu-util-common.c:630 +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system boot-up +#: src/fu-util-common.c:635 +#, c-format +msgid "%s CPU Microcode Update" +msgstr "" + +#. TRANSLATORS: battery refers to the system power source +#: src/fu-util-common.c:639 +#, c-format +msgid "%s Battery Update" +msgstr "" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#: src/fu-util-common.c:644 +#, c-format +msgid "%s Camera Update" +msgstr "" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#: src/fu-util-common.c:648 +#, c-format +msgid "%s TPM Update" +msgstr "" + +#. TRANSLATORS: TouchPad refers to a flat input device +#: src/fu-util-common.c:652 +#, c-format +msgid "%s Touchpad Update" +msgstr "" + +#. TRANSLATORS: Mouse refers to a handheld input device +#: src/fu-util-common.c:656 +#, c-format +msgid "%s Mouse Update" +msgstr "" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#: src/fu-util-common.c:660 +#, c-format +msgid "%s Keyboard Update" +msgstr "" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#: src/fu-util-common.c:664 +#, c-format +msgid "%s Storage Controller Update" +msgstr "" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#: src/fu-util-common.c:669 +#, c-format +msgid "%s Network Interface Update" +msgstr "" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#: src/fu-util-common.c:674 +#, c-format +msgid "%s Display Update" +msgstr "" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#: src/fu-util-common.c:679 +#, c-format +msgid "%s BMC Update" +msgstr "" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#: src/fu-util-common.c:684 +#, c-format +msgid "%s USB Receiver Update" +msgstr "" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#: src/fu-util-common.c:688 +#, c-format +msgid "%s Drive Update" +msgstr "" + +#. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC +#: src/fu-util-common.c:692 +#, c-format +msgid "%s Flash Drive Update" +msgstr "" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#: src/fu-util-common.c:697 +#, c-format +msgid "%s SSD Update" +msgstr "" + +#. TRANSLATORS: GPU refers to a Graphics Processing Unit, e.g. +#. * the "video card" +#: src/fu-util-common.c:702 +#, c-format +msgid "%s GPU Update" +msgstr "" + +#. TRANSLATORS: Dock refers to the port replicator hardware laptops are +#. * cradled in, or lowered onto +#: src/fu-util-common.c:707 +#, c-format +msgid "%s Dock Update" +msgstr "" + +#. TRANSLATORS: Dock refers to the port replicator device connected +#. * by plugging in a USB cable -- which may or may not also provide power +#: src/fu-util-common.c:712 +#, c-format +msgid "%s USB Dock Update" +msgstr "" + +#. TRANSLATORS: a device that can read your fingerprint pattern +#: src/fu-util-common.c:716 +#, c-format +msgid "%s Fingerprint Reader Update" +msgstr "" + +#. TRANSLATORS: a large pressure-sensitive drawing area typically used +#. * by artists and digital artists +#: src/fu-util-common.c:721 +#, c-format +msgid "%s Graphics Tablet Update" +msgstr "" + +#. TRANSLATORS: an input device used by gamers, e.g. a joystick +#: src/fu-util-common.c:725 +#, c-format +msgid "%s Input Controller Update" +msgstr "" + +#. TRANSLATORS: two miniature speakers attached to your ears +#: src/fu-util-common.c:729 +#, c-format +msgid "%s Headphones Update" +msgstr "" + +#. TRANSLATORS: headphones with an integrated microphone +#: src/fu-util-common.c:733 +#, c-format +msgid "%s Headset Update" +msgstr "" + +#. 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` +#: src/fu-util-common.c:740 +#, c-format +msgid "%s Update" +msgstr "" + +#. TRANSLATORS: duration in seconds +#: src/fu-util-common.c:990 +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: duration in minutes +#: src/fu-util-common.c:997 +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: duration in minutes +#: src/fu-util-common.c:1004 +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: duration in days! +#: src/fu-util-common.c:1010 +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: Device cannot be removed easily +#: src/fu-util-common.c:1021 +msgid "Internal device" +msgstr "" + +#. TRANSLATORS: Device is updatable in this or any other mode +#: src/fu-util-common.c:1026 +msgid "Updatable" +msgstr "" + +#. TRANSLATORS: Must be plugged into an outlet +#: src/fu-util-common.c:1030 +msgid "System requires external power source" +msgstr "" + +#. TRANSLATORS: Is locked and can be unlocked +#: src/fu-util-common.c:1034 +msgid "Device is locked" +msgstr "" + +#. TRANSLATORS: Is found in current metadata +#: src/fu-util-common.c:1038 +msgid "Supported on remote server" +msgstr "" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +#: src/fu-util-common.c:1042 +msgid "Requires a bootloader" +msgstr "" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +#: src/fu-util-common.c:1046 +msgid "Needs a reboot after installation" +msgstr "" + +#. TRANSLATORS: Requires system shutdown to apply firmware +#: src/fu-util-common.c:1050 +msgid "Needs shutdown after installation" +msgstr "" + +#. TRANSLATORS: Has been reported to a metadata server +#: src/fu-util-common.c:1054 +msgid "Reported to remote server" +msgstr "" + +#. TRANSLATORS: User has been notified +#: src/fu-util-common.c:1058 +msgid "User has been notified" +msgstr "" + +#. TRANSLATORS: Is currently in bootloader mode +#: src/fu-util-common.c:1062 +msgid "Is in bootloader mode" +msgstr "" + +#. TRANSLATORS: the hardware is waiting to be replugged +#: src/fu-util-common.c:1066 +msgid "Hardware is waiting to be replugged" +msgstr "" + +#. TRANSLATORS: Device update needs to be separately activated +#: src/fu-util-common.c:1074 +msgid "Device update needs activation" +msgstr "" + +#. TRANSLATORS: Device will not return after update completes +#: src/fu-util-common.c:1082 +msgid "Device will not re-appear after update completes" +msgstr "" + +#. TRANSLATORS: Device supports some form of checksum verification +#: src/fu-util-common.c:1086 +msgid "Cryptographic hash verification is available" +msgstr "" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +#: src/fu-util-common.c:1094 +msgid "Device stages updates" +msgstr "" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +#: src/fu-util-common.c:1098 +msgid "Device can recover flash failures" +msgstr "" + +#. TRANSLATORS: Device remains usable during update +#: src/fu-util-common.c:1102 +msgid "Device is usable for the duration of the update" +msgstr "" + +#. TRANSLATORS: a version check is required for all firmware +#: src/fu-util-common.c:1106 +msgid "Device firmware is required to have a version check" +msgstr "" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +#: src/fu-util-common.c:1110 +msgid "Device is required to install all provided releases" +msgstr "" + +#. TRANSLATORS: there is more than one supplier of the firmware +#: src/fu-util-common.c:1114 +msgid "Device supports switching to a different branch of firmware" +msgstr "" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +#: src/fu-util-common.c:1118 +msgid "Device will backup firmware before installing" +msgstr "" + +#. TRANSLATORS: on some systems certain devices have to have matching versions, +#. * e.g. the EFI driver for a given network card cannot be different +#: src/fu-util-common.c:1123 +msgid "All devices of the same type will be updated at the same time" +msgstr "" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +#: src/fu-util-common.c:1128 +msgid "Only version upgrades are allowed" +msgstr "" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power state +#. * or is out of wireless range +#: src/fu-util-common.c:1133 +msgid "Device is unreachable" +msgstr "" + +#. TRANSLATORS: we might ask the user the recovery key when next booting Windows +#: src/fu-util-common.c:1137 +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "" + +#. TRANSLATORS: the vendor is no longer supporting the device +#: src/fu-util-common.c:1141 +msgid "End of life" +msgstr "" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +#: src/fu-util-common.c:1145 +msgid "Signed Payload" +msgstr "" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +#: src/fu-util-common.c:1149 +msgid "Unsigned Payload" +msgstr "" + +#. TRANSLATORS: this device is not actually real +#: src/fu-util-common.c:1153 +msgid "Emulated" +msgstr "" + +#. TRANSLATORS: we're saving all USB events for emulation +#: src/fu-util-common.c:1157 +msgid "Tagged for emulation" +msgstr "" + +#. TRANSLATORS: stay on one firmware version unless the new version is explicitly +#. * specified +#: src/fu-util-common.c:1162 +msgid "Installing a specific release is explicitly required" +msgstr "" + +#. TRANSLATORS: we can save all device enumeration events for emulation +#: src/fu-util-common.c:1166 +msgid "Can tag for emulation" +msgstr "" + +#. TRANSLATORS: ask the user to do a simple task which should be translated +#: src/fu-util-common.c:1181 +msgid "Message" +msgstr "" + +#. TRANSLATORS: show the user a generic image that can be themed +#: src/fu-util-common.c:1185 +msgid "Image" +msgstr "" + +#. TRANSLATORS: ask the user a question, and it will not be translated +#: src/fu-util-common.c:1189 +msgid "Message (custom)" +msgstr "" + +#. TRANSLATORS: show the user a random image from the internet +#: src/fu-util-common.c:1193 +msgid "Image (custom)" +msgstr "" + +#. TRANSLATORS: the update state of the specific device +#: src/fu-util-common.c:1203 +msgid "Pending" +msgstr "" + +#. TRANSLATORS: the update state of the specific device +#: src/fu-util-common.c:1207 +msgid "Success" +msgstr "" + +#. TRANSLATORS: the update state of the specific device +#: src/fu-util-common.c:1211 +msgid "Failed" +msgstr "" + +#. TRANSLATORS: the update state of the specific device +#: src/fu-util-common.c:1215 +msgid "Transient failure" +msgstr "" + +#. TRANSLATORS: the update state of the specific device +#: src/fu-util-common.c:1219 +msgid "Needs reboot" +msgstr "" + +#. TRANSLATORS: as in laptop battery power +#: src/fu-util-common.c:1235 +msgid "System power is too low" +msgstr "" + +#. TRANSLATORS: as in laptop battery power +#: src/fu-util-common.c:1239 +#, c-format +msgid "System power is too low (%u%%, requires %u%%)" +msgstr "" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +#: src/fu-util-common.c:1245 +msgid "Device is unreachable, or out of wireless range" +msgstr "" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#: src/fu-util-common.c:1251 +msgid "Device battery power is too low" +msgstr "" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#: src/fu-util-common.c:1254 +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +#: src/fu-util-common.c:1260 +msgid "Device is waiting for the update to be applied" +msgstr "" + +#. TRANSLATORS: as in, wired mains power for a laptop +#: src/fu-util-common.c:1264 +msgid "Device requires AC power to be connected" +msgstr "" + +#. TRANSLATORS: lid means "laptop top cover" +#: src/fu-util-common.c:1268 +msgid "Device cannot be updated while the lid is closed" +msgstr "" + +#. TRANSLATORS: emulated means we are pretending to be a different model +#: src/fu-util-common.c:1272 +msgid "Device is emulated" +msgstr "" + +#. TRANSLATORS: The device cannot be updated due to missing vendor's license." +#: src/fu-util-common.c:1276 +msgid "Device requires a software license to update" +msgstr "" + +#. TRANSLATORS: an application is preventing system updates +#: src/fu-util-common.c:1280 +msgid "All devices are prevented from update by system inhibit" +msgstr "" + +#. TRANSLATORS: another application is updating the device already +#: src/fu-util-common.c:1284 +msgid "An update is in progress" +msgstr "" + +#. TRANSLATORS: device cannot be interrupted, for instance taking a phone call +#: src/fu-util-common.c:1288 +msgid "Device is in use" +msgstr "" + +#. TRANSLATORS: device does not have a display connected +#: src/fu-util-common.c:1292 +msgid "Device requires a display to be plugged in" +msgstr "" + +#. TRANSLATORS: we have two ways of communicating with the device, so we hide one +#: src/fu-util-common.c:1296 +msgid "Device is lower priority than an equivalent device" +msgstr "" + +#. TRANSLATORS: Name of hardware +#: src/fu-util-common.c:1327 +msgid "Unknown Device" +msgstr "" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +#: src/fu-util-common.c:1332 +msgid "Device ID" +msgstr "" + +#. TRANSLATORS: one line summary of device +#: src/fu-util-common.c:1335 src/fu-util-common.c:1893 +msgid "Summary" +msgstr "" + +#. TRANSLATORS: version number of previous firmware +#: src/fu-util-common.c:1352 +msgid "Previous version" +msgstr "" + +#. TRANSLATORS: version number of current firmware +#: src/fu-util-common.c:1356 +msgid "Current version" +msgstr "" + +#. TRANSLATORS: smallest version number installable on device +#: src/fu-util-common.c:1362 +msgid "Minimum Version" +msgstr "" + +#. TRANSLATORS: firmware version of bootloader +#: src/fu-util-common.c:1367 +msgid "Bootloader Version" +msgstr "" + +#. TRANSLATORS: manufacturer of hardware +#: src/fu-util-common.c:1376 src/fu-util-common.c:1379 +#: src/fu-util-common.c:1383 src/fu-util-common.c:1940 +msgid "Vendor" +msgstr "" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree +#: src/fu-util-common.c:1390 +msgid "Release Branch" +msgstr "" + +#. TRANSLATORS: length of time the update takes to apply +#: src/fu-util-common.c:1398 +msgid "Install Duration" +msgstr "" + +#. TRANSLATORS: serial number of hardware +#: src/fu-util-common.c:1402 +msgid "Serial Number" +msgstr "" + +#. TRANSLATORS: hardware state, e.g. "pending" +#: src/fu-util-common.c:1410 +msgid "Update State" +msgstr "" + +#. TRANSLATORS: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#: src/fu-util-common.c:1421 +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "" + +#. TRANSLATORS: refers to the battery inside the peripheral device +#: src/fu-util-common.c:1425 src/fu-util-common.c:1430 +msgid "Battery" +msgstr "" + +#. TRANSLATORS: error message from last update attempt +#: src/fu-util-common.c:1441 +msgid "Update Error" +msgstr "" + +#. TRANSLATORS: reasons the device is not updatable +#: src/fu-util-common.c:1445 +msgid "Problems" +msgstr "" + +#. TRANSLATORS: the original time/date the device was modified +#: src/fu-util-common.c:1465 +msgid "Last modified" +msgstr "" + +#. TRANSLATORS: global ID common to all similar hardware +#: src/fu-util-common.c:1491 +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: description of device ability +#: src/fu-util-common.c:1499 +msgid "Device Flags" +msgstr "" + +#. TRANSLATORS: description of the device requests +#: src/fu-util-common.c:1520 +msgid "Device Requests" +msgstr "" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +#: src/fu-util-common.c:1544 src/fu-util-common.c:1989 +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: Plugin is active only if hardware is found +#: src/fu-util-common.c:1570 +msgid "Enabled if hardware matches" +msgstr "" + +#. TRANSLATORS: Plugin is active and in use +#: src/fu-util-common.c:1574 +msgid "Ready" +msgstr "" + +#. TRANSLATORS: Plugin is inactive and not used +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:1578 src/fu-util-common.c:2195 +msgid "Disabled" +msgstr "" + +#. TRANSLATORS: not required for this system +#: src/fu-util-common.c:1582 +msgid "Required hardware was not found" +msgstr "" + +#. TRANSLATORS: system is not booted in UEFI mode +#: src/fu-util-common.c:1586 +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +#: src/fu-util-common.c:1591 +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "" + +#. TRANSLATORS: user needs to run a command, %1 is 'fwupdmgr unlock' +#: src/fu-util-common.c:1595 +#, c-format +msgid "Firmware updates disabled; run '%s' to enable" +msgstr "" + +#. TRANSLATORS: user needs to run a command +#: src/fu-util-common.c:1600 +msgid "Authentication details are required" +msgstr "" + +#. TRANSLATORS: no peeking +#: src/fu-util-common.c:1604 +msgid "Configuration is only readable by the system administrator" +msgstr "" + +#. TRANSLATORS: the plugin was created from a .so object, and was not built-in +#: src/fu-util-common.c:1608 +msgid "Loaded from an external module" +msgstr "" + +#. TRANSLATORS: check various UEFI and ACPI tables are unchanged after the update +#: src/fu-util-common.c:1612 +msgid "Will measure elements of system integrity around an update" +msgstr "" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +#: src/fu-util-common.c:1616 +msgid "Required efivarfs filesystem was not found" +msgstr "" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +#: src/fu-util-common.c:1620 +msgid "UEFI ESP partition not detected or configured" +msgstr "" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +#: src/fu-util-common.c:1624 +msgid "UEFI ESP partition may not be set up correctly" +msgstr "" + +#. TRANSLATORS: Failed to open plugin, hey Arch users +#: src/fu-util-common.c:1628 +msgid "Plugin dependencies missing" +msgstr "" + +#. TRANSLATORS: The kernel does not support this plugin +#: src/fu-util-common.c:1632 +msgid "Running kernel is too old" +msgstr "" + +#. TRANSLATORS: The plugin is only for testing +#: src/fu-util-common.c:1636 +msgid "Plugin is only for testing" +msgstr "" + +#. TRANSLATORS: The plugin enumeration might change the device current mode +#: src/fu-util-common.c:1640 +msgid "Plugin enumeration may change device state" +msgstr "" + +#. TRANSLATORS: description of plugin state, e.g. disabled +#: src/fu-util-common.c:1695 +msgid "Flags" +msgstr "" + +#. TRANSLATORS: a non-free software license +#: src/fu-util-common.c:1740 +msgid "Proprietary" +msgstr "" + +#. TRANSLATORS: the release urgency +#: src/fu-util-common.c:1754 +msgid "Low" +msgstr "" + +#. TRANSLATORS: the release urgency +#: src/fu-util-common.c:1758 +msgid "Medium" +msgstr "" + +#. TRANSLATORS: the release urgency +#: src/fu-util-common.c:1762 +msgid "High" +msgstr "" + +#. TRANSLATORS: the release urgency +#: src/fu-util-common.c:1766 +msgid "Critical" +msgstr "" + +#. TRANSLATORS: We verified the payload against the server +#: src/fu-util-common.c:1779 +msgid "Trusted payload" +msgstr "" + +#. TRANSLATORS: We verified the metadata against the server +#: src/fu-util-common.c:1783 +msgid "Trusted metadata" +msgstr "" + +#. TRANSLATORS: version is newer +#: src/fu-util-common.c:1787 +msgid "Is upgrade" +msgstr "" + +#. TRANSLATORS: version is older +#: src/fu-util-common.c:1791 +msgid "Is downgrade" +msgstr "" + +#. TRANSLATORS: version cannot be installed due to policy +#: src/fu-util-common.c:1795 +msgid "Blocked version" +msgstr "" + +#. TRANSLATORS: version cannot be installed due to policy +#: src/fu-util-common.c:1799 +msgid "Not approved" +msgstr "" + +#. TRANSLATORS: is not the main firmware stream +#: src/fu-util-common.c:1803 +msgid "Alternate branch" +msgstr "" + +#. TRANSLATORS: is not supported by the vendor +#: src/fu-util-common.c:1807 +msgid "Community supported" +msgstr "" + +#. TRANSLATORS: someone we trust has tested this +#: src/fu-util-common.c:1811 +msgid "Tested by trusted vendor" +msgstr "" + +#. TRANSLATORS: the %s is a vendor name, e.g. Lenovo +#: src/fu-util-common.c:1824 +#, c-format +msgid "Tested by %s" +msgstr "" + +#. TRANSLATORS: when the release was tested +#: src/fu-util-common.c:1827 +msgid "Tested" +msgstr "" + +#. TRANSLATORS: the OS the release was tested on +#: src/fu-util-common.c:1839 +msgid "Distribution" +msgstr "" + +#. TRANSLATORS: the firmware old version +#: src/fu-util-common.c:1844 +msgid "Old version" +msgstr "" + +#. TRANSLATORS: the fwupd version the release was tested on +#: src/fu-util-common.c:1850 +msgid "Version[fwupd]" +msgstr "" + +#. TRANSLATORS: version number of new firmware +#: src/fu-util-common.c:1873 +msgid "New version" +msgstr "" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +#: src/fu-util-common.c:1878 src/fu-util-common.c:2032 +msgid "Remote ID" +msgstr "" + +#. TRANSLATORS: the exact component on the server +#: src/fu-util-common.c:1883 +msgid "Release ID" +msgstr "" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree +#: src/fu-util-common.c:1888 +msgid "Branch" +msgstr "" + +#. TRANSLATORS: one line variant of release (e.g. 'China') +#: src/fu-util-common.c:1898 +msgid "Variant" +msgstr "" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +#: src/fu-util-common.c:1904 +msgid "License" +msgstr "" + +#. TRANSLATORS: file size of the download +#: src/fu-util-common.c:1907 +msgid "Size" +msgstr "" + +#. TRANSLATORS: when the update was built +#: src/fu-util-common.c:1909 +msgid "Created" +msgstr "" + +#. TRANSLATORS: how important the release is +#: src/fu-util-common.c:1915 +msgid "Urgency" +msgstr "" + +#. TRANSLATORS: more details about the update link +#: src/fu-util-common.c:1925 +msgid "Details" +msgstr "" + +#. TRANSLATORS: source (as in code) link +#: src/fu-util-common.c:1930 +msgid "Source" +msgstr "" + +#. TRANSLATORS: Software Bill of Materials link +#: src/fu-util-common.c:1935 +msgid "SBOM" +msgstr "" + +#. TRANSLATORS: length of time the update takes to apply +#: src/fu-util-common.c:1946 +msgid "Duration" +msgstr "" + +#. TRANSLATORS: helpful messages for the update +#: src/fu-util-common.c:1951 +msgid "Update Message" +msgstr "" + +#. TRANSLATORS: helpful image for the update +#: src/fu-util-common.c:1956 +msgid "Update Image" +msgstr "" + +#. TRANSLATORS: release attributes +#: src/fu-util-common.c:1960 +msgid "Release Flags" +msgstr "" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +#: src/fu-util-common.c:2001 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: hash to that exact firmware archive +#. TRANSLATORS: remote checksum +#: src/fu-util-common.c:2013 src/fu-util-common.c:2061 +msgid "Checksum" +msgstr "" + +#. TRANSLATORS: remote type, e.g. remote or local +#: src/fu-util-common.c:2035 +msgid "Type" +msgstr "" + +#. TRANSLATORS: if we can get metadata from peer-to-peer clients +#: src/fu-util-common.c:2048 +msgid "P2P Metadata" +msgstr "" + +#. TRANSLATORS: if we can get metadata from peer-to-peer clients +#: src/fu-util-common.c:2055 +msgid "P2P Firmware" +msgstr "" + +#. TRANSLATORS: the age of the metadata +#: src/fu-util-common.c:2068 +msgid "Age" +msgstr "" + +#. TRANSLATORS: how often we should refresh the metadata +#: src/fu-util-common.c:2074 +msgid "Refresh Interval" +msgstr "" + +#. TRANSLATORS: the numeric priority +#: src/fu-util-common.c:2081 +msgid "Priority" +msgstr "" + +#. TRANSLATORS: remote filename base +#: src/fu-util-common.c:2084 +msgid "Username" +msgstr "" + +#. TRANSLATORS: remote filename base +#: src/fu-util-common.c:2089 +msgid "Password" +msgstr "Salasõna" + +#. TRANSLATORS: filename of the local file +#: src/fu-util-common.c:2094 +msgid "Filename" +msgstr "" + +#. TRANSLATORS: filename of the local file +#: src/fu-util-common.c:2099 +msgid "Filename Signature" +msgstr "" + +#. TRANSLATORS: full path of the remote.conf file +#: src/fu-util-common.c:2104 +msgid "Filename Source" +msgstr "" + +#. TRANSLATORS: remote URI +#: src/fu-util-common.c:2109 +msgid "Metadata URI" +msgstr "" + +#. TRANSLATORS: remote URI +#: src/fu-util-common.c:2114 +msgid "Metadata Signature" +msgstr "" + +#. TRANSLATORS: remote URI +#: src/fu-util-common.c:2119 +msgid "Firmware Base URI" +msgstr "" + +#. TRANSLATORS: URI to send success/failure reports +#: src/fu-util-common.c:2124 +msgid "Report URI" +msgstr "" + +#. TRANSLATORS: Boolean value to automatically send reports +#: src/fu-util-common.c:2129 +msgid "Automatic Reporting" +msgstr "" + +#. TRANSLATORS: warning message shown after update has been scheduled +#: src/fu-util-common.c:2143 +msgid "" +"The update will continue when the device USB cable has been unplugged and " +"then re-inserted." +msgstr "" + +#. TRANSLATORS: warning message shown after update has been scheduled +#: src/fu-util-common.c:2148 +msgid "The update will continue when the device USB cable has been unplugged." +msgstr "" + +#. TRANSLATORS: warning message shown after update has been scheduled +#: src/fu-util-common.c:2153 +msgid "" +"The update will continue when the device USB cable has been re-inserted." +msgstr "" + +#. TRANSLATORS: warning message +#: src/fu-util-common.c:2158 +msgid "Press unlock on the device to continue the update process." +msgstr "" + +#. TRANSLATORS: warning message shown after update has been scheduled +#: src/fu-util-common.c:2162 +msgid "" +"Do not turn off your computer or remove the AC adaptor while the update is " +"in progress." +msgstr "" + +#. TRANSLATORS: message shown after device has been marked for emulation +#: src/fu-util-common.c:2167 +msgid "Unplug and replug the device to continue the update process." +msgstr "" + +#. TRANSLATORS: warning message +#: src/fu-util-common.c:2171 +msgid "" +"The update will continue when the device power cable has been removed and re-" +"inserted." +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2183 +msgid "Valid" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2187 +msgid "Invalid" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2199 +msgid "Locked" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2203 +msgid "Unlocked" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2207 +msgid "Encrypted" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2211 +msgid "Unencrypted" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2215 +msgid "Tainted" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2219 +msgid "Untainted" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2223 +msgid "Found" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2227 +msgid "Not found" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2231 +msgid "Supported" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2235 +msgid "Not supported" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2253 +msgid "OK" +msgstr "" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +#: src/fu-util-common.c:2307 +msgid "(obsoleted)" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2325 +msgid "IOMMU device protection enabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2330 +msgid "IOMMU device protection disabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2349 +msgid "Kernel is no longer tainted" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2354 +msgid "Kernel is tainted" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2360 +msgid "Kernel lockdown disabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2365 +msgid "Kernel lockdown enabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2371 +msgid "Pre-boot DMA protection is disabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2376 +msgid "Pre-boot DMA protection is enabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2382 +msgid "Secure Boot disabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2387 +msgid "Secure Boot enabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2393 +msgid "All TPM PCRs are valid" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2398 +msgid "A TPM PCR is now an invalid value" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2403 +msgid "All TPM PCRs are now valid" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2409 +msgid "TPM PCR0 reconstruction is invalid" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2414 +msgid "TPM PCR0 reconstruction is now valid" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2420 +msgid "UEFI memory protection enabled but not locked" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2425 +msgid "UEFI memory protection enabled and locked" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2430 +msgid "UEFI memory protection is now locked" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2435 +msgid "UEFI memory protection is now unlocked" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2440 +msgid "The UEFI certificate store is now up to date" +msgstr "" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS region". +#. %2 refers to a result value, e.g. "Invalid" +#: src/fu-util-common.c:2465 +#, c-format +msgid "%s disappeared: %s" +msgstr "" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#: src/fu-util-common.c:2477 +#, c-format +msgid "%s appeared: %s" +msgstr "" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#: src/fu-util-common.c:2487 +#, c-format +msgid "%s changed: %s → %s" +msgstr "" + +#. TRANSLATORS: title for host security events +#: src/fu-util-common.c:2532 +msgid "Host Security Events" +msgstr "" + +#. TRANSLATORS: now list devices with unfixed high-priority issues +#: src/fu-util-common.c:2560 +msgid "There are devices with issues:" +msgstr "" + +#. TRANSLATORS: this is the HSI suffix +#: src/fu-util-common.c:2628 +msgid "Runtime Suffix" +msgstr "" + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +#: src/fu-util-common.c:2650 +msgid "This system has a low HSI security level." +msgstr "" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +#: src/fu-util-common.c:2658 +msgid "This system has HSI runtime issues." +msgstr "" + +#. TRANSLATORS: this is more background on a security measurement problem +#: src/fu-util-common.c:2667 +msgid "The TPM PCR0 differs from reconstruction." +msgstr "" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#: src/fu-util-common.c:2711 +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "" + +#. TRANSLATORS: %1 is the device vendor name +#: src/fu-util-common.c:2718 +#, c-format +msgid "" +"Your hardware may be damaged using this firmware, and installing this " +"release may void any warranty with %s." +msgstr "" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#: src/fu-util-common.c:2736 +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "" + +#. TRANSLATORS: should the branch be changed +#: src/fu-util-common.c:2745 +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +#: src/fu-util-common.c:2769 +msgid "" +"Some of the platform secrets may be invalidated when updating this firmware." +msgstr "" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +#: src/fu-util-common.c:2774 +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#: src/fu-util-common.c:2778 +#, c-format +msgid "See %s for more details." +msgstr "" + +#. TRANSLATORS: title text, shown as a warning +#: src/fu-util-common.c:2781 +msgid "Full Disk Encryption Detected" +msgstr "" + +#. TRANSLATORS: unsupported build of the package +#: src/fu-util-common.c:2805 +msgid "This package has not been validated, it may not work properly." +msgstr "" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +#: src/fu-util-common.c:2827 +msgid "Enable new remote?" +msgstr "" + +#. TRANSLATORS: should the remote still be enabled +#: src/fu-util-common.c:2833 +msgid "Agree and enable the remote?" +msgstr "" diff -Nru fwupd-2.0.8/po/eu.po fwupd-2.0.20/po/eu.po --- fwupd-2.0.8/po/eu.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/eu.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,16 +4,20 @@ # # Translators: # assar , 2017,2023-2024 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Basque (http://app.transifex.com/freedesktop/fwupd/language/eu/)\n" +"PO-Revision-Date: 2025-10-24 10:05+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Basque \n" +"Language: eu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: eu\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -432,7 +436,7 @@ #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" -msgstr "Firmwarearen eguneratzea instalatzen..." +msgstr "Firmwarearen eguneratzea instalatzen…" #. TRANSLATORS: Device cannot be removed easily msgid "Internal device" @@ -755,11 +759,6 @@ msgid "Successfully disabled remote" msgstr "Urrunekoa ongi desgaitu da" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Metadatu berriak ongi deskargatu dira:" - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "Urrunekoa ongi gaitu eta freskatu da" diff -Nru fwupd-2.0.8/po/fi.po fwupd-2.0.20/po/fi.po --- fwupd-2.0.8/po/fi.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/fi.po 2026-02-26 11:36:18.000000000 +0000 @@ -8,16 +8,22 @@ # Ricky Tigg, 2024 # Timo Jyrinki , 2021 # Ville-Pekka Vainio , 2021,2023 +# Ricky Tigg , 2025. +# Richard Hughes , 2025. +# Riku Viitanen , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Finnish (http://app.transifex.com/freedesktop/fwupd/language/fi/)\n" +"PO-Revision-Date: 2025-12-01 12:00+0000\n" +"Last-Translator: Riku Viitanen \n" +"Language-Team: Finnish \n" +"Language: fi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: fi\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.15-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -38,7 +44,7 @@ msgstr "Laitteen %s akkupäivitys" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "Laitteen %s suorittimen mikrokoodipäivitys" @@ -296,7 +302,7 @@ #, c-format msgid "%u device has a firmware upgrade available." msgid_plural "%u devices have a firmware upgrade available." -msgstr[0] "%u laitteille on saatavilla laiteohjelmistopäivitys." +msgstr[0] "%u laitteelle on saatavilla laiteohjelmistopäivitys." msgstr[1] "%u laitteille on saatavilla laiteohjelmistopäivitys." #. TRANSLATORS: this is shown in the MOTD @@ -327,6 +333,13 @@ msgstr[0] "%u sekunti" msgstr[1] "%u sekuntia" +#. TRANSLATORS: device tests can be specific to a CPU type +#, c-format +msgid "%u test was skipped" +msgid_plural "%u tests were skipped" +msgstr[0] "%u testi ohitettiin" +msgstr[1] "%u testiä ohitettiin" + #. TRANSLATORS: first percentage is current value, 2nd percentage is the #. * lowest limit the firmware update is allowed for the update to happen #, c-format @@ -343,15 +356,15 @@ #. TRANSLATORS: Title: if hardware enforces control of SPI replays msgid "AMD Firmware Replay Protection" -msgstr "AMD:n laiteohjelmiston toistosuojaus" +msgstr "AMD Firmware Replay Protection" #. TRANSLATORS: Title: if hardware enforces control of SPI writes msgid "AMD Firmware Write Protection" -msgstr "AMD:n laiteohjelmiston kirjoitussuojaus" +msgstr "AMD Firmware Write Protection" #. TRANSLATORS: Title: if firmware enforces rollback protection msgid "AMD Secure Processor Rollback Protection" -msgstr "AMD-suorittimen versiopalautuksen suojaus" +msgstr "AMD Secure Processor Rollback Protection" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "ARCHIVE FIRMWARE METAINFO [FIRMWARE] [METAINFO] [JCATFILE]" @@ -427,6 +440,10 @@ msgid "Allow switching firmware branch" msgstr "Salli laiteohjelmiston haaran vaihtaminen" +#. TRANSLATORS: error message +msgid "Already exists, and no --force specified" +msgstr "On jo olemassa, eikä --force ominaisuutta määritettynä" + #. TRANSLATORS: is not the main firmware stream msgid "Alternate branch" msgstr "Vaihtoehtoinen haara" @@ -457,14 +474,14 @@ #. TRANSLATORS: actually sending the update to the hardware msgid "Applying update…" -msgstr "Asennetaan päivitystä..." +msgstr "Päivitystä käytetään…" #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "Approved firmware:" msgid_plural "Approved firmware:" msgstr[0] "Hyväksytty laiteohjelmisto:" -msgstr[1] "Hyväksytty laiteohjelmisto:" +msgstr[1] "Hyväksytyt laiteohjelmistot:" #. TRANSLATORS: command description msgid "Asks the daemon to quit" @@ -566,6 +583,11 @@ msgid "Automatic Reporting" msgstr "Automaattinen raportointi" +#. TRANSLATORS: we can auto-uninhibit after a timeout +#, c-format +msgid "Automatically uninhibiting in %ums…" +msgstr "Automaattinen esto poistetaan %u ms…" + #. TRANSLATORS: can we JFDI? msgid "Automatically upload every time?" msgstr "Lähetetäänkö automaattisesti joka kerta?" @@ -576,7 +598,7 @@ #. TRANSLATORS: Title: if firmware enforces rollback protection msgid "BIOS Rollback Protection" -msgstr "BIOS-versiopalautuksen suojaus" +msgstr "BIOS Rollback Protection" #. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled msgid "BIOS firmware updates" @@ -600,7 +622,7 @@ #. TRANSLATORS: command description msgid "Bind new kernel driver" -msgstr "Sido uusi ydinohjain" +msgstr "Sido uusi kernel-ohjain" #. TRANSLATORS: there follows a list of hashes msgid "Blocked firmware files:" @@ -642,7 +664,7 @@ #. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, #. * enabled means supported by the processor msgid "CET Platform" -msgstr "CET Platform" +msgstr "CET-alusta" #. TRANSLATORS: longer description msgid "CPU Microcode must be updated to mitigate against various information-disclosure security issues." @@ -711,6 +733,10 @@ msgid "Community supported" msgstr "Yhteisön tukema" +#. TRANSLATORS: command description +msgid "Compares two versions for equality" +msgstr "Vertailee kahta versiota rinnakkain" + #. TRANSLATORS: title prefix for the BIOS settings dialog msgid "Configuration Change Suggested" msgstr "Ehdotetaan kokoonpanomuutosta" @@ -731,6 +757,10 @@ msgid "Convert a firmware file" msgstr "Muunna laiteohjelmistotiedosto" +#. TRANSLATORS: command description +msgid "Create an EFI boot entry" +msgstr "Luo EFI boot merkintä" + #. TRANSLATORS: when the update was built msgid "Created" msgstr "Luotu" @@ -763,6 +793,10 @@ msgid "Decompressing…" msgstr "Puretaan…" +#. TRANSLATORS: command description +msgid "Delete an EFI boot entry" +msgstr "Poista EFI boot merkintä" + #. TRANSLATORS: description of BIOS setting #. TRANSLATORS: multiline description of device msgid "Description" @@ -800,7 +834,7 @@ #. TRANSLATORS: the device is already connected msgid "Device already exists" -msgstr "Laite on jo olemassa." +msgstr "Laite on jo olemassa" #. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse msgid "Device battery power is too low" @@ -888,7 +922,7 @@ #. TRANSLATORS: longer description msgid "Device software updates are provided for this device." -msgstr "Tälle laitteelle tarjotaan ohjelmistopäivityksiä" +msgstr "Tälle laitteelle tarjotaan ohjelmistopäivityksiä." #. TRANSLATORS: Device supports a safety mechanism for flashing msgid "Device stages updates" @@ -921,7 +955,7 @@ #. TRANSLATORS: message letting the user there is an update #. * waiting, but there is a reason it cannot be deployed msgid "Devices with firmware updates that need user action: " -msgstr "Laitteet, joissa on firmware päivityksiä, jotka vaativat käyttäjän toimia:" +msgstr "Laitteet, joissa on firmware päivityksiä, jotka vaativat käyttäjän toimia: " #. TRANSLATORS: message letting the user know no device #. * upgrade available due to missing on LVFS @@ -932,7 +966,7 @@ #. TRANSLATORS: message letting the user know no #. * device upgrade available due to missing on LVFS msgid "Devices with no available firmware updates: " -msgstr "Laitteet, joille ei ole saatavilla laiteohjelmiston päivityksiä:" +msgstr "Laitteet, joille ei ole saatavilla laiteohjelmiston päivityksiä: " #. TRANSLATORS: message letting the user know no device upgrade available #. TRANSLATORS: message letting the user know no device @@ -1016,6 +1050,11 @@ msgid "Do you understand the consequences of changing the firmware branch?" msgstr "Ymmärrätkö laiteohjelmiston haaran vaihtamisen seuraukset?" +#. TRANSLATORS: ask the user if it's okay to convert, +#. * "it" being the data contained in the EFI boot entry +msgid "Do you want to convert it now?" +msgstr "Haluatko muuntaa sen heti?" + #. TRANSLATORS: offer to disable this nag msgid "Do you want to disable this feature for future updates?" msgstr "Haluatko poistaa tämän ominaisuuden käytöstä tulevia päivityksiä varten?" @@ -1067,6 +1106,10 @@ msgid "Duration" msgstr "Kesto" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "EMULATION-FILE [ARCHIVE-FILE]" +msgstr "EMULATION-FILE [ARCHIVE-FILE]" + #. TRANSLATORS: longer description msgid "Each system should have tests to ensure firmware security." msgstr "Jokaiselle järjestelmälle pitäisi olla testejä, jotka varmistavat laiteohjelmiston turvallisuuden." @@ -1189,10 +1232,6 @@ msgstr "TIEDOSTONIMI SERTIFIKAATTI YKSITYINEN AVAIN" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "TIEDOSTONIMI LAITETUNNUS" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" msgstr "TIEDOSTONIMI SIIRTYMÄ DATA [LAITEOHJELMISTON-TYYPPI]" @@ -1302,7 +1341,7 @@ #. TRANSLATORS: Title: if we can verify the firmware checksums msgid "Firmware Attestation" -msgstr "Laiteohjelmiston vahvistaminen" +msgstr "Laiteohjelmiston vahvistus" #. TRANSLATORS: longer description msgid "Firmware Attestation checks device software using a reference copy, to make sure that it has not been changed." @@ -1426,7 +1465,7 @@ #. TRANSLATORS: Title: if the part has been fused msgid "Fused Platform" -msgstr "Alustassa on sulake" +msgstr "Fused Platform" #. TRANSLATORS: Title: if the part has been fused msgid "Fused platform" @@ -1463,6 +1502,10 @@ msgstr "Hae kaikki järjestelmään rekisteröidyt laajennukset" #. TRANSLATORS: command description +msgid "Get all known version formats" +msgstr "Hae kaikki tunnetut versioformaatit" + +#. TRANSLATORS: command description msgid "Get device report metadata" msgstr "Hae laitteistoraportin metatiedot" @@ -1487,10 +1530,6 @@ msgstr "Hakee estettyjen laiteohjelmistojen luettelon" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Hae luettelo liitettyjen laitteiden päivityksistä" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "Hakee laitteen julkaisut" @@ -1515,17 +1554,32 @@ msgstr "Koneen suojaustapahtumat" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "Host Security ID (HSI) ei ole tuettu" #. TRANSLATORS: success, so say thank you to the user msgid "Host Security ID attributes uploaded successfully, thanks!" -msgstr "Koneen tietoturvatunnisteen (HSI) attribuuttien lähetys onnistui. Kiitos!" +msgstr "Koneen turvatunnisteen (HSI) attribuuttien lähetys onnistui. Kiitos!" #. TRANSLATORS: this is a string like 'HSI:2-U' msgid "Host Security ID:" -msgstr "Koneen tietoturvatunniste (Host Security ID):" +msgstr "Koneen turvatunniste Host Security ID:" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX" +msgstr "INDEX" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX KEY [VALUE]" +msgstr "INDEX KEY [VALUE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX NAME TARGET [MOUNTPOINT]" +msgstr "INDEX NAME TARGET [MOUNTPOINT]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX1,INDEX2" +msgstr "INDEX1,INDEX2" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "INHIBIT-ID" @@ -1612,7 +1666,7 @@ #. TRANSLATORS: command description msgid "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" -msgstr "Asenna tietty laiteohjelmisto laitteeseen. Asennetaan kaikkiin mahdollisiin laitteisiin, kun CAB täsmää." +msgstr "Asenna tietty laiteohjelmisto laitteeseen. Asennetaan kaikkiin mahdollisiin laitteisiin, kun CAB täsmää" msgid "Install old version of signed system firmware" msgstr "Asenna järjestelmän laiteohjelmiston allekirjoitettu vanha versio" @@ -1707,7 +1761,7 @@ #. TRANSLATORS: Title: GDS is where the CPU leaks information msgid "Intel GDS Mitigation" -msgstr "Intel GDS lieventäminen" +msgstr "Intel GDS -lievennys" #. TRANSLATORS: Title: GDS is where the CPU leaks information msgid "Intel GDS mitigation" @@ -1747,16 +1801,28 @@ #. TRANSLATORS: error message msgid "Invalid arguments, expected GUID" -msgstr "Virheelliset argumentit, odotettu GUID" +msgstr "Virheelliset argumentit, GUID" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected INDEX KEY [VALUE]" +msgstr "Virheelliset argumentit, INDEX KEY [VALUE]" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected INDEX NAME TARGET [MOUNTPOINT]" +msgstr "Virheelliset argumentit, INDEX NAME TARGET [MOUNTPOINT]" #. TRANSLATOR: This is the error message for #. * incorrect parameter msgid "Invalid arguments, expected an AppStream ID" -msgstr "Virheelliset argumentit, odotettiin AppStream-tunnusta" +msgstr "Virheelliset argumentit, AppStream ID" #. TRANSLATORS: error message msgid "Invalid arguments, expected at least ARCHIVE FIRMWARE METAINFO" -msgstr "Virheelliset argumentit, odotettiin ainakin ARCHIVE FIRMWARE METAINFO" +msgstr "Virheelliset argumentit, ainakin ARCHIVE FIRMWARE METAINFO" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected base-16 integer" +msgstr "Virheelliset argumentit base-16 integer" #. TRANSLATORS: version is older msgid "Is downgrade" @@ -1778,19 +1844,19 @@ #. TRANSLATORS: HSI event title msgid "Kernel is no longer tainted" -msgstr "Ydin ei ole enää saastunut" +msgstr "Kernel ei ole enää saastunut" #. TRANSLATORS: HSI event title msgid "Kernel is tainted" -msgstr "Ydin on saastunut" +msgstr "Kernel on saastunut" #. TRANSLATORS: HSI event title msgid "Kernel lockdown disabled" -msgstr "Ytimen lukitus on poistettu käytöstä" +msgstr "Kernelin lukitus on poistettu" #. TRANSLATORS: HSI event title msgid "Kernel lockdown enabled" -msgstr "Ytimen lukitus on käytössä" +msgstr "Kernel on lukittu" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "LOCATION" @@ -1810,25 +1876,25 @@ #. TRANSLATORS: Title: lockdown is a security mode of the kernel msgid "Linux Kernel Lockdown" -msgstr "Linux-ytimen lukitus" +msgstr "Linux kernel lukitus" #. TRANSLATORS: longer description msgid "Linux Kernel Lockdown mode prevents administrator (root) accounts from accessing and changing critical parts of system software." -msgstr "Linux-ytimen lukitustila estää pääkäyttäjiä (root) lukemasta ja muokkaamasta järjestelmäohjelmistojen kriittisiä osia." +msgstr "Linux Kernel lukitustila estää pääkäyttäjiä (root) lukemasta ja muokkaamasta järjestelmäohjelmistojen kriittisiä osia." msgid "Linux Kernel Swap temporarily saves information to disk as you work. If the information is not protected, it could be accessed by someone if they obtained the disk." -msgstr "Linux-ytimen sivutus (swap) tallentaa tietoja käytön aikana väliaikaisesti levylle. Jos näitä tietoja ei suojata, levyn käsiinsä saava taho pystyy lukemaan ne." +msgstr "Linux Kernel Swap tallentaa tietoja käytön aikana väliaikaisesti levylle. Jos näitä tietoja ei suojata, levyn käsiinsä saava taho pystyy lukemaan ne." #. TRANSLATORS: Title: if it's tainted or not msgid "Linux Kernel Verification" -msgstr "Linux-ytimen varmennus" +msgstr "Linux kernel vahvistus" msgid "Linux Kernel Verification makes sure that critical system software has not been tampered with. Using device drivers which are not provided with the system can prevent this security feature from working correctly." -msgstr "Linux-ytimen varmennus varmentaa, että kriittistä järjestelmäohjelmistoa ei ole peukaloitu. Sellaisten laiteajurien käyttö, joita ei toimiteta järjestelmän mukana, saattaa estää tämän turvallisuusominaisuuden oikeanlaisen toiminnan." +msgstr "Linux Kernel varmennus varmentaa, että kriittistä järjestelmäohjelmistoa ei ole peukaloitu. Sellaisten laiteajurien käyttö, joita ei toimiteta järjestelmän mukana, saattaa estää tämän turvallisuusominaisuuden oikeanlaisen toiminnan." #. TRANSLATORS: Title: swap space or swap partition msgid "Linux Swap" -msgstr "Linuxin sivutus (Swap)" +msgstr "Linux Swap" msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Linux-laitetoimittajien laiteohjelmistopalvelu (vakaat laiteohjelmistot)" @@ -1838,15 +1904,15 @@ #. TRANSLATORS: Title: if it's tainted or not msgid "Linux kernel" -msgstr "Linux-ydin" +msgstr "Linux kernel" #. TRANSLATORS: Title: lockdown is a security mode of the kernel msgid "Linux kernel lockdown" -msgstr "Linux-ytimen lukitus" +msgstr "Linux kernel lukitus" #. TRANSLATORS: Title: swap space or swap partition msgid "Linux swap" -msgstr "Linuxin sivutus (swap)" +msgstr "Linux swap" #. TRANSLATORS: command description msgid "List EFI boot files" @@ -2061,6 +2127,11 @@ msgid "No updates available for remaining devices" msgstr "Muille laitteille ei ole saatavilla päivityksiä" +#. TRANSLATORS: error message +#, c-format +msgid "No volume matched %s" +msgstr "Ei vastaavaa asemaa %s" + #. TRANSLATORS: version cannot be installed due to policy msgid "Not approved" msgstr "Hylätty" @@ -2099,10 +2170,6 @@ msgstr "Vain versioiden päivitykset ovat sallittuja" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Tuloste JSON-muodossa" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Ohita oletusarvoinen ESP-polku" @@ -2124,11 +2191,11 @@ #. TRANSLATORS: reading new dbx from the update msgid "Parsing dbx update…" -msgstr "Jäsennetään dbx-päivitystä..." +msgstr "Jäsennetään dbx-päivitystä…" #. TRANSLATORS: reading existing dbx from the system msgid "Parsing system dbx…" -msgstr "Jäsennetään järjestelmän dbx:ää..." +msgstr "Jäsennetään järjestelmän dbx:ta…" #. TRANSLATORS: remote filename base msgid "Password" @@ -2171,7 +2238,8 @@ msgid "Please enter a number from 0 to %u: " msgstr "Anna numero 0 –%u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " msgstr "Valitse %s tai %s: " @@ -2229,7 +2297,7 @@ #. TRANSLATORS: Title: if fwupd supports HSI on this chip msgid "Processor Security Checks" -msgstr "Suorittimen turvallisuustarkastukset" +msgstr "Suorittimen tietoturvatarkistukset" #. TRANSLATORS: Title: if firmware enforces rollback protection msgid "Processor rollback protection" @@ -2360,7 +2428,7 @@ #. TRANSLATORS: command description msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" -msgstr "Nouda BIOS-asetuksia. Jos parametreja ei anneta, noudetaan kaikki asetukset." +msgstr "Nouda BIOS-asetuksia. Jos parametreja ei anneta, noudetaan kaikki asetukset" #. TRANSLATORS: command description msgid "Return all the hardware IDs for the machine" @@ -2405,12 +2473,20 @@ #. TRANSLATORS: The kernel does not support this plugin msgid "Running kernel is too old" -msgstr "Ajossa oleva ydin on liian vanha" +msgstr "Ajossa oleva kernel on liian vanha" #. TRANSLATORS: this is the HSI suffix msgid "Runtime Suffix" msgstr "Ajonaikainen pääte" +#. TRANSLATORS: Software Bill of Materials link +msgid "SBOM" +msgstr "SBOM" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SECTION" +msgstr "SECTION" + #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "SETTING VALUE" msgstr "ASETUS ARVO" @@ -2477,11 +2553,11 @@ #. TRANSLATORS: HSI event title msgid "Secure Boot disabled" -msgstr "Turvakäynnistys (secure boot) pois käytöstä" +msgstr "Secure Boot poistettu" #. TRANSLATORS: HSI event title msgid "Secure Boot enabled" -msgstr "Turvakäynnistys (secure boot) käytössä" +msgstr "Secure Boot käytössä" msgid "Security hardening for HSI" msgstr "HSI turvallisuuden käsittely" @@ -2489,7 +2565,7 @@ #. TRANSLATORS: the %1 is a URL to a wiki page #, c-format msgid "See %s for more details." -msgstr "Lisätietoja sivulla %s" +msgstr "Lisätietoja %s:lta." #. TRANSLATORS: %s is a link to a website #, c-format @@ -2520,6 +2596,18 @@ msgid "Set one or more BIOS settings" msgstr "Aseta yksi tai useampi BIOS-asetus" +#. TRANSLATORS: command description +msgid "Set or remove an EFI boot hive entry" +msgstr "Aseta tai poista EFI boot hive merkintä" + +#. TRANSLATORS: command description +msgid "Set the EFI boot next" +msgstr "Aseta EFI boot seuraavaksi" + +#. TRANSLATORS: command description +msgid "Set the EFI boot order" +msgstr "Aseta EFI boot käynnistysjärjestys" + #. TRANSLATORS: command line option msgid "Set the download retries for transient errors" msgstr "Aseta lataus yrittämään uudelleen virheiden varalta" @@ -2647,11 +2735,6 @@ msgid "Successfully disabled test devices" msgstr "Testilaitteiden käytöstä poistaminen onnistui" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Uusien metatietojen lataus onnistui: " - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "Lähteen käyttöönotto ja päivitys onnistui" @@ -2773,7 +2856,7 @@ #. TRANSLATORS: Title: Whether firmware is locked down msgid "System Management Mode" -msgstr "System Management Mode" +msgstr "Järjestelmänhallintatila" #. TRANSLATORS: this CLI tool is now preventing system updates msgid "System Update Inhibited" @@ -2832,7 +2915,7 @@ #. TRANSLATORS: Title: TPM = Trusted Platform Module msgid "TPM v2.0" -msgstr "TPM v2.0" +msgstr "TPM v. 2.0" #. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 msgid "Tag" @@ -2869,6 +2952,14 @@ msgid "Tested by trusted vendor" msgstr "Luotettavan valmistajan testaama" +#. TRANSLATORS: the boot entry was in a legacy format +msgid "The EFI boot entry is not in hive format, and shim may not be new enough to read it." +msgstr "EFI boot merkintä ei ollut hive-formaatissa, eikä shim-tiedosto välttämättä ole riittävän uusi sen lukemiseen." + +#. TRANSLATORS: try to treat the legacy format as a hive +msgid "The EFI boot entry was not in hive format, falling back" +msgstr "EFI boot ei ollut hive-formaatissa ja palautui takaisin" + #. TRANSLATORS: longer description msgid "The Intel Management Engine Key Manifest must be valid so that the device firmware can be trusted by the CPU." msgstr "Intel Management Engine -avainmanifestin täytyy olla kelvollinen, jotta suoritin voi luottaa laiteohjelmistoon." @@ -2897,6 +2988,18 @@ msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." msgstr "UEFI:n alusta-avaimen perusteella tiedetään, tuleeko laiteohjelmisto luotettavasta lähteestä." +#. TRANSLATORS: HSI event title +msgid "The UEFI certificate store is now up to date" +msgstr "UEFI-varmenteet on nyt päivitetty" + +#. TRANSLATORS: longer description +msgid "The UEFI db contains the list of valid certificates that can be used to authorize what EFI binaries are allowed to run." +msgstr "UEFI-tietokanta sisältää luettelon voimassa olevista varmenteista, joilla valtuutetaan EFI-binäärien suoritusoikeudet." + +#. TRANSLATORS: longer description +msgid "The UEFI system can set up memory attributes at boot which prevent common exploits from running." +msgstr "UEFI voi määrittää käynnistyksen yhteydessä muistin ominaisuuksia, jotka estää hyökkäysten suorittamisen." + #. TRANSLATORS: the user is SOL for support... msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" msgstr "Palvelu on ladannut 3. osapuolen koodia, eivätkä kehittäjät enää jatkossa tue sitä!" @@ -2991,7 +3094,7 @@ #. TRANSLATORS: the %1 is a kernel command line key=value #, c-format msgid "This tool can add a kernel argument of '%s', but it will only be active after restarting the computer." -msgstr "Tämä työkalu voi lisätä ytimen argumentin '%s', mutta se on aktiivinen vasta tietokoneen uudelleenkäynnistyksen jälkeen." +msgstr "Tämä työkalu voi lisätä kernel argumentin \"%s\", mutta se on aktiivinen vasta tietokoneen uudelleenkäynnistyksen jälkeen." #. TRANSLATORS: the %1 is a BIOS setting name. #. * %2 and %3 are the values, e.g. "True" or "Windows10" @@ -3002,7 +3105,7 @@ #. TRANSLATORS: the %1 is a kernel command line key=value #, c-format msgid "This tool can change the kernel argument from '%s' to '%s', but it will only be active after restarting the computer." -msgstr "Tämä työkalu voi muuttaa ytimen argumentin '%s':sta '%s':ksi, mutta se on aktiivinen vasta tietokoneen uudelleenkäynnistyksen jälkeen." +msgstr "Tämä työkalu voi muuttaa kernel argumentin \"%s\":sta \"%s\":ksi, mutta se on aktiivinen vasta tietokoneen uudelleenkäynnistyksen jälkeen." #. TRANSLATORS: CLI description msgid "This tool will read and parse the TPM event log from the system firmware." @@ -3030,7 +3133,7 @@ #. TRANSLATORS: Title: Bootservice is when only readable from early-boot msgid "UEFI Bootservice Variables" -msgstr "UEFI-käynnistyspalvelun muuttujat" +msgstr "UEFI Boot käynnistyspalvelun muuttujat" #. TRANSLATORS: partition refers to something on disk, again, hey Arch users msgid "UEFI ESP partition may not be set up correctly" @@ -3040,30 +3143,38 @@ msgid "UEFI ESP partition not detected or configured" msgstr "UEFIn ESP-osiota ei havaittu tai määritetty" +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI Memory Protection" +msgstr "UEFI-muistin suojaus" + #. TRANSLATORS: Title: PK is the 'platform key' for the machine msgid "UEFI Platform Key" -msgstr "UEFI:n alusta-avain" +msgstr "UEFI-alusta-avain" #. TRANSLATORS: Title: SB is a way of locking down UEFI msgid "UEFI Secure Boot" -msgstr "UEFI-turvakäynnistys (secure boot)" +msgstr "UEFI Secure Boot" #. TRANSLATORS: longer description msgid "UEFI Secure Boot prevents malicious software from being loaded when the device starts." -msgstr "UEFI-turvakäynnistys (secure boot) estää haittaohjelmien lataamisen laitteen käynnistyessä." +msgstr "UEFI Secure Boot estää haittaohjelmien lataamisen laitteen käynnistyessä." #. TRANSLATORS: longer description msgid "UEFI boot service variables should not be readable from runtime mode." -msgstr "UEFI-käynnistyspalvelun muuttujia ei pitäisi pystyä lukemaan ajon aikana." +msgstr "EFI boot palvelun muuttujia ei pitäisi pystyä lukemaan ajon aikana." #. TRANSLATORS: Title: Bootservice is when only readable from early-boot msgid "UEFI bootservice variables" -msgstr "UEFI-käynnistyspalvelun muuttujat" +msgstr "UEFI Boot käynnistyspalvelun muuttujat" #. TRANSLATORS: capsule updates are an optional BIOS feature msgid "UEFI capsule updates not available or enabled in firmware setup" msgstr "UEFI-kapselipäivitykset eivät ole saatavilla tai niitä ei ole otettu käyttöön laiteohjelmiston asetuksissa" +#. TRANSLATORS: Title: is UEFI db up-to-date +msgid "UEFI db" +msgstr "UEFI db" + #. TRANSLATORS: program name msgid "UEFI dbx Utility" msgstr "UEFI-dbx-apuohjelma" @@ -3072,13 +3183,33 @@ msgid "UEFI firmware can not be updated in legacy BIOS mode" msgstr "UEFI-laiteohjelmiston päivitys ei onnistu vanhassa BIOS-tilassa" +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI memory protection" +msgstr "UEFI-muistin suojaus" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled and locked" +msgstr "UEFI-muistisuojaus on käytössä ja lukittu" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled but not locked" +msgstr "UEFI-muistisuojaus on käytössä, mutta ei lukittu" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now locked" +msgstr "UEFI-muistisuojaus on nyt lukittu" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now unlocked" +msgstr "UEFI-muistisuojaus on nyt poistettu" + #. TRANSLATORS: Title: PK is the 'platform key' for the machine msgid "UEFI platform key" msgstr "UEFI:n alusta-avain" #. TRANSLATORS: Title: SB is a way of locking down UEFI msgid "UEFI secure boot" -msgstr "UEFI-turvakäynnistys (secure boot)" +msgstr "UEFI turvallinen käynnistys" #. TRANSLATORS: error message msgid "Unable to connect to service" @@ -3198,13 +3329,6 @@ msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" msgstr "Päivittää kaikki määritetyt laitteet uusimpaan laiteohjelmistoversioon - tai kaikki laitteet, jos määritystä ei ole annettu" -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "Päivitykset on julkaistu %u paikalliselle laitteelle" -msgstr[1] "Päivityksiä on julkaistu %u paikallisesta laitteesta %u" - msgid "Updating" msgstr "Päivitetään" @@ -3252,7 +3376,7 @@ #. * where $1 is something like 'fwupdmgr --help' #, c-format msgid "Use %s for help" -msgstr "Käytä %ssaadaksesi apua" +msgstr "Käytä %s saadaksesi apua" #. TRANSLATORS: CTRL^C [holding control, and then pressing C] will exit the #. program @@ -3267,13 +3391,17 @@ msgid "Username" msgstr "Käyttäjätunnus" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "VERSION1 VERSION2 [FORMAT]" +msgstr "VERSION1 VERSION2 [FORMAT]" + #. TRANSLATORS: Suffix: the HSI result msgid "Valid" msgstr "Kunnossa" #. TRANSLATORS: ESP refers to the EFI System Partition msgid "Validating ESP contents…" -msgstr "Vahvistetaan ESP:n sisältöä..." +msgstr "Vahvistetaan ESP:n sisältöä…" #. TRANSLATORS: one line variant of release (e.g. 'China') msgid "Variant" @@ -3346,7 +3474,7 @@ #. * of firmware that works together #, c-format msgid "Your system is set up to the BKC of %s." -msgstr "Järjestelmäsi on asetettu BKC:hen (paras tunnettu konfiguraatio) %s." +msgstr "Järjestelmä on asetettu BKC-tilaan \"paras tunnettu konfiguraatio\" %s." #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "[APPSTREAM_ID]" @@ -3381,10 +3509,26 @@ msgstr "[TIEDOSTONIMI1] [TIEDOSTONIMI2]" #. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FWUPD-VERSION]" +msgstr "[FWUPD-VERSION]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[REASON] [TIMEOUT]" +msgstr "[REASON] [TIMEOUT]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SECTION] KEY VALUE" +msgstr "[SECTION] KEY VALUE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "[SETTING1] [SETTING2] [--no-authenticate]" msgstr "[ASETUS1] [ASETUS2] [--no-authenticate]" #. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2]..." +msgstr "[SETTING1] [SETTING2]..." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "[SMBIOS-FILE|HWIDS-FILE]" msgstr "[SMBIOS-TIEDOSTO|HWIDS-TIEDOSTO]" diff -Nru fwupd-2.0.8/po/fr.po fwupd-2.0.20/po/fr.po --- fwupd-2.0.8/po/fr.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/fr.po 2026-02-26 11:36:18.000000000 +0000 @@ -7,18 +7,23 @@ # Corentin Noël , 2020 # Franck , 2015 # Julien Humbert , 2020-2021 +# Léane GRASSER, 2025 # Paragoumba , 2024 # Yolopix ​, 2022 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: French (http://app.transifex.com/freedesktop/fwupd/language/fr/)\n" +"PO-Revision-Date: 2025-10-24 10:07+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: French \n" +"Language: fr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: fr\n" "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -107,6 +112,10 @@ msgid "Alias to %s" msgstr "Alias de %s" +#. TRANSLATORS: error message +msgid "Already exists, and no --force specified" +msgstr "Existe déjà, et aucun --force spécifié" + #. TRANSLATORS: explain why we want to reboot msgid "An update requires a reboot to complete." msgstr "Une mise à jour nécessite un redémarrage pour se terminer." @@ -144,10 +153,22 @@ msgstr "Authentification…" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to enable emulation data collection" +msgstr "Une authentification est requise pour activer la collecte des données d'émulation" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to load hardware emulation data" +msgstr "Une authentification est requise pour charger les données d'émulation de matériel" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to read BIOS settings" msgstr "Authentification requise pour lire les paramètres du BIOS" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to save hardware emulation data" +msgstr "Une authentification est requise pour enregistrer les données d'émulation de matériel" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to set the list of approved firmware" msgstr "Une authentification est nécessaire pour définir la liste des micrologiciels approuvés" @@ -156,6 +177,10 @@ msgstr "Une authentification est nécessaire pour signer les données en utilisant le certificat du client" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to stop the firmware update service" +msgstr "Une authentification est requise pour arrêter le service de mise à jour de micrologiciels" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "Authentification requise pour déverrouiller un périphérique" @@ -171,6 +196,11 @@ msgid "Automatic Reporting" msgstr "Rapports automatiques" +#. TRANSLATORS: we can auto-uninhibit after a timeout +#, c-format +msgid "Automatically uninhibiting in %ums…" +msgstr "Désinhibition automatique dans %u ms…" + #. TRANSLATORS: can we JFDI? msgid "Automatically upload every time?" msgstr "Envoyer automatiquement à chaque fois ?" @@ -209,6 +239,10 @@ msgstr "Modifié" #. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "Vérifier si un périphérique est en attente d'un redémarrage pour terminer sa mise à jour" + +#. TRANSLATORS: command description msgid "Checks cryptographic hash matches firmware" msgstr "Vérifie que l'empreinte cryptographique correspond au micrologiciel" @@ -221,6 +255,10 @@ msgid "Command not found" msgstr "Commande non trouvée" +#. TRANSLATORS: command description +msgid "Compares two versions for equality" +msgstr "Tester l'égalité de deux versions" + #. TRANSLATORS: the release urgency msgid "Critical" msgstr "Critique" @@ -309,6 +347,11 @@ msgid "Do not write to the history database" msgstr "Ne pas écrire dans la base de données de l'historique" +#. TRANSLATORS: ask the user if it's okay to convert, +#. * "it" being the data contained in the EFI boot entry +msgid "Do you want to convert it now?" +msgstr "Voulez-vous la convertir maintenant ?" + #. TRANSLATORS: success msgid "Done!" msgstr "Terminé !" @@ -332,6 +375,9 @@ msgid "Enable" msgstr "Activer" +msgid "Enable emulation data collection" +msgstr "Activer la collecte de données d'émulation" + #. TRANSLATORS: if the remote is enabled #. TRANSLATORS: Suffix: the HSI result msgid "Enabled" @@ -482,9 +528,17 @@ msgstr "Obtenir la liste des périphériques supportant les mises à jour de micrologiciel" #. TRANSLATORS: command description +msgid "Get all known version formats" +msgstr "Obtenir tous les formats de version connus" + +#. TRANSLATORS: command description msgid "Gets details about a firmware file" msgstr "Obtenir les détails d'un fichier de micrologiciel" +#. TRANSLATORS: command description +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "Obtenir la liste des mises à jour des périphériques spécifiés, ou de tous les périphériques si aucun n'est spécifié" + #. TRANSLATORS: the hardware is waiting to be replugged msgid "Hardware is waiting to be replugged" msgstr "Le matériel attend d'être rebranché" @@ -529,7 +583,7 @@ #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" -msgstr "Installation de la mise à jour du micrologiciel..." +msgstr "Installation de la mise à jour du micrologiciel…" #. TRANSLATORS: %1 is a device name #, c-format @@ -548,6 +602,10 @@ msgid "Invalid" msgstr "Invalide" +#. TRANSLATORS: error message +msgid "Invalid arguments, expected base-16 integer" +msgstr "Arguments invalides, nombre entier en base 16 attendu" + #. TRANSLATORS: Is currently in bootloader mode msgid "Is in bootloader mode" msgstr "Est en mode bootloader" @@ -652,10 +710,19 @@ 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é" +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +msgstr "Aucun redémarrage n'est nécessaire" + #. TRANSLATORS: this is an error string msgid "No updates available" msgstr "Pas de mise à jour disponible" +#. TRANSLATORS: error message +#, c-format +msgid "No volume matched %s" +msgstr "Aucun volume ne correspond à %s" + #. TRANSLATORS: Suffix: the HSI result msgid "Not found" msgstr "Non trouvé" @@ -668,11 +735,19 @@ msgid "OK" msgstr "OK" +#. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Installer uniquement sur les périphériques émulés" + #. TRANSLATORS: some devices can only be updated to a new semver and cannot #. * be downgraded or reinstalled with the existing version msgid "Only version upgrades are allowed" msgstr "Seules les mises à jour de version sont autorisées" +#. TRANSLATORS: command line option +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Sortie au format JSON (désactive toutes les invites interactives)" + #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "PATH" msgstr "CHEMIN" @@ -765,9 +840,17 @@ msgid "Restarting device…" msgstr "Redémarrage du périphérique…" +#. TRANSLATORS: Software Bill of Materials link +msgid "SBOM" +msgstr "SBOM" + +#. TRANSLATORS: Title: Whether firmware is locked down +msgid "SMM locked down" +msgstr "SMM verrouillé" + #. TRANSLATORS: scheduling an update to be done on the next boot msgid "Scheduling…" -msgstr "Planification..." +msgstr "Planification…" #. TRANSLATORS: HSI event title msgid "Secure Boot disabled" @@ -856,6 +939,9 @@ msgid "Specify the dbx database file" msgstr "Indiquer le fichier de base de données dbx" +msgid "Stop the fwupd service" +msgstr "Arrêter le service fwupd" + #. TRANSLATORS: the update state of the specific device msgid "Success" msgstr "Succès" @@ -886,6 +972,14 @@ msgid "Supported" msgstr "Pris en charge" +#. TRANSLATORS: longer description +msgid "System management mode is used by the firmware to access resident BIOS code and data." +msgstr "Le mode de gestion du système (SMM) est utilisé par le micrologiciel pour accéder au code et aux données BIOS résidentes." + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low" +msgstr "L'alimentation du système est trop faible" + #. TRANSLATORS: Must be plugged into an outlet msgid "System requires external power source" msgstr "Le système nécessite une source d'alimentation externe" @@ -910,6 +1004,18 @@ 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 est un service libre qui opère en tant qu'entité légale indépendante et n'est aucunement connecté à $OS_RELEASE:NAME$. Votre distributeur n'a pas forcément vérifié la compatibilité des mises à jour de micrologiciel avec votre système ou avec les appareils connectés. Tous les micrologiciels ne sont fournis que par le fabricant original de votre équipement." +#. TRANSLATORS: HSI event title +msgid "The UEFI certificate store is now up to date" +msgstr "Le magasin de certificats UEFI est maintenant à jour" + +#. TRANSLATORS: longer description +msgid "The UEFI db contains the list of valid certificates that can be used to authorize what EFI binaries are allowed to run." +msgstr "La db UEFI contient la liste des certificats valides pouvant être utilisés pour autoriser ou non un binaire EFI à s'exécuter." + +#. TRANSLATORS: longer description +msgid "The UEFI system can set up memory attributes at boot which prevent common exploits from running." +msgstr "Le système UEFI peut configurer des attributs mémoire au démarrage qui empêchent l'exécution d'exploits courants." + #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator msgid "There is no approved firmware." @@ -931,14 +1037,42 @@ msgid "UEFI ESP partition not detected or configured" msgstr "Partition ESP UEFI non détectée ou non configurée" +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI Memory Protection" +msgstr "Protection de la mémoire UEFI" + #. TRANSLATORS: capsule updates are an optional BIOS feature msgid "UEFI capsule updates not available or enabled in firmware setup" msgstr "Les mises à jour de capsule UEFI ne sont pas disponibles ou pas activées dans la configuration du micrologiciel" +#. TRANSLATORS: Title: is UEFI db up-to-date +msgid "UEFI db" +msgstr "db UEFI" + #. TRANSLATORS: program name msgid "UEFI dbx Utility" msgstr "Utilitaire dbx UEFI" +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI memory protection" +msgstr "Protection de la mémoire UEFI" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled and locked" +msgstr "Protection de la mémoire UEFI activée et verrouillée" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled but not locked" +msgstr "Protection de la mémoire UEFI activée mais non verrouillée" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now locked" +msgstr "La protection de la mémoire UEFI est désormais verrouillée" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now unlocked" +msgstr "La protection de la mémoire UEFI est désormais déverrouillée" + #. TRANSLATORS: Title: PK is the 'platform key' for the machine msgid "UEFI platform key" msgstr "Clé de plate-forme UEFI" diff -Nru fwupd-2.0.8/po/fur.po fwupd-2.0.20/po/fur.po --- fwupd-2.0.8/po/fur.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/fur.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,16 +4,20 @@ # # Translators: # Fabio Tomat , 2017-2018,2020,2023-2024 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Friulian (http://app.transifex.com/freedesktop/fwupd/language/fur/)\n" +"PO-Revision-Date: 2025-10-24 10:08+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Friulian \n" +"Language: fur\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: fur\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -50,7 +54,7 @@ #. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT #, c-format msgid "%s override" -msgstr "Override %s" +msgstr "Override %s" #. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number #, c-format @@ -169,7 +173,7 @@ #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on a removable device" -msgstr "La autenticazion e je necessarie par tornâ indaûr ae version precedente dal firmware suntun dispositîf estraibil " +msgstr "La autenticazion e je necessarie par tornâ indaûr ae version precedente dal firmware suntun dispositîf estraibil" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on this machine" @@ -518,7 +522,7 @@ #. TRANSLATORS: another fwupdtool instance is already running msgid "Failed to lock" -msgstr " Impussibil blocâ" +msgstr "Impussibil blocâ" #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" @@ -671,10 +675,6 @@ msgstr "Al oten la liste dai firmware blocâts" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Al oten la liste di inzornaments pal hardware tacât" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "Al oten lis publicazions par un dispositîf" @@ -908,7 +908,7 @@ #. TRANSLATORS: the update state of the specific device msgid "Needs reboot" -msgstr " Al necessite di tornâ a inviâ il sisteme" +msgstr "Al necessite di tornâ a inviâ il sisteme" #. TRANSLATORS: version number of new firmware msgid "New version" @@ -1061,7 +1061,7 @@ #. * %1 is the device name and %2 is a version string #, c-format msgid "Reinstall %s to %s?" -msgstr "Tornâ a instalâ %s %s? " +msgstr "Tornâ a instalâ %s %s?" #. TRANSLATORS: command description msgid "Reinstall current firmware on the device" @@ -1235,11 +1235,6 @@ msgid "Successfully disabled remote" msgstr "Sorzint esterne disabilitade cun sucès" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Gnûf metadât discjariât cun sucès:" - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "Sorzint esterne inzornade e abilitade cun sucès" @@ -1320,7 +1315,7 @@ #. * %1 is the old branch name, %2 is the new branch name #, c-format msgid "Switch branch from %s to %s?" -msgstr "Cambiâ ram di %s a %s? " +msgstr "Cambiâ ram di %s a %s?" #. TRANSLATORS: command description msgid "Switch the firmware branch on the device" diff -Nru fwupd-2.0.8/po/fwupd.pot fwupd-2.0.20/po/fwupd.pot --- fwupd-2.0.8/po/fwupd.pot 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/po/fwupd.pot 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1,4664 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the fwupd package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: fwupd\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2025-11-26 09:06+0000\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: data/remotes.d/lvfs.metainfo.xml:6 +msgid "Linux Vendor Firmware Service (stable firmware)" +msgstr "" + +#. TRANSLATORS: do not translate the variables marked using $ +#: data/remotes.d/lvfs.metainfo.xml:12 +#: data/remotes.d/lvfs-testing.metainfo.xml:12 +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 "" + +#: data/remotes.d/lvfs.metainfo.xml:19 +#: data/remotes.d/lvfs-testing.metainfo.xml:25 +msgid "" +"Enabling this functionality is done at your own risk, which means you have " +"to contact your original equipment manufacturer regarding any problems " +"caused by these updates. Only problems with the update process itself should " +"be filed at $OS_RELEASE:BUG_REPORT_URL$." +msgstr "" + +#: data/remotes.d/lvfs-testing.metainfo.xml:6 +msgid "Linux Vendor Firmware Service (testing firmware)" +msgstr "" + +#: data/remotes.d/lvfs-testing.metainfo.xml:19 +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: description of a BIOS setting +#: libfwupdplugin/fu-bios-settings.c:325 +msgid "Settings will apply after system reboots" +msgstr "" + +#. TRANSLATORS: description of a BIOS setting +#: libfwupdplugin/fu-bios-settings.c:329 +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "" + +#: libfwupdplugin/fu-bios-settings.c:339 +msgid "Enable" +msgstr "" + +#. TRANSLATORS: if the remote is enabled +#. TRANSLATORS: Suffix: the HSI result +#: libfwupdplugin/fu-bios-settings.c:342 src/fu-util-common.c:2047 +#: src/fu-util-common.c:2196 +msgid "Enabled" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:17 +msgid "Stop the fwupd service" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:19 +msgid "Authentication is required to stop the firmware update service" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:28 +msgid "Install signed system firmware" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:30 +#: policy/org.freedesktop.fwupd.policy.in:41 +msgid "Authentication is required to update the firmware on this machine" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:39 +msgid "Install unsigned system firmware" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:51 +msgid "Install old version of signed system firmware" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:53 +#: policy/org.freedesktop.fwupd.policy.in:65 +msgid "Authentication is required to downgrade the firmware on this machine" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:63 +msgid "Install old version of unsigned system firmware" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:75 +#: policy/org.freedesktop.fwupd.policy.in:98 +msgid "Install signed device firmware" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:77 +#: policy/org.freedesktop.fwupd.policy.in:88 +msgid "Authentication is required to update the firmware on a removable device" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:86 +#: policy/org.freedesktop.fwupd.policy.in:109 +msgid "Install unsigned device firmware" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:100 +#: policy/org.freedesktop.fwupd.policy.in:111 +msgid "" +"Authentication is required to downgrade the firmware on a removable device" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:121 +msgid "Unlock the device to allow access" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:123 +msgid "Authentication is required to unlock a device" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:132 +msgid "Modify daemon configuration" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:134 +msgid "Authentication is required to modify daemon configuration" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:144 +msgid "Reset daemon configuration" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:146 +msgid "Authentication is required to reset daemon configuration to defaults" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:156 +msgid "Activate the new firmware on the device" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:158 +msgid "Authentication is required to switch to the new firmware version" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:167 +msgid "Update the stored device verification information" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:169 +msgid "" +"Authentication is required to update the stored checksums for the device" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:178 +msgid "Modify a configured remote" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:180 +msgid "" +"Authentication is required to modify a configured remote used for firmware " +"updates" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:190 +msgid "Clean a configured remote" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:192 +msgid "Authentication is required to delete metadata from a remote" +msgstr "" + +#. TRANSLATORS: firmware approved by the admin +#: policy/org.freedesktop.fwupd.policy.in:201 src/fu-util.c:5697 +msgid "Sets the list of approved firmware" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:203 +msgid "Authentication is required to set the list of approved firmware" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:212 +msgid "Sign data using the client certificate" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:214 +msgid "Authentication is required to sign data using the client certificate" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:223 +msgid "Get BIOS settings" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:225 +msgid "Authentication is required to read BIOS settings" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:234 +msgid "Set one or more BIOS settings" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:236 +msgid "Authentication is required to modify BIOS settings" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:246 +#: policy/org.freedesktop.fwupd.policy.in:257 +msgid "Security hardening for HSI" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:248 +msgid "Authentication is required to fix a host security issue" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:259 +msgid "Authentication is required to undo the fix for a host security issue" +msgstr "" + +#. TRANSLATORS: command description +#: policy/org.freedesktop.fwupd.policy.in:268 src/fu-tool.c:5773 +#: src/fu-util.c:5833 +msgid "Load device emulation data" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:270 +msgid "Authentication is required to load hardware emulation data" +msgstr "" + +#. TRANSLATORS: command description +#: policy/org.freedesktop.fwupd.policy.in:279 src/fu-util.c:5840 +msgid "Save device emulation data" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:281 +msgid "Authentication is required to save hardware emulation data" +msgstr "" + +#: policy/org.freedesktop.fwupd.policy.in:291 +msgid "Enable emulation data collection" +msgstr "" + +#. TRANSLATORS: this is the PolicyKit modal dialog +#: policy/org.freedesktop.fwupd.policy.in:293 +msgid "Authentication is required to enable emulation data collection" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/tpm/fu-tpm-eventlog.c:130 plugins/uefi-dbx/fu-dbxtool.c:112 +#: src/fu-util.c:5282 +msgid "Show extra debugging information" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/tpm/fu-tpm-eventlog.c:138 +msgid "Dump PCR contents to a file on disk" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/tpm/fu-tpm-eventlog.c:146 +msgid "Only show single PCR value" +msgstr "" + +#. TRANSLATORS: we're poking around as a power user +#: plugins/tpm/fu-tpm-eventlog.c:160 plugins/uefi-dbx/fu-dbxtool.c:248 +#: src/fu-tool.c:6177 +msgid "This program may only work correctly as root" +msgstr "" + +#. TRANSLATORS: program name +#: plugins/tpm/fu-tpm-eventlog.c:164 +msgid "fwupd TPM event log utility" +msgstr "" + +#. TRANSLATORS: CLI description +#: plugins/tpm/fu-tpm-eventlog.c:168 +msgid "" +"This tool will read and parse the TPM event log from the system firmware." +msgstr "" + +#. TRANSLATORS: the user didn't read the man page +#: plugins/tpm/fu-tpm-eventlog.c:172 plugins/uefi-dbx/fu-dbxtool.c:184 +#: src/fu-tool.c:6035 src/fu-util.c:5928 +msgid "Failed to parse arguments" +msgstr "" + +#. TRANSLATORS: failed to read measurements file +#: plugins/tpm/fu-tpm-eventlog.c:186 +msgid "Failed to parse file" +msgstr "" + +#. TRANSLATORS: this is shown when updating the firmware after the reboot +#: plugins/uefi-capsule/fu-uefi-capsule-plugin.c:565 +msgid "Installing firmware update…" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/uefi-dbx/fu-dbxtool.c:120 +msgid "Show the calculated version of the dbx" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/uefi-dbx/fu-dbxtool.c:128 +msgid "List entries in dbx" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/uefi-dbx/fu-dbxtool.c:136 +msgid "Apply update files" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/uefi-dbx/fu-dbxtool.c:144 +msgid "Specify the dbx database file" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: plugins/uefi-dbx/fu-dbxtool.c:146 src/fu-util.c:5831 src/fu-util.c:5838 +msgid "FILENAME" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/uefi-dbx/fu-dbxtool.c:153 +msgid "Override the default ESP path" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: plugins/uefi-dbx/fu-dbxtool.c:155 +msgid "PATH" +msgstr "" + +#. TRANSLATORS: command line option +#: plugins/uefi-dbx/fu-dbxtool.c:162 +msgid "Apply update even when not advised" +msgstr "" + +#. TRANSLATORS: description of dbxtool +#: plugins/uefi-dbx/fu-dbxtool.c:177 +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "" + +#. TRANSLATORS: program name +#: plugins/uefi-dbx/fu-dbxtool.c:180 +msgid "UEFI dbx Utility" +msgstr "" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +#: plugins/uefi-dbx/fu-dbxtool.c:209 plugins/uefi-dbx/fu-dbxtool.c:279 +msgid "Failed to load local dbx" +msgstr "" + +#. TRANSLATORS: could not read existing system data +#: plugins/uefi-dbx/fu-dbxtool.c:218 plugins/uefi-dbx/fu-dbxtool.c:270 +msgid "Failed to load system dbx" +msgstr "" + +#. TRANSLATORS: the detected version number of the dbx +#: plugins/uefi-dbx/fu-dbxtool.c:225 +msgid "Version" +msgstr "" + +#. TRANSLATORS: user did not include a filename parameter +#: plugins/uefi-dbx/fu-dbxtool.c:261 +msgid "Filename required" +msgstr "" + +#. TRANSLATORS: reading existing dbx from the system +#: plugins/uefi-dbx/fu-dbxtool.c:266 +msgid "Parsing system dbx…" +msgstr "" + +#. TRANSLATORS: reading new dbx from the update +#: plugins/uefi-dbx/fu-dbxtool.c:275 +msgid "Parsing dbx update…" +msgstr "" + +#. TRANSLATORS: could not parse file +#: plugins/uefi-dbx/fu-dbxtool.c:288 +msgid "Failed to parse local dbx" +msgstr "" + +#. TRANSLATORS: same or newer update already applied +#: plugins/uefi-dbx/fu-dbxtool.c:296 +msgid "Cannot apply as dbx update has already been applied." +msgstr "" + +#. TRANSLATORS: ESP refers to the EFI System Partition +#: plugins/uefi-dbx/fu-dbxtool.c:303 +msgid "Validating ESP contents…" +msgstr "" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +#: plugins/uefi-dbx/fu-dbxtool.c:311 +msgid "Failed to validate ESP contents" +msgstr "" + +#. TRANSLATORS: actually sending the update to the hardware +#: plugins/uefi-dbx/fu-dbxtool.c:318 +msgid "Applying update…" +msgstr "" + +#. TRANSLATORS: dbx file failed to be applied as an update +#: plugins/uefi-dbx/fu-dbxtool.c:331 +msgid "Failed to apply update" +msgstr "" + +#. TRANSLATORS: success +#: plugins/uefi-dbx/fu-dbxtool.c:336 +msgid "Done!" +msgstr "" + +#. TRANSLATORS: user did not tell the tool what to do +#: plugins/uefi-dbx/fu-dbxtool.c:343 +msgid "No action specified!" +msgstr "" + +#. TRANSLATORS: the user isn't reading the question +#: src/fu-console.c:197 +#, c-format +msgid "Please enter a number from 0 to %u:" +msgstr "" + +#. TRANSLATORS: the user isn't reading the question +#. -- %1 is 'Y' and %2 is 'N' +#: src/fu-console.c:239 +#, c-format +msgid "Please enter either %s or %s:" +msgstr "" + +#. TRANSLATORS: daemon is inactive +#: src/fu-console.c:375 +msgid "Idle…" +msgstr "" + +#. TRANSLATORS: decompressing the firmware file +#: src/fu-console.c:379 +msgid "Decompressing…" +msgstr "" + +#. TRANSLATORS: parsing the firmware information +#: src/fu-console.c:383 +msgid "Loading…" +msgstr "" + +#. TRANSLATORS: restarting the device to pick up new F/W +#: src/fu-console.c:387 +msgid "Restarting device…" +msgstr "" + +#. TRANSLATORS: reading from the flash chips +#: src/fu-console.c:391 +msgid "Reading…" +msgstr "" + +#. TRANSLATORS: writing to the flash chips +#: src/fu-console.c:395 +msgid "Writing…" +msgstr "" + +#. TRANSLATORS: erasing contents of the flash chips +#: src/fu-console.c:399 +msgid "Erasing…" +msgstr "" + +#. TRANSLATORS: verifying we wrote the firmware correctly +#: src/fu-console.c:403 +msgid "Verifying…" +msgstr "" + +#. TRANSLATORS: scheduling an update to be done on the next boot +#: src/fu-console.c:407 +msgid "Scheduling…" +msgstr "" + +#. TRANSLATORS: downloading from a remote server +#: src/fu-console.c:411 +msgid "Downloading…" +msgstr "" + +#. TRANSLATORS: waiting for user to authenticate +#: src/fu-console.c:415 +msgid "Authenticating…" +msgstr "" + +#. TRANSLATORS: waiting for device to do something +#: src/fu-console.c:420 +msgid "Waiting…" +msgstr "" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +#. TRANSLATORS: Suffix: the fallback HSI result +#: src/fu-console.c:427 src/fu-util-common.c:1735 src/fu-util-common.c:1775 +#: src/fu-util-common.c:2262 +msgid "Unknown" +msgstr "" + +#. TRANSLATORS: time remaining for completing firmware flash +#: src/fu-console.c:481 +msgid "Less than one minute remaining" +msgstr "" + +#. TRANSLATORS: more than a minute +#: src/fu-console.c:486 +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: this is a prefix on the console +#: src/fu-console.c:567 +msgid "WARNING" +msgstr "" + +#. TRANSLATORS: turn on all debugging +#: src/fu-debug.c:241 +msgid "Show debugging information for all domains" +msgstr "" + +#. TRANSLATORS: turn on all debugging +#: src/fu-debug.c:249 +msgid "Do not include timestamp prefix" +msgstr "" + +#. TRANSLATORS: turn on all debugging +#: src/fu-debug.c:257 +msgid "Do not include log domain prefix" +msgstr "" + +#. TRANSLATORS: this is for daemon development +#: src/fu-debug.c:265 +msgid "Show daemon verbose information for a particular domain" +msgstr "" + +#. TRANSLATORS: for the --verbose arg +#: src/fu-debug.c:345 +msgid "Debugging Options" +msgstr "" + +#. TRANSLATORS: for the --verbose arg +#: src/fu-debug.c:347 +msgid "Show debugging options" +msgstr "" + +#. TRANSLATORS: this is shown in the MOTD +#: src/fu-engine-helper.c:202 +#, c-format +msgid "%u device has been updated and needs a reboot." +msgid_plural "%u devices have been updated and need a reboot." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: this is shown in the MOTD +#: src/fu-engine-helper.c:209 +#, c-format +msgid "Reboot to complete the update." +msgstr "" + +#. TRANSLATORS: this is shown in the MOTD +#: src/fu-engine-helper.c:215 +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr sync` +#: src/fu-engine-helper.c:223 +#, c-format +msgid "Run `%s` to complete this action." +msgstr "" + +#. TRANSLATORS: this is shown in the MOTD +#: src/fu-engine-helper.c:230 +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr get-upgrades` +#: src/fu-engine-helper.c:238 +#, c-format +msgid "Run `%s` for more information." +msgstr "" + +#. TRANSLATORS: exit after we've started up, used for user profiling +#: src/fu-main.c:88 +msgid "Exit after a small delay" +msgstr "" + +#. TRANSLATORS: exit straight away, used for automatic profiling +#: src/fu-main.c:96 +msgid "Exit after the engine has loaded" +msgstr "" + +#. TRANSLATORS: program name +#: src/fu-main.c:114 +msgid "Firmware Update Daemon" +msgstr "" + +#. TRANSLATORS: program summary +#: src/fu-main.c:119 +msgid "Firmware Update D-Bus Service" +msgstr "" + +#. TRANSLATORS: show the user a warning +#: src/fu-remote-list.c:149 +msgid "" +"Your distributor may not have verified any of the firmware updates for " +"compatibility with your system or connected devices." +msgstr "" + +#. TRANSLATORS: show the user a warning +#: src/fu-remote-list.c:155 +msgid "Enabling this remote is done at your own risk." +msgstr "" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +#: src/fu-security-attr-common.c:22 +msgid "SPI write" +msgstr "" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +#: src/fu-security-attr-common.c:26 +msgid "SPI lock" +msgstr "" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +#: src/fu-security-attr-common.c:30 +msgid "SPI BIOS region" +msgstr "" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +#: src/fu-security-attr-common.c:34 +msgid "SPI BIOS Descriptor" +msgstr "" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +#: src/fu-security-attr-common.c:38 +msgid "Pre-boot DMA protection" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +#: src/fu-security-attr-common.c:42 src/fu-security-attr-common.c:267 +msgid "Intel BootGuard" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +#: src/fu-security-attr-common.c:47 +msgid "Intel BootGuard verified boot" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +#: src/fu-security-attr-common.c:52 +msgid "Intel BootGuard ACM protected" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +#: src/fu-security-attr-common.c:57 +msgid "Intel BootGuard error policy" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +#: src/fu-security-attr-common.c:62 +msgid "Intel BootGuard OTP fuse" +msgstr "" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +#: src/fu-security-attr-common.c:67 +msgid "CET Platform" +msgstr "" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * Utilized by OS means the distribution enabled it +#: src/fu-security-attr-common.c:72 +msgid "CET OS Support" +msgstr "" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +#: src/fu-security-attr-common.c:76 +msgid "SMAP" +msgstr "" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +#: src/fu-security-attr-common.c:80 src/fu-security-attr-common.c:299 +msgid "Encrypted RAM" +msgstr "" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +#: src/fu-security-attr-common.c:85 +msgid "IOMMU" +msgstr "" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +#: src/fu-security-attr-common.c:89 +msgid "Linux kernel lockdown" +msgstr "" + +#. TRANSLATORS: Title: if it's tainted or not +#: src/fu-security-attr-common.c:93 +msgid "Linux kernel" +msgstr "" + +#. TRANSLATORS: Title: swap space or swap partition +#: src/fu-security-attr-common.c:97 +msgid "Linux swap" +msgstr "" + +#. TRANSLATORS: Title: sleep state +#: src/fu-security-attr-common.c:101 +msgid "Suspend-to-ram" +msgstr "" + +#. TRANSLATORS: Title: a better sleep state +#: src/fu-security-attr-common.c:105 +msgid "Suspend-to-idle" +msgstr "" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +#: src/fu-security-attr-common.c:109 +msgid "UEFI platform key" +msgstr "" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +#: src/fu-security-attr-common.c:113 +msgid "UEFI secure boot" +msgstr "" + +#. TRANSLATORS: Title: Bootservice is when only readable from early-boot +#: src/fu-security-attr-common.c:117 +msgid "UEFI bootservice variables" +msgstr "" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be empty +#: src/fu-security-attr-common.c:121 +msgid "TPM empty PCRs" +msgstr "" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +#: src/fu-security-attr-common.c:125 +msgid "TPM PCR0 reconstruction" +msgstr "" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +#: src/fu-security-attr-common.c:129 src/fu-security-attr-common.c:348 +msgid "TPM v2.0" +msgstr "" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#: src/fu-security-attr-common.c:135 +#, c-format +msgid "%s manufacturing mode" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +#: src/fu-security-attr-common.c:138 +msgid "MEI manufacturing mode" +msgstr "" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#: src/fu-security-attr-common.c:144 +#, c-format +msgid "%s override" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +#: src/fu-security-attr-common.c:150 +msgid "MEI override" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and key refer +#. * to the private/public key used to secure loading of firmware +#: src/fu-security-attr-common.c:155 +msgid "MEI key manifest" +msgstr "" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#. +#: src/fu-security-attr-common.c:164 +#, c-format +msgid "%s v%s" +msgstr "" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#: src/fu-security-attr-common.c:168 +#, c-format +msgid "%s version" +msgstr "" + +#: src/fu-security-attr-common.c:170 +msgid "MEI version" +msgstr "" + +#. TRANSLATORS: Title: if firmware updates are available +#: src/fu-security-attr-common.c:174 +msgid "Firmware updates" +msgstr "" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +#: src/fu-security-attr-common.c:178 +msgid "Firmware attestation" +msgstr "" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +#: src/fu-security-attr-common.c:182 +msgid "fwupd plugins" +msgstr "" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +#: src/fu-security-attr-common.c:187 +msgid "Platform debugging" +msgstr "" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +#: src/fu-security-attr-common.c:191 +msgid "Supported CPU" +msgstr "" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +#: src/fu-security-attr-common.c:195 +msgid "Processor rollback protection" +msgstr "" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +#: src/fu-security-attr-common.c:199 +msgid "SPI replay protection" +msgstr "" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +#: src/fu-security-attr-common.c:203 +msgid "SPI write protection" +msgstr "" + +#. TRANSLATORS: Title: if the part has been fused +#: src/fu-security-attr-common.c:207 +msgid "Fused platform" +msgstr "" + +#. TRANSLATORS: Title: if we are emulating a different host +#: src/fu-security-attr-common.c:211 +msgid "Emulated host" +msgstr "" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +#: src/fu-security-attr-common.c:215 +msgid "BIOS rollback protection" +msgstr "" + +#. TRANSLATORS: Title: GDS is where the CPU leaks information +#: src/fu-security-attr-common.c:219 +msgid "Intel GDS mitigation" +msgstr "" + +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +#: src/fu-security-attr-common.c:223 +msgid "BIOS firmware updates" +msgstr "" + +#. TRANSLATORS: Title: Whether firmware is locked down +#: src/fu-security-attr-common.c:227 +msgid "SMM locked down" +msgstr "" + +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +#: src/fu-security-attr-common.c:231 +msgid "UEFI memory protection" +msgstr "" + +#. TRANSLATORS: Title: is UEFI db up-to-date +#: src/fu-security-attr-common.c:235 src/fu-security-attr-common.c:428 +msgid "UEFI db" +msgstr "" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +#: src/fu-security-attr-common.c:247 +msgid "Firmware Write Protection" +msgstr "" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +#: src/fu-security-attr-common.c:251 +msgid "Firmware Write Protection Lock" +msgstr "" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +#: src/fu-security-attr-common.c:255 +msgid "Firmware BIOS Region" +msgstr "" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +#: src/fu-security-attr-common.c:259 +msgid "Firmware BIOS Descriptor" +msgstr "" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +#: src/fu-security-attr-common.c:263 +msgid "Pre-boot DMA Protection" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +#: src/fu-security-attr-common.c:272 +msgid "Intel BootGuard Verified Boot" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +#: src/fu-security-attr-common.c:277 +msgid "Intel BootGuard ACM Protected" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +#: src/fu-security-attr-common.c:282 +msgid "Intel BootGuard Error Policy" +msgstr "" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +#: src/fu-security-attr-common.c:286 +msgid "Intel BootGuard Fuse" +msgstr "" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +#: src/fu-security-attr-common.c:291 +msgid "Control-flow Enforcement Technology" +msgstr "" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +#: src/fu-security-attr-common.c:295 +msgid "Supervisor Mode Access Prevention" +msgstr "" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +#: src/fu-security-attr-common.c:304 +msgid "IOMMU Protection" +msgstr "" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +#: src/fu-security-attr-common.c:308 +msgid "Linux Kernel Lockdown" +msgstr "" + +#. TRANSLATORS: Title: if it's tainted or not +#: src/fu-security-attr-common.c:312 +msgid "Linux Kernel Verification" +msgstr "" + +#. TRANSLATORS: Title: swap space or swap partition +#: src/fu-security-attr-common.c:316 +msgid "Linux Swap" +msgstr "" + +#. TRANSLATORS: Title: sleep state +#: src/fu-security-attr-common.c:320 +msgid "Suspend To RAM" +msgstr "" + +#. TRANSLATORS: Title: a better sleep state +#: src/fu-security-attr-common.c:324 +msgid "Suspend To Idle" +msgstr "" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +#: src/fu-security-attr-common.c:328 +msgid "UEFI Platform Key" +msgstr "" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +#: src/fu-security-attr-common.c:332 +msgid "UEFI Secure Boot" +msgstr "" + +#. TRANSLATORS: Title: Bootservice is when only readable from early-boot +#: src/fu-security-attr-common.c:336 +msgid "UEFI Bootservice Variables" +msgstr "" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be empty +#: src/fu-security-attr-common.c:340 +msgid "TPM Platform Configuration" +msgstr "" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +#: src/fu-security-attr-common.c:344 +msgid "TPM Reconstruction" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +#: src/fu-security-attr-common.c:352 +msgid "Intel Management Engine Manufacturing Mode" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is enabled +#. * with a jumper -- luckily it is probably not accessible to end users on consumer +#. * boards +#: src/fu-security-attr-common.c:358 +msgid "Intel Management Engine Override" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and key refers +#. * to the private/public key used to secure loading of firmware +#: src/fu-security-attr-common.c:363 +msgid "MEI Key Manifest" +msgstr "" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +#: src/fu-security-attr-common.c:367 +msgid "Intel Management Engine Version" +msgstr "" + +#. TRANSLATORS: Title: if firmware updates are available +#: src/fu-security-attr-common.c:371 +msgid "Firmware Updates" +msgstr "" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +#: src/fu-security-attr-common.c:375 +msgid "Firmware Attestation" +msgstr "" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +#: src/fu-security-attr-common.c:379 +msgid "Firmware Updater Verification" +msgstr "" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +#: src/fu-security-attr-common.c:384 +msgid "Platform Debugging" +msgstr "" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +#: src/fu-security-attr-common.c:388 +msgid "Processor Security Checks" +msgstr "" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +#: src/fu-security-attr-common.c:392 +msgid "AMD Secure Processor Rollback Protection" +msgstr "" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +#: src/fu-security-attr-common.c:396 +msgid "AMD Firmware Replay Protection" +msgstr "" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +#: src/fu-security-attr-common.c:400 +msgid "AMD Firmware Write Protection" +msgstr "" + +#. TRANSLATORS: Title: if the part has been fused +#: src/fu-security-attr-common.c:404 +msgid "Fused Platform" +msgstr "" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +#: src/fu-security-attr-common.c:408 +msgid "BIOS Rollback Protection" +msgstr "" + +#. TRANSLATORS: Title: GDS is where the CPU leaks information +#: src/fu-security-attr-common.c:412 +msgid "Intel GDS Mitigation" +msgstr "" + +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +#: src/fu-security-attr-common.c:416 +msgid "BIOS Firmware Updates" +msgstr "" + +#. TRANSLATORS: Title: Whether firmware is locked down +#: src/fu-security-attr-common.c:420 +msgid "System Management Mode" +msgstr "" + +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +#: src/fu-security-attr-common.c:424 +msgid "UEFI Memory Protection" +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:442 +msgid "" +"Firmware Write Protection protects device firmware memory from being " +"tampered with." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:447 +msgid "" +"Firmware BIOS Region protects device firmware memory from being tampered " +"with." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:452 +msgid "" +"Firmware BIOS Descriptor protects device firmware memory from being tampered " +"with." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:457 +msgid "" +"Pre-boot DMA protection prevents devices from accessing system memory after " +"being connected to the computer." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:465 +msgid "" +"Intel BootGuard prevents unauthorized device software from operating when " +"the device is started." +msgstr "" + +#: src/fu-security-attr-common.c:471 +msgid "" +"Intel BootGuard Error Policy ensures the device does not continue to start " +"if its device software has been tampered with." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:477 +msgid "" +"Control-Flow Enforcement Technology detects and prevents certain methods for " +"running malicious software on the device." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:482 +msgid "" +"Supervisor Mode Access Prevention ensures critical parts of device memory " +"are not accessed by less secure programs." +msgstr "" + +#: src/fu-security-attr-common.c:488 +msgid "" +"Encrypted RAM makes it impossible for information that is stored in device " +"memory to be read if the memory chip is removed and accessed." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:493 +msgid "" +"IOMMU Protection prevents connected devices from accessing unauthorized " +"parts of system memory." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:498 +msgid "" +"Linux Kernel Lockdown mode prevents administrator (root) accounts from " +"accessing and changing critical parts of system software." +msgstr "" + +#: src/fu-security-attr-common.c:504 +msgid "" +"Linux Kernel Verification makes sure that critical system software has not " +"been tampered with. Using device drivers which are not provided with the " +"system can prevent this security feature from working correctly." +msgstr "" + +#: src/fu-security-attr-common.c:511 +msgid "" +"Linux Kernel Swap temporarily saves information to disk as you work. If the " +"information is not protected, it could be accessed by someone if they " +"obtained the disk." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:517 +msgid "" +"Suspend to RAM allows the device to quickly go to sleep in order to save " +"power. While the device has been suspended, its memory could be physically " +"removed and its information accessed." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:523 +msgid "" +"Suspend to Idle allows the device to quickly go to sleep in order to save " +"power. While the device has been suspended, its memory could be physically " +"removed and its information accessed." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:529 +msgid "" +"The UEFI Platform Key is used to determine if device software comes from a " +"trusted source." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:534 +msgid "" +"UEFI Secure Boot prevents malicious software from being loaded when the " +"device starts." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:539 +msgid "UEFI boot service variables should not be readable from runtime mode." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:543 +msgid "" +"The TPM (Trusted Platform Module) Platform Configuration is used to check " +"whether the device start process has been modified." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:548 +msgid "" +"The TPM (Trusted Platform Module) Reconstruction is used to check whether " +"the device start process has been modified." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:553 +msgid "" +"TPM (Trusted Platform Module) is a computer chip that detects when hardware " +"components have been tampered with." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:559 +msgid "" +"Manufacturing Mode is used when the device is manufactured and security " +"features are not yet enabled." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:564 +msgid "" +"Intel Management Engine Override disables checks for device software " +"tampering." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:569 +msgid "" +"The Intel Management Engine Key Manifest must be valid so that the device " +"firmware can be trusted by the CPU." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:574 +msgid "" +"The Intel Management Engine controls device components and needs to have a " +"recent version to avoid security issues." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:579 +msgid "Device software updates are provided for this device." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:583 +msgid "" +"Firmware Attestation checks device software using a reference copy, to make " +"sure that it has not been changed." +msgstr "" + +#: src/fu-security-attr-common.c:589 +msgid "" +"Firmware Updater Verification checks that software used for updating has not " +"been tampered with." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:595 +msgid "" +"Platform Debugging allows device security features to be disabled. This " +"should only be used by hardware manufacturers." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:600 +msgid "Each system should have tests to ensure firmware security." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:606 +msgid "" +"Rollback Protection prevents device software from being downgraded to an " +"older version that has security problems." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:611 +msgid "" +"CPU Microcode must be updated to mitigate against various information-" +"disclosure security issues." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:616 +msgid "Enabling firmware updates for the BIOS allows fixing security issues." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:620 +msgid "" +"System management mode is used by the firmware to access resident BIOS code " +"and data." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:625 +msgid "" +"The UEFI system can set up memory attributes at boot which prevent common " +"exploits from running." +msgstr "" + +#. TRANSLATORS: longer description +#: src/fu-security-attr-common.c:630 +msgid "" +"The UEFI db contains the list of valid certificates that can be used to " +"authorize what EFI binaries are allowed to run." +msgstr "" + +#. TRANSLATORS: %s is a link to a website +#: src/fu-tool.c:145 src/fu-util.c:4848 +#, c-format +msgid "See %s for more information." +msgstr "" + +#. TRANSLATORS: another fwupdtool instance is already running +#: src/fu-tool.c:229 +msgid "Failed to lock" +msgstr "" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +#. TRANSLATORS: this is from ctrl+c +#: src/fu-tool.c:288 src/fu-util.c:5234 +msgid "Cancelled" +msgstr "" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +#: src/fu-tool.c:409 src/fu-util.c:102 +msgid "Action Required:" +msgstr "" + +#. TRANSLATORS: device has been chosen by the daemon for the user +#: src/fu-tool.c:585 src/fu-util.c:187 +msgid "Selected device" +msgstr "" + +#. TRANSLATORS: this is to abort the interactive prompt +#: src/fu-tool.c:601 src/fu-tool.c:3035 src/fu-tool.c:4139 src/fu-tool.c:4397 +#: src/fu-util.c:203 src/fu-util.c:2556 src/fu-util.c:3751 +msgid "Cancel" +msgstr "" + +#. TRANSLATORS: get interactive prompt +#: src/fu-tool.c:609 src/fu-util.c:213 +msgid "Choose device" +msgstr "" + +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no +#. * device upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no +#. * device upgrade available due to missing on LVFS +#: src/fu-tool.c:778 src/fu-tool.c:1788 src/fu-util.c:2799 src/fu-util.c:3286 +msgid "Devices with no available firmware updates:" +msgstr "" + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device +#. * upgrade available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device +#. * upgrade available +#: src/fu-tool.c:788 src/fu-tool.c:1778 src/fu-util.c:2809 src/fu-util.c:3276 +msgid "Devices with the latest available firmware version:" +msgstr "" + +#: src/fu-tool.c:800 +msgid "No updates available for remaining devices" +msgstr "" + +#. TRANSLATORS: nothing attached that can be upgraded +#: src/fu-tool.c:987 src/fu-util.c:605 +msgid "No hardware detected with firmware update capability" +msgstr "" + +#. TRANSLATORS: %1 is a device name +#: src/fu-tool.c:1026 src/fu-util.c:148 +#, c-format +msgid "Updating %s…" +msgstr "" + +#. TRANSLATORS: %1 is a device name +#: src/fu-tool.c:1030 src/fu-util.c:156 +#, c-format +msgid "Installing on %s…" +msgstr "" + +#. TRANSLATORS: %1 is a device name +#: src/fu-tool.c:1034 +#, c-format +msgid "Reading from %s…" +msgstr "" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid closed +#: src/fu-tool.c:1594 src/fu-util.c:3082 +#, c-format +msgid "%s is not currently updatable" +msgstr "" + +#. TRANSLATORS: message letting the user there is an update +#. * waiting, but there is a reason it cannot be deployed +#: src/fu-tool.c:1798 src/fu-util.c:3296 +msgid "Devices with firmware updates that need user action:" +msgstr "" + +#. TRANSLATORS: success message -- a per-system setting value +#: src/fu-tool.c:2257 src/fu-util.c:4039 +msgid "Successfully modified configuration value" +msgstr "" + +#. TRANSLATORS: success message -- a per-system setting value +#: src/fu-tool.c:2284 +msgid "Successfully reset configuration section" +msgstr "" + +#. TRANSLATORS: success message for a per-remote setting change +#: src/fu-tool.c:2321 src/fu-util.c:3359 +msgid "Successfully modified remote" +msgstr "" + +#. TRANSLATORS: success message +#: src/fu-tool.c:2354 src/fu-tool.c:2405 src/fu-util.c:3445 src/fu-util.c:3497 +msgid "Successfully cleaned remote" +msgstr "" + +#. TRANSLATORS: success message +#: src/fu-tool.c:2389 src/fu-util.c:3478 +msgid "Successfully disabled remote" +msgstr "" + +#. TRANSLATORS: this is now useless +#: src/fu-tool.c:2399 src/fu-util.c:3488 +msgid "Delete the now-unused remote cache files?" +msgstr "" + +#. TRANSLATORS: success message +#: src/fu-tool.c:2532 src/fu-util.c:3393 src/fu-util.c:3405 +msgid "Successfully enabled remote" +msgstr "" + +#. TRANSLATORS: comment explaining result of command +#: src/fu-tool.c:2559 +msgid "Successfully disabled test devices" +msgstr "" + +#. TRANSLATORS: comment explaining result of command +#: src/fu-tool.c:2606 +msgid "Successfully enabled test devices" +msgstr "" + +#. TRANSLATORS: shown when shutting down to switch to the new version +#: src/fu-tool.c:2702 +msgid "Activating firmware update" +msgstr "" + +#. TRANSLATORS: this is when a device is hotplugged +#: src/fu-tool.c:2877 +msgid "Device added:" +msgstr "" + +#. TRANSLATORS: this is when a device is hotplugged +#: src/fu-tool.c:2892 +msgid "Device removed:" +msgstr "" + +#. TRANSLATORS: this is when a device has been updated +#: src/fu-tool.c:2907 +msgid "Device changed:" +msgstr "" + +#. TRANSLATORS: this is when the daemon state changes +#: src/fu-tool.c:2919 +msgid "Changed" +msgstr "" + +#. TRANSLATORS: nothing found +#: src/fu-tool.c:2977 +msgid "No firmware IDs found" +msgstr "" + +#. TRANSLATORS: nothing found +#: src/fu-tool.c:3007 +msgid "No firmware found" +msgstr "" + +#. TRANSLATORS: get interactive prompt +#: src/fu-tool.c:3041 +msgid "Choose firmware" +msgstr "" + +#. TRANSLATORS: decompressing images from a container firmware +#: src/fu-tool.c:3268 +msgid "Writing file:" +msgstr "" + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#: src/fu-tool.c:3933 +msgid "Metadata is already up to date" +msgstr "" + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. * refresh recently -- %1 is '--force' +#: src/fu-tool.c:3941 src/fu-util.c:2293 +#, c-format +msgid "Metadata is up to date; use %s to refresh again." +msgstr "" + +#. TRANSLATORS: error message for unsupported feature +#: src/fu-tool.c:4046 src/fu-tool.c:4561 src/fu-tool.c:4598 src/fu-util.c:4433 +#: src/fu-util.c:4940 src/fu-util.c:5105 +msgid "Host Security ID (HSI) is not supported" +msgstr "" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +#: src/fu-tool.c:4083 src/fu-util.c:4472 +msgid "Host Security ID:" +msgstr "" + +#. TRANSLATORS: Volume has been chosen by the user +#: src/fu-tool.c:4132 +msgid "Selected volume" +msgstr "" + +#. TRANSLATORS: get interactive prompt +#: src/fu-tool.c:4145 +msgid "Choose volume" +msgstr "" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +#: src/fu-tool.c:4407 src/fu-util.c:3761 +msgid "Choose branch" +msgstr "" + +#. TRANSLATORS: Configured a BIOS setting to a value +#: src/fu-tool.c:4537 src/fu-util.c:4877 +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "" + +#. TRANSLATOR: This is the error message for +#. * incorrect parameter +#: src/fu-tool.c:4571 src/fu-tool.c:4608 src/fu-util.c:4950 src/fu-util.c:5115 +msgid "Invalid arguments, expected an AppStream ID" +msgstr "" + +#. TRANSLATORS: we've fixed a security problem on the machine +#: src/fu-tool.c:4586 src/fu-util.c:4960 +msgid "Fixed successfully" +msgstr "" + +#. TRANSLATORS: we've fixed a security problem on the machine +#: src/fu-tool.c:4623 src/fu-util.c:5128 +msgid "Fix reverted successfully" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4660 src/fu-util.c:4918 +msgid "This system doesn't support firmware settings" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4669 src/fu-util.c:4926 +msgid "Unable to find attribute" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4806 +msgid "Invalid arguments, expected INDEX NAME TARGET [MOUNTPOINT]" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4820 +msgid "Already exists, and no --force specified" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4848 +#, c-format +msgid "No volume matched %s" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4872 +msgid "Invalid arguments, expected base-16 integer" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:4933 +msgid "Invalid arguments, expected INDEX KEY [VALUE]" +msgstr "" + +#. TRANSLATORS: try to treat the legacy format as a hive +#: src/fu-tool.c:4951 +msgid "The EFI boot entry was not in hive format, falling back" +msgstr "" + +#. TRANSLATORS: the boot entry was in a legacy format +#: src/fu-tool.c:4969 +msgid "" +"The EFI boot entry is not in hive format, and shim may not be new enough to " +"read it." +msgstr "" + +#. TRANSLATORS: ask the user if it's okay to convert, +#. * "it" being the data contained in the EFI boot entry +#: src/fu-tool.c:4977 +msgid "Do you want to convert it now?" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:5112 +msgid "Invalid arguments, expected GUID" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-tool.c:5140 +msgid "Invalid arguments, expected at least ARCHIVE FIRMWARE METAINFO" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5272 src/fu-util.c:5290 +msgid "Show client and daemon versions" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5280 src/fu-util.c:5306 +msgid "Allow reinstalling existing firmware versions" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5288 src/fu-util.c:5314 +msgid "Allow downgrading firmware versions" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5296 src/fu-util.c:5322 +msgid "Allow switching firmware branch" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5304 src/fu-util.c:5338 +msgid "Force the action by relaxing some runtime checks" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5312 +msgid "Ignore firmware checksum failures" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5320 +msgid "Ignore firmware hardware mismatch failures" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5328 +msgid "Ignore non-critical firmware requirements" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5336 src/fu-util.c:5386 +msgid "Do not check or prompt for reboot after update" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5344 +msgid "Do not search the firmware when parsing" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5352 src/fu-util.c:5394 +msgid "Do not perform device safety checks" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5360 src/fu-util.c:5402 +msgid "Do not prompt for devices" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5368 src/fu-util.c:5418 +msgid "Show all results" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5376 src/fu-util.c:5426 +msgid "Show devices that are not updatable" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5384 src/fu-tool.c:5392 +msgid "Manually enable specific plugins" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5400 +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5408 +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5416 src/fu-util.c:5434 +msgid "Ignore SSL strict checks when downloading files" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5424 src/fu-util.c:5450 +msgid "" +"Filter with a set of device flags using a ~ prefix to exclude, e.g. " +"'internal,~needs-reboot'" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5433 src/fu-util.c:5459 +msgid "" +"Filter with a set of release flags using a ~ prefix to exclude, e.g. " +"'trusted-release,~trusted-metadata'" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5442 src/fu-util.c:5346 +msgid "Answer yes to all questions" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-tool.c:5450 src/fu-util.c:5468 +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5504 src/fu-tool.c:5517 src/fu-util.c:5562 +msgid "FILE" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5506 +msgid "Dump SMBIOS data from a file" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5512 src/fu-util.c:5763 +msgid "Get all enabled plugins registered with the system" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5519 src/fu-util.c:5564 +msgid "Gets details about a firmware file" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5525 src/fu-util.c:5531 +msgid "Show history of firmware updates" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5530 src/fu-tool.c:5598 src/fu-tool.c:5612 src/fu-tool.c:5639 +#: src/fu-tool.c:5654 src/fu-tool.c:5757 src/fu-tool.c:5764 src/fu-util.c:5517 +#: src/fu-util.c:5569 src/fu-util.c:5577 src/fu-util.c:5585 src/fu-util.c:5613 +#: src/fu-util.c:5633 src/fu-util.c:5647 src/fu-util.c:5682 src/fu-util.c:5716 +#: src/fu-util.c:5845 src/fu-util.c:5852 +msgid "[DEVICE-ID|GUID]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5532 src/fu-util.c:5571 +msgid "" +"Gets the list of updates for all specified devices, or all devices if " +"unspecified" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5539 src/fu-util.c:5525 +msgid "Get all devices that support firmware updates" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5545 +msgid "Get all device flags supported by fwupd" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5551 +msgid "Watch for hardware changes" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5556 +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5558 +msgid "Install a raw firmware blob on a device" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5563 src/fu-util.c:5555 +msgid "FILE [DEVICE-ID|GUID]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5565 +msgid "" +"Install a specific firmware on a device, all possible devices will also be " +"installed once the CAB matches" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5571 src/fu-tool.c:5578 src/fu-tool.c:5591 src/fu-util.c:5592 +#: src/fu-util.c:5599 src/fu-util.c:5606 +msgid "DEVICE-ID|GUID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5573 +msgid "Reinstall firmware on a device" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5580 +msgid "Attach to firmware mode" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5586 +msgid "Get device report metadata" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5593 +msgid "Detach to bootloader mode" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5600 +msgid "Unbind current driver" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5605 +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5607 +msgid "Bind new kernel driver" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5614 +msgid "Activate pending devices" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5619 +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5621 src/fu-util.c:5880 +msgid "Return all the hardware IDs for the machine" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5626 +msgid "HWIDS-FILE" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5628 +msgid "Save a file that allows generation of hardware IDs" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5634 +msgid "Monitor the daemon for events" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5641 src/fu-util.c:5579 +msgid "" +"Updates all specified devices to latest firmware version, or all devices if " +"unspecified" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5647 +msgid "TEXT" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5649 +msgctxt "command-description" +msgid "Sign data using the client certificate" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5656 +msgid "Update the stored metadata with current contents" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5661 +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5663 +msgid "Sign a firmware with a new key" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5668 src/fu-tool.c:5675 +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5670 +msgid "Read a firmware blob from a device" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5677 +msgid "Read a firmware from a device" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5682 +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5684 +msgid "Patch a firmware blob at a known offset" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5690 +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5692 +msgid "Convert a firmware file" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5697 +msgid "BUILDER-XML FILENAME-DST" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5699 +msgid "Build a firmware file" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5704 src/fu-tool.c:5711 src/fu-tool.c:5718 +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5706 +msgid "Parse and show details about a firmware file" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5713 +msgid "Export a firmware file structure to XML" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5720 +msgid "Extract a firmware blob to images" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5726 +msgid "List the available firmware types" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5732 +msgid "List the available firmware GTypes" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5738 src/fu-util.c:5628 +msgid "Gets the configured remotes" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5743 src/fu-util.c:5640 +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5745 src/fu-util.c:5642 +msgid "Refresh metadata from remote server" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5750 +msgid "[FWUPD-VERSION]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5752 src/fu-util.c:5731 +msgid "Gets the host security attributes" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5759 src/fu-util.c:5847 +msgid "Adds devices to watch for future emulation" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5766 src/fu-util.c:5854 +msgid "Removes devices to watch for future emulation" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5771 +msgid "EMULATION-FILE [ARCHIVE-FILE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5779 +msgid "Mounts the ESP" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5785 +msgid "Unmounts the ESP" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5791 +msgid "Lists files on the ESP" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5796 src/fu-util.c:5723 +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5798 src/fu-util.c:5725 +msgid "Switch the firmware branch on the device" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5803 +msgid "DEVICE-ID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5805 src/fu-util.c:5608 +msgid "Gets the results from the last update" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5811 +msgid "Erase all firmware update history" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5817 +msgid "[SETTING1] [SETTING2]..." +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5819 src/fu-util.c:5819 +msgid "" +"Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5824 +msgid "SETTING VALUE" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5826 +msgid "Set a BIOS setting" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5831 +msgid "ARCHIVE FIRMWARE METAINFO [FIRMWARE] [METAINFO] [JCATFILE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5833 +msgid "Build a cabinet archive from a firmware blob and XML metadata" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5838 +msgctxt "command-argument" +msgid "GUID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5840 +msgid "List EFI variables with a specific GUID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5848 +msgid "List EFI boot parameters" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5853 src/fu-tool.c:5867 +msgid "INDEX" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5855 +msgid "Set the EFI boot next" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5860 +msgid "INDEX1,INDEX2" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5862 +msgid "Set the EFI boot order" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5869 +msgid "Delete an EFI boot entry" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5874 +msgid "INDEX NAME TARGET [MOUNTPOINT]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5876 +msgid "Create an EFI boot entry" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5881 +msgid "INDEX KEY [VALUE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5883 +msgid "Set or remove an EFI boot hive entry" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5890 +msgid "List EFI boot files" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5895 src/fu-tool.c:5902 src/fu-util.c:5859 src/fu-util.c:5866 +msgid "[APPSTREAM_ID]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5897 src/fu-util.c:5861 +msgid "Fix a specific host security attribute" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5904 src/fu-util.c:5868 +msgid "Undo the host security attribute fix" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5909 +msgid "[DEVICE]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5911 +msgid "Run the post-reboot cleanup action" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5916 src/fu-util.c:5702 +msgid "[SECTION] KEY VALUE" +msgstr "" + +#. TRANSLATORS: sets something in the daemon configuration file +#: src/fu-tool.c:5918 src/fu-util.c:5704 +msgid "Modifies a daemon configuration value" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5923 src/fu-util.c:5709 +msgid "SECTION" +msgstr "" + +#. TRANSLATORS: sets something in the daemon configuration file +#: src/fu-tool.c:5925 src/fu-util.c:5711 +msgid "Resets a daemon configuration section" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5930 src/fu-util.c:5654 +msgid "REMOTE-ID KEY VALUE" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5932 src/fu-util.c:5656 +msgid "Modifies a given remote" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5937 src/fu-tool.c:5944 src/fu-tool.c:5951 src/fu-util.c:5661 +#: src/fu-util.c:5668 src/fu-util.c:5675 +msgid "REMOTE-ID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5939 src/fu-util.c:5670 +msgid "Cleans a given remote" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5946 src/fu-util.c:5663 +msgid "Enables a given remote" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5953 src/fu-util.c:5677 +msgid "Disables a given remote" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5959 +msgid "Enables virtual testing devices" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5965 +msgid "Disables virtual testing devices" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5971 +msgid "Get all known version formats" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5976 +msgid "VERSION1 VERSION2 [FORMAT]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5978 +msgid "Compares two versions for equality" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-tool.c:5983 src/fu-util.c:5620 +msgid "WORD" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-tool.c:5985 src/fu-util.c:5622 +msgid "Finds firmware releases from the metadata" +msgstr "" + +#. TRANSLATORS: CLI description +#: src/fu-tool.c:6023 +msgid "" +"This tool allows an administrator to use the fwupd plugins without being " +"installed on the host system." +msgstr "" + +#. TRANSLATORS: program name +#: src/fu-tool.c:6027 src/fu-util.c:5921 +msgid "Firmware Utility" +msgstr "" + +#. TRANSLATORS: try to help +#: src/fu-tool.c:6047 src/fu-util.c:5939 +msgid "" +"Ignoring SSL strict checks, to do this automatically in the future export " +"DISABLE_SSL_STRICT in your environment" +msgstr "" + +#. TRANSLATORS: the user didn't read the man page, %1 is '--filter' +#. TRANSLATORS: the user didn't read the man page, +#. * %1 is '--filter-release' +#. TRANSLATORS: the user didn't read the man page, %1 is '--filter' +#. TRANSLATORS: the user didn't read the man page, +#. * %1 is '--filter-release' +#: src/fu-tool.c:6061 src/fu-tool.c:6075 src/fu-util.c:5965 src/fu-util.c:5979 +#, c-format +msgid "Failed to parse flags for %s" +msgstr "" + +#. TRANSLATORS: explain how to get help, %1 is +#. * 'fwupdtool --help' +#. TRANSLATORS: explain how to get help, +#. * where $1 is something like 'fwupdmgr --help' +#: src/fu-tool.c:6154 src/fu-util.c:6142 +#, c-format +msgid "Use %s for help" +msgstr "" + +#. TRANSLATORS: %1 is a device name +#: src/fu-util.c:152 +#, c-format +msgid "Downgrading %s…" +msgstr "" + +#. TRANSLATORS: a list of failed updates +#: src/fu-util.c:336 +msgid "Devices that were not updated correctly:" +msgstr "" + +#. TRANSLATORS: a list of successful updates +#: src/fu-util.c:352 +msgid "Devices that have been updated successfully:" +msgstr "" + +#. TRANSLATORS: explain why we want to upload +#: src/fu-util.c:367 +msgid "" +"Uploading firmware reports helps hardware vendors to quickly identify " +"failing and successful updates on real devices." +msgstr "" + +#. TRANSLATORS: ask the user to upload +#: src/fu-util.c:374 +msgid "Review and upload report now?" +msgstr "" + +#. TRANSLATORS: metadata is downloaded +#: src/fu-util.c:376 src/fu-util.c:2659 src/fu-util.c:3403 src/fu-util.c:5067 +msgid "Requires internet connection" +msgstr "" + +#. TRANSLATORS: offer to disable this nag +#: src/fu-util.c:381 +msgid "Do you want to disable this feature for future updates?" +msgstr "" + +#. TRANSLATORS: offer to stop asking the question +#: src/fu-util.c:415 +msgid "Do you want to upload reports automatically for future updates?" +msgstr "" + +#. TRANSLATORS: no rebooting needed +#: src/fu-util.c:562 +msgid "No reboot is necessary" +msgstr "" + +#. TRANSLATORS: success message +#: src/fu-util.c:685 +msgid "Successfully installed firmware" +msgstr "" + +#. TRANSLATORS: this is for the device tests +#: src/fu-util.c:817 +msgid "Did not find any devices with matching GUIDs" +msgstr "" + +#. TRANSLATORS: this is for the device tests +#: src/fu-util.c:872 +msgid "OK!" +msgstr "" + +#. TRANSLATORS: the inhibit ID is a short string like dbus-123456 +#: src/fu-util.c:1260 +#, c-format +msgid "Inhibit ID is %s." +msgstr "" + +#. TRANSLATORS: we can auto-uninhibit after a timeout +#: src/fu-util.c:1265 +#, c-format +msgid "Automatically uninhibiting in %ums…" +msgstr "" + +#. TRANSLATORS: CTRL^C [holding control, and then pressing C] will exit the program +#: src/fu-util.c:1270 +msgid "Use CTRL^C to cancel." +msgstr "" + +#. TRANSLATORS: this CLI tool is now preventing system updates +#: src/fu-util.c:1272 +msgid "System Update Inhibited" +msgstr "" + +#. TRANSLATORS: the device is already connected +#: src/fu-util.c:1341 src/fu-util.c:1347 +msgid "Device already exists" +msgstr "" + +#. TRANSLATORS: the device showed up in time +#: src/fu-util.c:1375 +#, c-format +msgid "Successfully waited %.0fms for device" +msgstr "" + +#. TRANSLATORS: device tests can be specific to a CPU type +#: src/fu-util.c:1440 +#, c-format +msgid "%u test was skipped" +msgid_plural "%u tests were skipped" +msgstr[0] "" +msgstr[1] "" + +#. show the user the entire data blob +#: src/fu-util.c:1664 src/fu-util.c:4159 src/fu-util.c:5052 +msgid "Target" +msgstr "" + +#: src/fu-util.c:1665 src/fu-util.c:4161 src/fu-util.c:5053 +msgid "Payload" +msgstr "" + +#: src/fu-util.c:1667 src/fu-util.c:4163 +msgid "Signature" +msgstr "" + +#: src/fu-util.c:1668 src/fu-util.c:4164 +msgid "Proceed with upload?" +msgstr "" + +#. TRANSLATORS: the server sent the user a small message +#: src/fu-util.c:1694 +msgid "Update failure is a known issue, visit this URL for more information:" +msgstr "" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#: src/fu-util.c:1737 +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: key for a offline report filename +#: src/fu-util.c:1868 +msgid "Saved report" +msgstr "" + +#. TRANSLATORS: %1 is a device name, e.g. "ThinkPad Universal +#. * ThunderBolt 4 Dock" and %2 is "fwupdmgr activate" +#: src/fu-util.c:1906 +#, c-format +msgid "%s is pending activation; use %s to complete the update." +msgstr "" + +#. TRANSLATORS: success message when user refreshes device checksums +#: src/fu-util.c:2151 +msgid "Successfully updated device checksums" +msgstr "" + +#. TRANSLATORS: explain why no metadata available +#: src/fu-util.c:2168 +msgid "No remotes are currently enabled so no metadata is available." +msgstr "" + +#. TRANSLATORS: explain why no metadata available +#: src/fu-util.c:2172 +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "" + +#. TRANSLATORS: Turn on the remote +#: src/fu-util.c:2175 +msgid "Enable this remote?" +msgstr "" + +#: src/fu-util.c:2263 +msgid "Updating" +msgstr "" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +#: src/fu-util.c:2317 +msgid "Successfully downloaded new metadata:" +msgstr "" + +#. TRANSLATORS: no devices that can be upgraded with new firmware +#: src/fu-util.c:2325 +msgid "No devices are updatable" +msgstr "" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#: src/fu-util.c:2332 +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#: src/fu-util.c:2341 +#, c-format +msgid "" +"%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "" +"%u devices are supported in the enabled remotes (an update has been " +"published)" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: success message -- the user can do this by-hand too +#: src/fu-util.c:2388 +msgid "Successfully refreshed metadata manually" +msgstr "" + +#. TRANSLATORS: no repositories to download from +#: src/fu-util.c:2446 +msgid "No releases available" +msgstr "" + +#. TRANSLATORS: no repositories to download from +#: src/fu-util.c:2504 +msgid "No matching releases for search token" +msgstr "" + +#. TRANSLATORS: get interactive prompt +#: src/fu-util.c:2565 +msgid "Choose release" +msgstr "" + +#. TRANSLATORS: success message when user verified device checksums +#: src/fu-util.c:2596 +msgid "Successfully verified device checksums" +msgstr "" + +#. TRANSLATORS: the metadata is very out of date; %u is a number > 1 +#: src/fu-util.c:2647 +#, c-format +msgid "" +"Firmware metadata has not been updated for %u day and may not be up to date." +msgid_plural "" +"Firmware metadata has not been updated for %u days and may not be up to date." +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: ask if we can update metadata +#: src/fu-util.c:2657 +msgid "Update now?" +msgstr "" + +#. TRANSLATORS: this is an error string +#: src/fu-util.c:2826 +msgid "No updatable devices" +msgstr "" + +#. TRANSLATORS: this is an error string +#: src/fu-util.c:2835 +msgid "No updates available" +msgstr "" + +#. TRANSLATORS: no repositories to download from +#: src/fu-util.c:2864 +msgid "No remotes available" +msgstr "" + +#. TRANSLATORS: BKC is the industry name for the best known configuration and is a set +#. * of firmware that works together +#: src/fu-util.c:2971 +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "" + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync` +#: src/fu-util.c:2977 +#, c-format +msgid "" +"This device will be reverted back to %s when the %s command is performed." +msgstr "" + +#. TRANSLATORS: the best known configuration is a set of software that we know works +#. * well together. In the OEM and ODM industries it is often called a BKC +#: src/fu-util.c:2985 +msgid "Deviate from the best known configuration?" +msgstr "" + +#. TRANSLATORS: prompt to apply the update +#: src/fu-util.c:2990 src/fu-util.c:4405 src/fu-util-common.c:380 +#: src/fu-util-common.c:2789 +msgid "Perform operation?" +msgstr "" + +#. TRANSLATORS: ask if we can update the metadata +#: src/fu-util.c:3401 +msgid "Do you want to refresh this remote now?" +msgstr "" + +#. TRANSLATORS: success message +#: src/fu-util.c:3417 +msgid "Successfully enabled and refreshed remote" +msgstr "" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#: src/fu-util.c:3535 +#, c-format +msgid "No downgrades for %s" +msgstr "" + +#. TRANSLATORS: shown when shutting down to switch to the new version +#: src/fu-util.c:3894 +msgid "Activating firmware update for" +msgstr "" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +#: src/fu-util.c:3908 +msgid "Successfully activated all devices" +msgstr "" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +#: src/fu-util.c:3980 +msgid "There is no approved firmware." +msgstr "" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +#: src/fu-util.c:3986 +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: changes only take effect on restart +#: src/fu-util.c:4029 src/fu-util.c:4065 +msgid "Restart the daemon to make the change effective?" +msgstr "" + +#. TRANSLATORS: success message -- a per-system setting value +#: src/fu-util.c:4074 +msgid "Successfully reset configuration values" +msgstr "" + +#. TRANSLATORS: ask the user to share, %s is something +#. * like: "Linux Vendor Firmware Service" +#: src/fu-util.c:4137 +#, c-format +msgid "Upload these anonymous results to the %s to help other users?" +msgstr "" + +#. TRANSLATORS: success, so say thank you to the user +#: src/fu-util.c:4188 +msgid "Host Security ID attributes uploaded successfully, thanks!" +msgstr "" + +#. TRANSLATORS: can we JFDI? +#: src/fu-util.c:4196 +msgid "Automatically upload every time?" +msgstr "" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +#: src/fu-util.c:4350 +msgid "Configuration Change Suggested" +msgstr "" + +#. TRANSLATORS: the %1 is a BIOS setting name. +#. * %2 and %3 are the values, e.g. "True" or "Windows10" +#: src/fu-util.c:4362 +#, c-format +msgid "" +"This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, " +"but it will only be active after restarting the computer." +msgstr "" + +#. TRANSLATORS: the user has to manually recover; we can't do it +#: src/fu-util.c:4371 +msgid "" +"You should ensure you are comfortable restoring the setting from the system " +"firmware setup, as this change may cause the system to not boot into Linux " +"or cause other system instability." +msgstr "" + +#. TRANSLATORS: the %1 is a kernel command line key=value +#: src/fu-util.c:4381 +#, c-format +msgid "" +"This tool can change the kernel argument from '%s' to '%s', but it will only " +"be active after restarting the computer." +msgstr "" + +#. TRANSLATORS: the %1 is a kernel command line key=value +#: src/fu-util.c:4389 +#, c-format +msgid "" +"This tool can add a kernel argument of '%s', but it will only be active " +"after restarting the computer." +msgstr "" + +#. TRANSLATORS: the user has to manually recover; we can't do it +#: src/fu-util.c:4396 +msgid "" +"You should ensure you are comfortable restoring the setting from a recovery " +"or installation disk, as this change may cause the system to not boot into " +"Linux or cause other system instability." +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-util.c:4599 +msgid "Unable to connect to service" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-util.c:4608 +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "" + +#. TRANSLATORS: user selected something not possible +#: src/fu-util.c:4702 +msgid "Firmware is already blocked" +msgstr "" + +#. TRANSLATORS: we will not offer this firmware to the user +#: src/fu-util.c:4707 +msgid "Blocking firmware:" +msgstr "" + +#. TRANSLATORS: nothing to show +#: src/fu-util.c:4738 src/fu-util.c:4788 +msgid "There are no blocked firmware files" +msgstr "" + +#. TRANSLATORS: user selected something not possible +#: src/fu-util.c:4757 +msgid "Firmware is not already blocked" +msgstr "" + +#. TRANSLATORS: we will now offer this firmware to the user +#: src/fu-util.c:4762 +msgid "Unblocking firmware:" +msgstr "" + +#. TRANSLATORS: there follows a list of hashes +#: src/fu-util.c:4793 +msgid "Blocked firmware files:" +msgstr "" + +#. TRANSLATORS: explain why we want to upload +#: src/fu-util.c:5057 +#, c-format +msgid "" +"Uploading a device list allows the %s team to know what hardware exists, and " +"allows us to put pressure on vendors that do not upload firmware updates for " +"their hardware." +msgstr "" + +#. TRANSLATORS: ask the user to upload +#: src/fu-util.c:5065 +msgid "Upload data now?" +msgstr "" + +#. TRANSLATORS: success, so say thank you to the user +#: src/fu-util.c:5091 +msgid "Device list uploaded successfully, thanks!" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5298 +msgid "Set the download retries for transient errors" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5330 +msgid "Only install onto emulated devices" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5354 +msgid "Sign the uploaded data with the client certificate" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5362 +msgid "Do not check for unreported history" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5370 +msgid "Do not check for old metadata" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5378 +msgid "Do not check if download remotes should be enabled" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5410 +msgid "Do not write to the history database" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5442 +msgid "Only use peer-to-peer networking when downloading files" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5476 +msgid "Do not prompt to fix security issues" +msgstr "" + +#. TRANSLATORS: command line option +#: src/fu-util.c:5484 +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5519 +msgid "Check if any devices are pending a reboot to complete update" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5537 +msgid "Share firmware history with the developers" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5543 +msgid "Export firmware history for manual upload" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5548 +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5550 +msgid "Install a specific firmware file on all devices that match" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5557 +msgid "Install a firmware file in cabinet format on this hardware" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5587 +msgid "Checks cryptographic hash matches firmware" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5594 +msgid "Unlocks the device for firmware access" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5601 +msgid "Clears the results from the last update" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5615 +msgid "Gets the releases for a device" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5635 +msgid "Downgrades the firmware on a device" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5649 +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5684 +msgid "Activate devices" +msgstr "" + +#. TRANSLATORS: firmware approved by the admin +#: src/fu-util.c:5690 +msgid "Gets the list of approved firmware" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5695 +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5718 +msgid "Reinstall current firmware on the device" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5737 +msgid "Sync firmware versions to the chosen configuration" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5742 src/fu-util.c:5749 +msgid "[CHECKSUM]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5744 +msgid "Blocks a specific firmware from being installed" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5751 +msgid "Unblocks a specific firmware from being installed" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5757 +msgid "Gets the list of blocked firmware" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5768 +msgid "LOCATION" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5770 +msgid "Download a file" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5775 src/fu-util.c:5782 +msgid "[FILENAME1] [FILENAME2]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5777 +msgid "Test a device using a JSON manifest" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5784 +msgid "Emulate a device using a JSON manifest" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5789 +msgid "[REASON] [TIMEOUT]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5791 +msgid "Inhibit the system to prevent upgrades" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5796 +msgid "INHIBIT-ID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5798 +msgid "Uninhibit the system to allow upgrades" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5803 +msgid "GUID|DEVICE-ID" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5805 +msgid "Wait for a device to appear" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5811 +msgid "Asks the daemon to quit" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5817 +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +#: src/fu-util.c:5824 +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5826 +msgid "Sets one or more BIOS settings" +msgstr "" + +#. TRANSLATORS: command description +#: src/fu-util.c:5874 +msgid "Upload the list of updatable devices to a remote server" +msgstr "" + +#. TRANSLATORS: CLI description +#: src/fu-util.c:5916 +msgid "" +"This tool allows an administrator to query and control the fwupd daemon, " +"allowing them to perform actions such as installing or downgrading firmware." +msgstr "" + +#. TRANSLATORS: try to help +#: src/fu-util.c:5953 +msgid "" +"The system clock has not been set correctly and downloading files may fail." +msgstr "" + +#. TRANSLATORS: error message for Windows +#: src/fu-util.c:6056 +msgid "Failed to connect to Windows service, please ensure it's running." +msgstr "" + +#. TRANSLATORS: could not contact the fwupd service over D-Bus +#: src/fu-util.c:6060 +msgid "Failed to connect to daemon" +msgstr "" + +#. TRANSLATORS: the user is SOL for support... +#: src/fu-util.c:6070 +msgid "" +"The daemon has loaded 3rd party code and is no longer supported by the " +"upstream developers!" +msgstr "" + +#. TRANSLATORS: a feature is something like "can show an image" +#: src/fu-util.c:6120 +msgid "Failed to set front-end features" +msgstr "" + +#. TRANSLATORS: The BIOS setting can only be changed to fixed values +#: src/fu-util-bios-setting.c:33 +msgid "Enumeration" +msgstr "" + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +#: src/fu-util-bios-setting.c:37 +msgid "Integer" +msgstr "" + +#. TRANSLATORS: The BIOS setting accepts strings +#: src/fu-util-bios-setting.c:41 +msgid "String" +msgstr "" + +#. TRANSLATORS: type of BIOS setting +#: src/fu-util-bios-setting.c:107 +msgid "Setting type" +msgstr "" + +#. TRANSLATORS: tell a user how to get information +#: src/fu-util-bios-setting.c:115 +#, c-format +msgid "Run without '%s' to see" +msgstr "" + +#. TRANSLATORS: current value of a BIOS setting +#: src/fu-util-bios-setting.c:118 +msgid "Current Value" +msgstr "" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +#: src/fu-util-bios-setting.c:124 src/fu-util-common.c:1986 +msgid "Description" +msgstr "" + +#. TRANSLATORS: item is TRUE +#: src/fu-util-bios-setting.c:129 +msgid "True" +msgstr "" + +#. TRANSLATORS: item is FALSE +#: src/fu-util-bios-setting.c:132 +msgid "False" +msgstr "" + +#. TRANSLATORS: BIOS setting is read only +#: src/fu-util-bios-setting.c:135 +msgid "Read Only" +msgstr "" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +#: src/fu-util-bios-setting.c:149 +msgid "Minimum value" +msgstr "" + +#. TRANSLATORS: Highest valid integer for BIOS setting +#: src/fu-util-bios-setting.c:151 +msgid "Maximum value" +msgstr "" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +#: src/fu-util-bios-setting.c:156 +msgid "Scalar Increment" +msgstr "" + +#. TRANSLATORS: Shortest valid string for BIOS setting +#: src/fu-util-bios-setting.c:160 +msgid "Minimum length" +msgstr "" + +#. TRANSLATORS: Longest valid string for BIOS setting +#: src/fu-util-bios-setting.c:162 +msgid "Maximum length" +msgstr "" + +#. TRANSLATORS: Possible values for a bios setting +#: src/fu-util-bios-setting.c:168 +msgid "Possible Values" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-util-bios-setting.c:200 +msgid "Invalid arguments" +msgstr "" + +#. TRANSLATORS: the vendor did not upload this +#: src/fu-util-common.c:268 +msgid "" +"This firmware is provided by LVFS community members and is not provided (or " +"supported) by the original hardware vendor." +msgstr "" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +#: src/fu-util-common.c:274 +msgid "Installing this update may also void any device warranty." +msgstr "" + +#. TRANSLATORS: naughty vendor +#: src/fu-util-common.c:282 +msgid "The vendor did not supply any release notes." +msgstr "" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#: src/fu-util-common.c:310 +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#: src/fu-util-common.c:319 +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#: src/fu-util-common.c:328 +#, c-format +msgid "Reinstall %s to %s?" +msgstr "" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#: src/fu-util-common.c:350 +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#. +#: src/fu-util-common.c:362 +#, c-format +msgid "" +"%s must remain connected for the duration of the update to avoid damage." +msgstr "" + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#. +#: src/fu-util-common.c:371 +#, c-format +msgid "" +"%s must remain plugged into a power source for the duration of the update to " +"avoid damage." +msgstr "" + +#. TRANSLATORS: explain why +#: src/fu-util-common.c:401 +msgid "An update requires the system to shutdown to complete." +msgstr "" + +#. TRANSLATORS: shutdown to apply the update +#: src/fu-util-common.c:404 +msgid "Shutdown now?" +msgstr "" + +#. TRANSLATORS: explain why we want to reboot +#: src/fu-util-common.c:415 +msgid "An update requires a reboot to complete." +msgstr "" + +#. TRANSLATORS: reboot to apply the update +#: src/fu-util-common.c:417 +msgid "Restart now?" +msgstr "" + +#. TRANSLATORS: this is a command alias, e.g. 'get-devices' +#: src/fu-util-common.c:475 +#, c-format +msgid "Alias to %s" +msgstr "" + +#. TRANSLATORS: error message +#: src/fu-util-common.c:523 +msgid "Command not found" +msgstr "" + +#. TRANSLATORS: this is the default branch name when unset +#: src/fu-util-common.c:572 +msgid "default" +msgstr "" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Unifying Receiver` +#: src/fu-util-common.c:588 +#, c-format +msgid "%s Device Update" +msgstr "" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#: src/fu-util-common.c:593 +#, c-format +msgid "%s Configuration Update" +msgstr "" + +#. TRANSLATORS: the entire system, e.g. all internal devices, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#: src/fu-util-common.c:598 +#, c-format +msgid "%s System Update" +msgstr "" + +#. TRANSLATORS: the EC is typically the keyboard controller chip, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#: src/fu-util-common.c:603 +#, c-format +msgid "%s Embedded Controller Update" +msgstr "" + +#. TRANSLATORS: ME stands for Management Engine, the Intel AMT thing, +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#: src/fu-util-common.c:608 +#, c-format +msgid "%s ME Update" +msgstr "" + +#. TRANSLATORS: ME stands for Management Engine (with Intel AMT), +#. * where the first %s is the device name, e.g. 'ThinkPad P50` +#: src/fu-util-common.c:613 +#, c-format +msgid "%s Corporate ME Update" +msgstr "" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#: src/fu-util-common.c:618 +#, c-format +msgid "%s Consumer ME Update" +msgstr "" + +#. 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` +#: src/fu-util-common.c:624 +#, c-format +msgid "%s Controller Update" +msgstr "" + +#. 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` +#: src/fu-util-common.c:630 +#, c-format +msgid "%s Thunderbolt Controller Update" +msgstr "" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system boot-up +#: src/fu-util-common.c:635 +#, c-format +msgid "%s CPU Microcode Update" +msgstr "" + +#. TRANSLATORS: battery refers to the system power source +#: src/fu-util-common.c:639 +#, c-format +msgid "%s Battery Update" +msgstr "" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#: src/fu-util-common.c:644 +#, c-format +msgid "%s Camera Update" +msgstr "" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#: src/fu-util-common.c:648 +#, c-format +msgid "%s TPM Update" +msgstr "" + +#. TRANSLATORS: TouchPad refers to a flat input device +#: src/fu-util-common.c:652 +#, c-format +msgid "%s Touchpad Update" +msgstr "" + +#. TRANSLATORS: Mouse refers to a handheld input device +#: src/fu-util-common.c:656 +#, c-format +msgid "%s Mouse Update" +msgstr "" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#: src/fu-util-common.c:660 +#, c-format +msgid "%s Keyboard Update" +msgstr "" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#: src/fu-util-common.c:664 +#, c-format +msgid "%s Storage Controller Update" +msgstr "" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#: src/fu-util-common.c:669 +#, c-format +msgid "%s Network Interface Update" +msgstr "" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#: src/fu-util-common.c:674 +#, c-format +msgid "%s Display Update" +msgstr "" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#: src/fu-util-common.c:679 +#, c-format +msgid "%s BMC Update" +msgstr "" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#: src/fu-util-common.c:684 +#, c-format +msgid "%s USB Receiver Update" +msgstr "" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#: src/fu-util-common.c:688 +#, c-format +msgid "%s Drive Update" +msgstr "" + +#. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC +#: src/fu-util-common.c:692 +#, c-format +msgid "%s Flash Drive Update" +msgstr "" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#: src/fu-util-common.c:697 +#, c-format +msgid "%s SSD Update" +msgstr "" + +#. TRANSLATORS: GPU refers to a Graphics Processing Unit, e.g. +#. * the "video card" +#: src/fu-util-common.c:702 +#, c-format +msgid "%s GPU Update" +msgstr "" + +#. TRANSLATORS: Dock refers to the port replicator hardware laptops are +#. * cradled in, or lowered onto +#: src/fu-util-common.c:707 +#, c-format +msgid "%s Dock Update" +msgstr "" + +#. TRANSLATORS: Dock refers to the port replicator device connected +#. * by plugging in a USB cable -- which may or may not also provide power +#: src/fu-util-common.c:712 +#, c-format +msgid "%s USB Dock Update" +msgstr "" + +#. TRANSLATORS: a device that can read your fingerprint pattern +#: src/fu-util-common.c:716 +#, c-format +msgid "%s Fingerprint Reader Update" +msgstr "" + +#. TRANSLATORS: a large pressure-sensitive drawing area typically used +#. * by artists and digital artists +#: src/fu-util-common.c:721 +#, c-format +msgid "%s Graphics Tablet Update" +msgstr "" + +#. TRANSLATORS: an input device used by gamers, e.g. a joystick +#: src/fu-util-common.c:725 +#, c-format +msgid "%s Input Controller Update" +msgstr "" + +#. TRANSLATORS: two miniature speakers attached to your ears +#: src/fu-util-common.c:729 +#, c-format +msgid "%s Headphones Update" +msgstr "" + +#. TRANSLATORS: headphones with an integrated microphone +#: src/fu-util-common.c:733 +#, c-format +msgid "%s Headset Update" +msgstr "" + +#. 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` +#: src/fu-util-common.c:740 +#, c-format +msgid "%s Update" +msgstr "" + +#. TRANSLATORS: duration in seconds +#: src/fu-util-common.c:990 +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: duration in minutes +#: src/fu-util-common.c:997 +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: duration in minutes +#: src/fu-util-common.c:1004 +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: duration in days! +#: src/fu-util-common.c:1010 +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: Device cannot be removed easily +#: src/fu-util-common.c:1020 +msgid "Internal device" +msgstr "" + +#. TRANSLATORS: Device is updatable in this or any other mode +#: src/fu-util-common.c:1025 +msgid "Updatable" +msgstr "" + +#. TRANSLATORS: Must be plugged into an outlet +#: src/fu-util-common.c:1029 +msgid "System requires external power source" +msgstr "" + +#. TRANSLATORS: Is locked and can be unlocked +#: src/fu-util-common.c:1033 +msgid "Device is locked" +msgstr "" + +#. TRANSLATORS: Is found in current metadata +#: src/fu-util-common.c:1037 +msgid "Supported on remote server" +msgstr "" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +#: src/fu-util-common.c:1041 +msgid "Requires a bootloader" +msgstr "" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +#: src/fu-util-common.c:1045 +msgid "Needs a reboot after installation" +msgstr "" + +#. TRANSLATORS: Requires system shutdown to apply firmware +#: src/fu-util-common.c:1049 +msgid "Needs shutdown after installation" +msgstr "" + +#. TRANSLATORS: Has been reported to a metadata server +#: src/fu-util-common.c:1053 +msgid "Reported to remote server" +msgstr "" + +#. TRANSLATORS: User has been notified +#: src/fu-util-common.c:1057 +msgid "User has been notified" +msgstr "" + +#. TRANSLATORS: Is currently in bootloader mode +#: src/fu-util-common.c:1061 +msgid "Is in bootloader mode" +msgstr "" + +#. TRANSLATORS: the hardware is waiting to be replugged +#: src/fu-util-common.c:1065 +msgid "Hardware is waiting to be replugged" +msgstr "" + +#. TRANSLATORS: Device update needs to be separately activated +#: src/fu-util-common.c:1073 +msgid "Device update needs activation" +msgstr "" + +#. TRANSLATORS: Device will not return after update completes +#: src/fu-util-common.c:1081 +msgid "Device will not re-appear after update completes" +msgstr "" + +#. TRANSLATORS: Device supports some form of checksum verification +#: src/fu-util-common.c:1085 +msgid "Cryptographic hash verification is available" +msgstr "" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +#: src/fu-util-common.c:1093 +msgid "Device stages updates" +msgstr "" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +#: src/fu-util-common.c:1097 +msgid "Device can recover flash failures" +msgstr "" + +#. TRANSLATORS: Device remains usable during update +#: src/fu-util-common.c:1101 +msgid "Device is usable for the duration of the update" +msgstr "" + +#. TRANSLATORS: a version check is required for all firmware +#: src/fu-util-common.c:1105 +msgid "Device firmware is required to have a version check" +msgstr "" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +#: src/fu-util-common.c:1109 +msgid "Device is required to install all provided releases" +msgstr "" + +#. TRANSLATORS: there is more than one supplier of the firmware +#: src/fu-util-common.c:1113 +msgid "Device supports switching to a different branch of firmware" +msgstr "" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +#: src/fu-util-common.c:1117 +msgid "Device will backup firmware before installing" +msgstr "" + +#. TRANSLATORS: on some systems certain devices have to have matching versions, +#. * e.g. the EFI driver for a given network card cannot be different +#: src/fu-util-common.c:1122 +msgid "All devices of the same type will be updated at the same time" +msgstr "" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +#: src/fu-util-common.c:1127 +msgid "Only version upgrades are allowed" +msgstr "" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power state +#. * or is out of wireless range +#: src/fu-util-common.c:1132 +msgid "Device is unreachable" +msgstr "" + +#. TRANSLATORS: we might ask the user the recovery key when next booting Windows +#: src/fu-util-common.c:1136 +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "" + +#. TRANSLATORS: the vendor is no longer supporting the device +#: src/fu-util-common.c:1140 +msgid "End of life" +msgstr "" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +#: src/fu-util-common.c:1144 +msgid "Signed Payload" +msgstr "" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +#: src/fu-util-common.c:1148 +msgid "Unsigned Payload" +msgstr "" + +#. TRANSLATORS: this device is not actually real +#: src/fu-util-common.c:1152 +msgid "Emulated" +msgstr "" + +#. TRANSLATORS: we're saving all USB events for emulation +#: src/fu-util-common.c:1156 +msgid "Tagged for emulation" +msgstr "" + +#. TRANSLATORS: stay on one firmware version unless the new version is explicitly +#. * specified +#: src/fu-util-common.c:1161 +msgid "Installing a specific release is explicitly required" +msgstr "" + +#. TRANSLATORS: we can save all device enumeration events for emulation +#: src/fu-util-common.c:1165 +msgid "Can tag for emulation" +msgstr "" + +#. TRANSLATORS: ask the user to do a simple task which should be translated +#: src/fu-util-common.c:1179 +msgid "Message" +msgstr "" + +#. TRANSLATORS: show the user a generic image that can be themed +#: src/fu-util-common.c:1183 +msgid "Image" +msgstr "" + +#. TRANSLATORS: ask the user a question, and it will not be translated +#: src/fu-util-common.c:1187 +msgid "Message (custom)" +msgstr "" + +#. TRANSLATORS: show the user a random image from the internet +#: src/fu-util-common.c:1191 +msgid "Image (custom)" +msgstr "" + +#. TRANSLATORS: the update state of the specific device +#: src/fu-util-common.c:1201 +msgid "Pending" +msgstr "" + +#. TRANSLATORS: the update state of the specific device +#: src/fu-util-common.c:1205 +msgid "Success" +msgstr "" + +#. TRANSLATORS: the update state of the specific device +#: src/fu-util-common.c:1209 +msgid "Failed" +msgstr "" + +#. TRANSLATORS: the update state of the specific device +#: src/fu-util-common.c:1213 +msgid "Transient failure" +msgstr "" + +#. TRANSLATORS: the update state of the specific device +#: src/fu-util-common.c:1217 +msgid "Needs reboot" +msgstr "" + +#. TRANSLATORS: as in laptop battery power +#: src/fu-util-common.c:1233 +msgid "System power is too low" +msgstr "" + +#. TRANSLATORS: as in laptop battery power +#: src/fu-util-common.c:1237 +#, c-format +msgid "System power is too low (%u%%, requires %u%%)" +msgstr "" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +#: src/fu-util-common.c:1243 +msgid "Device is unreachable, or out of wireless range" +msgstr "" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#: src/fu-util-common.c:1249 +msgid "Device battery power is too low" +msgstr "" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#: src/fu-util-common.c:1252 +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +#: src/fu-util-common.c:1258 +msgid "Device is waiting for the update to be applied" +msgstr "" + +#. TRANSLATORS: as in, wired mains power for a laptop +#: src/fu-util-common.c:1262 +msgid "Device requires AC power to be connected" +msgstr "" + +#. TRANSLATORS: lid means "laptop top cover" +#: src/fu-util-common.c:1266 +msgid "Device cannot be updated while the lid is closed" +msgstr "" + +#. TRANSLATORS: emulated means we are pretending to be a different model +#: src/fu-util-common.c:1270 +msgid "Device is emulated" +msgstr "" + +#. TRANSLATORS: The device cannot be updated due to missing vendor's license. +#: src/fu-util-common.c:1274 +msgid "Device requires a software license to update" +msgstr "" + +#. TRANSLATORS: an application is preventing system updates +#: src/fu-util-common.c:1278 +msgid "All devices are prevented from update by system inhibit" +msgstr "" + +#. TRANSLATORS: another application is updating the device already +#: src/fu-util-common.c:1282 +msgid "An update is in progress" +msgstr "" + +#. TRANSLATORS: device cannot be interrupted, for instance taking a phone call +#: src/fu-util-common.c:1286 +msgid "Device is in use" +msgstr "" + +#. TRANSLATORS: device does not have a display connected +#: src/fu-util-common.c:1290 +msgid "Device requires a display to be plugged in" +msgstr "" + +#. TRANSLATORS: we have two ways of communicating with the device, so we hide one +#: src/fu-util-common.c:1294 +msgid "Device is lower priority than an equivalent device" +msgstr "" + +#. TRANSLATORS: firmware is signed with insecure key +#: src/fu-util-common.c:1298 +msgid "System has been signed with an insecure key" +msgstr "" + +#. TRANSLATORS: firmware is locked from the BIOS +#: src/fu-util-common.c:1302 +msgid "Device firmware has been locked " +msgstr "" + +#. TRANSLATORS: Name of hardware +#: src/fu-util-common.c:1333 +msgid "Unknown Device" +msgstr "" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +#: src/fu-util-common.c:1338 +msgid "Device ID" +msgstr "" + +#. TRANSLATORS: one line summary of device +#: src/fu-util-common.c:1341 src/fu-util-common.c:1899 +msgid "Summary" +msgstr "" + +#. TRANSLATORS: version number of previous firmware +#: src/fu-util-common.c:1358 +msgid "Previous version" +msgstr "" + +#. TRANSLATORS: version number of current firmware +#: src/fu-util-common.c:1362 +msgid "Current version" +msgstr "" + +#. TRANSLATORS: smallest version number installable on device +#: src/fu-util-common.c:1368 +msgid "Minimum Version" +msgstr "" + +#. TRANSLATORS: firmware version of bootloader +#: src/fu-util-common.c:1373 +msgid "Bootloader Version" +msgstr "" + +#. TRANSLATORS: manufacturer of hardware +#: src/fu-util-common.c:1382 src/fu-util-common.c:1385 +#: src/fu-util-common.c:1389 src/fu-util-common.c:1946 +msgid "Vendor" +msgstr "" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree +#: src/fu-util-common.c:1396 +msgid "Release Branch" +msgstr "" + +#. TRANSLATORS: length of time the update takes to apply +#: src/fu-util-common.c:1404 +msgid "Install Duration" +msgstr "" + +#. TRANSLATORS: serial number of hardware +#: src/fu-util-common.c:1408 +msgid "Serial Number" +msgstr "" + +#. TRANSLATORS: hardware state, e.g. "pending" +#: src/fu-util-common.c:1416 +msgid "Update State" +msgstr "" + +#. TRANSLATORS: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#: src/fu-util-common.c:1427 +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "" + +#. TRANSLATORS: refers to the battery inside the peripheral device +#: src/fu-util-common.c:1431 src/fu-util-common.c:1436 +msgid "Battery" +msgstr "" + +#. TRANSLATORS: error message from last update attempt +#: src/fu-util-common.c:1447 +msgid "Update Error" +msgstr "" + +#. TRANSLATORS: reasons the device is not updatable +#: src/fu-util-common.c:1451 +msgid "Problems" +msgstr "" + +#. TRANSLATORS: the original time/date the device was modified +#: src/fu-util-common.c:1471 +msgid "Last modified" +msgstr "" + +#. TRANSLATORS: global ID common to all similar hardware +#: src/fu-util-common.c:1497 +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: description of device ability +#: src/fu-util-common.c:1505 +msgid "Device Flags" +msgstr "" + +#. TRANSLATORS: description of the device requests +#: src/fu-util-common.c:1526 +msgid "Device Requests" +msgstr "" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +#: src/fu-util-common.c:1550 src/fu-util-common.c:1995 +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: Plugin is active only if hardware is found +#: src/fu-util-common.c:1576 +msgid "Enabled if hardware matches" +msgstr "" + +#. TRANSLATORS: Plugin is active and in use +#: src/fu-util-common.c:1580 +msgid "Ready" +msgstr "" + +#. TRANSLATORS: Plugin is inactive and not used +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:1584 src/fu-util-common.c:2200 +msgid "Disabled" +msgstr "" + +#. TRANSLATORS: not required for this system +#: src/fu-util-common.c:1588 +msgid "Required hardware was not found" +msgstr "" + +#. TRANSLATORS: system is not booted in UEFI mode +#: src/fu-util-common.c:1592 +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +#: src/fu-util-common.c:1597 +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "" + +#. TRANSLATORS: user needs to run a command, %1 is 'fwupdmgr unlock' +#: src/fu-util-common.c:1601 +#, c-format +msgid "Firmware updates disabled; run '%s' to enable" +msgstr "" + +#. TRANSLATORS: user needs to run a command +#: src/fu-util-common.c:1606 +msgid "Authentication details are required" +msgstr "" + +#. TRANSLATORS: no peeking +#: src/fu-util-common.c:1610 +msgid "Configuration is only readable by the system administrator" +msgstr "" + +#. TRANSLATORS: the plugin was created from a .so object, and was not built-in +#: src/fu-util-common.c:1614 +msgid "Loaded from an external module" +msgstr "" + +#. TRANSLATORS: check various UEFI and ACPI tables are unchanged after the update +#: src/fu-util-common.c:1618 +msgid "Will measure elements of system integrity around an update" +msgstr "" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +#: src/fu-util-common.c:1622 +msgid "Required efivarfs filesystem was not found" +msgstr "" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +#: src/fu-util-common.c:1626 +msgid "UEFI ESP partition not detected or configured" +msgstr "" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +#: src/fu-util-common.c:1630 +msgid "UEFI ESP partition may not be set up correctly" +msgstr "" + +#. TRANSLATORS: Failed to open plugin, hey Arch users +#: src/fu-util-common.c:1634 +msgid "Plugin dependencies missing" +msgstr "" + +#. TRANSLATORS: The kernel does not support this plugin +#: src/fu-util-common.c:1638 +msgid "Running kernel is too old" +msgstr "" + +#. TRANSLATORS: The plugin is only for testing +#: src/fu-util-common.c:1642 +msgid "Plugin is only for testing" +msgstr "" + +#. TRANSLATORS: The plugin enumeration might change the device current mode +#: src/fu-util-common.c:1646 +msgid "Plugin enumeration may change device state" +msgstr "" + +#. TRANSLATORS: description of plugin state, e.g. disabled +#: src/fu-util-common.c:1701 +msgid "Flags" +msgstr "" + +#. TRANSLATORS: a non-free software license +#: src/fu-util-common.c:1746 +msgid "Proprietary" +msgstr "" + +#. TRANSLATORS: the release urgency +#: src/fu-util-common.c:1760 +msgid "Low" +msgstr "" + +#. TRANSLATORS: the release urgency +#: src/fu-util-common.c:1764 +msgid "Medium" +msgstr "" + +#. TRANSLATORS: the release urgency +#: src/fu-util-common.c:1768 +msgid "High" +msgstr "" + +#. TRANSLATORS: the release urgency +#: src/fu-util-common.c:1772 +msgid "Critical" +msgstr "" + +#. TRANSLATORS: We verified the payload against the server +#: src/fu-util-common.c:1785 +msgid "Trusted payload" +msgstr "" + +#. TRANSLATORS: We verified the metadata against the server +#: src/fu-util-common.c:1789 +msgid "Trusted metadata" +msgstr "" + +#. TRANSLATORS: version is newer +#: src/fu-util-common.c:1793 +msgid "Is upgrade" +msgstr "" + +#. TRANSLATORS: version is older +#: src/fu-util-common.c:1797 +msgid "Is downgrade" +msgstr "" + +#. TRANSLATORS: version cannot be installed due to policy +#: src/fu-util-common.c:1801 +msgid "Blocked version" +msgstr "" + +#. TRANSLATORS: version cannot be installed due to policy +#: src/fu-util-common.c:1805 +msgid "Not approved" +msgstr "" + +#. TRANSLATORS: is not the main firmware stream +#: src/fu-util-common.c:1809 +msgid "Alternate branch" +msgstr "" + +#. TRANSLATORS: is not supported by the vendor +#: src/fu-util-common.c:1813 +msgid "Community supported" +msgstr "" + +#. TRANSLATORS: someone we trust has tested this +#: src/fu-util-common.c:1817 +msgid "Tested by trusted vendor" +msgstr "" + +#. TRANSLATORS: the %s is a vendor name, e.g. Lenovo +#: src/fu-util-common.c:1830 +#, c-format +msgid "Tested by %s" +msgstr "" + +#. TRANSLATORS: when the release was tested +#: src/fu-util-common.c:1833 +msgid "Tested" +msgstr "" + +#. TRANSLATORS: the OS the release was tested on +#: src/fu-util-common.c:1845 +msgid "Distribution" +msgstr "" + +#. TRANSLATORS: the firmware old version +#: src/fu-util-common.c:1850 +msgid "Old version" +msgstr "" + +#. TRANSLATORS: the fwupd version the release was tested on +#: src/fu-util-common.c:1856 +msgid "Version[fwupd]" +msgstr "" + +#. TRANSLATORS: version number of new firmware +#: src/fu-util-common.c:1879 +msgid "New version" +msgstr "" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +#: src/fu-util-common.c:1884 src/fu-util-common.c:2038 +msgid "Remote ID" +msgstr "" + +#. TRANSLATORS: the exact component on the server +#: src/fu-util-common.c:1889 +msgid "Release ID" +msgstr "" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree +#: src/fu-util-common.c:1894 +msgid "Branch" +msgstr "" + +#. TRANSLATORS: one line variant of release (e.g. 'China') +#: src/fu-util-common.c:1904 +msgid "Variant" +msgstr "" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +#: src/fu-util-common.c:1910 +msgid "License" +msgstr "" + +#. TRANSLATORS: file size of the download +#: src/fu-util-common.c:1913 +msgid "Size" +msgstr "" + +#. TRANSLATORS: when the update was built +#: src/fu-util-common.c:1915 +msgid "Created" +msgstr "" + +#. TRANSLATORS: how important the release is +#: src/fu-util-common.c:1921 +msgid "Urgency" +msgstr "" + +#. TRANSLATORS: more details about the update link +#: src/fu-util-common.c:1931 +msgid "Details" +msgstr "" + +#. TRANSLATORS: source (as in code) link +#: src/fu-util-common.c:1936 +msgid "Source" +msgstr "" + +#. TRANSLATORS: Software Bill of Materials link +#: src/fu-util-common.c:1941 +msgid "SBOM" +msgstr "" + +#. TRANSLATORS: length of time the update takes to apply +#: src/fu-util-common.c:1952 +msgid "Duration" +msgstr "" + +#. TRANSLATORS: helpful messages for the update +#: src/fu-util-common.c:1957 +msgid "Update Message" +msgstr "" + +#. TRANSLATORS: helpful image for the update +#: src/fu-util-common.c:1962 +msgid "Update Image" +msgstr "" + +#. TRANSLATORS: release attributes +#: src/fu-util-common.c:1966 +msgid "Release Flags" +msgstr "" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +#: src/fu-util-common.c:2007 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "" +msgstr[1] "" + +#. TRANSLATORS: hash to that exact firmware archive +#. TRANSLATORS: remote checksum +#: src/fu-util-common.c:2019 src/fu-util-common.c:2067 +msgid "Checksum" +msgstr "" + +#. TRANSLATORS: remote type, e.g. remote or local +#: src/fu-util-common.c:2041 +msgid "Type" +msgstr "" + +#. TRANSLATORS: if we can get metadata from peer-to-peer clients +#: src/fu-util-common.c:2054 +msgid "P2P Metadata" +msgstr "" + +#. TRANSLATORS: if we can get metadata from peer-to-peer clients +#: src/fu-util-common.c:2061 +msgid "P2P Firmware" +msgstr "" + +#. TRANSLATORS: the age of the metadata +#: src/fu-util-common.c:2073 +msgid "Age" +msgstr "" + +#. TRANSLATORS: how often we should refresh the metadata +#: src/fu-util-common.c:2079 +msgid "Refresh Interval" +msgstr "" + +#. TRANSLATORS: the numeric priority +#: src/fu-util-common.c:2086 +msgid "Priority" +msgstr "" + +#. TRANSLATORS: remote filename base +#: src/fu-util-common.c:2089 +msgid "Username" +msgstr "" + +#. TRANSLATORS: remote filename base +#: src/fu-util-common.c:2094 +msgid "Password" +msgstr "" + +#. TRANSLATORS: filename of the local file +#: src/fu-util-common.c:2099 +msgid "Filename" +msgstr "" + +#. TRANSLATORS: filename of the local file +#: src/fu-util-common.c:2104 +msgid "Filename Signature" +msgstr "" + +#. TRANSLATORS: full path of the remote.conf file +#: src/fu-util-common.c:2109 +msgid "Filename Source" +msgstr "" + +#. TRANSLATORS: remote URI +#: src/fu-util-common.c:2114 +msgid "Metadata URI" +msgstr "" + +#. TRANSLATORS: remote URI +#: src/fu-util-common.c:2119 +msgid "Metadata Signature" +msgstr "" + +#. TRANSLATORS: remote URI +#: src/fu-util-common.c:2124 +msgid "Firmware Base URI" +msgstr "" + +#. TRANSLATORS: URI to send success/failure reports +#: src/fu-util-common.c:2129 +msgid "Report URI" +msgstr "" + +#. TRANSLATORS: Boolean value to automatically send reports +#: src/fu-util-common.c:2134 +msgid "Automatic Reporting" +msgstr "" + +#. TRANSLATORS: warning message shown after update has been scheduled +#: src/fu-util-common.c:2148 +msgid "" +"The update will continue when the device USB cable has been unplugged and " +"then re-inserted." +msgstr "" + +#. TRANSLATORS: warning message shown after update has been scheduled +#: src/fu-util-common.c:2153 +msgid "The update will continue when the device USB cable has been unplugged." +msgstr "" + +#. TRANSLATORS: warning message shown after update has been scheduled +#: src/fu-util-common.c:2158 +msgid "" +"The update will continue when the device USB cable has been re-inserted." +msgstr "" + +#. TRANSLATORS: warning message +#: src/fu-util-common.c:2163 +msgid "Press unlock on the device to continue the update process." +msgstr "" + +#. TRANSLATORS: warning message shown after update has been scheduled +#: src/fu-util-common.c:2167 +msgid "" +"Do not turn off your computer or remove the AC adaptor while the update is " +"in progress." +msgstr "" + +#. TRANSLATORS: message shown after device has been marked for emulation +#: src/fu-util-common.c:2172 +msgid "Unplug and replug the device to continue the update process." +msgstr "" + +#. TRANSLATORS: warning message +#: src/fu-util-common.c:2176 +msgid "" +"The update will continue when the device power cable has been removed and re-" +"inserted." +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2188 +msgid "Valid" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2192 +msgid "Invalid" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2204 +msgid "Locked" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2208 +msgid "Unlocked" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2212 +msgid "Encrypted" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2216 +msgid "Unencrypted" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2220 +msgid "Tainted" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2224 +msgid "Untainted" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2228 +msgid "Found" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2232 +msgid "Not found" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2236 +msgid "Supported" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2240 +msgid "Not supported" +msgstr "" + +#. TRANSLATORS: Suffix: the HSI result +#: src/fu-util-common.c:2258 +msgid "OK" +msgstr "" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +#: src/fu-util-common.c:2312 +msgid "(obsoleted)" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2330 +msgid "IOMMU device protection enabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2335 +msgid "IOMMU device protection disabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2354 +msgid "Kernel is no longer tainted" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2359 +msgid "Kernel is tainted" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2365 +msgid "Kernel lockdown disabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2370 +msgid "Kernel lockdown enabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2376 +msgid "Pre-boot DMA protection is disabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2381 +msgid "Pre-boot DMA protection is enabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2387 +msgid "Secure Boot disabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2392 +msgid "Secure Boot enabled" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2398 +msgid "All TPM PCRs are valid" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2403 +msgid "A TPM PCR is now an invalid value" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2408 +msgid "All TPM PCRs are now valid" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2414 +msgid "TPM PCR0 reconstruction is invalid" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2419 +msgid "TPM PCR0 reconstruction is now valid" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2425 +msgid "UEFI memory protection enabled but not locked" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2430 +msgid "UEFI memory protection enabled and locked" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2435 +msgid "UEFI memory protection is now locked" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2440 +msgid "UEFI memory protection is now unlocked" +msgstr "" + +#. TRANSLATORS: HSI event title +#: src/fu-util-common.c:2445 +msgid "The UEFI certificate store is now up to date" +msgstr "" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS region". +#. %2 refers to a result value, e.g. "Invalid" +#: src/fu-util-common.c:2470 +#, c-format +msgid "%s disappeared: %s" +msgstr "" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#: src/fu-util-common.c:2482 +#, c-format +msgid "%s appeared: %s" +msgstr "" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#: src/fu-util-common.c:2492 +#, c-format +msgid "%s changed: %s → %s" +msgstr "" + +#. TRANSLATORS: title for host security events +#: src/fu-util-common.c:2537 +msgid "Host Security Events" +msgstr "" + +#. TRANSLATORS: now list devices with unfixed high-priority issues +#: src/fu-util-common.c:2565 +msgid "There are devices with issues:" +msgstr "" + +#. TRANSLATORS: this is the HSI suffix +#: src/fu-util-common.c:2633 +msgid "Runtime Suffix" +msgstr "" + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +#: src/fu-util-common.c:2655 +msgid "This system has a low HSI security level." +msgstr "" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +#: src/fu-util-common.c:2663 +msgid "This system has HSI runtime issues." +msgstr "" + +#. TRANSLATORS: this is more background on a security measurement problem +#: src/fu-util-common.c:2672 +msgid "The TPM PCR0 differs from reconstruction." +msgstr "" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#: src/fu-util-common.c:2716 +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "" + +#. TRANSLATORS: %1 is the device vendor name +#: src/fu-util-common.c:2723 +#, c-format +msgid "" +"Your hardware may be damaged using this firmware, and installing this " +"release may void any warranty with %s." +msgstr "" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#: src/fu-util-common.c:2741 +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "" + +#. TRANSLATORS: should the branch be changed +#: src/fu-util-common.c:2750 +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +#: src/fu-util-common.c:2774 +msgid "" +"Some of the platform secrets may be invalidated when updating this firmware." +msgstr "" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +#: src/fu-util-common.c:2779 +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#: src/fu-util-common.c:2783 +#, c-format +msgid "See %s for more details." +msgstr "" + +#. TRANSLATORS: title text, shown as a warning +#: src/fu-util-common.c:2786 +msgid "Full Disk Encryption Detected" +msgstr "" + +#. TRANSLATORS: unsupported build of the package +#: src/fu-util-common.c:2810 +msgid "This package has not been validated, it may not work properly." +msgstr "" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +#: src/fu-util-common.c:2832 +msgid "Enable new remote?" +msgstr "" + +#. TRANSLATORS: should the remote still be enabled +#: src/fu-util-common.c:2838 +msgid "Agree and enable the remote?" +msgstr "" diff -Nru fwupd-2.0.8/po/gl.po fwupd-2.0.20/po/gl.po --- fwupd-2.0.8/po/gl.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/gl.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,23 +4,27 @@ # # Translators: # Fran Diéguez , 2020 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Galician (http://app.transifex.com/freedesktop/fwupd/language/gl/)\n" +"PO-Revision-Date: 2025-10-24 10:08+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Galician \n" +"Language: gl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: gl\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format msgid "%.0f minute remaining" msgid_plural "%.0f minutes remaining" -msgstr[0] " Falta %.0f minuto" -msgstr[1] " Faltan %.0f minutos" +msgstr[0] "Falta %.0f minuto" +msgstr[1] "Faltan %.0f minutos" #. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT #, c-format @@ -166,7 +170,7 @@ #. TRANSLATORS: this is when a device has been updated msgid "Device changed:" -msgstr "Dispositivo cambiado" +msgstr "Dispositivo cambiado:" #. TRANSLATORS: this is when a device is hotplugged msgid "Device removed:" @@ -200,7 +204,7 @@ #. TRANSLATORS: %1 is a device name #, c-format msgid "Downgrading %s…" -msgstr "Desactualizando %s" +msgstr "Desactualizando %s…" #. TRANSLATORS: downloading from a remote server msgid "Downloading…" @@ -208,7 +212,7 @@ #. TRANSLATORS: command description msgid "Dump SMBIOS data from a file" -msgstr "Volcar os datos da SMBIOS desde un ficheiro " +msgstr "Volcar os datos da SMBIOS desde un ficheiro" #. TRANSLATORS: Turn on the remote msgid "Enable this remote?" @@ -328,10 +332,6 @@ msgid "Gets the host security attributes" msgstr "Obtén os atributos de seguranza do equipo" -#. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Obtén a lista de actualizacións para o hardware conectado" - #. TRANSLATORS: this is a string like 'HSI:2-U' msgid "Host Security ID:" msgstr "ID de seguranza do equipo:" diff -Nru fwupd-2.0.8/po/he.po fwupd-2.0.20/po/he.po --- fwupd-2.0.8/po/he.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/he.po 2026-02-26 11:36:18.000000000 +0000 @@ -6,18 +6,22 @@ # dhead666 , 2015 # gk , 2015 # 63f334ffc0709ba0fc2361b80bf3c0f0_00ffd1e , 2021 -# Yaron Shahrabani , 2021,2023-2024 +# Yaron Shahrabani , 2021,2023-2024, 2025. # Yosef Or Boczko , 2023 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Hebrew (http://app.transifex.com/freedesktop/fwupd/language/he/)\n" +"PO-Revision-Date: 2025-12-02 23:01+0000\n" +"Last-Translator: Yaron Shahrabani \n" +"Language-Team: Hebrew \n" +"Language: he\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: he\n" -"Plural-Forms: nplurals=3; plural=(n == 1 && n % 1 == 0) ? 0 : (n == 2 && n % 1 == 0) ? 1: 2;\n" +"Plural-Forms: nplurals=3; plural=(n == 1) ? 0 : ((n == 2) ? 1 : 2);\n" +"X-Generator: Weblate 5.15-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -235,8 +239,8 @@ msgid "Approved firmware:" msgid_plural "Approved firmware:" msgstr[0] "קושחה מאומתת:" -msgstr[1] "קושחה מאומתת:" -msgstr[2] "קושחה מאומתת:" +msgstr[1] "קושחות מאומתות:" +msgstr[2] "קושחות מאומתות:" #. TRANSLATORS: command description msgid "Attach to firmware mode" @@ -678,7 +682,7 @@ #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" -msgstr " פענוח הארגומנטים נכשל" +msgstr "פענוח הארגומנטים נכשל" #. TRANSLATORS: failed to read measurements file msgid "Failed to parse file" @@ -790,10 +794,6 @@ msgstr "מקבל את רשימת הקושחות החסומות" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "מקבל את רשימת העדכונים לחומרה שמחוברת" - -#. TRANSLATORS: command description msgid "Gets the results from the last update" msgstr "מקבל את התוצאות מהעדכון האחרון" @@ -1063,10 +1063,6 @@ msgstr "מותר עדכוני גרסאות בלבד" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "פלט במבנה JSON" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "מעקף נתיב ה־ESP כברירת מחדל" @@ -1334,11 +1330,6 @@ msgid "Successfully activated all devices" msgstr "כל ההתקנים הופעלו בהצלחה" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "נתוני על חדשים התקבלו בהצלחה:" - #. TRANSLATORS: success message msgid "Successfully enabled remote" msgstr "המרוחק הופעל בהצלחה" @@ -1416,7 +1407,7 @@ #. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name #, c-format msgid "The firmware from %s is not supplied by %s, the hardware vendor." -msgstr "הקושחה הזאת מבית %s אינה מסופקת על ידי %s, יצרן החומרה" +msgstr "הקושחה הזאת מבית %s אינה מסופקת על ידי %s, יצרן החומרה." #. TRANSLATORS: try to help msgid "The system clock has not been set correctly and downloading files may fail." diff -Nru fwupd-2.0.8/po/hi.po fwupd-2.0.20/po/hi.po --- fwupd-2.0.8/po/hi.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/hi.po 2026-02-26 11:36:18.000000000 +0000 @@ -5,16 +5,20 @@ # Translators: # Prashant Gupta , 2015 # Scrambled777 , 2024 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Hindi (http://app.transifex.com/freedesktop/fwupd/language/hi/)\n" +"PO-Revision-Date: 2025-10-24 10:10+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Hindi \n" +"Language: hi\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: hi\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -35,7 +39,7 @@ msgstr "%s बैटरी अद्यतन" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "%s CPU माइक्रोकोड अद्यतन" @@ -1112,10 +1116,6 @@ msgstr "फाइलनाम प्रमाणपत्र निजी-कुंजी" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "फाइलनाम उपकरण-ID" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" msgstr "फाइलनाम ऑफ़सेट डेटा [फर्मवेयर-प्रकार]" @@ -1239,7 +1239,7 @@ #. TRANSLATORS: remote URI msgid "Firmware Base URI" -msgstr "फर्मवेयर बेस URI" +msgstr "फर्मवेयर बेस URI" #. TRANSLATORS: program summary msgid "Firmware Update D-Bus Service" @@ -1376,7 +1376,7 @@ #. TRANSLATORS: command description msgid "Gets details about a firmware file" -msgstr "फर्मवेयर फाइल की अधिक जानकारी प्राप्त करें " +msgstr "फर्मवेयर फाइल की अधिक जानकारी प्राप्त करें" #. TRANSLATORS: command description msgid "Gets the configured remotes" @@ -1395,10 +1395,6 @@ msgstr "अवरुद्ध फर्मवेयर की सूची प्राप्त करता है" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "जुड़े हार्डवेयर के लिए अद्यतनों की सूची प्राप्त करता है" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "उपकरण के लिए रिलीज़ प्राप्त करता है" @@ -1423,7 +1419,6 @@ msgstr "होस्ट की सुरक्षा घटनाएं" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "होस्ट सुरक्षा ID (HSI) समर्थित नहीं है" @@ -1989,10 +1984,6 @@ msgstr "केवल संस्करण उन्नयन की अनुमति है" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "JSON प्रारूप में आउटपुट" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "तयशुदा ESP पथ अधिलेखित करें" @@ -2490,11 +2481,6 @@ msgid "Successfully disabled remote" msgstr "रिमोट सफलतापूर्वक अक्षम किया गया" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "नया मेटाडेटा सफलतापूर्वक डाउनलोड किया गया: " - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "रिमोट को सफलतापूर्वक सक्षम और ताजा किया गया" diff -Nru fwupd-2.0.8/po/hr.po fwupd-2.0.20/po/hr.po --- fwupd-2.0.8/po/hr.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/hr.po 2026-02-26 11:36:18.000000000 +0000 @@ -5,20 +5,24 @@ # Translators: # FIRST AUTHOR , 2016 # gogo , 2016 -# Milo Ivir , 2020-2021 -# Milo Ivir , 2023 +# Milo Ivir , 2020-2021, 2025. +# Milo Ivir , 2021,2023,2025 # Milo Ivir , 2021 # gogo , 2016-2023 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Croatian (http://app.transifex.com/freedesktop/fwupd/language/hr/)\n" +"PO-Revision-Date: 2025-10-24 10:13+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Croatian \n" +"Language: hr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: hr\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" +"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" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -40,7 +44,7 @@ msgstr "%s nadopuna baterije" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "%s nadopuna CPU mikrokôda" @@ -127,6 +131,21 @@ msgid "%s Graphics Tablet Update" msgstr "%s nadopuna grafičkog tableta" +#. TRANSLATORS: two miniature speakers attached to your ears +#, c-format +msgid "%s Headphones Update" +msgstr "%s nadopuna slušalica" + +#. TRANSLATORS: headphones with an integrated microphone +#, c-format +msgid "%s Headset Update" +msgstr "%s nadopuna slušalica s mikrofonom" + +#. TRANSLATORS: an input device used by gamers, e.g. a joystick +#, c-format +msgid "%s Input Controller Update" +msgstr "%s nadopuna kontrolera ulaza" + #. TRANSLATORS: Keyboard refers to an input device for typing #, c-format msgid "%s Keyboard Update" @@ -234,6 +253,12 @@ msgid "%s is not currently updatable" msgstr "%s trenutno nije nadopunjiv" +#. TRANSLATORS: %1 is a device name, e.g. "ThinkPad Universal +#. * ThunderBolt 4 Dock" and %2 is "fwupdmgr activate" +#, c-format +msgid "%s is pending activation; use %s to complete the update." +msgstr "%s čeka aktivaciju; Koristi %s za dovršavanje nadopune." + #. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT #, c-format msgid "%s manufacturing mode" @@ -290,6 +315,24 @@ msgstr[1] "%u uređaja nema najbolje poznato podešavanje." msgstr[2] "%u uređaja nema najbolje poznato podešavanje." +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#, c-format +msgid "%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "%u devices are supported in the enabled remotes (an update has been published)" +msgstr[0] "%u podržan uređaj u omogućenim daljinskim uređajim (objavljena je nadopuna)" +msgstr[1] "%u podržana uređaja u omogućenim daljinskim uređajim (objavljena je nadopuna)" +msgstr[2] "%u podržanih uređaja u omogućenim daljinskim uređajim (objavljena je nadopuna)" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "%u uređaj se može nadopuniti" +msgstr[1] "%u uređaja se mogu nadopuniti" +msgstr[2] "%u uređaja se mogu nadopuniti" + #. TRANSLATORS: duration in minutes #, c-format msgid "%u hour" @@ -314,6 +357,14 @@ msgstr[1] "%u sekunde" msgstr[2] "%u sekundi" +#. TRANSLATORS: device tests can be specific to a CPU type +#, c-format +msgid "%u test was skipped" +msgid_plural "%u tests were skipped" +msgstr[0] "%u test je preskočen" +msgstr[1] "%u testa su preskočena" +msgstr[2] "%u testova su preskočeni" + #. TRANSLATORS: first percentage is current value, 2nd percentage is the #. * lowest limit the firmware update is allowed for the update to happen #, c-format @@ -330,11 +381,11 @@ #. TRANSLATORS: Title: if hardware enforces control of SPI replays msgid "AMD Firmware Replay Protection" -msgstr "AMD firmver zaštita ponavljanja" +msgstr "Zaštita ponavljanja AMD firmvera" #. TRANSLATORS: Title: if hardware enforces control of SPI writes msgid "AMD Firmware Write Protection" -msgstr "AMD firmver zaštita zpisivanja" +msgstr "Zaštita zpisivanja AMD firmvera" #. TRANSLATORS: Title: if firmware enforces rollback protection msgid "AMD Secure Processor Rollback Protection" @@ -363,6 +414,10 @@ msgid "Activating firmware update for" msgstr "Aktivacija nadopune firmvera za" +#. TRANSLATORS: command description +msgid "Adds devices to watch for future emulation" +msgstr "Dodaje uređaje za praćenje za buduću emulaciju" + #. TRANSLATORS: the age of the metadata msgid "Age" msgstr "Dob" @@ -384,6 +439,10 @@ msgid "All TPM PCRs are valid" msgstr "Svi TPM PCR-ovi su valjani" +#. TRANSLATORS: an application is preventing system updates +msgid "All devices are prevented from update by system inhibit" +msgstr "Blokada sustava sprečava nadopune svih uređaja" + #. TRANSLATORS: on some systems certain devices have to have matching #. versions, #. * e.g. the EFI driver for a given network card cannot be different @@ -400,7 +459,11 @@ #. TRANSLATORS: command line option msgid "Allow switching firmware branch" -msgstr "Dopusti prebacivanje između firmver grana" +msgstr "Dopusti prebacivanje između ogranaka firmvera" + +#. TRANSLATORS: error message +msgid "Already exists, and no --force specified" +msgstr "Već postoji, i nijedna --force nije navedena" #. TRANSLATORS: is not the main firmware stream msgid "Alternate branch" @@ -439,12 +502,16 @@ msgid "Approved firmware:" msgid_plural "Approved firmware:" msgstr[0] "Odobreni firmver:" -msgstr[1] "Odobreni firmveri:" -msgstr[2] "Odobreni firmveri:" +msgstr[1] "Odobreni firmver:" +msgstr[2] "Odobreni firmver:" + +#. TRANSLATORS: command description +msgid "Asks the daemon to quit" +msgstr "Zatraži da pozadinski program prekine" #. TRANSLATORS: command description msgid "Attach to firmware mode" -msgstr "Prebaci u način firmvera" +msgstr "Prebaci u firmver način rada" #. TRANSLATORS: waiting for user to authenticate msgid "Authenticating…" @@ -463,6 +530,18 @@ msgstr "Potrebna je ovjera za vraćanje starije inačicu firmvera na ovom računalu" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to enable emulation data collection" +msgstr "Potrebna je ovjera za omogućavanje prikupljanja podataka emulacije" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to fix a host security issue" +msgstr "Potrebna je ovjera za ispravljanje sigurnosnog problema glavnog računala" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to load hardware emulation data" +msgstr "Potrebna je ovjera za učitavanje podataka o hardverskoj emulaciji" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify BIOS settings" msgstr "Potrebna je ovjera za promjenu BIOS postavki" @@ -479,6 +558,14 @@ msgstr "Potrebna je ovjera za čitanje BIOS postavki" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to reset daemon configuration to defaults" +msgstr "Potrebna je ovjera za vraćanje konfiguracije pozadinskom programa na zadane postavke" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to save hardware emulation data" +msgstr "Potrebna je ovjera za spremanje podataka o hardverskoj emulaciji" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to set the list of approved firmware" msgstr "Potrebna je ovjera za postavljanje popisa odobrenih firmvera" @@ -487,10 +574,18 @@ msgstr "Potrebna je ovjera za potpisivanje podataka vjerodajnicom klijenta" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to stop the firmware update service" +msgstr "Potrebna je ovjera za zaustavljanje usluge nadopunjavanja firmvera" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to switch to the new firmware version" msgstr "Potrebna je ovjera za prebacivanje na novu inačicu firmvera" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to undo the fix for a host security issue" +msgstr "Potrebna je ovjera za poništavanje ispravka sigurnosnog problema za glavno računalo" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "Potrebna je ovjera za otključavanje uređaja" @@ -510,14 +605,27 @@ msgid "Automatic Reporting" msgstr "Automatsko izvještavanje" +#. TRANSLATORS: we can auto-uninhibit after a timeout +#, c-format +msgid "Automatically uninhibiting in %ums…" +msgstr "Automatsko uklanjanje blokade za %u ms …" + #. TRANSLATORS: can we JFDI? msgid "Automatically upload every time?" msgstr "Automatski pošalji svaki put?" +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +msgid "BIOS Firmware Updates" +msgstr "Nadopune BIOS firmvera" + #. TRANSLATORS: Title: if firmware enforces rollback protection msgid "BIOS Rollback Protection" msgstr "Zaštita od vraćanja na stariju inačicu BIOS-a" +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +msgid "BIOS firmware updates" +msgstr "Nadopune BIOS firmvera" + #. TRANSLATORS: Title: if firmware enforces rollback protection msgid "BIOS rollback protection" msgstr "Zaštita od vraćanja na stariju inačicu BIOS-a" @@ -563,9 +671,31 @@ msgstr "Ogranak" #. TRANSLATORS: command description +msgid "Build a cabinet archive from a firmware blob and XML metadata" +msgstr "Izradi CAB arhivu od firmware bloba i XML metapodataka" + +#. TRANSLATORS: command description msgid "Build a firmware file" msgstr "Izgradi datoteku firmvera" +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * Utilized by OS means the distribution enabled it +msgid "CET OS Support" +msgstr "Podrška za CET OS" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "CET Platform" +msgstr "CET platforma" + +#. TRANSLATORS: longer description +msgid "CPU Microcode must be updated to mitigate against various information-disclosure security issues." +msgstr "Mikrokod CPU-a se mora nadopuniti kako bi se smanjio rizik od različitih sigurnosnih problema s otkrivanjem informacija." + +#. TRANSLATORS: we can save all device enumeration events for emulation +msgid "Can tag for emulation" +msgstr "Može se označiti za emulaciju" + #. TRANSLATORS: this is to abort the interactive prompt msgid "Cancel" msgstr "Odustani" @@ -584,6 +714,10 @@ msgstr "Promijenjeno" #. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "Provjeri čekaju li neki uređaji ponovno pokretanje za dovršavanje nadopune" + +#. TRANSLATORS: command description msgid "Checks cryptographic hash matches firmware" msgstr "Provjeri podudarnost kriptografske jedinstvene vrijednosti firmvera" @@ -595,7 +729,7 @@ #. TRANSLATORS: get interactive prompt, where branch is the #. * supplier of the firmware, e.g. "non-free" or "free" msgid "Choose branch" -msgstr "Odaberi granu" +msgstr "Odaberi ogranak" #. TRANSLATORS: get interactive prompt msgid "Choose device" @@ -621,6 +755,10 @@ msgid "Community supported" msgstr "Podržano od strane zajednice" +#. TRANSLATORS: command description +msgid "Compares two versions for equality" +msgstr "Uspoređuje dvije inačice radi jednakosti" + #. TRANSLATORS: title prefix for the BIOS settings dialog msgid "Configuration Change Suggested" msgstr "Predložena promjena podešavanja" @@ -629,6 +767,14 @@ msgid "Configuration is only readable by the system administrator" msgstr "Podešavanje može čitati samo administrator sustava" +#. TRANSLATORS: longer description +msgid "Control-Flow Enforcement Technology detects and prevents certain methods for running malicious software on the device." +msgstr "Tehnologija „Control-Flow Enforcement“ otkriva i sprječava određene metode za pokretanje zlonamjernog softvera na uređaju." + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Control-flow Enforcement Technology" +msgstr "CET (Tehnologija provedbe kontrole toka)" + #. TRANSLATORS: command description msgid "Convert a firmware file" msgstr "Pretvori datoteku firmvera" @@ -692,10 +838,18 @@ msgid "Device ID" msgstr "ID uređaja" +#. TRANSLATORS: description of the device requests +msgid "Device Requests" +msgstr "Zahtjevi uređaja" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device added:" msgstr "Uređaj dodan:" +#. TRANSLATORS: the device is already connected +msgid "Device already exists" +msgstr "Uređaj već postoji" + #. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse msgid "Device battery power is too low" msgstr "Energija baterije uređaja je preniska" @@ -703,7 +857,7 @@ #. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse #, c-format msgid "Device battery power is too low (%u%%, requires %u%%)" -msgstr "energija baterije uređaja je preniska (%u%%, zahtijeva %u%%)" +msgstr "Energija baterije uređaja je preniska (%u%%, zahtijeva %u%%)" #. TRANSLATORS: Device supports a safety mechanism for flashing msgid "Device can recover flash failures" @@ -721,10 +875,19 @@ msgid "Device is emulated" msgstr "Uređaj je emuliran" +#. TRANSLATORS: device cannot be interrupted, for instance taking a phone call +msgid "Device is in use" +msgstr "Uređaj se koristi" + #. TRANSLATORS: Is locked and can be unlocked msgid "Device is locked" msgstr "Uređaj je zaključan" +#. TRANSLATORS: we have two ways of communicating with the device, so we hide +#. one +msgid "Device is lower priority than an equivalent device" +msgstr "Uređaj ima niži prioritet od ekvivalentnog uređaja" + #. TRANSLATORS: the device cannot update from A->C and has to go A->B->C msgid "Device is required to install all provided releases" msgstr "Uređaj je potreban za instalaciju svih dostupnih izdanja" @@ -747,6 +910,10 @@ msgid "Device is waiting for the update to be applied" msgstr "Uređaj čeka na primjenu nadopune" +#. TRANSLATORS: success, so say thank you to the user +msgid "Device list uploaded successfully, thanks!" +msgstr "Popis uređaja je uspješno prenesen, hvala!" + #. TRANSLATORS: this is when a device is hotplugged msgid "Device removed:" msgstr "Uređaj uklonjen:" @@ -755,6 +922,10 @@ msgid "Device requires AC power to be connected" msgstr "Uređaj zahtijeva vanjski izvor energije" +#. TRANSLATORS: device does not have a display connected +msgid "Device requires a display to be plugged in" +msgstr "Uređaj zahtijeva priključivanje ekrana" + #. TRANSLATORS: The device cannot be updated due to missing vendor's license." msgid "Device requires a software license to update" msgstr "Uređaj zahtijeva softversku licencu za nadopunu" @@ -791,6 +962,11 @@ msgid "Devices that were not updated correctly:" msgstr "Uređaji koji nisu ispravno nadopunjeni:" +#. TRANSLATORS: message letting the user there is an update +#. * waiting, but there is a reason it cannot be deployed +msgid "Devices with firmware updates that need user action: " +msgstr "Uređaji s nadopunama firmvera koje trebaju radnju korisnika: " + #. TRANSLATORS: message letting the user know no device #. * upgrade available due to missing on LVFS #. TRANSLATORS: message letting the user know no @@ -824,6 +1000,10 @@ msgid "Disables a given remote" msgstr "Onemogućuje zadane udaljene lokacije" +#. TRANSLATORS: command description +msgid "Disables virtual testing devices" +msgstr "Onemogućuje virtualne uređaje za testiranje" + #. TRANSLATORS: the OS the release was tested on msgid "Distribution" msgstr "Distribucija" @@ -861,8 +1041,12 @@ msgstr "Ne upitaj za uređaje" #. TRANSLATORS: command line option +msgid "Do not prompt to fix security issues" +msgstr "Ne upitaj ispravljanje sigurnosne probleme" + +#. TRANSLATORS: command line option msgid "Do not search the firmware when parsing" -msgstr "Ne pretražuj firmver pri obradi" +msgstr "Ne pretražuj firmver tijekom obrade" #. TRANSLATORS: warning message shown after update has been scheduled msgid "Do not turn off your computer or remove the AC adaptor while the update is in progress." @@ -929,7 +1113,7 @@ #. TRANSLATORS: longer description msgid "Each system should have tests to ensure firmware security." -msgstr "Svaki uređaj bi se trebao testirati firmver, kako bi se osigurala sigurnost firmvera." +msgstr "Svaki sustav bi trebao imati testove kako bi se osigurala sigurnost firmvera." #. TRANSLATORS: command description msgid "Emulate a device using a JSON manifest" @@ -946,13 +1130,16 @@ msgid "Enable" msgstr "Omogući" +msgid "Enable emulation data collection" +msgstr "Omogući prikupljanja podataka emulacije" + #. TRANSLATORS: a remote here is like a 'repo' or software source msgid "Enable new remote?" -msgstr "Omogući udaljenu lokaciju?" +msgstr "Omogućiti udaljenu lokaciju?" #. TRANSLATORS: Turn on the remote msgid "Enable this remote?" -msgstr "Omogući ovu udaljenu lokaciju?" +msgstr "Omogućiti ovu udaljenu lokaciju?" #. TRANSLATORS: if the remote is enabled #. TRANSLATORS: Suffix: the HSI result @@ -967,6 +1154,14 @@ msgid "Enables a given remote" msgstr "Omogućuje zadane udaljene lokacije" +#. TRANSLATORS: command description +msgid "Enables virtual testing devices" +msgstr "Omogućuje virtualne uređaje za testiranje" + +#. TRANSLATORS: longer description +msgid "Enabling firmware updates for the BIOS allows fixing security issues." +msgstr "Omogućavanje nadopuna firmvera za BIOS omogućuje ispravljanje sigurnosnih problema." + msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." msgstr "Omogućujete ovu funkcionalnost na vlastiti rizik, što znači da morate kontaktirati svog izvornog proizvođača opreme u vezi problema uzrokovanih tim nadopunama. Samo probleme sa samim postupkom nadopune treba prijaviti na $OS_RELEASE:BUG_REPORT_URL$." @@ -1014,6 +1209,10 @@ msgstr "Izvezi strukturu datoteke frimvera u XML" #. TRANSLATORS: command description +msgid "Export firmware history for manual upload" +msgstr "Izvezi povijesti firmvera za ručni prijenos" + +#. TRANSLATORS: command description msgid "Extract a firmware blob to images" msgstr "Izdvoji blob firmvera u slike" @@ -1034,12 +1233,12 @@ msgstr "PRIVATNI-KLJUČ NAZIV DATOTEKE VJERODAJNICE" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "NAZIV DATOTEKE ID-UREĐAJA" +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "NAZIV DATOTEKE: ID UREĐAJA [VERZIJA]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" -msgstr "NAZIV DATOTEKE POMAKA PODATAKA [FIRMVER-VRSTA]" +msgstr "NAZIV DATOTEKE POMAKA PODATAKA [FIRMWARE-VRSTA]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME [DEVICE-ID|GUID]" @@ -1047,11 +1246,11 @@ #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME [FIRMWARE-TYPE]" -msgstr "NAZIV DATOTEKE [FIRMVER-VRSTA]" +msgstr "NAZIV DATOTEKE [FIRMWARE-VRSTA]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" -msgstr "NAZIV DATOTEKE-SRC NAZIV DATOTEKE-DST [FIRMVER-VRSTA-SRC] [FIRMVER-VRSTA-DST]" +msgstr "NAZIV DATOTEKE-SRC NAZIV DATOTEKE-DST [FIRMWARE-VRSTA-SRC] [FIRMWARE-VRSTA-DST]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" @@ -1094,10 +1293,24 @@ msgid "Failed to parse file" msgstr "Neuspjela obrada datoteke" +#. TRANSLATORS: the user didn't read the man page, %1 is '--filter' +#. TRANSLATORS: the user didn't read the man page, +#. * %1 is '--filter-release' +#. TRANSLATORS: the user didn't read the man page, %1 is '--filter' +#. TRANSLATORS: the user didn't read the man page, +#. * %1 is '--filter-release' +#, c-format +msgid "Failed to parse flags for %s" +msgstr "Neuspjela obrada oznaka za %s" + #. TRANSLATORS: could not parse file msgid "Failed to parse local dbx" msgstr "Neuspjela obrada lokalnog dbx-a" +#. TRANSLATORS: a feature is something like "can show an image" +msgid "Failed to set front-end features" +msgstr "Neuspjelo postavljanje značajki sučelja" + #. TRANSLATORS: something with a blocked hash exists #. * in the users ESP -- which would be bad! msgid "Failed to validate ESP contents" @@ -1127,6 +1340,10 @@ msgid "Filter with a set of device flags using a ~ prefix to exclude, e.g. 'internal,~needs-reboot'" msgstr "Filtar sa skupom oznaka uređaja koji koristi ~ prefiks za izuzimanje, npr. 'internal,~needs-reboot'" +#. TRANSLATORS: command description +msgid "Finds firmware releases from the metadata" +msgstr "Pronalazi izdanja firmvera iz metapodataka" + #. TRANSLATORS: Title: if we can verify the firmware checksums msgid "Firmware Attestation" msgstr "Frimver provjera" @@ -1153,7 +1370,7 @@ #. TRANSLATORS: remote URI msgid "Firmware Base URI" -msgstr "Osnovni URI frimvera" +msgstr "Osnovni URI firmvera" #. TRANSLATORS: program summary msgid "Firmware Update D-Bus Service" @@ -1161,11 +1378,11 @@ #. TRANSLATORS: program name msgid "Firmware Update Daemon" -msgstr "Pozadinski program nadopune firmvera" +msgstr "Pozadinski program nadopune firmwera" #. TRANSLATORS: Title: if the fwupd plugins are all present and correct msgid "Firmware Updater Verification" -msgstr "Provjera firmver nadopunitelja" +msgstr "Provjera firmwera nadopunitelja" msgid "Firmware Updater Verification checks that software used for updating has not been tampered with." msgstr "Provjera firmver nadopunitelja provjerava je li softver za nadopunu neovlašteno mijenjan." @@ -1214,6 +1431,19 @@ msgid "Firmware updates" msgstr "Nadopune frimvera" +#. TRANSLATORS: user needs to run a command, %1 is 'fwupdmgr unlock' +#, c-format +msgid "Firmware updates disabled; run '%s' to enable" +msgstr "Nadopuna firmvera onemogućena; Pokreni „%s“ za omogućivanje" + +#. TRANSLATORS: we've fixed a security problem on the machine +msgid "Fix reverted successfully" +msgstr "Ispravak je uspješno vraćen" + +#. TRANSLATORS: we've fixed a security problem on the machine +msgid "Fixed successfully" +msgstr "Uspješno ispravljeno" + #. TRANSLATORS: description of plugin state, e.g. disabled msgid "Flags" msgstr "Oznake" @@ -1247,8 +1477,8 @@ msgid "GUID" msgid_plural "GUIDs" msgstr[0] "GUID" -msgstr[1] "GUID" -msgstr[2] "GUID" +msgstr[1] "GUID-ovi" +msgstr[2] "GUID-ovi" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgctxt "command-argument" @@ -1256,55 +1486,59 @@ msgstr "GUID" msgid "Get BIOS settings" -msgstr "Prikaži BIOS postavke" +msgstr "Dohvati BIOS postavke" #. TRANSLATORS: command description msgid "Get all device flags supported by fwupd" -msgstr "Prikaži sve oznake uređaja koje podržava fwupd" +msgstr "Dohvati sve oznake uređaja koje fwupd podržava" #. TRANSLATORS: command description msgid "Get all devices that support firmware updates" -msgstr "Prikaži sve uređaje koji podržavaju nadopunu firmvera" +msgstr "Dohvati sve uređaje koji podržavaju nadopunu firmvera" #. TRANSLATORS: command description msgid "Get all enabled plugins registered with the system" -msgstr "Prikaži sve omogućene priključke registrirane sa sustavom" +msgstr "Dohvati sve omogućene priključke registrirane sa sustavom" + +#. TRANSLATORS: command description +msgid "Get all known version formats" +msgstr "Dohvati sve poznate formate inačica" #. TRANSLATORS: command description msgid "Get device report metadata" -msgstr "Preuzmi metapodatke izvještaja uređaja" +msgstr "Dohvati metapodatke izvještaja uređaja" #. TRANSLATORS: command description msgid "Gets details about a firmware file" -msgstr "Prikaži pojedinosti datoteke firmvera" +msgstr "Dohvaća pojedinosti datoteke firmvera" #. TRANSLATORS: command description msgid "Gets the configured remotes" -msgstr "Prikazuje udaljena podešavanja" +msgstr "Dohvaća udaljena podešavanja" #. TRANSLATORS: command description msgid "Gets the host security attributes" -msgstr "Prikaži sigurnosne značajke poslužitelja" +msgstr "Dohvaća sigurnosne značajke poslužitelja" #. TRANSLATORS: firmware approved by the admin msgid "Gets the list of approved firmware" -msgstr "Preuzima popis odobrenih firmvera" +msgstr "Dohvaća popis odobrenih firmvera" #. TRANSLATORS: command description msgid "Gets the list of blocked firmware" -msgstr "Preuzima popis blokiranih firmvera" +msgstr "Dohvaća popis blokiranih firmvera" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Prikaži popis nadopuna za povezani hardver" +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "Dohvaća popis nadopuna za sve navedene uređaje ili sve uređaje ako nisu navedeni" #. TRANSLATORS: command description msgid "Gets the releases for a device" -msgstr "Prikazuje izdanja za uređaj" +msgstr "Dohvaća izdanja za uređaj" #. TRANSLATORS: command description msgid "Gets the results from the last update" -msgstr "Prikaži rezultate posljednje nadopune" +msgstr "Dohvaća rezultate posljednje nadopune" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "HWIDS-FILE" @@ -1323,13 +1557,16 @@ msgstr "Događaji sigurnosti računala" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" -msgstr "ID sigurnosti sustava (HSI) nije podržan" +msgstr "ID sigurnosti računala (HSI) nije podržan" + +#. TRANSLATORS: success, so say thank you to the user +msgid "Host Security ID attributes uploaded successfully, thanks!" +msgstr "Atributi ID-a sigurnosti računala su uspješno preneseni, hvala!" #. TRANSLATORS: this is a string like 'HSI:2-U' msgid "Host Security ID:" -msgstr "Sigurnosni ID poslužitelja:" +msgstr "ID sigurnosti računala:" #. TRANSLATORS: Title: #. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit @@ -1369,15 +1606,44 @@ msgid "Ignore firmware hardware mismatch failures" msgstr "Zanemari neuspjela poklapanja hardvera firmvera" +#. TRANSLATORS: command line option +msgid "Ignore non-critical firmware requirements" +msgstr "Zanemari nekritične zahtjeve firmvera" + #. TRANSLATORS: try to help msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" msgstr "Zanemaruju se stroge SSL provjere, kako bi se ovo ubuduće automatski izvezlo DISABLE_SSL_STRICT u svojem okruženju" +#. TRANSLATORS: show the user a generic image that can be themed +msgid "Image" +msgstr "Slika" + +#. TRANSLATORS: show the user a random image from the internet +msgid "Image (custom)" +msgstr "Slika (prilagođeno)" + +#. TRANSLATORS: the inhibit ID is a short string like dbus-123456 +#, c-format +msgid "Inhibit ID is %s." +msgstr "ID blokade je %s." + +#. TRANSLATORS: command description +msgid "Inhibit the system to prevent upgrades" +msgstr "Blokiraj sustav za sprečavanje nadogradnji" + #. TRANSLATORS: length of time the update takes to apply msgid "Install Duration" msgstr "Trajanje instalacije" #. TRANSLATORS: command description +msgid "Install a firmware file in cabinet format on this hardware" +msgstr "Instalirajte firmware file u CAB formatu na ovaj hardver" + +#. TRANSLATORS: command description +msgid "Install a specific firmware file on all devices that match" +msgstr "Instaliraj određenu datoteku firmvera na sve uređaje koji odgovaraju" + +#. TRANSLATORS: command description msgid "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" msgstr "Instaliraj određeni firmver na uređaj, svi mogući uređaji će biti instalirani jednom kada se CAB podudara" @@ -1399,6 +1665,12 @@ msgid "Install unsigned system firmware" msgstr "Instaliraj firmver nepotpisan sustavom" +#. TRANSLATORS: stay on one firmware version unless the new version is +#. explicitly +#. * specified +msgid "Installing a specific release is explicitly required" +msgstr "Instalacija određenog izdanja je izričito potrebna" + #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "Instalacija nadopune firmvera…" @@ -1466,6 +1738,14 @@ msgid "Intel BootGuard verified boot" msgstr "Intel BootGuard provjereno pokretanje" +#. TRANSLATORS: Title: GDS is where the CPU leaks information +msgid "Intel GDS Mitigation" +msgstr "Sigurnosna mjera protiv Intel GDS-a" + +#. TRANSLATORS: Title: GDS is where the CPU leaks information +msgid "Intel GDS mitigation" +msgstr "Sigurnosna mjera protiv Intel GDS-a" + #. TRANSLATORS: Title: MEI = Intel Management Engine msgid "Intel Management Engine Manufacturing Mode" msgstr "Način rada Intel pogona upravljanja" @@ -1498,6 +1778,31 @@ msgid "Invalid arguments" msgstr "Nevaljani argumenti" +#. TRANSLATORS: error message +msgid "Invalid arguments, expected GUID" +msgstr "Nevaljani argumenti, očekivan je GUID" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected INDEX KEY [VALUE]" +msgstr "Nevaljani argumenti, očekivano je INDEX KEY [VALUE]" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected INDEX NAME TARGET [MOUNTPOINT]" +msgstr "Nevaljani argumenti, očekivano je INDEX NAME TARGET [MOUNTPOINT]" + +#. TRANSLATOR: This is the error message for +#. * incorrect parameter +msgid "Invalid arguments, expected an AppStream ID" +msgstr "Nevaljani argumenti, očekivan je AppStream ID" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected at least ARCHIVE FIRMWARE METAINFO" +msgstr "Nevaljani argumenti, očekivano je barem ARCHIVE FIRMWARE METAINFO" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected base-16 integer" +msgstr "Nevaljani argumenti, očekivan je cijeli broj s bazom 16" + #. TRANSLATORS: version is older msgid "Is downgrade" msgstr "Je nadogradnja na stariju inačicu" @@ -1589,17 +1894,25 @@ msgid "Linux swap" msgstr "Linux swap" +#. TRANSLATORS: command description +msgid "List EFI variables with a specific GUID" +msgstr "Prikaži popis EFI varijabla s određenim GUID-om" + #. TRANSLATORS: command line option msgid "List entries in dbx" -msgstr "Prikaži unose u dbx-u" +msgstr "Prikaži popis unosa u dbx-u" + +#. TRANSLATORS: command description +msgid "List the available firmware GTypes" +msgstr "Prikaži popis dostupnih GTypes vrsta firmwara" #. TRANSLATORS: command description msgid "List the available firmware types" -msgstr "Prikaži dostupne vrste firmvera" +msgstr "Prikaži popis dostupnih vrsta firmvera" #. TRANSLATORS: command description msgid "Lists files on the ESP" -msgstr "Prikazuje datoteke na ESP-u" +msgstr "Prikazuje popis datoteke na ESP-u" #. TRANSLATORS: command description msgid "Load device emulation data" @@ -1665,9 +1978,17 @@ msgid "Medium" msgstr "Srednja" +#. TRANSLATORS: ask the user to do a simple task which should be translated +msgid "Message" +msgstr "Poruka" + +#. TRANSLATORS: ask the user a question, and it will not be translated +msgid "Message (custom)" +msgstr "Poruka (prilagođena)" + #. TRANSLATORS: remote URI msgid "Metadata Signature" -msgstr "Metapodaci potpisa" +msgstr "Potpis metapodataka" #. TRANSLATORS: remote URI msgid "Metadata URI" @@ -1677,6 +1998,12 @@ msgid "Metadata can be obtained from the Linux Vendor Firmware Service." msgstr "Metapodaci se mogu dobiti od Firmver Usluge Linux Proizvođača (LVFS)." +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. * refresh recently -- %1 is '--force' +#, c-format +msgid "Metadata is up to date; use %s to refresh again." +msgstr "Metapodaci su aktualni; koristi %s za ponovno osvježavanje." + #. TRANSLATORS: smallest version number installable on device msgid "Minimum Version" msgstr "Najmanja inačica" @@ -1695,13 +2022,13 @@ #. TRANSLATORS: command description msgid "Modifies a given remote" -msgstr "Promjena zadane udaljene lokacije" +msgstr "Mijenja udaljenu lokaciju" msgid "Modify a configured remote" msgstr "Promijeni zadanu udaljenu lokaciju" msgid "Modify daemon configuration" -msgstr "Prilagodi podešavanje pozadinskog programa" +msgstr "Promijeni podešavanje pozadinskog programa" #. TRANSLATORS: command description msgid "Monitor the daemon for events" @@ -1731,6 +2058,10 @@ msgid "No action specified!" msgstr "Nema zadane radnje!" +#. TRANSLATORS: no devices that can be upgraded with new firmware +msgid "No devices are updatable" +msgstr "Nijedan uređaj se ne može nadopunit" + #. TRANSLATORS: message letting the user know no device downgrade available #. * %1 is the device name #, c-format @@ -1739,11 +2070,23 @@ #. TRANSLATORS: nothing found msgid "No firmware IDs found" -msgstr "Nema pronađenog ID firmvera" +msgstr "Nije pronađen nijedan ID firmvera" + +#. TRANSLATORS: nothing found +msgid "No firmware found" +msgstr "Nije pronađen nijedan firmver" #. TRANSLATORS: nothing attached that can be upgraded msgid "No hardware detected with firmware update capability" -msgstr "Nema otkrivenog hardvera s mogućnosti nadopune firmvera" +msgstr "Nije pronađen nijedan hardver s mogućnosti nadopune firmvera" + +#. TRANSLATORS: no repositories to download from +msgid "No matching releases for search token" +msgstr "Nema odgovarajućih izdanja za token pretraživanja" + +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +msgstr "Ponovno pokretanje nije potrebno" #. TRANSLATORS: no repositories to download from msgid "No releases available" @@ -1793,22 +2136,38 @@ msgstr "Stara inačica" #. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Instaliraj samo na emulirane uređaje" + +#. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Prikaži samo jednu PRC vrijednost" +#. TRANSLATORS: command line option +msgid "Only use peer-to-peer networking when downloading files" +msgstr "Koristi peer-to-peer umrežavanje samo prilikom preuzimanja datoteka" + #. TRANSLATORS: some devices can only be updated to a new semver and cannot #. * be downgraded or reinstalled with the existing version msgid "Only version upgrades are allowed" -msgstr "Samo nadogradnje inačice su dopuštene" +msgstr "Dopuštene su samo nadogradnje inačica" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Izlaz u JSON formatu" +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Rezkutat u JSON formatu (onemogućuje sve interaktivne upite)" #. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Zaobiđi zadanu ESP putanju" +#. TRANSLATORS: if we can get metadata from peer-to-peer clients +msgid "P2P Firmware" +msgstr "P2P firmver" + +#. TRANSLATORS: if we can get metadata from peer-to-peer clients +msgid "P2P Metadata" +msgstr "P2P metapodaci" + #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "PATH" msgstr "PUTANJA" @@ -1866,10 +2225,24 @@ msgid "Please enter a number from 0 to %u: " msgstr "Odaberite broj od 0 do %u: " +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' +#, c-format +msgid "Please enter either %s or %s: " +msgstr "Unesi %s ili %s: " + #. TRANSLATORS: Failed to open plugin, hey Arch users msgid "Plugin dependencies missing" msgstr "Zavisnosti priključka nedostaju" +#. TRANSLATORS: The plugin enumeration might change the device current mode +msgid "Plugin enumeration may change device state" +msgstr "Nabrajanje dodataka može promijeniti stanje uređaja" + +#. TRANSLATORS: The plugin is only for testing +msgid "Plugin is only for testing" +msgstr "Dodatak je samo za testiranje" + #. TRANSLATORS: Possible values for a bios setting msgid "Possible Values" msgstr "Moguće vrijednosti" @@ -1892,7 +2265,7 @@ #. TRANSLATORS: longer description msgid "Pre-boot DMA protection prevents devices from accessing system memory after being connected to the computer." -msgstr "DMA zaštita predpokretanja sprječava pristup uređaja memoriji sustava nakon što se povežu s računalom. " +msgstr "DMA zaštita predpokretanja sprječava pristup uređaja memoriji sustava nakon što se povežu s računalom." #. TRANSLATORS: warning message msgid "Press unlock on the device to continue the update process." @@ -1954,6 +2327,14 @@ msgid "Reading…" msgstr "Čitanje…" +#. TRANSLATORS: Plugin is active and in use +msgid "Ready" +msgstr "Spremno" + +#. TRANSLATORS: how often we should refresh the metadata +msgid "Refresh Interval" +msgstr "Interval osvježavanja" + #. TRANSLATORS: command description msgid "Refresh metadata from remote server" msgstr "Osvježi metapodatke s udaljenog poslužitelja" @@ -1962,7 +2343,7 @@ #. * %1 is the device name and %2 is a version string #, c-format msgid "Reinstall %s to %s?" -msgstr "Ponovno instaliraj %s na %s?" +msgstr "Ponovno instalirati %s na %s?" #. TRANSLATORS: command description msgid "Reinstall current firmware on the device" @@ -1989,6 +2370,10 @@ msgid "Remote ID" msgstr "Udaljeni ID" +#. TRANSLATORS: command description +msgid "Removes devices to watch for future emulation" +msgstr "Uklanja uređaje za praćenje buduće emulacije" + #. TRANSLATORS: URI to send success/failure reports msgid "Report URI" msgstr "URI izvještaja" @@ -2013,6 +2398,9 @@ msgid "Requires internet connection" msgstr "Potreban je pristup internetu" +msgid "Reset daemon configuration" +msgstr "Resetiraj podešavanja pozadinskog programa" + #. TRANSLATORS: reboot to apply the update msgid "Restart now?" msgstr "Ponovno pokreni odmah?" @@ -2033,10 +2421,26 @@ msgid "Return all the hardware IDs for the machine" msgstr "Vrati sve ID-ove hardvera za uređaj" +#. TRANSLATORS: ask the user to upload +msgid "Review and upload report now?" +msgstr "Pregledati i učitati izvještaj sada?" + #. TRANSLATORS: longer description msgid "Rollback Protection prevents device software from being downgraded to an older version that has security problems." msgstr "Zaštita od vraćanja na stariju inačicu sprječava softver uređaja da ga vrati na stariju inačicu koja ima sigurnosne probleme." +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr get-upgrades` +#, c-format +msgid "Run `%s` for more information." +msgstr "Pokrenit`%s` za više informacija." + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr sync` +#, c-format +msgid "Run `%s` to complete this action." +msgstr "Pokrenit`%s` za završavanje ove radnje." + #. TRANSLATORS: command line option msgid "Run the plugin composite cleanup routine when using install-blob" msgstr "Pokreni rutinu čišćenja sastavljanja priključka kada se koristi install-blob" @@ -2058,6 +2462,10 @@ msgid "Runtime Suffix" msgstr "Sufiks vremenskog izvršavanja" +#. TRANSLATORS: Software Bill of Materials link +msgid "SBOM" +msgstr "SBOM" + #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "SETTING VALUE" msgstr "VRIJEDNOST POSTAVKE" @@ -2066,6 +2474,14 @@ msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" msgstr "POSTAVKA1 VRIJEDNOST1 [POSTAVKA2] [VRIJEDNOST2]" +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "SMAP" +msgstr "SMAP" + +#. TRANSLATORS: Title: Whether firmware is locked down +msgid "SMM locked down" +msgstr "SMM jezaključan" + #. TRANSLATORS: Title: SPI refers to the flash chip in the computer msgid "SPI BIOS Descriptor" msgstr "SPI BIOS opisnik" @@ -2102,6 +2518,10 @@ msgid "Save device emulation data" msgstr "Spremi emulirane podatke uređaja" +#. TRANSLATORS: key for a offline report filename +msgid "Saved report" +msgstr "Spremljeni izvještaj" + #. TRANSLATORS: Scalar increment for integer BIOS setting msgid "Scalar Increment" msgstr "Skalarni umnožak" @@ -2170,7 +2590,7 @@ #. TRANSLATORS: command description msgid "Share firmware history with the developers" -msgstr "Podijeli povijest firmvera sa razvijateljima" +msgstr "Podijeli povijest firmvera s razvijateljima" #. TRANSLATORS: command line option msgid "Show all results" @@ -2251,13 +2671,16 @@ msgid "Specify the dbx database file" msgstr "Odredi datoteku dbx baze podataka" +msgid "Stop the fwupd service" +msgstr "Zaustavi uslugu fwupd" + #. TRANSLATORS: The BIOS setting accepts strings msgid "String" msgstr "Izraz" #. TRANSLATORS: the update state of the specific device msgid "Success" -msgstr "Uspješno" +msgstr "Uspjeh" #. TRANSLATORS: success message -- where activation is making the new #. * firmware take effect, usually after updating offline @@ -2268,19 +2691,27 @@ msgid "Successfully disabled remote" msgstr "Udaljena lokacija je uspješno onemogućena" +#. TRANSLATORS: comment explaining result of command +msgid "Successfully disabled test devices" +msgstr "Testni uređaji su uspješno onemogućeni" + #. TRANSLATORS: success message -- where 'metadata' is information #. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Novi metapodaci su uspješno preuzeti: " +msgid "Successfully downloaded new metadata:" +msgstr "Novi metapodaci su uspješno preuzeti:" #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" -msgstr "Udaljena lokacija je uspješno omogućena i provjerena" +msgstr "Udaljena lokacija je uspješno omogućena i osvježena" #. TRANSLATORS: success message msgid "Successfully enabled remote" msgstr "Udaljena lokacija je uspješno omogućena" +#. TRANSLATORS: comment explaining result of command +msgid "Successfully enabled test devices" +msgstr "Testni uređaji su uspješno omogućeni" + #. TRANSLATORS: success message msgid "Successfully installed firmware" msgstr "Firmver je uspješno instaliran" @@ -2297,6 +2728,14 @@ msgid "Successfully refreshed metadata manually" msgstr "Metapodaci su ručno uspješno osvježni" +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully reset configuration section" +msgstr "Odjeljak konfiguracije je uspješno resetiran" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully reset configuration values" +msgstr "Vrijednosti konfiguracije su uspješno resetirane" + #. TRANSLATORS: success message when user refreshes device checksums msgid "Successfully updated device checksums" msgstr "Kontrolni zbroj uređaja je uspješno nadopunjen" @@ -2306,18 +2745,31 @@ #, c-format msgid "Successfully uploaded %u report" msgid_plural "Successfully uploaded %u reports" -msgstr[0] "Uspješno poslan %u izvještaj" -msgstr[1] "Uspješno poslana %u izvještaja" -msgstr[2] "Uspješno poslano %u izvještaja" +msgstr[0] "Uspješno je poslan %u izvještaj" +msgstr[1] "Uspješno su poslana %u izvještaja" +msgstr[2] "Uspješno je poslano %u izvještaja" #. TRANSLATORS: success message when user verified device checksums msgid "Successfully verified device checksums" msgstr "Kontrolni zbroj uređaja je uspješno provjeren" +#. TRANSLATORS: the device showed up in time +#, c-format +msgid "Successfully waited %.0fms for device" +msgstr "Uspješno se čekalo %.0f ms na uređaj" + #. TRANSLATORS: one line summary of device msgid "Summary" msgstr "Sažetak" +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Supervisor Mode Access Prevention" +msgstr "Sprečavanje pristupa u nadzornom načina rada" + +#. TRANSLATORS: longer description +msgid "Supervisor Mode Access Prevention ensures critical parts of device memory are not accessed by less secure programs." +msgstr "Sprečavanje pristupa u nadzornom načina rada osigurava da manje sigurni programi ne pristupaju kritičnim dijelovima memorije uređaja." + #. TRANSLATORS: Suffix: the HSI result msgid "Supported" msgstr "Podržano" @@ -2364,6 +2816,31 @@ msgid "Switch the firmware branch on the device" msgstr "Zamijeni ogranak firmvera na uređaju" +#. TRANSLATORS: command description +msgid "Sync firmware versions to the chosen configuration" +msgstr "Sinkroniziraj inačice firmvera s odabranom konfiguracijom" + +#. TRANSLATORS: Title: Whether firmware is locked down +msgid "System Management Mode" +msgstr "Način upravljanja sustavom" + +#. TRANSLATORS: this CLI tool is now preventing system updates +msgid "System Update Inhibited" +msgstr "Blokirano nadopunjavanje sustava" + +#. TRANSLATORS: longer description +msgid "System management mode is used by the firmware to access resident BIOS code and data." +msgstr "Firmware koristi način upravljanja sustavom za pristup rezidentnom BIOS kodu i podacima." + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low" +msgstr "Energija sustava je preniska" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low (%u%%, requires %u%%)" +msgstr "Energija sustava je preniska (%u %%, zahtijeva %u %%)" + #. TRANSLATORS: Must be plugged into an outlet msgid "System requires external power source" msgstr "Sustav zahtijeva vanjski izvor energije" @@ -2438,9 +2915,13 @@ msgid "Tested by %s" msgstr "Testirao %s" +#. TRANSLATORS: someone we trust has tested this +msgid "Tested by trusted vendor" +msgstr "Testirano od pouzdanog dobavljača" + #. TRANSLATORS: longer description msgid "The Intel Management Engine Key Manifest must be valid so that the device firmware can be trusted by the CPU." -msgstr "Manifest ključa Intel pogon upravljanja (Intel Management Engine) mora biti valjan tako da firmver uređaja može vjerovati CPU." +msgstr "Manifest ključa Intel pogon upravljanja (Intel Management Engine) mora biti valjan tako da CPU može vjerovati firmveru uređaja." #. TRANSLATORS: longer description msgid "The Intel Management Engine controls device components and needs to have a recent version to avoid security issues." @@ -2473,7 +2954,7 @@ #. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name #, c-format msgid "The firmware from %s is not supplied by %s, the hardware vendor." -msgstr "%s firmver nije isporučen od strane %s dobavljača hardvera." +msgstr "Firmver od %s nije isporučen od %s, dobavljača hardvera." #. TRANSLATORS: try to help msgid "The system clock has not been set correctly and downloading files may fail." @@ -2516,7 +2997,7 @@ #. TRANSLATORS: the vendor did not upload this msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." -msgstr "Ovaj firmver je omogućen od strane LVFS članova zajednice i nije omogućen (ili podržan) od strane izvornog proizvođača hardvera." +msgstr "Ovaj firmver je omogućen od LVFS članova zajednice i nije omogućen (ili podržan) od izvornog proizvođača hardvera." #. TRANSLATORS: unsupported build of the package msgid "This package has not been validated, it may not work properly." @@ -2583,10 +3064,22 @@ msgid "Type" msgstr "Vrsta" +#. TRANSLATORS: Title: Bootservice is when only readable from early-boot +msgid "UEFI Bootservice Variables" +msgstr "UEFI varijable usluge pokretanja sustava" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition may not be set up correctly" +msgstr "UEFI ESP particija možda nije ispravno postavljena" + #. TRANSLATORS: partition refers to something on disk, again, hey Arch users msgid "UEFI ESP partition not detected or configured" msgstr "UEFI ESP particija nije otkrivena ili podešena" +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI Memory Protection" +msgstr "UEFI zaštita memorije" + #. TRANSLATORS: Title: PK is the 'platform key' for the machine msgid "UEFI Platform Key" msgstr "UEFI ključ platforme" @@ -2599,17 +3092,45 @@ msgid "UEFI Secure Boot prevents malicious software from being loaded when the device starts." msgstr "UEFI sigurno pokretanje (SecureBoot) sprječava učitavanje zlonamjernog softvera pri pokretanju uređaja." +#. TRANSLATORS: Title: Bootservice is when only readable from early-boot +msgid "UEFI bootservice variables" +msgstr "UEFI varijable usluge pokretanja sustava" + #. TRANSLATORS: capsule updates are an optional BIOS feature msgid "UEFI capsule updates not available or enabled in firmware setup" msgstr "Nadopune UEFI kapsule nisu dostupne ili omogućene u frimver postavljanju" +#. TRANSLATORS: Title: is UEFI db up-to-date +msgid "UEFI db" +msgstr "UEFI db" + #. TRANSLATORS: program name msgid "UEFI dbx Utility" msgstr "UEFI dbx pomagalo" #. TRANSLATORS: system is not booted in UEFI mode msgid "UEFI firmware can not be updated in legacy BIOS mode" -msgstr "UEFI firmver ne može se nadopuniti u zastarjelom BIOS načinu" +msgstr "UEFI firmver se ne može nadopuniti u zastarjelom BIOS načinu" + +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI memory protection" +msgstr "UEFI zaštita memorije" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled and locked" +msgstr "UEFI zaštita memorije omogućena i zaključana" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled but not locked" +msgstr "UEFI zaštita memorije omogućena, ali nije zaključana" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now locked" +msgstr "UEFI zaštita memorije je sada zaključana" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now unlocked" +msgstr "UEFI zaštita memorije je sada otključana" #. TRANSLATORS: Title: PK is the 'platform key' for the machine msgid "UEFI platform key" @@ -2633,7 +3154,7 @@ #. TRANSLATORS: we will now offer this firmware to the user msgid "Unblocking firmware:" -msgstr "Neblokiranje firmvera:" +msgstr "Deblokiranje firmvera:" #. TRANSLATORS: command description msgid "Unblocks a specific firmware from being installed" @@ -2643,6 +3164,10 @@ msgid "Unencrypted" msgstr "Nije šifrirano" +#. TRANSLATORS: command description +msgid "Uninhibit the system to allow upgrades" +msgstr "Blokiraj sustav za dopuštanje nadogradnji" + #. TRANSLATORS: current daemon status is unknown #. TRANSLATORS: we don't know the license of the update #. TRANSLATORS: unknown release urgency @@ -2669,6 +3194,10 @@ msgid "Unmounts the ESP" msgstr "Demontira ESP" +#. TRANSLATORS: message shown after device has been marked for emulation +msgid "Unplug and replug the device to continue the update process." +msgstr "Isključi i ponovo priključi uređaj za nastavljanje postupke nadopunjavanja." + #. TRANSLATORS: firmware payload is unsigned and it is possible to modify it msgid "Unsigned Payload" msgstr "Nepotpisani sadržaj prijenosa" @@ -2739,6 +3268,20 @@ msgid "Upgrade %s from %s to %s?" msgstr "Nadogradi %s sa %s na %s?" +#. TRANSLATORS: ask the user to upload +msgid "Upload data now?" +msgstr "Sada poslati podatke?" + +#. TRANSLATORS: command description +msgid "Upload the list of updatable devices to a remote server" +msgstr "Prenesi popis uređaja koji se mogu nadopuniti na udaljeni poslužitelj" + +#. TRANSLATORS: ask the user to share, %s is something +#. * like: "Linux Vendor Firmware Service" +#, c-format +msgid "Upload these anonymous results to the %s to help other users?" +msgstr "Prenijeti ove anonimne rezultate na %s kao pomoć za druge korisnike?" + #. TRANSLATORS: explain why we want to upload msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." msgstr "Slanje izvještaja firmvera pomaže proizvođačima hardvera brzo otkrivanje nedostatka i brzu nadopunu na stvarnim uređajima." @@ -2800,6 +3343,10 @@ msgid "WARNING" msgstr "UPOZORENJE" +#. TRANSLATORS: command description +msgid "Wait for a device to appear" +msgstr "Pričekaj da se uređaj pojavi" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Čekanje…" diff -Nru fwupd-2.0.8/po/hu.po fwupd-2.0.20/po/hu.po --- fwupd-2.0.8/po/hu.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/hu.po 2026-02-26 11:36:18.000000000 +0000 @@ -10,16 +10,20 @@ # Gabor Kelemen , 2016 # Gábor Kelemen , 2016 # kelemeng , 2016 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Hungarian (http://app.transifex.com/freedesktop/fwupd/language/hu/)\n" +"PO-Revision-Date: 2025-10-24 10:16+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Hungarian \n" +"Language: hu\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: hu\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -40,7 +44,7 @@ msgstr "%s akkumulátorfrissítés" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "%s CPU-mikrokódfrissítés" @@ -1067,10 +1071,6 @@ msgstr "FÁJLNÉV TANÚSÍTVÁNY SZEMÉLYES-KULCS" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "FÁJLNÉV ESZKÖZAZONOSÍTÓ" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" msgstr "FÁJLNÉV ELTOLÁS ADATOK [BELSŐVEZÉRLŐPROGRAM-TÍPUS]" @@ -1338,10 +1338,6 @@ msgstr "Lekéri a tiltott belső vezérlőprogramok listáját" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Lekéri a frissítések listáját a csatlakoztatott hardverhez" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "Lekéri az eszközhöz tartozó kiadásokat" @@ -1366,7 +1362,6 @@ msgstr "A gép biztonsági eseményei" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "A kiszolgálóbiztonsági azonosító (HSI) nem támogatott" @@ -1911,10 +1906,6 @@ msgstr "Csak verziófrissítések engedélyezettek" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Kiment JSON-formátumban" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Az alapértelmezett ESP-útvonal felülbírálása" @@ -2393,11 +2384,6 @@ msgid "Successfully disabled remote" msgstr "A távoli tároló sikeresen letiltva" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Az új metaadatok sikeresen letöltve: " - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "A távoli tároló sikeresen engedélyezve és frissítve" diff -Nru fwupd-2.0.8/po/id.po fwupd-2.0.20/po/id.po --- fwupd-2.0.8/po/id.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/id.po 2026-02-26 11:36:18.000000000 +0000 @@ -5,16 +5,20 @@ # Translators: # Andika Triwidada , 2017-2019,2021,2024 # Andika Triwidada , 2021 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Indonesian (http://app.transifex.com/freedesktop/fwupd/language/id/)\n" +"PO-Revision-Date: 2025-10-24 10:17+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Indonesian \n" +"Language: id\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: id\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -34,7 +38,7 @@ msgstr "Pembaruan Baterai %s" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "Pembaruan Microcode CPU %s" @@ -1168,10 +1172,6 @@ msgstr "NAMABERKAS SERTIFIKAT KUNCI-PRIVAT" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "NAMABERKAS ID-PERANTI" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" msgstr "DATA OFSET NAMA-BERKAS [TIPE-FIRMWARE]" @@ -1464,10 +1464,6 @@ msgstr "Mendapatkan daftar firmware yang diblokir" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Dapatkan daftar pemutakhiran bagi perangkat keras yang tersambung" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "Mendapatkan rilis-rilis bagi sebuah peranti" @@ -1492,7 +1488,6 @@ msgstr "Kejadian Keamanan Host" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "Host Security ID (HSI) tidak didukung" @@ -2075,10 +2070,6 @@ msgstr "Hanya peningkatan versi yang diizinkan" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Keluaran dalam format JSON" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Timpa path ESP default" @@ -2147,7 +2138,8 @@ msgid "Please enter a number from 0 to %u: " msgstr "Silakan masukkan angka dari 0 hingga %u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " msgstr "Silakan masukkan %s atau %s: " @@ -2623,11 +2615,6 @@ msgid "Successfully disabled test devices" msgstr "Sukses menonaktifkan perangkat pengujian" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Metadata baru berhasil diunduh: " - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "Berhasil mengaktifkan dan menyegarkan remote" @@ -3155,12 +3142,6 @@ msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" msgstr "Memperbarui semua perangkat yang ditentukan ke versi firmware terbaru, atau semua perangkat jika tidak ditentukan" -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "Pembaruan telah dipublikasikan untuk %u dari %u perangkat lokal" - msgid "Updating" msgstr "Memutakhirkan" diff -Nru fwupd-2.0.8/po/it.po fwupd-2.0.20/po/it.po --- fwupd-2.0.8/po/it.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/it.po 2026-02-26 11:36:18.000000000 +0000 @@ -6,16 +6,20 @@ # Daniele Guarascio, 2024 # Gianvito Cavasoli , 2016 # Milo Casagrande , 2017-2023 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Italian (http://app.transifex.com/freedesktop/fwupd/language/it/)\n" +"PO-Revision-Date: 2025-10-24 10:20+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Italian \n" +"Language: it\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: it\n" "Plural-Forms: nplurals=3; plural=n == 1 ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -37,7 +41,7 @@ msgstr "Aggiornamento batteria %s" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "Aggiornamento microcode CPU %s" @@ -493,7 +497,7 @@ #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to switch to the new firmware version" -msgstr "È richiesto autenticarsi per passare alla nuova versione del firmware " +msgstr "È richiesto autenticarsi per passare alla nuova versione del firmware" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to undo the fix for a host security issue" @@ -847,7 +851,7 @@ #. TRANSLATORS: command line option msgid "Do not check for unreported history" -msgstr "Non controlla la cronologia non segnalata " +msgstr "Non controlla la cronologia non segnalata" #. TRANSLATORS: command line option msgid "Do not check if download remotes should be enabled" @@ -1040,10 +1044,6 @@ msgstr "NOMEFILE CERTIFICATO CHIAVE-PRIVATA" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "NOMEFILE ID-DISPOSITIVO" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" msgstr "FILENAME OFFSET DATA [TIPO-FIRMWARE]" @@ -1282,10 +1282,6 @@ msgstr "Recupera l'elenco del firmware bloccato" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Ottiene l'elenco degli aggiornamenti per l'hardware connesso" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "Ottiene i rilasci di un dispositivo" @@ -1780,10 +1776,6 @@ msgstr "Sono consentiti solo avanzamenti di versione" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Output in formato JSON" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Sovrascrive il percorso ESP predefinito" @@ -2233,11 +2225,6 @@ msgid "Successfully disabled remote" msgstr "Remoto disabilitato con successo" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Nuovi metadati scaricati con successo:" - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "Remoto abilitato e aggiornato con successo" @@ -2433,7 +2420,7 @@ #. command name, e.g. `fwupdmgr sync` #, c-format msgid "This device will be reverted back to %s when the %s command is performed." -msgstr "Questo dispositivo verrà retrocesso alla versione %s all'esecuzione del comando %s. " +msgstr "Questo dispositivo verrà retrocesso alla versione %s all'esecuzione del comando %s." #. TRANSLATORS: the vendor did not upload this msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." diff -Nru fwupd-2.0.8/po/ja.po fwupd-2.0.20/po/ja.po --- fwupd-2.0.8/po/ja.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/ja.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,38 +4,531 @@ # # Translators: # Green , 2021 +# Makoto Sakaguchi , 2025 # Nobuhiro Iwamatsu , 2021 # Ryo Nakano, 2024 # Takuro Onoue , 2021 # YOSHIDUMI, Rentaro, 2021 +# Richard Hughes , 2025. +# Mikenu Takeru , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Japanese (http://app.transifex.com/freedesktop/fwupd/language/ja/)\n" +"PO-Revision-Date: 2025-11-14 16:51+0000\n" +"Last-Translator: Mikenu Takeru \n" +"Language-Team: Japanese \n" +"Language: ja\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ja\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.15-dev\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "残り %.0f 分です" + +#. TRANSLATORS: BMC refers to baseboard management controller which +#. * is the device that updates all the other firmware on the system +#, c-format +msgid "%s BMC Update" +msgstr "%s BMC アップデート" + +#. TRANSLATORS: battery refers to the system power source +#, c-format +msgid "%s Battery Update" +msgstr "%s バッテリーアップデート" + +#. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU +#. * at system boot-up +#, c-format +msgid "%s CPU Microcode Update" +msgstr "%s CPU マイクロコードアップデート" + +#. TRANSLATORS: camera can refer to the laptop internal +#. * camera in the bezel or external USB webcam +#, c-format +msgid "%s Camera Update" +msgstr "%s カメラアップデート" + +#. TRANSLATORS: a specific part of hardware, +#. * the first %s is the device name, e.g. 'Secure Boot` +#, c-format +msgid "%s Configuration Update" +msgstr "%s 構成の更新" + +#. TRANSLATORS: ME stands for Management Engine, where +#. * the first %s is the device name, e.g. 'ThinkPad P50` +#, c-format +msgid "%s Consumer ME Update" +msgstr "%s コンシューマー ME アップデート" + +#. 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 コントローラーアップデート" + +#. 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 アップデート" + +#. 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 デバイスアップデート" + +#. TRANSLATORS: Video Display refers to the laptop internal display or +#. * external monitor +#, c-format +msgid "%s Display Update" +msgstr "%s ディスプレイアップデート" + +#. TRANSLATORS: Dock refers to the port replicator hardware laptops are +#. * cradled in, or lowered onto +#, c-format +msgid "%s Dock Update" +msgstr "%s Dock アップデート" + +#. TRANSLATORS: drive refers to a storage device, e.g. SATA disk +#, c-format +msgid "%s Drive Update" +msgstr "%s デバイスアップデート" + +#. 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 組み込みコントローラーアップデート" + +#. TRANSLATORS: a device that can read your fingerprint pattern +#, c-format +msgid "%s Fingerprint Reader Update" +msgstr "%s 指紋リーダーアップデート" + +#. TRANSLATORS: flash refers to solid state storage, e.g. UFS or eMMC +#, c-format +msgid "%s Flash Drive Update" +msgstr "%s フラッシュドライブアップデート" + +#. TRANSLATORS: GPU refers to a Graphics Processing Unit, e.g. +#. * the "video card" +#, c-format +msgid "%s GPU Update" +msgstr "%s GPU アップデート" + +#. TRANSLATORS: a large pressure-sensitive drawing area typically used +#. * by artists and digital artists +#, c-format +msgid "%s Graphics Tablet Update" +msgstr "%s グラフィックスタブレットアップデート" + +#. TRANSLATORS: two miniature speakers attached to your ears +#, c-format +msgid "%s Headphones Update" +msgstr "%s ヘッドフォンアップデート" + +#. TRANSLATORS: headphones with an integrated microphone +#, c-format +msgid "%s Headset Update" +msgstr "%s ヘッドセットアップデート" + +#. TRANSLATORS: an input device used by gamers, e.g. a joystick +#, c-format +msgid "%s Input Controller Update" +msgstr "%s 入力コントローラーアップデート" + +#. TRANSLATORS: Keyboard refers to an input device for typing +#, c-format +msgid "%s Keyboard Update" +msgstr "%s キーボードアップデート" + +#. 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 アップデート" + +#. TRANSLATORS: Mouse refers to a handheld input device +#, c-format +msgid "%s Mouse Update" +msgstr "%s マウスアップデート" + +#. TRANSLATORS: Network Interface refers to the physical +#. * PCI card, not the logical wired connection +#, c-format +msgid "%s Network Interface Update" +msgstr "%s ネットワークインターフェースアップデート" + +#. TRANSLATORS: SSD refers to a Solid State Drive, e.g. non-rotating +#. * SATA or NVMe disk +#, c-format +msgid "%s SSD Update" +msgstr "%s SSD アップデート" + +#. TRANSLATORS: Storage Controller is typically a RAID or SAS adapter +#, c-format +msgid "%s Storage Controller Update" +msgstr "%s ストレージコントローラーアップデート" + +#. 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 システムアップデート" + +#. TRANSLATORS: TPM refers to a Trusted Platform Module +#, c-format +msgid "%s TPM Update" +msgstr "%s TPM アップデート" + +#. 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 コントローラーアップデート" + +#. TRANSLATORS: TouchPad refers to a flat input device +#, c-format +msgid "%s Touchpad Update" +msgstr "%s タッチパッドアップデート" + +#. TRANSLATORS: Dock refers to the port replicator device connected +#. * by plugging in a USB cable -- which may or may not also provide power +#, c-format +msgid "%s USB Dock Update" +msgstr "%s USB Dock アップデート" + +#. TRANSLATORS: Receiver refers to a radio device, e.g. a tiny Bluetooth +#. * device that stays in the USB port so the wireless peripheral works +#, c-format +msgid "%s USB Receiver Update" +msgstr "%s USB レシーバーアップデート" + +#. 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 アップデート" + +#. TRANSLATORS: warn the user before updating, %1 is a device name +#, c-format +msgid "%s and all connected devices may not be usable while updating." +msgstr "アップデート中は %s および接続されているすべてのデバイスが使用できなくなる可能性があります。" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "Encrypted RAM". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s appeared: %s" +msgstr "%s が出現しました: %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "UEFI platform +#. key". +#. * %2 and %3 refer to results value, e.g. "Valid" and "Invalid" +#, c-format +msgid "%s changed: %s → %s" +msgstr "%s が変更されました: %s → %s" + +#. TRANSLATORS: %1 refers to some kind of security test, e.g. "SPI BIOS +#. region". +#. %2 refers to a result value, e.g. "Invalid" +#, c-format +msgid "%s disappeared: %s" +msgstr "%s が消失しました: %s" + +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s は現在更新できません" + +#. TRANSLATORS: %1 is a device name, e.g. "ThinkPad Universal +#. * ThunderBolt 4 Dock" and %2 is "fwupdmgr activate" +#, c-format +msgid "%s is pending activation; use %s to complete the update." +msgstr "%s のアクティベーションが保留中です。%s を実行してアップデートを完了してください。" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s manufacturing mode" +msgstr "%s 製造モード" + +#. TRANSLATORS: warn the user before +#. * updating, %1 is a device name +#, c-format +msgid "%s must remain connected for the duration of the update to avoid damage." +msgstr "損傷を避けるため、アップデート中は %s を接続したままにしておく必要があります。" + +#. TRANSLATORS: warn the user before updating, %1 is a machine +#. * name +#, c-format +msgid "%s must remain plugged into a power source for the duration of the update to avoid damage." +msgstr "損傷を避けるため、アップデート中は %s を電源に接続したままにしておく必要があります。" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s override" +msgstr "%s オーバーライド" + +#. TRANSLATORS: Title: %1 is ME kind, e.g. CSME/TXT, %2 is a version number +#, c-format +msgid "%s v%s" +msgstr "%s v%s" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s バージョン" + +#. TRANSLATORS: duration in days! +#, c-format +msgid "%u day" +msgid_plural "%u days" +msgstr[0] "%u 日" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u 個のデバイスでファームウェアアップグレードが利用可能です。" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device is not the best known configuration." +msgid_plural "%u devices are not the best known configuration." +msgstr[0] "%u 個のデバイスが推奨設定になっていません。" + +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#, c-format +msgid "%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "%u devices are supported in the enabled remotes (an update has been published)" +msgstr[0] "%u 個のデバイスが、有効なリモートでサポートされています(更新が公開されています)" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "%u 個のデバイスが更新可能です" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u hour" +msgid_plural "%u hours" +msgstr[0] "%u 時間" + +#. TRANSLATORS: duration in minutes +#, c-format +msgid "%u minute" +msgid_plural "%u minutes" +msgstr[0] "%u 分" + +#. TRANSLATORS: duration in seconds +#, c-format +msgid "%u second" +msgid_plural "%u seconds" +msgstr[0] "%u 秒" + +#. TRANSLATORS: device tests can be specific to a CPU type +#, c-format +msgid "%u test was skipped" +msgid_plural "%u tests were skipped" +msgstr[0] "%u 個のテストがスキップされました" + +#. TRANSLATORS: first percentage is current value, 2nd percentage is the +#. * lowest limit the firmware update is allowed for the update to happen +#, c-format +msgid "%u%% (threshold %u%%)" +msgstr "%u%% (しきい値 %u%%)" + +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(廃止済み)" + +#. TRANSLATORS: HSI event title +msgid "A TPM PCR is now an invalid value" +msgstr "TPM PCR が不正な値になりました" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "AMD Firmware Replay Protection" +msgstr "AMD ファームウェアリプレイ保護" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "AMD ファームウェア書き込み保護" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "AMD Secure Processor Rollback Protection" +msgstr "AMD セキュアプロセッサ ロールバック保護" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "ARCHIVE FIRMWARE METAINFO [FIRMWARE] [METAINFO] [JCATFILE]" +msgstr "ARCHIVE FIRMWARE METAINFO [FIRMWARE] [METAINFO] [JCATFILE]" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "操作が必要です:" + +#. TRANSLATORS: command description +msgid "Activate devices" +msgstr "デバイスを有効化する" + +#. TRANSLATORS: command description +msgid "Activate pending devices" +msgstr "保留中のデバイスを有効化する" msgid "Activate the new firmware on the device" msgstr "機器上で新しいファームウェアを実行可能にする" +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update" +msgstr "ファームウェアアップデートを適用中" + +#. TRANSLATORS: shown when shutting down to switch to the new version +msgid "Activating firmware update for" +msgstr "ファームウェアアップデートを有効化しています" + +#. TRANSLATORS: command description +msgid "Adds devices to watch for future emulation" +msgstr "エミュレーション対象として監視するデバイスを追加する" + +#. TRANSLATORS: the age of the metadata +msgid "Age" +msgstr "経過日数" + +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "同意してリモートを有効にしますか?" + #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format msgid "Alias to %s" msgstr "%sの別名" +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are now valid" +msgstr "すべての TPM PCR は現在有効です" + +#. TRANSLATORS: HSI event title +msgid "All TPM PCRs are valid" +msgstr "すべての TPM PCR は有効" + +#. TRANSLATORS: an application is preventing system updates +msgid "All devices are prevented from update by system inhibit" +msgstr "すべてのデバイスはシステム抑制によりアップデートが妨げられています" + +#. TRANSLATORS: on some systems certain devices have to have matching +#. versions, +#. * e.g. the EFI driver for a given network card cannot be different +msgid "All devices of the same type will be updated at the same time" +msgstr "同じ種類のすべてのデバイスは同時に更新されます" + +#. TRANSLATORS: command line option +msgid "Allow downgrading firmware versions" +msgstr "ファームウェアバージョンのダウングレードを許可する" + +#. TRANSLATORS: command line option +msgid "Allow reinstalling existing firmware versions" +msgstr "既存のファームウェアバージョンの再インストールを許可する" + +#. TRANSLATORS: command line option +msgid "Allow switching firmware branch" +msgstr "ファームウェアブランチの切り替えを許可する" + +#. TRANSLATORS: error message +msgid "Already exists, and no --force specified" +msgstr "既に存在します。上書きするには --force オプションを指定してください" + +#. TRANSLATORS: is not the main firmware stream +msgid "Alternate branch" +msgstr "代替ブランチ" + +#. TRANSLATORS: another application is updating the device already +msgid "An update is in progress" +msgstr "アップデートが進行中です" + +#. TRANSLATORS: explain why we want to reboot +msgid "An update requires a reboot to complete." +msgstr "アップデートを完了するには、再起動が必要です。" + +#. TRANSLATORS: explain why +msgid "An update requires the system to shutdown to complete." +msgstr "アップデートを完了するには、システムをシャットダウンする必要があります。" + +#. TRANSLATORS: command line option +msgid "Answer yes to all questions" +msgstr "すべての質問に『はい』で答える" + +#. TRANSLATORS: command line option +msgid "Apply update even when not advised" +msgstr "推奨されていない場合でもアップデートを適用する" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "アップデートファイルを適用する" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "アップデートを適用しています…" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "Approved firmware:" +msgid_plural "Approved firmware:" +msgstr[0] "承認済みファームウェア:" + +#. TRANSLATORS: command description +msgid "Asks the daemon to quit" +msgstr "デーモンに終了を要求" + +#. TRANSLATORS: command description +msgid "Attach to firmware mode" +msgstr "ファームウェアモードに切り替える" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "認証しています…" + +#. TRANSLATORS: user needs to run a command +msgid "Authentication details are required" +msgstr "認証情報の入力が必要です" + #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on a removable device" -msgstr "外付け機器でファームウェアをダウングレードするには認証が必要です" +msgstr "取り外し可能なデバイスのファームウェアをダウングレードするには認証が必要です" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on this machine" msgstr "この機器のファームウェアをダウングレードするには認証が必要です" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to enable emulation data collection" +msgstr "エミュレーションデータ収集を有効にするには認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to fix a host security issue" +msgstr "ホストのセキュリティ問題を修正するには認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to load hardware emulation data" +msgstr "ハードウェアエミュレーションデータを読み込むには認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify BIOS settings" +msgstr "BIOS 設定を変更するには認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify a configured remote used for firmware updates" msgstr "ファームウェア更新用の遠隔構成を変更するには認証が必要です" @@ -44,10 +537,18 @@ msgstr "デーモン構成の変更には認証が必要です" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to read BIOS settings" +msgstr "BIOS 設定を読み取るには認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to reset daemon configuration to defaults" msgstr "デーモン構成をデフォルトにリセットするには認証が必要です" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to save hardware emulation data" +msgstr "ハードウェアエミュレーションデータを保存するには認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to set the list of approved firmware" msgstr "認証済みファームウェアの一覧を設定するには認証が必要です" @@ -64,12 +565,16 @@ msgstr "新版のファームウェアに切り替えるには認証が必要です" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to undo the fix for a host security issue" +msgstr "ホストのセキュリティ問題の修正を元に戻すには認証が必要です" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" -msgstr "機器のロックを解除するには認証が必要です" +msgstr "デバイスのロックを解除するには認証が必要です" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on a removable device" -msgstr "外付け機器でファームウェアを更新するには認証が必要です" +msgstr "取り外し可能なデバイスのファームウェアを更新するには認証が必要です" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on this machine" @@ -79,17 +584,715 @@ msgid "Authentication is required to update the stored checksums for the device" msgstr "保存された機器の検査合計を更新するには認証が必要です" +#. TRANSLATORS: Boolean value to automatically send reports +msgid "Automatic Reporting" +msgstr "自動レポート" + +#. TRANSLATORS: we can auto-uninhibit after a timeout +#, c-format +msgid "Automatically uninhibiting in %ums…" +msgstr "%u ミリ秒後に自動で抑制を解除します…" + +#. TRANSLATORS: can we JFDI? +msgid "Automatically upload every time?" +msgstr "毎回自動的にアップロードしますか?" + +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +msgid "BIOS Firmware Updates" +msgstr "BIOS ファームウェア更新" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "BIOS Rollback Protection" +msgstr "BIOS ロールバック保護" + +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +msgid "BIOS firmware updates" +msgstr "BIOS ファームウェアアップデート" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "BIOS rollback protection" +msgstr "BIOS ロールバック保護" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "LVFS または Windows Update 経由で提供される BIOS アップデート" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "BUILDER-XML FILENAME-DST" +msgstr "BUILDER-XML FILENAME-DST" + +#. TRANSLATORS: refers to the battery inside the peripheral device +msgid "Battery" +msgstr "バッテリー" + +#. TRANSLATORS: command description +msgid "Bind new kernel driver" +msgstr "新しいカーネルドライバーを結び付ける" + +#. TRANSLATORS: there follows a list of hashes +msgid "Blocked firmware files:" +msgstr "ブロックされたファームウェアファイル:" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Blocked version" +msgstr "ブロックされたバージョン" + +#. TRANSLATORS: we will not offer this firmware to the user +msgid "Blocking firmware:" +msgstr "ファームウェアブロック中:" + +#. TRANSLATORS: command description +msgid "Blocks a specific firmware from being installed" +msgstr "特定のファームウェアのインストールをブロック" + +#. TRANSLATORS: firmware version of bootloader +msgid "Bootloader Version" +msgstr "ブートローダーバージョン" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree +msgid "Branch" +msgstr "ブランチ" + +#. TRANSLATORS: command description +msgid "Build a cabinet archive from a firmware blob and XML metadata" +msgstr "ファームウェアブロブとXMLメタデータからキャビネットアーカイブを構築します" + +#. TRANSLATORS: command description +msgid "Build a firmware file" +msgstr "ファームウェアファイルをビルドする" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * Utilized by OS means the distribution enabled it +msgid "CET OS Support" +msgstr "CET OS サポート" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology, +#. * enabled means supported by the processor +msgid "CET Platform" +msgstr "CET プラットフォーム" + +#. TRANSLATORS: longer description +msgid "CPU Microcode must be updated to mitigate against various information-disclosure security issues." +msgstr "CPU マイクロコードは、さまざまな情報漏洩に関するセキュリティ問題に対処するために更新する必要があります。" + +#. TRANSLATORS: we can save all device enumeration events for emulation +msgid "Can tag for emulation" +msgstr "エミュレーション用にタグ付け可能" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "キャンセル" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +#. TRANSLATORS: this is from ctrl+c +msgid "Cancelled" +msgstr "キャンセルされました" + +#. TRANSLATORS: same or newer update already applied +msgid "Cannot apply as dbx update has already been applied." +msgstr "dbx アップデートは既に適用されているため、適用できません。" + +#. TRANSLATORS: this is when the daemon state changes +msgid "Changed" +msgstr "変更されました" + +#. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "アップデートを完了するために再起動が必要なデバイスがあるかをチェックする" + +#. TRANSLATORS: command description +msgid "Checks cryptographic hash matches firmware" +msgstr "暗号学的ハッシュでファームウェアファイルの整合性を検証する" + +#. TRANSLATORS: hash to that exact firmware archive +#. TRANSLATORS: remote checksum +msgid "Checksum" +msgstr "チェックサム" + +#. TRANSLATORS: get interactive prompt, where branch is the +#. * supplier of the firmware, e.g. "non-free" or "free" +msgid "Choose branch" +msgstr "ブランチを選択" + +#. TRANSLATORS: get interactive prompt +msgid "Choose device" +msgstr "デバイスを選択" + +#. TRANSLATORS: get interactive prompt +msgid "Choose firmware" +msgstr "ファームウェアを選択" + +#. TRANSLATORS: get interactive prompt +msgid "Choose release" +msgstr "リリースを選択" + +#. TRANSLATORS: get interactive prompt +msgid "Choose volume" +msgstr "ボリュームを選択" + +#. TRANSLATORS: command description +msgid "Clears the results from the last update" +msgstr "前回のアップデート結果をクリアする" + #. TRANSLATORS: error message msgid "Command not found" msgstr "コマンドが見つかりません" +#. TRANSLATORS: is not supported by the vendor +msgid "Community supported" +msgstr "コミュニティサポート" + +#. TRANSLATORS: command description +msgid "Compares two versions for equality" +msgstr "2つのバージョンが等しいかどうかを比較します" + +#. TRANSLATORS: title prefix for the BIOS settings dialog +msgid "Configuration Change Suggested" +msgstr "設定変更の提案" + +#. TRANSLATORS: no peeking +msgid "Configuration is only readable by the system administrator" +msgstr "この設定はシステム管理者のみが読み取れます" + +#. TRANSLATORS: longer description +msgid "Control-Flow Enforcement Technology detects and prevents certain methods for running malicious software on the device." +msgstr "制御フロー強制技術は、デバイス上で悪意のあるソフトウェアを実行しようとする特定の手法を検出して防ぎます。" + +#. TRANSLATORS: Title: CET = Control-flow Enforcement Technology +msgid "Control-flow Enforcement Technology" +msgstr "制御フロー実行強制技術" + +#. TRANSLATORS: command description +msgid "Convert a firmware file" +msgstr "ファームウェアファイルを変換する" + +#. TRANSLATORS: command description +msgid "Create an EFI boot entry" +msgstr "EFI ブートエントリを作成する" + +#. TRANSLATORS: when the update was built +msgid "Created" +msgstr "作成日" + +#. TRANSLATORS: the release urgency +msgid "Critical" +msgstr "緊急" + +#. TRANSLATORS: Device supports some form of checksum verification +msgid "Cryptographic hash verification is available" +msgstr "暗号学的ハッシュ検証が利用可能です" + +#. TRANSLATORS: current value of a BIOS setting +msgid "Current Value" +msgstr "現在の値" + +#. TRANSLATORS: version number of current firmware +msgid "Current version" +msgstr "現在のバージョン" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "DEVICE-ID|GUID" +msgstr "DEVICE-ID|GUID" + +#. TRANSLATORS: for the --verbose arg +msgid "Debugging Options" +msgstr "デバッグオプション" + +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "展開中…" + +#. TRANSLATORS: command description +msgid "Delete an EFI boot entry" +msgstr "EFI ブートエントリを削除する" + +#. TRANSLATORS: description of BIOS setting +#. TRANSLATORS: multiline description of device +msgid "Description" +msgstr "説明" + +#. TRANSLATORS: command description +msgid "Detach to bootloader mode" +msgstr "ブートローダーモードに切り替える" + +#. TRANSLATORS: more details about the update link +msgid "Details" +msgstr "詳細" + +#. TRANSLATORS: the best known configuration is a set of software that we know +#. works +#. * well together. In the OEM and ODM industries it is often called a BKC +msgid "Deviate from the best known configuration?" +msgstr "既知の最適構成から逸脱しますか?" + +#. TRANSLATORS: description of device ability +msgid "Device Flags" +msgstr "デバイスフラグ" + +#. TRANSLATORS: ID for hardware, typically a SHA1 sum +msgid "Device ID" +msgstr "デバイス ID" + +#. TRANSLATORS: description of the device requests +msgid "Device Requests" +msgstr "デバイスリクエスト" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "デバイスが追加されました:" + +#. TRANSLATORS: the device is already connected +msgid "Device already exists" +msgstr "デバイスは既に接続されています" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +msgid "Device battery power is too low" +msgstr "デバイスのバッテリー残量が不足しています" + +#. TRANSLATORS: for example the batteries *inside* the Bluetooth mouse +#, c-format +msgid "Device battery power is too low (%u%%, requires %u%%)" +msgstr "デバイスのバッテリー残量が不足しています (現在 %u%%、%u%% 必要)" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device can recover flash failures" +msgstr "デバイスはフラッシュの失敗から復旧できます" + +#. TRANSLATORS: lid means "laptop top cover" +msgid "Device cannot be updated while the lid is closed" +msgstr "デバイスは蓋が閉じている間は更新できません" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "デバイスが変更されました:" + +#. TRANSLATORS: a version check is required for all firmware +msgid "Device firmware is required to have a version check" +msgstr "デバイスファームウェアにはバージョンチェックが必要です" + +#. TRANSLATORS: emulated means we are pretending to be a different model +msgid "Device is emulated" +msgstr "このデバイスはエミュレートされています" + +#. TRANSLATORS: device cannot be interrupted, for instance taking a phone call +msgid "Device is in use" +msgstr "デバイスは使用中です" + +#. TRANSLATORS: Is locked and can be unlocked +msgid "Device is locked" +msgstr "デバイスがロックされています" + +#. TRANSLATORS: we have two ways of communicating with the device, so we hide +#. one +msgid "Device is lower priority than an equivalent device" +msgstr "デバイスの優先度が同等のデバイスよりも低いです" + +#. TRANSLATORS: the device cannot update from A->C and has to go A->B->C +msgid "Device is required to install all provided releases" +msgstr "デバイスは提供されたすべてのリリースをインストールする必要があります" + +#. TRANSLATORS: currently unreachable, perhaps because it is in a lower power +#. state +#. * or is out of wireless range +msgid "Device is unreachable" +msgstr "デバイスに接続できません" + +#. TRANSLATORS: for example, a Bluetooth mouse that is in powersave mode +msgid "Device is unreachable, or out of wireless range" +msgstr "デバイスに接続できないか、ワイヤレス範囲外です" + +#. TRANSLATORS: Device remains usable during update +msgid "Device is usable for the duration of the update" +msgstr "アップデート中もデバイスは使用可能です" + +#. TRANSLATORS: usually this is when we're waiting for a reboot +msgid "Device is waiting for the update to be applied" +msgstr "デバイスはアップデートの適用を待機しています" + +#. TRANSLATORS: success, so say thank you to the user +msgid "Device list uploaded successfully, thanks!" +msgstr "デバイス一覧のアップロードが完了しました。ありがとうございます!" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "デバイスが削除されました:" + +#. TRANSLATORS: as in, wired mains power for a laptop +msgid "Device requires AC power to be connected" +msgstr "デバイスには AC 電源の接続が必要です" + +#. TRANSLATORS: device does not have a display connected +msgid "Device requires a display to be plugged in" +msgstr "このデバイスにはディスプレイの接続が必要です" + +#. TRANSLATORS: The device cannot be updated due to missing vendor's license." +msgid "Device requires a software license to update" +msgstr "このデバイスのアップデートにはソフトウェアライセンスが必要です" + +#. TRANSLATORS: longer description +msgid "Device software updates are provided for this device." +msgstr "このデバイスには、デバイスソフトウェアの更新が提供されています。" + +#. TRANSLATORS: Device supports a safety mechanism for flashing +msgid "Device stages updates" +msgstr "デバイスはアップデートを段階的に適用します" + +#. TRANSLATORS: there is more than one supplier of the firmware +msgid "Device supports switching to a different branch of firmware" +msgstr "デバイスは異なるファームウェアブランチへの切り替えに対応しています" + +#. TRANSLATORS: Device update needs to be separately activated +msgid "Device update needs activation" +msgstr "デバイスのアップデートには有効化が必要です" + +#. TRANSLATORS: save the old firmware to disk before installing the new one +msgid "Device will backup firmware before installing" +msgstr "デバイスはインストール前にファームウェアをバックアップします" + +#. TRANSLATORS: Device will not return after update completes +msgid "Device will not re-appear after update completes" +msgstr "アップデート完了後、デバイスは再認識されません" + +#. TRANSLATORS: a list of successful updates +msgid "Devices that have been updated successfully:" +msgstr "正常に更新されたデバイス:" + +#. TRANSLATORS: a list of failed updates +msgid "Devices that were not updated correctly:" +msgstr "正常に更新されなかったデバイス:" + +#. TRANSLATORS: message letting the user there is an update +#. * waiting, but there is a reason it cannot be deployed +msgid "Devices with firmware updates that need user action: " +msgstr "ファームウェアアップデートにユーザー操作が必要なデバイス: " + +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no +#. * device upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no device +#. * upgrade available due to missing on LVFS +#. TRANSLATORS: message letting the user know no +#. * device upgrade available due to missing on LVFS +msgid "Devices with no available firmware updates: " +msgstr "利用可能なファームウェアアップデートがないデバイス: " + +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device +#. * upgrade available +#. TRANSLATORS: message letting the user know no device upgrade available +#. TRANSLATORS: message letting the user know no device +#. * upgrade available +msgid "Devices with the latest available firmware version:" +msgstr "ファームウェアが最新のデバイス:" + +#. TRANSLATORS: this is for the device tests +msgid "Did not find any devices with matching GUIDs" +msgstr "一致する GUID を持つデバイスが見つかりませんでした" + +#. TRANSLATORS: Plugin is inactive and not used +#. TRANSLATORS: Suffix: the HSI result +msgid "Disabled" +msgstr "無効" + +#. TRANSLATORS: command description +msgid "Disables a given remote" +msgstr "指定されたリモートを無効にする" + +#. TRANSLATORS: command description +msgid "Disables virtual testing devices" +msgstr "仮想テストデバイスを無効にする" + +#. TRANSLATORS: the OS the release was tested on +msgid "Distribution" +msgstr "ディストリビューション" + +#. TRANSLATORS: command line option +msgid "Do not check for old metadata" +msgstr "古いメタデータをチェックしない" + +#. TRANSLATORS: command line option +msgid "Do not check for unreported history" +msgstr "未報告の履歴をチェックしない" + +#. TRANSLATORS: command line option +msgid "Do not check if download remotes should be enabled" +msgstr "ダウンロード用リモートを有効にするべきかをチェックしない" + +#. TRANSLATORS: command line option +msgid "Do not check or prompt for reboot after update" +msgstr "更新後の再起動確認とプロンプト表示を無効にする" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include log domain prefix" +msgstr "ログドメイン接頭辞を含めない" + +#. TRANSLATORS: turn on all debugging +msgid "Do not include timestamp prefix" +msgstr "タイムスタンプ接頭辞を含めない" + +#. TRANSLATORS: command line option +msgid "Do not perform device safety checks" +msgstr "デバイス安全性チェックを実行しない" + +#. TRANSLATORS: command line option +msgid "Do not prompt for devices" +msgstr "デバイス選択のプロンプト表示を行わない" + +#. TRANSLATORS: command line option +msgid "Do not prompt to fix security issues" +msgstr "セキュリティ問題の修正を促さない" + +#. TRANSLATORS: command line option +msgid "Do not search the firmware when parsing" +msgstr "解析時にファームウェアを検索しない" + +#. TRANSLATORS: warning message shown after update has been scheduled +msgid "Do not turn off your computer or remove the AC adaptor while the update is in progress." +msgstr "アップデート中は、コンピューターの電源を切ったり、AC アダプターを取り外したりしないでください。" + +#. TRANSLATORS: command line option +msgid "Do not write to the history database" +msgstr "履歴データベースに書き込まない" + +#. TRANSLATORS: should the branch be changed +msgid "Do you understand the consequences of changing the firmware branch?" +msgstr "ファームウェアブランチを変更することによる影響を理解していますか?" + +#. TRANSLATORS: ask the user if it's okay to convert, +#. * "it" being the data contained in the EFI boot entry +msgid "Do you want to convert it now?" +msgstr "今すぐ変換しますか?" + +#. TRANSLATORS: offer to disable this nag +msgid "Do you want to disable this feature for future updates?" +msgstr "今後のアップデートでこの機能を無効にしますか?" + +#. TRANSLATORS: ask if we can update the metadata +msgid "Do you want to refresh this remote now?" +msgstr "このリモートを今すぐ更新しますか?" + +#. TRANSLATORS: offer to stop asking the question +msgid "Do you want to upload reports automatically for future updates?" +msgstr "今後のアップデートでレポートを自動でアップロードしますか?" + +#. TRANSLATORS: command line option +msgid "Don't prompt for authentication (less details may be shown)" +msgstr "認証を求めない (表示される詳細情報が少なくなる場合があります)" + +#. TRANSLATORS: success +msgid "Done!" +msgstr "完了!" + +#. TRANSLATORS: message letting the user know an downgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Downgrade %s from %s to %s?" +msgstr "%s を %s から %s にダウングレードしますか?" + +#. TRANSLATORS: command description +msgid "Downgrades the firmware on a device" +msgstr "デバイスのファームウェアをダウングレードする" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Downgrading %s…" +msgstr "%s をダウングレードしています…" + +#. TRANSLATORS: command description +msgid "Download a file" +msgstr "ファイルをダウンロード" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "ダウンロード中…" + +#. TRANSLATORS: command description +msgid "Dump SMBIOS data from a file" +msgstr "ファイルから SMBIOS データをダンプする" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Duration" +msgstr "所要時間" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "EMULATION-FILE [ARCHIVE-FILE]" +msgstr "EMULATION-FILE [ARCHIVE-FILE]" + +#. TRANSLATORS: longer description +msgid "Each system should have tests to ensure firmware security." +msgstr "各システムには、ファームウェアセキュリティを確保するためのテストが必要です。" + +#. TRANSLATORS: command description +msgid "Emulate a device using a JSON manifest" +msgstr "JSON マニフェストを使用してデバイスをエミュレート" + +#. TRANSLATORS: this device is not actually real +msgid "Emulated" +msgstr "エミュレート済み" + +#. TRANSLATORS: Title: if we are emulating a different host +msgid "Emulated host" +msgstr "ホストエミュレーション" + +msgid "Enable" +msgstr "有効" + +msgid "Enable emulation data collection" +msgstr "エミュレーションデータ収集を有効にする" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "新しいリモートを有効にしますか?" + +#. TRANSLATORS: Turn on the remote +msgid "Enable this remote?" +msgstr "このリモートを有効にしますか?" + +#. TRANSLATORS: if the remote is enabled +#. TRANSLATORS: Suffix: the HSI result +msgid "Enabled" +msgstr "有効" + +#. TRANSLATORS: Plugin is active only if hardware is found +msgid "Enabled if hardware matches" +msgstr "ハードウェアが一致する場合に有効" + +#. TRANSLATORS: command description +msgid "Enables a given remote" +msgstr "指定されたリモートを有効にする" + +#. TRANSLATORS: command description +msgid "Enables virtual testing devices" +msgstr "仮想テストデバイスを有効にする" + +#. TRANSLATORS: longer description +msgid "Enabling firmware updates for the BIOS allows fixing security issues." +msgstr "BIOS のファームウェアアップデートを有効にすることで、セキュリティ問題を修正できます。" + msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." -msgstr "この機能を有効にすることは、お客様自身の責任で行ってください。つまり、これらの更新によって発生した問題については、製造元のメーカーに連絡する必要があります。更新プロセス自体に関する問題だけを $OS_RELEASE:BUG_REPORT_URL$ へ提出してください。" +msgstr "この機能を有効にすることは、お客様自身の責任で行ってください。つまり、これらの更新によって発生した問題については、製造元に連絡する必要があります。更新プロセス自体に関する問題だけを $OS_RELEASE:BUG_REPORT_URL$ へ提出してください。" + +#. TRANSLATORS: show the user a warning +msgid "Enabling this remote is done at your own risk." +msgstr "このリモートの有効化は自己責任で行ってください。" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Encrypted" +msgstr "暗号化済み" + +#. TRANSLATORS: Title: Memory contents are encrypted, e.g. Intel TME +msgid "Encrypted RAM" +msgstr "暗号化 RAM" + +msgid "Encrypted RAM makes it impossible for information that is stored in device memory to be read if the memory chip is removed and accessed." +msgstr "暗号化 RAM により、メモリチップを取り外してアクセスしても、デバイスメモリに保存された情報を読み取ることができなくなります。" + +#. TRANSLATORS: the vendor is no longer supporting the device +msgid "End of life" +msgstr "サポート終了" + +#. TRANSLATORS: The BIOS setting can only be changed to fixed values +msgid "Enumeration" +msgstr "列挙型" + +#. TRANSLATORS: command description +msgid "Erase all firmware update history" +msgstr "すべてのファームウェア更新履歴を消去する" + +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "消去しています…" + +#. TRANSLATORS: exit after we've started up, used for user profiling +msgid "Exit after a small delay" +msgstr "起動後、短時間で終了" + +#. TRANSLATORS: exit straight away, used for automatic profiling +msgid "Exit after the engine has loaded" +msgstr "エンジン読み込み後、即座に終了" + +#. TRANSLATORS: command description +msgid "Export a firmware file structure to XML" +msgstr "ファームウェアファイル構造をXMLにエクスポートする" + +#. TRANSLATORS: command description +msgid "Export firmware history for manual upload" +msgstr "手動アップロード用にファームウェア履歴をエクスポートする" + +#. TRANSLATORS: command description +msgid "Extract a firmware blob to images" +msgstr "ファームウェアブロブからイメージを抽出する" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE" +msgstr "FILE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILE [DEVICE-ID|GUID]" +msgstr "FILE [DEVICE-ID|GUID]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME" msgstr "ファイル名" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME CERTIFICATE PRIVATE-KEY" +msgstr "FILENAME CERTIFICATE PRIVATE-KEY" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "FILENAME DEVICE-ID [VERSION]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" +msgstr "FILENAME OFFSET DATA [FIRMWARE-TYPE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [DEVICE-ID|GUID]" +msgstr "FILENAME [DEVICE-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME [FIRMWARE-TYPE]" +msgstr "FILENAME [FIRMWARE-TYPE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" +msgstr "FILENAME-SRC FILENAME-DST [FIRMWARE-TYPE-SRC] [FIRMWARE-TYPE-DST]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" +msgstr "FILENAME|CHECKSUM1[,CHECKSUM2][,CHECKSUM3]" + +#. TRANSLATORS: the update state of the specific device +msgid "Failed" +msgstr "失敗" + +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "アップデートの適用に失敗しました" + +#. TRANSLATORS: error message for Windows +msgid "Failed to connect to Windows service, please ensure it's running." +msgstr "Windows サービスへの接続に失敗しました。サービスが実行されていることを確認してください。" + +#. TRANSLATORS: could not contact the fwupd service over D-Bus +msgid "Failed to connect to daemon" +msgstr "デーモンへの接続に失敗しました" + +#. TRANSLATORS: could not read existing system data +#. TRANSLATORS: could not read file +msgid "Failed to load local dbx" +msgstr "ローカルの dbx の読み込みに失敗しました" + +#. TRANSLATORS: could not read existing system data +msgid "Failed to load system dbx" +msgstr "システムの dbx の読み込みに失敗しました" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "ロックに失敗しました" + #. TRANSLATORS: the user didn't read the man page msgid "Failed to parse arguments" msgstr "引数の解析に失敗しました" @@ -98,6 +1301,392 @@ msgid "Failed to parse file" msgstr "ファイルの解析に失敗しました" +#. TRANSLATORS: the user didn't read the man page, %1 is '--filter' +#. TRANSLATORS: the user didn't read the man page, +#. * %1 is '--filter-release' +#. TRANSLATORS: the user didn't read the man page, %1 is '--filter' +#. TRANSLATORS: the user didn't read the man page, +#. * %1 is '--filter-release' +#, c-format +msgid "Failed to parse flags for %s" +msgstr "%s のフラグ解析に失敗しました" + +#. TRANSLATORS: could not parse file +msgid "Failed to parse local dbx" +msgstr "ローカルの dbx の解析に失敗しました" + +#. TRANSLATORS: a feature is something like "can show an image" +msgid "Failed to set front-end features" +msgstr "フロントエンド機能の設定に失敗しました" + +#. TRANSLATORS: something with a blocked hash exists +#. * in the users ESP -- which would be bad! +msgid "Failed to validate ESP contents" +msgstr "ESP の内容検証に失敗しました" + +#. TRANSLATORS: item is FALSE +msgid "False" +msgstr "False" + +#. TRANSLATORS: filename of the local file +msgid "Filename" +msgstr "ファイル名" + +#. TRANSLATORS: filename of the local file +msgid "Filename Signature" +msgstr "ファイル名シグネチャ" + +#. TRANSLATORS: full path of the remote.conf file +msgid "Filename Source" +msgstr "ファイル名ソース" + +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +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: command line option +msgid "Filter with a set of release flags using a ~ prefix to exclude, e.g. 'trusted-release,~trusted-metadata'" +msgstr "リリースフラグのセットを使用してフィルターします。除外するには ~ プレフィックスを使用します。例: 'trusted-release,~trusted-metadata'" + +#. TRANSLATORS: command description +msgid "Finds firmware releases from the metadata" +msgstr "メタデータからファームウェアのリリースを探す" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware Attestation" +msgstr "ファームウェア認証" + +#. TRANSLATORS: longer description +msgid "Firmware Attestation checks device software using a reference copy, to make sure that it has not been changed." +msgstr "ファームウェア認証は、参照版を使用してデバイスソフトウェアをチェックし、改ざんされていないことを確認します。" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware BIOS Descriptor" +msgstr "ファームウェア BIOS ディスクリプタ" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Descriptor protects device firmware memory from being tampered with." +msgstr "ファームウェア BIOS ディスクリプタは、デバイスファームウェア領域を改ざんから保護します。" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "ファームウェア BIOS リージョン" + +#. TRANSLATORS: longer description +msgid "Firmware BIOS Region protects device firmware memory from being tampered with." +msgstr "ファームウェア BIOS リージョンは、デバイスファームウェア領域を改ざんから保護します。" + +#. TRANSLATORS: remote URI +msgid "Firmware Base URI" +msgstr "ファームウェアベース URI" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "ファームウェアアップデート D-Bus サービス" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "ファームウェア更新デーモン" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "ファームウェアアップデータ検証" + +msgid "Firmware Updater Verification checks that software used for updating has not been tampered with." +msgstr "ファームウェアアップデーター検証は、アップデートに使用されるソフトウェアが改ざんされていないことを確認します。" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "ファームウェアアップデート" + +#. TRANSLATORS: program name +msgid "Firmware Utility" +msgstr "ファームウェアユーティリティ" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "ファームウェア書き込み保護" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "ファームウェア書き込みロック" + +#. TRANSLATORS: longer description +msgid "Firmware Write Protection protects device firmware memory from being tampered with." +msgstr "ファームウェア書き込み保護は、デバイスファームウェア領域を改ざんから保護します。" + +#. TRANSLATORS: Title: if we can verify the firmware checksums +msgid "Firmware attestation" +msgstr "ファームウェア認証" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is already blocked" +msgstr "ファームウェアはブロックされています" + +#. TRANSLATORS: user selected something not possible +msgid "Firmware is not already blocked" +msgstr "ファームウェアはブロックされていません" + +#. TRANSLATORS: the metadata is very out of date; %u is a number > 1 +#, c-format +msgid "Firmware metadata has not been updated for %u day and may not be up to date." +msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." +msgstr[0] "ファームウェアメタデータが %u 日間更新されておらず、最新でない可能性があります。" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "ファームウェアアップデート" + +#. TRANSLATORS: user needs to run a command, %1 is 'fwupdmgr unlock' +#, c-format +msgid "Firmware updates disabled; run '%s' to enable" +msgstr "ファームウェアアップデートは無効です。有効にするには '%s' を実行してください" + +#. TRANSLATORS: command description +msgid "Fix a specific host security attribute" +msgstr "特定のホストセキュリティ属性を修正する" + +#. TRANSLATORS: we've fixed a security problem on the machine +msgid "Fix reverted successfully" +msgstr "修正の取り消しが完了しました" + +#. TRANSLATORS: we've fixed a security problem on the machine +msgid "Fixed successfully" +msgstr "修正が完了しました" + +#. TRANSLATORS: description of plugin state, e.g. disabled +msgid "Flags" +msgstr "フラグ" + +#. TRANSLATORS: command line option +msgid "Force the action by relaxing some runtime checks" +msgstr "実行時チェックを緩和してアクションを強制する" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Found" +msgstr "検出" + +#. TRANSLATORS: title text, shown as a warning +msgid "Full Disk Encryption Detected" +msgstr "ディスク全体の暗号化を検出" + +#. TRANSLATORS: we might ask the user the recovery key when next booting +#. Windows +msgid "Full disk encryption secrets may be invalidated when updating" +msgstr "アップデート時にフルディスク暗号化のシークレットが無効になる場合があります" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused Platform" +msgstr "Fused プラットフォーム" + +#. TRANSLATORS: Title: if the part has been fused +msgid "Fused platform" +msgstr "Fused プラットフォーム" + +#. TRANSLATORS: global ID common to all similar hardware +msgid "GUID" +msgid_plural "GUIDs" +msgstr[0] "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgctxt "command-argument" +msgid "GUID" +msgstr "GUID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "GUID|DEVICE-ID" +msgstr "GUID|DEVICE-ID" + +msgid "Get BIOS settings" +msgstr "BIOS 設定を取得" + +#. TRANSLATORS: command description +msgid "Get all device flags supported by fwupd" +msgstr "fwupd でサポートされているすべてのデバイスフラグを取得する" + +#. TRANSLATORS: command description +msgid "Get all devices that support firmware updates" +msgstr "ファームウェアアップデートに対応しているすべてのデバイスを取得する" + +#. TRANSLATORS: command description +msgid "Get all enabled plugins registered with the system" +msgstr "システムに登録された有効なプラグインをすべて取得する" + +#. TRANSLATORS: command description +msgid "Get all known version formats" +msgstr "既知のバージョン形式をすべて取得する" + +#. TRANSLATORS: command description +msgid "Get device report metadata" +msgstr "デバイスレポートのメタデータを取得します" + +#. TRANSLATORS: command description +msgid "Gets details about a firmware file" +msgstr "ファームウェアファイルの詳細を取得する" + +#. TRANSLATORS: command description +msgid "Gets the configured remotes" +msgstr "設定されたリモートを取得する" + +#. TRANSLATORS: command description +msgid "Gets the host security attributes" +msgstr "ホストセキュリティ属性を取得する" + +#. TRANSLATORS: firmware approved by the admin +msgid "Gets the list of approved firmware" +msgstr "承認済みファームウェアの一覧を取得する" + +#. TRANSLATORS: command description +msgid "Gets the list of blocked firmware" +msgstr "ブロックされたファームウェアのリストを取得" + +#. TRANSLATORS: command description +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "指定されたすべてのデバイス、または未指定の場合はすべてのデバイスの更新リストを取得する" + +#. TRANSLATORS: command description +msgid "Gets the releases for a device" +msgstr "デバイスの利用可能なファームウェアバージョンを取得する" + +#. TRANSLATORS: command description +msgid "Gets the results from the last update" +msgstr "前回のアップデート結果を取得する" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "HWIDS-FILE" +msgstr "HWIDS-FILE" + +#. TRANSLATORS: the hardware is waiting to be replugged +msgid "Hardware is waiting to be replugged" +msgstr "ハードウェアの再接続を待機しています" + +#. TRANSLATORS: the release urgency +msgid "High" +msgstr "高" + +#. TRANSLATORS: title for host security events +msgid "Host Security Events" +msgstr "ホストセキュリティイベント" + +#. TRANSLATORS: error message for unsupported feature +msgid "Host Security ID (HSI) is not supported" +msgstr "ホストセキュリティ ID (HSI) はサポートされていません" + +#. TRANSLATORS: success, so say thank you to the user +msgid "Host Security ID attributes uploaded successfully, thanks!" +msgstr "ホストセキュリティ ID 属性をアップロードしました。ありがとうございます!" + +#. TRANSLATORS: this is a string like 'HSI:2-U' +msgid "Host Security ID:" +msgstr "ホストセキュリティ ID:" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX" +msgstr "INDEX" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX KEY [VALUE]" +msgstr "INDEX KEY [VALUE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX NAME TARGET [MOUNTPOINT]" +msgstr "INDEX NAME TARGET [MOUNTPOINT]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INDEX1,INDEX2" +msgstr "INDEX1,INDEX2" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "INHIBIT-ID" +msgstr "INHIBIT-ID" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "IOMMU 保護" + +#. TRANSLATORS: longer description +msgid "IOMMU Protection prevents connected devices from accessing unauthorized parts of system memory." +msgstr "IOMMU 保護は、接続されたデバイスがシステムメモリへの不正アクセスを防止します。" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection disabled" +msgstr "IOMMU デバイス保護が無効" + +#. TRANSLATORS: HSI event title +msgid "IOMMU device protection enabled" +msgstr "IOMMU デバイス保護が有効" + +#. TRANSLATORS: daemon is inactive +msgid "Idle…" +msgstr "待機中…" + +#. TRANSLATORS: command line option +msgid "Ignore SSL strict checks when downloading files" +msgstr "ファイルダウンロード時に SSL 厳密チェックを無視する" + +#. TRANSLATORS: command line option +msgid "Ignore firmware checksum failures" +msgstr "ファームウェアチェックサム失敗を無視する" + +#. TRANSLATORS: command line option +msgid "Ignore firmware hardware mismatch failures" +msgstr "ファームウェアとハードウェアの不整合失敗を無視する" + +#. TRANSLATORS: command line option +msgid "Ignore non-critical firmware requirements" +msgstr "非重要なファームウェア要件を無視する" + +#. TRANSLATORS: try to help +msgid "Ignoring SSL strict checks, to do this automatically in the future export DISABLE_SSL_STRICT in your environment" +msgstr "SSL の厳格なチェックを無視し、将来的にこれを自動化するには、環境変数 DISABLE_SSL_STRICT をエクスポートしてください" + +#. TRANSLATORS: show the user a generic image that can be themed +msgid "Image" +msgstr "画像" + +#. TRANSLATORS: show the user a random image from the internet +msgid "Image (custom)" +msgstr "画像 (カスタム)" + +#. TRANSLATORS: the inhibit ID is a short string like dbus-123456 +#, c-format +msgid "Inhibit ID is %s." +msgstr "抑制 ID は %s です。" + +#. TRANSLATORS: command description +msgid "Inhibit the system to prevent upgrades" +msgstr "アップグレードを防ぐためにシステムをロック" + +#. TRANSLATORS: length of time the update takes to apply +msgid "Install Duration" +msgstr "インストール所要時間" + +#. TRANSLATORS: command description +msgid "Install a firmware file in cabinet format on this hardware" +msgstr "選択したデバイスにキャビネット形式のファームウェアファイルをインストールする" + +#. TRANSLATORS: command description +msgid "Install a raw firmware blob on a device" +msgstr "デバイスに生のファームウェアブロブをインストールする" + +#. TRANSLATORS: command description +msgid "Install a specific firmware file on all devices that match" +msgstr "条件に一致するすべてのデバイスに特定のファームウェアファイルをインストールする" + +#. TRANSLATORS: command description +msgid "Install a specific firmware on a device, all possible devices will also be installed once the CAB matches" +msgstr "デバイスに特定のファームウェアをインストールする。CAB が一致すると、対応するすべてのデバイスにもファームウェアがインストールされる" + msgid "Install old version of signed system firmware" msgstr "旧版の署名済みシステムファームウェアを導入する" @@ -116,33 +1705,1015 @@ msgid "Install unsigned system firmware" msgstr "署名のないシステムファームウェアを導入する" +#. TRANSLATORS: stay on one firmware version unless the new version is +#. explicitly +#. * specified +msgid "Installing a specific release is explicitly required" +msgstr "特定のリリースを明示的にインストールする必要があります" + #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" msgstr "ファームウェアの更新をインストールしています…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "%s をインストール中…" + +#. TRANSLATORS: if it breaks, you get to keep both pieces +msgid "Installing this update may also void any device warranty." +msgstr "このファームウェアアップデートをインストールすると、機器の保証が無効になる可能性があります。" + +#. TRANSLATORS: The BIOS setting only accepts integers in a fixed range +msgid "Integer" +msgstr "整数" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard" +msgstr "Intel BootGuard" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM Protected" +msgstr "Intel BootGuard ACM 保護済み" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * ACM means to verify the integrity of Initial Boot Block +msgid "Intel BootGuard ACM protected" +msgstr "Intel BootGuard ACM 保護済み" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard Error Policy" +msgstr "Intel BootGuard エラーポリシー" + +msgid "Intel BootGuard Error Policy ensures the device does not continue to start if its device software has been tampered with." +msgstr "Intel BootGuard エラーポリシーは、デバイスソフトウェアが改ざんされた場合、デバイスの起動を停止します。" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel +msgid "Intel BootGuard Fuse" +msgstr "Intel BootGuard Fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * OTP = one time programmable +msgid "Intel BootGuard OTP fuse" +msgstr "Intel BootGuard OTP fuse" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard Verified Boot" +msgstr "Intel BootGuard 検証済みブート" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * error policy is what to do on failure +msgid "Intel BootGuard error policy" +msgstr "Intel BootGuard エラーポリシー" + +#. TRANSLATORS: longer description +msgid "Intel BootGuard prevents unauthorized device software from operating when the device is started." +msgstr "Intel BootGuard は、デバイス起動時に許可されていないデバイスソフトウェアの動作を防止します。" + +#. TRANSLATORS: Title: BootGuard is a trademark from Intel, +#. * verified boot refers to the way the boot process is verified +msgid "Intel BootGuard verified boot" +msgstr "Intel BootGuard 検証済みブート" + +#. TRANSLATORS: Title: GDS is where the CPU leaks information +msgid "Intel GDS Mitigation" +msgstr "Intel GDS 軽減機能" + +#. TRANSLATORS: Title: GDS is where the CPU leaks information +msgid "Intel GDS mitigation" +msgstr "Intel GDS 軽減機能" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Manufacturing Mode" +msgstr "インテル・マネジメント・エンジン 製造モード" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the "override" is +#. enabled +#. * with a jumper -- luckily it is probably not accessible to end users on +#. consumer +#. * boards +msgid "Intel Management Engine Override" +msgstr "インテル・マネジメント・エンジン オーバーライド" + +#. TRANSLATORS: longer description +msgid "Intel Management Engine Override disables checks for device software tampering." +msgstr "インテル・マネジメント・エンジン オーバーライドは、デバイスソフトウェア改ざん検出機能を無効化します。" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "Intel Management Engine Version" +msgstr "インテル・マネジメント・エンジン バージョン" + +#. TRANSLATORS: Device cannot be removed easily +msgid "Internal device" +msgstr "内蔵デバイス" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Invalid" +msgstr "無効" + +#. TRANSLATORS: error message +msgid "Invalid arguments" +msgstr "無効な引数" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected GUID" +msgstr "無効な引数です。GUID を指定してください" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected INDEX KEY [VALUE]" +msgstr "無効な引数です。引数は INDEX KEY [VALUE] の形式で指定してください" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected INDEX NAME TARGET [MOUNTPOINT]" +msgstr "無効な引数です。引数は INDEX NAME TARGET [MOUNTPOINT] の形式で指定してください" + +#. TRANSLATOR: This is the error message for +#. * incorrect parameter +msgid "Invalid arguments, expected an AppStream ID" +msgstr "無効な引数です。AppStream ID を指定してください" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected at least ARCHIVE FIRMWARE METAINFO" +msgstr "引数が無効です。少なくとも ARCHIVE FIRMWARE METAINFO が必要です" + +#. TRANSLATORS: error message +msgid "Invalid arguments, expected base-16 integer" +msgstr "引数が無効です。16進数の整数が必要です" + +#. TRANSLATORS: version is older +msgid "Is downgrade" +msgstr "ダウングレードです" + +#. TRANSLATORS: Is currently in bootloader mode +msgid "Is in bootloader mode" +msgstr "ブートローダーモードです" + +#. TRANSLATORS: version is newer +msgid "Is upgrade" +msgstr "アップグレードです" + +#. TRANSLATORS: issue fixed with the release, e.g. CVE +msgid "Issue" +msgid_plural "Issues" +msgstr[0] "解決された問題" + +#. TRANSLATORS: HSI event title +msgid "Kernel is no longer tainted" +msgstr "カーネルは汚染されていません" + +#. TRANSLATORS: HSI event title +msgid "Kernel is tainted" +msgstr "カーネルが汚染されました" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown disabled" +msgstr "カーネルロックダウンが無効" + +#. TRANSLATORS: HSI event title +msgid "Kernel lockdown enabled" +msgstr "カーネルロックダウンが有効" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "LOCATION" +msgstr "LOCATION" + +#. TRANSLATORS: the original time/date the device was modified +msgid "Last modified" +msgstr "最終変更日時" + +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "残り 1 分未満です" + +#. TRANSLATORS: e.g. GPLv2+, Proprietary etc +msgid "License" +msgstr "ライセンス" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "Linux カーネルのロックダウン" + +#. TRANSLATORS: longer description +msgid "Linux Kernel Lockdown mode prevents administrator (root) accounts from accessing and changing critical parts of system software." +msgstr "Linux カーネルのロックダウンは、管理者 (root) 権限による重要システムソフトウェアへの変更を阻止します。" + +msgid "Linux Kernel Swap temporarily saves information to disk as you work. If the information is not protected, it could be accessed by someone if they obtained the disk." +msgstr "Linux カーネルスワップは、作業中のデータを一時的にディスクへ退避させます。保護されていないデータは外部から読み取られるリスクがあります。" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "Linux カーネル検証" + +msgid "Linux Kernel Verification makes sure that critical system software has not been tampered with. Using device drivers which are not provided with the system can prevent this security feature from working correctly." +msgstr "Linux カーネル検証は、重要なシステムソフトウェアが改ざんされていないことを保証します。 システム提供外のデバイスドライバ使用時は、このセキュリティ機能が正しく働かない場合があります。" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "Linux スワップ" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "Linux ベンダーファームウェアサービス (安定版ファームウェア)" msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "Linux ベンダーファームウェアサービス (試験版ファームウェア)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "Linux カーネル" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "Linux カーネルのロックダウン" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "Linux スワップ" + +#. TRANSLATORS: command description +msgid "List EFI boot files" +msgstr "EFI ブートファイルを一覧表示する" + +#. TRANSLATORS: command description +msgid "List EFI boot parameters" +msgstr "EFI ブートパラメータを一覧表示する" + +#. TRANSLATORS: command description +msgid "List EFI variables with a specific GUID" +msgstr "特定の GUID を持つ EFI 変数を一覧表示する" + +#. TRANSLATORS: command line option +msgid "List entries in dbx" +msgstr "dbx のエントリを一覧表示する" + +#. TRANSLATORS: command description +msgid "List the available firmware GTypes" +msgstr "利用可能なファームウェア GType を一覧表示する" + +#. TRANSLATORS: command description +msgid "List the available firmware types" +msgstr "利用可能なファームウェアタイプを一覧表示する" + +#. TRANSLATORS: command description +msgid "Lists files on the ESP" +msgstr "ESP 上のファイルを一覧表示する" + +#. TRANSLATORS: command description +msgid "Load device emulation data" +msgstr "デバイスエミュレーションデータを読み込む" + +#. TRANSLATORS: the plugin was created from a .so object, and was not built-in +msgid "Loaded from an external module" +msgstr "外部モジュールから読み込まれました" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "ロード中…" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Locked" +msgstr "ロック済み" + +#. TRANSLATORS: the release urgency +msgid "Low" +msgstr "低" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and key refers +#. * to the private/public key used to secure loading of firmware +msgid "MEI Key Manifest" +msgstr "MEI キーマニフェスト" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and key refer +#. * to the private/public key used to secure loading of firmware +msgid "MEI key manifest" +msgstr "MEI キーマニフェスト" + +#. TRANSLATORS: Title: MEI = Intel Management Engine +msgid "MEI manufacturing mode" +msgstr "MEI 製造モード" + +#. TRANSLATORS: Title: MEI = Intel Management Engine, and the +#. * "override" is the physical PIN that can be driven to +#. * logic high -- luckily it is probably not accessible to +#. * end users on consumer boards +msgid "MEI override" +msgstr "MEI オーバーライド" + +msgid "MEI version" +msgstr "MEI バージョン" + +#. TRANSLATORS: command line option +msgid "Manually enable specific plugins" +msgstr "特定のプラグインを手動で有効にする" + +#. TRANSLATORS: longer description +msgid "Manufacturing Mode is used when the device is manufactured and security features are not yet enabled." +msgstr "製造モードはデバイスの製造時に使用され、セキュリティ機能がまだ有効化されていません。" + +#. TRANSLATORS: Longest valid string for BIOS setting +msgid "Maximum length" +msgstr "最大長" + +#. TRANSLATORS: Highest valid integer for BIOS setting +msgid "Maximum value" +msgstr "最大値" + +#. TRANSLATORS: the release urgency +msgid "Medium" +msgstr "中" + +#. TRANSLATORS: ask the user to do a simple task which should be translated +msgid "Message" +msgstr "メッセージ" + +#. TRANSLATORS: ask the user a question, and it will not be translated +msgid "Message (custom)" +msgstr "メッセージ (カスタム)" + +#. TRANSLATORS: remote URI +msgid "Metadata Signature" +msgstr "メタデータシグネチャ" + +#. TRANSLATORS: remote URI +msgid "Metadata URI" +msgstr "メタデータ URI" + +#. TRANSLATORS: explain why no metadata available +msgid "Metadata can be obtained from the Linux Vendor Firmware Service." +msgstr "メタデータは Linux ベンダーファームウェアサービスから取得できます。" + +#. TRANSLATORS: error message for a user who ran fwupdmgr +#. * refresh recently -- %1 is '--force' +#, c-format +msgid "Metadata is up to date; use %s to refresh again." +msgstr "メタデータは最新です。再度更新する場合は %s を使用してください。" + +#. TRANSLATORS: smallest version number installable on device +msgid "Minimum Version" +msgstr "最小バージョン" + +#. TRANSLATORS: Shortest valid string for BIOS setting +msgid "Minimum length" +msgstr "最小長" + +#. TRANSLATORS: Lowest valid integer for BIOS setting +msgid "Minimum value" +msgstr "最小値" + +#. TRANSLATORS: sets something in the daemon configuration file +msgid "Modifies a daemon configuration value" +msgstr "デーモンの設定値を変更する" + +#. TRANSLATORS: command description +msgid "Modifies a given remote" +msgstr "指定されたリモートを変更する" + msgid "Modify a configured remote" msgstr "遠隔構成を変更する" msgid "Modify daemon configuration" msgstr "デーモン構成を変更する" +#. TRANSLATORS: command description +msgid "Monitor the daemon for events" +msgstr "デーモンのイベントを監視する" + +#. TRANSLATORS: command description +msgid "Mounts the ESP" +msgstr "ESP をマウント" + +#. TRANSLATORS: Requires a reboot to apply firmware or to reload hardware +msgid "Needs a reboot after installation" +msgstr "インストール後に再起動が必要です" + +#. TRANSLATORS: the update state of the specific device +msgid "Needs reboot" +msgstr "再起動が必要です" + +#. TRANSLATORS: Requires system shutdown to apply firmware +msgid "Needs shutdown after installation" +msgstr "インストール後にシャットダウンが必要です" + +#. TRANSLATORS: version number of new firmware +msgid "New version" +msgstr "新バージョン" + +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "アクションが指定されていません!" + +#. TRANSLATORS: no devices that can be upgraded with new firmware +msgid "No devices are updatable" +msgstr "更新可能なデバイスはありません" + +#. TRANSLATORS: message letting the user know no device downgrade available +#. * %1 is the device name +#, c-format +msgid "No downgrades for %s" +msgstr "%s にダウングレードはありません" + +#. TRANSLATORS: nothing found +msgid "No firmware IDs found" +msgstr "ファームウェア ID が見つかりません" + +#. TRANSLATORS: nothing found +msgid "No firmware found" +msgstr "ファームウェアが見つかりません" + +#. TRANSLATORS: nothing attached that can be upgraded +msgid "No hardware detected with firmware update capability" +msgstr "ファームウェアアップデートに対応したハードウェアは検出されませんでした" + +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +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: this is an error string +msgid "No updatable devices" +msgstr "アップデート可能なデバイスがありません" + +#. TRANSLATORS: this is an error string +msgid "No updates available" +msgstr "アップデートはありません" + +msgid "No updates available for remaining devices" +msgstr "その他のデバイスにはアップデートがありません" + +#. TRANSLATORS: error message +#, c-format +msgid "No volume matched %s" +msgstr "%s に該当するボリュームがありません" + +#. TRANSLATORS: version cannot be installed due to policy +msgid "Not approved" +msgstr "未承認" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "未検出" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not supported" +msgstr "未対応" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "OK" + +#. TRANSLATORS: this is for the device tests +msgid "OK!" +msgstr "OK!" + +#. TRANSLATORS: the firmware old version +msgid "Old version" +msgstr "旧バージョン" + +#. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "エミュレートされたデバイスにのみインストールする" + +#. TRANSLATORS: command line option +msgid "Only show single PCR value" +msgstr "単一の PCR 値のみ表示する" + +#. TRANSLATORS: command line option +msgid "Only use peer-to-peer networking when downloading files" +msgstr "ファイルダウンロード時にピアツーピア通信のみを使用する" + +#. TRANSLATORS: some devices can only be updated to a new semver and cannot +#. * be downgraded or reinstalled with the existing version +msgid "Only version upgrades are allowed" +msgstr "バージョンアップグレードのみが許可されています" + +#. TRANSLATORS: command line option +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "JSON 形式で出力 (対話プロンプトをすべて無効化)" + +#. TRANSLATORS: command line option +msgid "Override the default ESP path" +msgstr "デフォルトの ESP パスをオーバーライドする" + +#. TRANSLATORS: if we can get metadata from peer-to-peer clients +msgid "P2P Firmware" +msgstr "P2P ファームウェア" + +#. TRANSLATORS: if we can get metadata from peer-to-peer clients +msgid "P2P Metadata" +msgstr "P2P メタデータ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "PATH" + +#. TRANSLATORS: command description +msgid "Parse and show details about a firmware file" +msgstr "ファームウェアファイルを解析して詳細を表示する" + +#. TRANSLATORS: reading new dbx from the update +msgid "Parsing dbx update…" +msgstr "dbx アップデートを解析しています…" + +#. TRANSLATORS: reading existing dbx from the system +msgid "Parsing system dbx…" +msgstr "システムの dbx を読み込んで解析しています…" + +#. TRANSLATORS: remote filename base +msgid "Password" +msgstr "パスワード" + +#. TRANSLATORS: command description +msgid "Patch a firmware blob at a known offset" +msgstr "ファームウェアブロブの既知のオフセットにパッチを適用する" + +msgid "Payload" +msgstr "ペイロード" + +#. TRANSLATORS: the update state of the specific device +msgid "Pending" +msgstr "保留中" + +#. TRANSLATORS: prompt to apply the update +msgid "Perform operation?" +msgstr "操作を実行しますか?" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform Debugging" +msgstr "プラットフォームデバッグ" + +#. TRANSLATORS: longer description +msgid "Platform Debugging allows device security features to be disabled. This should only be used by hardware manufacturers." +msgstr "プラットフォームデバッグは、デバイスのセキュリティ機能を無効化します。ハードウェアの製造元のみが使用してください。" + +#. TRANSLATORS: Title: Allows debugging of parts using proprietary hardware +msgid "Platform debugging" +msgstr "プラットフォームデバッグ" + +#. TRANSLATORS: 'recovery key' here refers to a code, rather than a physical +#. metal thing +msgid "Please ensure you have the volume recovery key before continuing." +msgstr "続行する前に、ボリューム回復キーを準備してください。" + +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "0 から %u までの数値を入力してください:" + +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' +#, c-format +msgid "Please enter either %s or %s: " +msgstr "%s か %s のいずれかを入力してください:" + +#. TRANSLATORS: Failed to open plugin, hey Arch users +msgid "Plugin dependencies missing" +msgstr "プラグインの依存関係が見つかりません" + +#. TRANSLATORS: The plugin enumeration might change the device current mode +msgid "Plugin enumeration may change device state" +msgstr "プラグインの列挙によって、デバイスの現在のモードが変更される可能性があります" + +#. TRANSLATORS: The plugin is only for testing +msgid "Plugin is only for testing" +msgstr "このプラグインはテスト専用です" + +#. TRANSLATORS: Possible values for a bios setting +msgid "Possible Values" +msgstr "選択可能な値" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA Protection" +msgstr "プリブート DMA 保護" + +#. TRANSLATORS: Title: DMA as in https://en.wikipedia.org/wiki/DMA_attack +msgid "Pre-boot DMA protection" +msgstr "プリブート DMA 保護" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is disabled" +msgstr "プリブート DMA 保護が無効" + +#. TRANSLATORS: HSI event title +msgid "Pre-boot DMA protection is enabled" +msgstr "プリブート DMA 保護が有効" + +#. TRANSLATORS: longer description +msgid "Pre-boot DMA protection prevents devices from accessing system memory after being connected to the computer." +msgstr "プリブート DMA 保護は、デバイスがコンピューターに接続された後のシステムメモリへの不正なアクセスを防止します。" + +#. TRANSLATORS: warning message +msgid "Press unlock on the device to continue the update process." +msgstr "更新処理を続行するには、デバイス上でロック解除を押してください。" + +#. TRANSLATORS: version number of previous firmware +msgid "Previous version" +msgstr "以前のバージョン" + +#. TRANSLATORS: the numeric priority +msgid "Priority" +msgstr "優先度" + +#. TRANSLATORS: reasons the device is not updatable +msgid "Problems" +msgstr "問題" + +msgid "Proceed with upload?" +msgstr "アップロードを続行しますか?" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Processor Security Checks" +msgstr "プロセッサセキュリティチェック" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "Processor rollback protection" +msgstr "プロセッサのロールバック保護" + +#. TRANSLATORS: a non-free software license +msgid "Proprietary" +msgstr "プロプライエタリ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID" +msgstr "REMOTE-ID" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "REMOTE-ID KEY VALUE" +msgstr "REMOTE-ID KEY VALUE" + +#. TRANSLATORS: BIOS setting is read only +msgid "Read Only" +msgstr "読み取り専用" + +#. TRANSLATORS: command description +msgid "Read a firmware blob from a device" +msgstr "デバイスからファームウェアブロブを読み取る" + +#. TRANSLATORS: command description +msgid "Read a firmware from a device" +msgstr "デバイスからファームウェアを読み取る" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "%s から読み込み中…" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "読み込み中…" + +#. TRANSLATORS: Plugin is active and in use +msgid "Ready" +msgstr "準備完了" + +#. TRANSLATORS: how often we should refresh the metadata +msgid "Refresh Interval" +msgstr "更新間隔" + +#. TRANSLATORS: command description +msgid "Refresh metadata from remote server" +msgstr "リモートサーバーからメタデータを更新する" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 is a version string +#, c-format +msgid "Reinstall %s to %s?" +msgstr "%s を %s に再インストールしますか?" + +#. TRANSLATORS: command description +msgid "Reinstall current firmware on the device" +msgstr "デバイスの現在のファームウェアを再インストールする" + +#. TRANSLATORS: command description +msgid "Reinstall firmware on a device" +msgstr "デバイスのファームウェアを再インストールする" + +#. TRANSLATORS: the stream of firmware, e.g. nonfree +msgid "Release Branch" +msgstr "リリースブランチ" + +#. TRANSLATORS: release attributes +msgid "Release Flags" +msgstr "リリースフラグ" + +#. TRANSLATORS: the exact component on the server +msgid "Release ID" +msgstr "リリース ID" + +#. TRANSLATORS: the server the file is coming from +#. TRANSLATORS: remote identifier, e.g. lvfs-testing +msgid "Remote ID" +msgstr "リモート ID" + +#. TRANSLATORS: command description +msgid "Removes devices to watch for future emulation" +msgstr "エミュレーション用監視デバイスを削除する" + +#. TRANSLATORS: URI to send success/failure reports +msgid "Report URI" +msgstr "レポート URI" + +#. TRANSLATORS: Has been reported to a metadata server +msgid "Reported to remote server" +msgstr "リモートサーバーに報告済み" + +#. TRANSLATORS: the user is using Gentoo/Arch and has screwed something up +msgid "Required efivarfs filesystem was not found" +msgstr "必要な efivarfs ファイルシステムが見つかりませんでした" + +#. TRANSLATORS: not required for this system +msgid "Required hardware was not found" +msgstr "必要なハードウェアが見つかりませんでした" + +#. TRANSLATORS: Requires a bootloader mode to be manually enabled by the user +msgid "Requires a bootloader" +msgstr "ブートローダーが必要です" + +#. TRANSLATORS: metadata is downloaded +msgid "Requires internet connection" +msgstr "インターネット接続が必要です" + msgid "Reset daemon configuration" msgstr "デーモン構成をリセットする" +#. TRANSLATORS: sets something in the daemon configuration file +msgid "Resets a daemon configuration section" +msgstr "デーモンの設定セクションをリセットする" + +#. TRANSLATORS: reboot to apply the update +msgid "Restart now?" +msgstr "今すぐ再起動しますか?" + +#. TRANSLATORS: changes only take effect on restart +msgid "Restart the daemon to make the change effective?" +msgstr "変更を有効にするためにデーモンを再起動しますか?" + +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "デバイスを再起動しています…" + +#. TRANSLATORS: command description +msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" +msgstr "BIOS 設定を取得します。引数未指定時はすべての設定を表示" + +#. TRANSLATORS: command description +msgid "Return all the hardware IDs for the machine" +msgstr "マシンのすべてのハードウェア ID を返す" + +#. TRANSLATORS: ask the user to upload +msgid "Review and upload report now?" +msgstr "レポートを確認して今すぐアップロードしますか?" + +#. TRANSLATORS: longer description +msgid "Rollback Protection prevents device software from being downgraded to an older version that has security problems." +msgstr "ロールバック保護は、セキュリティ上の問題がある古いバージョンへのデバイスソフトウェアのダウングレードを防ぎます。" + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr get-upgrades` +#, c-format +msgid "Run `%s` for more information." +msgstr "詳細情報については `%s` を実行してください。" + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr sync` +#, c-format +msgid "Run `%s` to complete this action." +msgstr "この操作を完了するには `%s` を実行してください。" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite cleanup routine when using install-blob" +msgstr "install-blob 使用時にプラグインコンポジットクリーンアップルーチンを実行する" + +#. TRANSLATORS: command line option +msgid "Run the plugin composite prepare routine when using install-blob" +msgstr "install-blob 使用時にプラグインコンポジット準備ルーチンを実行する" + +#. TRANSLATORS: command description +msgid "Run the post-reboot cleanup action" +msgstr "再起動後のクリーンアップアクションを実行する" + +#. TRANSLATORS: tell a user how to get information +#, c-format +msgid "Run without '%s' to see" +msgstr "詳細を確認するには、'%s' を付けずに実行してください" + +#. TRANSLATORS: The kernel does not support this plugin +msgid "Running kernel is too old" +msgstr "実行中のカーネルが古すぎます" + +#. TRANSLATORS: this is the HSI suffix +msgid "Runtime Suffix" +msgstr "ランタイム接尾辞" + +#. TRANSLATORS: Software Bill of Materials link +msgid "SBOM" +msgstr "SBOM" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SECTION" +msgstr "SECTION" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING VALUE" +msgstr "SETTING VALUE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SETTING1 VALUE1 [SETTING2] [VALUE2]" +msgstr "SETTING1 VALUE1 [SETTING2] [VALUE2]" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "SMAP" +msgstr "SMAP" + +#. TRANSLATORS: Title: Whether firmware is locked down +msgid "SMM locked down" +msgstr "SMM ロックダウン" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS Descriptor" +msgstr "SPI BIOS ディスクリプタ" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI BIOS region" +msgstr "SPI BIOS リージョン" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI ロック" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "SPI replay protection" +msgstr "SPI リプレイ保護" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI 書き込み" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "SPI write protection" +msgstr "SPI 書き込み保護" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" +msgstr "SUBSYSTEM DRIVER [DEVICE-ID|GUID]" + +#. TRANSLATORS: command description +msgid "Save a file that allows generation of hardware IDs" +msgstr "ハードウェア ID 生成用ファイルを保存する" + +#. TRANSLATORS: command description +msgid "Save device emulation data" +msgstr "デバイスエミュレーションデータを保存する" + +#. TRANSLATORS: key for a offline report filename +msgid "Saved report" +msgstr "保存済みレポート" + +#. TRANSLATORS: Scalar increment for integer BIOS setting +msgid "Scalar Increment" +msgstr "増分値" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "スケジューリングしています…" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot disabled" +msgstr "セキュアブートが無効" + +#. TRANSLATORS: HSI event title +msgid "Secure Boot enabled" +msgstr "セキュアブートが有効" + +msgid "Security hardening for HSI" +msgstr "HSI 向けセキュリティ強化" + +#. TRANSLATORS: the %1 is a URL to a wiki page +#, c-format +msgid "See %s for more details." +msgstr "詳細については %s をご覧ください。" + +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "詳細については %s を参照してください。" + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "選択中のデバイス" + +#. TRANSLATORS: Volume has been chosen by the user +msgid "Selected volume" +msgstr "選択中のボリューム" + +#. TRANSLATORS: serial number of hardware +msgid "Serial Number" +msgstr "シリアルナンバー" + +#. TRANSLATORS: Configured a BIOS setting to a value +#, c-format +msgid "Set BIOS setting '%s' using '%s'." +msgstr "BIOS設定「%s」を「%s」に設定しました。" + +#. TRANSLATORS: command description +msgid "Set a BIOS setting" +msgstr "BIOS設定を変更する" + +msgid "Set one or more BIOS settings" +msgstr "BIOS 設定を 1 つ以上設定する" + +#. TRANSLATORS: command description +msgid "Set or remove an EFI boot hive entry" +msgstr "EFIブートハイブのエントリを設定または削除する" + +#. TRANSLATORS: command description +msgid "Set the EFI boot next" +msgstr "次回起動時に使用する EFI ブートエントリを設定する" + +#. TRANSLATORS: command description +msgid "Set the EFI boot order" +msgstr "EFI ブート順序を設定する" + +#. TRANSLATORS: command line option +msgid "Set the download retries for transient errors" +msgstr "一時的エラー発生時のダウンロード再試行回数を設定する" + +#. TRANSLATORS: command description +msgid "Sets one or more BIOS settings" +msgstr "1つ以上のBIOS設定を変更する" + #. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "認証済みファームウェアの一覧を設定する" +#. TRANSLATORS: type of BIOS setting +msgid "Setting type" +msgstr "設定タイプ" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "設定はシステム再起動後に適用されます" + +#. TRANSLATORS: command description +msgid "Share firmware history with the developers" +msgstr "ファームウェア履歴を開発者と共有する" + +#. TRANSLATORS: command line option +msgid "Show all results" +msgstr "すべての結果を表示する" + +#. TRANSLATORS: command line option +msgid "Show client and daemon versions" +msgstr "クライアントとデーモンのバージョンを表示する" + +#. TRANSLATORS: this is for daemon development +msgid "Show daemon verbose information for a particular domain" +msgstr "特定ドメインのデーモン詳細情報を表示する" + +#. TRANSLATORS: turn on all debugging +msgid "Show debugging information for all domains" +msgstr "すべてのドメインのデバッグ情報を表示する" + +#. TRANSLATORS: for the --verbose arg +msgid "Show debugging options" +msgstr "デバッグオプションを表示する" + +#. TRANSLATORS: command line option +msgid "Show devices that are not updatable" +msgstr "更新できないデバイスを表示する" + #. TRANSLATORS: command line option msgid "Show extra debugging information" msgstr "追加のデバッグ情報を表示する" +#. TRANSLATORS: command description +msgid "Show history of firmware updates" +msgstr "ファームウェアの更新履歴を表示する" + +#. TRANSLATORS: command line option +msgid "Show the calculated version of the dbx" +msgstr "dbx の計算済みバージョンを表示する" + +#. TRANSLATORS: shutdown to apply the update +msgid "Shutdown now?" +msgstr "今すぐシャットダウンしますか?" + +#. TRANSLATORS: command description +msgid "Sign a firmware with a new key" +msgstr "新しいキーでファームウェアに署名する" + msgid "Sign data using the client certificate" msgstr "クライアント証明書を用いてデータに署名する" @@ -151,18 +2722,869 @@ msgid "Sign data using the client certificate" msgstr "クライアント証明書を用いてデータに署名する" +#. TRANSLATORS: command line option +msgid "Sign the uploaded data with the client certificate" +msgstr "アップロードされたデータをクライアント証明書で署名する" + +msgid "Signature" +msgstr "署名" + +#. TRANSLATORS: firmware is verified on-device the payload using strong crypto +msgid "Signed Payload" +msgstr "署名済みペイロード" + +#. TRANSLATORS: file size of the download +msgid "Size" +msgstr "サイズ" + +#. TRANSLATORS: the platform secret is stored in the PCRx registers on the TPM +msgid "Some of the platform secrets may be invalidated when updating this firmware." +msgstr "ファームウェアを更新すると、一部のプラットフォームシークレットが無効になる可能性があります。" + +#. TRANSLATORS: source (as in code) link +msgid "Source" +msgstr "ソース" + +#. TRANSLATORS: command line option +msgid "Specify the dbx database file" +msgstr "dbx のデータベースファイルを指定する" + msgid "Stop the fwupd service" msgstr "fwupd サービスを停止する" +#. TRANSLATORS: The BIOS setting accepts strings +msgid "String" +msgstr "文字列" + +#. TRANSLATORS: the update state of the specific device +msgid "Success" +msgstr "成功" + +#. TRANSLATORS: success message -- where activation is making the new +#. * firmware take effect, usually after updating offline +msgid "Successfully activated all devices" +msgstr "すべてのデバイスを有効化しました" + +#. TRANSLATORS: success message +msgid "Successfully disabled remote" +msgstr "リモートを正常に無効にしました" + +#. TRANSLATORS: comment explaining result of command +msgid "Successfully disabled test devices" +msgstr "テストデバイスを正常に無効にしました" + +#. TRANSLATORS: success message -- where 'metadata' is information +#. * about available firmware on the remote server +msgid "Successfully downloaded new metadata:" +msgstr "新しいメタデータのダウンロードに成功しました:" + +#. TRANSLATORS: success message +msgid "Successfully enabled and refreshed remote" +msgstr "リモートを有効化し、更新しました" + +#. TRANSLATORS: success message +msgid "Successfully enabled remote" +msgstr "リモートを正常に有効にしました" + +#. TRANSLATORS: comment explaining result of command +msgid "Successfully enabled test devices" +msgstr "テストデバイスを正常に有効にしました" + +#. TRANSLATORS: success message +msgid "Successfully installed firmware" +msgstr "ファームウェアのインストールが完了しました" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully modified configuration value" +msgstr "設定値を正常に変更しました" + +#. TRANSLATORS: success message for a per-remote setting change +msgid "Successfully modified remote" +msgstr "リモートを正常に変更しました" + +#. TRANSLATORS: success message -- the user can do this by-hand too +msgid "Successfully refreshed metadata manually" +msgstr "メタデータを手動で正常に更新しました" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully reset configuration section" +msgstr "設定セクションを正常にリセットしました" + +#. TRANSLATORS: success message -- a per-system setting value +msgid "Successfully reset configuration values" +msgstr "設定値をリセットしました" + +#. TRANSLATORS: success message when user refreshes device checksums +msgid "Successfully updated device checksums" +msgstr "デバイスのチェックサムを正常に更新しました" + +#. TRANSLATORS: success message -- where the user has uploaded +#. * success and/or failure reports to the remote server +#, c-format +msgid "Successfully uploaded %u report" +msgid_plural "Successfully uploaded %u reports" +msgstr[0] "%u 件のレポートを正常にアップロードしました" + +#. TRANSLATORS: success message when user verified device checksums +msgid "Successfully verified device checksums" +msgstr "デバイスのチェックサムを正常に検証しました" + +#. TRANSLATORS: the device showed up in time +#, c-format +msgid "Successfully waited %.0fms for device" +msgstr "デバイスが %.0f ミリ秒以内に認識されました" + +#. TRANSLATORS: one line summary of device +msgid "Summary" +msgstr "デバイスの概要" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "Supervisor Mode Access Prevention" +msgstr "スーパーバイザーモードアクセス防止" + +#. TRANSLATORS: longer description +msgid "Supervisor Mode Access Prevention ensures critical parts of device memory are not accessed by less secure programs." +msgstr "スーパーバイザーモードアクセス防止は、低権限プログラムによるデバイスの重要メモリ領域へのアクセスを阻止します。" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "対応済み" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "対応 CPU" + +#. TRANSLATORS: Is found in current metadata +msgid "Supported on remote server" +msgstr "リモートサーバーに登録されています" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend To Idle" +msgstr "Suspend to Idle" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend To RAM" +msgstr "Suspend to RAM" + +#. TRANSLATORS: longer description +msgid "Suspend to Idle allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Suspend to Idle は、電力節約のためにデバイスをスリープに移行します。デバイスのスリープ中にメモリ取り外しで情報漏えいの恐れがあります。" + +#. TRANSLATORS: longer description +msgid "Suspend to RAM allows the device to quickly go to sleep in order to save power. While the device has been suspended, its memory could be physically removed and its information accessed." +msgstr "Suspend to RAM は、電力節約のためにデバイスをスリープに移行します。デバイスのスリープ中にメモリ取り外しで情報漏えいの恐れがあります。" + +#. TRANSLATORS: Title: a better sleep state +msgid "Suspend-to-idle" +msgstr "Suspend-to-Idle" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend-to-ram" +msgstr "Suspend-to-RAM" + +#. TRANSLATORS: show and ask user to confirm -- +#. * %1 is the old branch name, %2 is the new branch name +#, c-format +msgid "Switch branch from %s to %s?" +msgstr "ブランチを %s から %s に切り替えますか?" + +#. TRANSLATORS: command description +msgid "Switch the firmware branch on the device" +msgstr "デバイスのファームウェアブランチを切り替える" + +#. TRANSLATORS: command description +msgid "Sync firmware versions to the chosen configuration" +msgstr "ファームウェアバージョンを選択した構成に同期" + +#. TRANSLATORS: Title: Whether firmware is locked down +msgid "System Management Mode" +msgstr "システム管理モード" + +#. TRANSLATORS: this CLI tool is now preventing system updates +msgid "System Update Inhibited" +msgstr "システム更新が抑制されています" + +#. TRANSLATORS: longer description +msgid "System management mode is used by the firmware to access resident BIOS code and data." +msgstr "システム管理モードは、ファームウェアが常駐 BIOS コードとデータにアクセスするために使用されます。" + +#. TRANSLATORS: as in laptop battery power +msgid "System power is too low" +msgstr "システムの電力残量が不足しています" + +#. TRANSLATORS: as in laptop battery power +#, c-format +msgid "System power is too low (%u%%, requires %u%%)" +msgstr "システムの電力残量が不足しています (現在 %u%%、%u%% 必要)" + +#. TRANSLATORS: Must be plugged into an outlet +msgid "System requires external power source" +msgstr "デバイスを電源に接続してください" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "TEXT" +msgstr "TEXT" + +#. TRANSLATORS: longer description +msgid "TPM (Trusted Platform Module) is a computer chip that detects when hardware components have been tampered with." +msgstr "TPM (トラステッド プラットフォーム モジュール) は、ハードウェア部品の改ざんを検出するセキュリティチップです。" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM PCR0 reconstruction" +msgstr "TPM PCR0 再構築" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is invalid" +msgstr "TPM PCR0 の再構築が無効" + +#. TRANSLATORS: HSI event title +msgid "TPM PCR0 reconstruction is now valid" +msgstr "TPM PCR0 の再構築は現在有効です" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "TPM プラットフォーム構成" + +#. TRANSLATORS: Title: the PCR is rebuilt from the TPM event log +msgid "TPM Reconstruction" +msgstr "TPM 再構成" + +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM empty PCRs" +msgstr "TPM 空 PCR" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + +#. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 +msgid "Tag" +msgid_plural "Tags" +msgstr[0] "タグ" + +#. TRANSLATORS: we're saving all USB events for emulation +msgid "Tagged for emulation" +msgstr "エミュレーション用にタグ付け済み" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Tainted" +msgstr "汚染済み" + +#. show the user the entire data blob +msgid "Target" +msgstr "対象" + +#. TRANSLATORS: command description +msgid "Test a device using a JSON manifest" +msgstr "JSON マニフェストを使用してデバイスをテスト" + +#. TRANSLATORS: when the release was tested +msgid "Tested" +msgstr "テスト済み" + +#. TRANSLATORS: the %s is a vendor name, e.g. Lenovo +#, c-format +msgid "Tested by %s" +msgstr "%s によってテスト済み" + +#. TRANSLATORS: someone we trust has tested this +msgid "Tested by trusted vendor" +msgstr "信頼できるベンダーによるテスト済み" + +#. TRANSLATORS: the boot entry was in a legacy format +msgid "The EFI boot entry is not in hive format, and shim may not be new enough to read it." +msgstr "EFI ブートエントリが hive 形式ではありません。shim が古い場合、読み取りに失敗する可能性があります。" + +#. TRANSLATORS: try to treat the legacy format as a hive +msgid "The EFI boot entry was not in hive format, falling back" +msgstr "EFI ブートエントリが hive 形式ではありません。レガシー形式として処理します" + +#. TRANSLATORS: longer description +msgid "The Intel Management Engine Key Manifest must be valid so that the device firmware can be trusted by the CPU." +msgstr "インテル・マネジメント・エンジンのキーマニフェストは、デバイスファームウェアが CPU によって信頼されるために有効である必要があります。" + +#. TRANSLATORS: longer description +msgid "The Intel Management Engine controls device components and needs to have a recent version to avoid security issues." +msgstr "インテル・マネジメント・エンジンはデバイスコンポーネントを制御しており、セキュリティ上の問題を回避するために最新のバージョンである必要があります。" + #. TRANSLATORS: do not translate the variables marked using $ 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 は独立した法人として機能する無料のサービスであり、$OS_RELEASE:NAME$ とは関係ありません。ディストリビューターは、システムまたは接続されているデバイスとの互換性について、ファームウェアの更新を確認していない可能性があります。すべてのファームウェアは、元の機器の製造元からのみ提供されています。" +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Platform Configuration is used to check whether the device start process has been modified." +msgstr "TPM (トラステッド プラットフォーム モジュール) プラットフォーム構成は、デバイス起動プロセスの改ざんをチェックするために利用されます。" + +#. TRANSLATORS: longer description +msgid "The TPM (Trusted Platform Module) Reconstruction is used to check whether the device start process has been modified." +msgstr "TPM (トラステッド プラットフォーム モジュール) 再構成は、デバイス起動プロセスの改ざんをチェックするために利用されます。" + +#. TRANSLATORS: this is more background on a security measurement problem +msgid "The TPM PCR0 differs from reconstruction." +msgstr "TPM PCR0 が再構築された値と異なります。" + +#. TRANSLATORS: longer description +msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." +msgstr "UEFI プラットフォームキーは、デバイスにロードされるソフトウェアが信頼済みのソースからのものであるかを判断するために使用されます。" + +#. TRANSLATORS: HSI event title +msgid "The UEFI certificate store is now up to date" +msgstr "UEFI 証明書ストアが最新になりました" + +#. TRANSLATORS: longer description +msgid "The UEFI db contains the list of valid certificates that can be used to authorize what EFI binaries are allowed to run." +msgstr "UEFI db には、EFI バイナリの実行を許可するための有効な証明書リストが含まれています。" + +#. TRANSLATORS: longer description +msgid "The UEFI system can set up memory attributes at boot which prevent common exploits from running." +msgstr "UEFI システムは、起動時にメモリ属性を設定することで、一般的なエクスプロイトの実行を防ぐことができます。" + +#. TRANSLATORS: the user is SOL for support... +msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" +msgstr "デーモンはサードパーティ製コードをロードしたため、今後はアップストリーム開発者のサポート対象外となります!" + +#. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name +#, c-format +msgid "The firmware from %s is not supplied by %s, the hardware vendor." +msgstr "ファームウェアの提供元 %s がハードウェアベンダーである %s ではありません。" + +#. TRANSLATORS: try to help +msgid "The system clock has not been set correctly and downloading files may fail." +msgstr "システムクロックが正しく設定されていません。ファイルダウンロードに失敗する可能性があります。" + +#. TRANSLATORS: warning message shown after update has been scheduled +msgid "The update will continue when the device USB cable has been re-inserted." +msgstr "デバイスの USB ケーブルを差し込むと、アップデートが続行されます。" + +#. TRANSLATORS: warning message shown after update has been scheduled +msgid "The update will continue when the device USB cable has been unplugged and then re-inserted." +msgstr "デバイスの USB ケーブルを抜き差しすると、アップデートが続行されます。" + +#. TRANSLATORS: warning message shown after update has been scheduled +msgid "The update will continue when the device USB cable has been unplugged." +msgstr "デバイスの USB ケーブルを抜くと、アップデートが続行されます。" + +#. TRANSLATORS: warning message +msgid "The update will continue when the device power cable has been removed and re-inserted." +msgstr "デバイスの電源ケーブルを抜き差しすると、アップデートが続行されます。" + +#. TRANSLATORS: naughty vendor +msgid "The vendor did not supply any release notes." +msgstr "ベンダーはリリースノートを提供していません。" + +#. TRANSLATORS: now list devices with unfixed high-priority issues +msgid "There are devices with issues:" +msgstr "問題のあるデバイス:" + +#. TRANSLATORS: nothing to show +msgid "There are no blocked firmware files" +msgstr "ブロックされたファームウェアファイルはありません" + +#. TRANSLATORS: approved firmware has been checked by +#. * the domain administrator +msgid "There is no approved firmware." +msgstr "承認済みファームウェアがありません。" + +#. TRANSLATORS: %1 is the current device version number, and %2 is the +#. command name, e.g. `fwupdmgr sync` +#, c-format +msgid "This device will be reverted back to %s when the %s command is performed." +msgstr "%s コマンドが実行されると、このデバイスは %s に戻されます。" + +#. TRANSLATORS: the vendor did not upload this +msgid "This firmware is provided by LVFS community members and is not provided (or supported) by the original hardware vendor." +msgstr "このファームウェアは LVFS コミュニティメンバーによって提供されたもので、元のハードウェアベンダーからの提供 (またはサポート) はありません。" + +#. TRANSLATORS: unsupported build of the package +msgid "This package has not been validated, it may not work properly." +msgstr "このパッケージは検証されていません。正常に動作しない可能性があります。" + +#. TRANSLATORS: we're poking around as a power user +msgid "This program may only work correctly as root" +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: error message +msgid "This system doesn't support firmware settings" +msgstr "このシステムはファームウェア設定に対応していません" + +#. TRANSLATORS: this is instructions on how to improve the HSI suffix +msgid "This system has HSI runtime issues." +msgstr "このシステムには HSI ランタイムの問題があります。" + +#. TRANSLATORS: this is instructions on how to improve the HSI security level +msgid "This system has a low HSI security level." +msgstr "システムの HSI セキュリティレベルが低いです。" + +#. TRANSLATORS: description of dbxtool +msgid "This tool allows an administrator to apply UEFI dbx updates." +msgstr "このツールは管理者に UEFI dbx のアップデートを許可します。" + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to query and control the fwupd daemon, allowing them to perform actions such as installing or downgrading firmware." +msgstr "このツールは、管理者が fwupd デーモンを照会・制御し、ファームウェアのインストールやダウングレードなどの操作を実行できます。" + +#. TRANSLATORS: CLI description +msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." +msgstr "このツールは、管理者が fwupd プラグインをホストシステムにインストールせずに利用できるようにします。" + +#. TRANSLATORS: the %1 is a kernel command line key=value +#, c-format +msgid "This tool can add a kernel argument of '%s', but it will only be active after restarting the computer." +msgstr "このツールはカーネル引数 '%s' を追加できますが、変更はコンピューターの再起動後に有効になります。" + +#. TRANSLATORS: the %1 is a BIOS setting name. +#. * %2 and %3 are the values, e.g. "True" or "Windows10" +#, c-format +msgid "This tool can change the BIOS setting '%s' from '%s' to '%s' automatically, but it will only be active after restarting the computer." +msgstr "このツールはBIOS設定 '%s' を '%s' から '%s' に自動的に変更できますが、変更はコンピューターの再起動後に有効になります。" + +#. TRANSLATORS: the %1 is a kernel command line key=value +#, c-format +msgid "This tool can change the kernel argument from '%s' to '%s', but it will only be active after restarting the computer." +msgstr "このツールはカーネル引数を '%s' から '%s' に変更できますが、変更はコンピューターの再起動後に有効になります。" + +#. TRANSLATORS: CLI description +msgid "This tool will read and parse the TPM event log from the system firmware." +msgstr "システムファームウェアから TPM イベントログを読み取り、解析する。" + +#. TRANSLATORS: the update state of the specific device +msgid "Transient failure" +msgstr "一時的なエラー" + +#. TRANSLATORS: item is TRUE +msgid "True" +msgstr "True" + +#. TRANSLATORS: We verified the metadata against the server +msgid "Trusted metadata" +msgstr "信頼済みメタデータ" + +#. TRANSLATORS: We verified the payload against the server +msgid "Trusted payload" +msgstr "信頼済みペイロード" + +#. TRANSLATORS: remote type, e.g. remote or local +msgid "Type" +msgstr "種類" + +#. TRANSLATORS: Title: Bootservice is when only readable from early-boot +msgid "UEFI Bootservice Variables" +msgstr "UEFI ブートサービス変数" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition may not be set up correctly" +msgstr "UEFI ESP パーティションが正しく設定されていない可能性があります" + +#. TRANSLATORS: partition refers to something on disk, again, hey Arch users +msgid "UEFI ESP partition not detected or configured" +msgstr "UEFI ESP パーティションが検出されないか、構成されていません" + +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI Memory Protection" +msgstr "UEFI メモリ保護" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "UEFI プラットフォームキー" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI Secure Boot" +msgstr "UEFI セキュアブート" + +#. TRANSLATORS: longer description +msgid "UEFI Secure Boot prevents malicious software from being loaded when the device starts." +msgstr "UEFI セキュアブートは、デバイス起動時に悪意のあるソフトウェアが読み込まれるのを防止します。" + +#. TRANSLATORS: longer description +msgid "UEFI boot service variables should not be readable from runtime mode." +msgstr "UEFI ブートサービス変数をランタイムモードから読み取れないようにします。" + +#. TRANSLATORS: Title: Bootservice is when only readable from early-boot +msgid "UEFI bootservice variables" +msgstr "UEFI ブートサービス変数" + +#. TRANSLATORS: capsule updates are an optional BIOS feature +msgid "UEFI capsule updates not available or enabled in firmware setup" +msgstr "UEFI カプセルアップデートはファームウェア設定で利用できないか、有効になっていません" + +#. TRANSLATORS: Title: is UEFI db up-to-date +msgid "UEFI db" +msgstr "UEFI データベース" + +#. TRANSLATORS: program name +msgid "UEFI dbx Utility" +msgstr "UEFI dbx ユーティリティ" + +#. TRANSLATORS: system is not booted in UEFI mode +msgid "UEFI firmware can not be updated in legacy BIOS mode" +msgstr "UEFI ファームウェアはレガシー BIOS モードでは更新できません" + +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI memory protection" +msgstr "UEFI メモリ保護" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled and locked" +msgstr "UEFI メモリ保護が有効でロックされています" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled but not locked" +msgstr "UEFI メモリ保護が有効ですがロックされていません" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now locked" +msgstr "UEFI メモリ保護がロックされました" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now unlocked" +msgstr "UEFI メモリ保護がロック解除されました" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI プラットフォームキー" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI セキュアブート" + +#. TRANSLATORS: error message +msgid "Unable to connect to service" +msgstr "サービスに接続できません" + +#. TRANSLATORS: error message +msgid "Unable to find attribute" +msgstr "属性が見つかりません" + +#. TRANSLATORS: command description +msgid "Unbind current driver" +msgstr "現在のドライバーを切り離す" + +#. TRANSLATORS: we will now offer this firmware to the user +msgid "Unblocking firmware:" +msgstr "ファームウェアブロック解除中:" + +#. TRANSLATORS: command description +msgid "Unblocks a specific firmware from being installed" +msgstr "特定のファームウェアのインストールブロックを解除" + +#. TRANSLATORS: command description +msgid "Undo the host security attribute fix" +msgstr "ホストセキュリティ属性の修正を取り消す" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Unencrypted" +msgstr "暗号化されていません" + +#. TRANSLATORS: command description +msgid "Uninhibit the system to allow upgrades" +msgstr "アップグレードを許可するためにシステムのロックを解除" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Unknown" +msgstr "不明" + +#. TRANSLATORS: Name of hardware +msgid "Unknown Device" +msgstr "不明なデバイス" + msgid "Unlock the device to allow access" msgstr "アクセスを許可するには機器のロックを解除してください" +#. TRANSLATORS: Suffix: the HSI result +msgid "Unlocked" +msgstr "ロック解除済み" + +#. TRANSLATORS: command description +msgid "Unlocks the device for firmware access" +msgstr "ファームウェア更新のためにデバイスをアンロックする" + +#. TRANSLATORS: command description +msgid "Unmounts the ESP" +msgstr "ESP をアンマウント" + +#. TRANSLATORS: message shown after device has been marked for emulation +msgid "Unplug and replug the device to continue the update process." +msgstr "デバイスを抜き差しして、アップデートプロセスを続行してください。" + +#. TRANSLATORS: firmware payload is unsigned and it is possible to modify it +msgid "Unsigned Payload" +msgstr "未署名ペイロード" + +#. TRANSLATORS: error message +#, c-format +msgid "Unsupported daemon version %s, client version is %s" +msgstr "サポートされていないデーモンバージョン %s です。クライアントバージョンは %s です" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Untainted" +msgstr "非汚染" + +#. TRANSLATORS: Device is updatable in this or any other mode +msgid "Updatable" +msgstr "アップデート可能" + +#. TRANSLATORS: error message from last update attempt +msgid "Update Error" +msgstr "アップデートエラー" + +#. TRANSLATORS: helpful image for the update +msgid "Update Image" +msgstr "更新イメージ" + +#. TRANSLATORS: helpful messages for the update +msgid "Update Message" +msgstr "アップデートメッセージ" + +#. TRANSLATORS: hardware state, e.g. "pending" +msgid "Update State" +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 をご覧ください:" + +#. TRANSLATORS: ask if we can update metadata +msgid "Update now?" +msgstr "今すぐ更新しますか?" + +#. TRANSLATORS: command description +msgid "Update the stored cryptographic hash with current ROM contents" +msgstr "現在の ROM 内容に基づいて保存済み暗号学的ハッシュを更新する" + msgid "Update the stored device verification information" msgstr "保存された機器の検証情報を更新する" + +#. TRANSLATORS: command description +msgid "Update the stored metadata with current contents" +msgstr "保存されたメタデータを最新の内容でアップデートする" + +#. TRANSLATORS: command description +msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" +msgstr "指定されたデバイスを最新ファームウェアバージョンに更新する。デバイス未指定の場合はすべてのデバイスを更新する" + +msgid "Updating" +msgstr "アップデート中" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "%s をアップデート中…" + +#. TRANSLATORS: message letting the user know an upgrade is available +#. * %1 is the device name and %2 and %3 are version strings +#, c-format +msgid "Upgrade %s from %s to %s?" +msgstr "%s を %s から %s にアップグレードしますか?" + +#. TRANSLATORS: ask the user to upload +msgid "Upload data now?" +msgstr "今すぐデータをアップロードしますか?" + +#. TRANSLATORS: command description +msgid "Upload the list of updatable devices to a remote server" +msgstr "アップデート可能なデバイスのリストをリモートサーバーにアップロード" + +#. TRANSLATORS: ask the user to share, %s is something +#. * like: "Linux Vendor Firmware Service" +#, c-format +msgid "Upload these anonymous results to the %s to help other users?" +msgstr "他のユーザーを支援するため、これらの匿名の結果を %s にアップロードしますか?" + +#. TRANSLATORS: explain why we want to upload +#, c-format +msgid "Uploading a device list allows the %s team to know what hardware exists, and allows us to put pressure on vendors that do not upload firmware updates for their hardware." +msgstr "デバイス一覧をアップロードすることで、%s チームは既存のハードウェアを把握し、ファームウェアアップデートを提供しないベンダーに対して改善を促すことができます。" + +#. TRANSLATORS: explain why we want to upload +msgid "Uploading firmware reports helps hardware vendors to quickly identify failing and successful updates on real devices." +msgstr "ファームウェアレポートをアップロードすると、ハードウェアベンダーは実デバイスでのアップデートの失敗と成功を迅速に特定できます。" + +#. TRANSLATORS: how important the release is +msgid "Urgency" +msgstr "重要度" + +#. TRANSLATORS: explain how to get help, %1 is +#. * 'fwupdtool --help' +#. TRANSLATORS: explain how to get help, +#. * where $1 is something like 'fwupdmgr --help' +#, c-format +msgid "Use %s for help" +msgstr "ヘルプを表示するには %s を使用してください" + +#. TRANSLATORS: CTRL^C [holding control, and then pressing C] will exit the +#. program +msgid "Use CTRL^C to cancel." +msgstr "キャンセルするには Ctrl+C を使用してください。" + +#. TRANSLATORS: User has been notified +msgid "User has been notified" +msgstr "ユーザーに通知済み" + +#. TRANSLATORS: remote filename base +msgid "Username" +msgstr "ユーザー名" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "VERSION1 VERSION2 [FORMAT]" +msgstr "VERSION1 VERSION2 [FORMAT]" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Valid" +msgstr "有効" + +#. TRANSLATORS: ESP refers to the EFI System Partition +msgid "Validating ESP contents…" +msgstr "ESP の内容を検証しています…" + +#. TRANSLATORS: one line variant of release (e.g. 'China') +msgid "Variant" +msgstr "バリアント" + +#. TRANSLATORS: manufacturer of hardware +msgid "Vendor" +msgstr "ベンダー" + +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "検証しています…" + +#. TRANSLATORS: the detected version number of the dbx +msgid "Version" +msgstr "バージョン" + +#. TRANSLATORS: the fwupd version the release was tested on +msgid "Version[fwupd]" +msgstr "バージョン [fwupd]" + +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING" +msgstr "警告" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "WORD" +msgstr "WORD" + +#. TRANSLATORS: command description +msgid "Wait for a device to appear" +msgstr "デバイスが表示されるまで待機" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "デバイスの応答を待っています…" + +#. TRANSLATORS: command description +msgid "Watch for hardware changes" +msgstr "ハードウェアの変更を監視する" + +#. TRANSLATORS: check various UEFI and ACPI tables are unchanged after the +#. update +msgid "Will measure elements of system integrity around an update" +msgstr "アップデート後にシステムの整合性を測定します" + +#. TRANSLATORS: decompressing images from a container firmware +msgid "Writing file:" +msgstr "ファイルを書き込み中:" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "書き込み中…" + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from a recovery or installation disk, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "この変更により、システムが Linux で起動しなくなったり、システムが不安定になったりする可能性があるため、リカバリディスクまたはインストールディスクから設定を復元する方法を理解していることを確認してください。" + +#. TRANSLATORS: the user has to manually recover; we can't do it +msgid "You should ensure you are comfortable restoring the setting from the system firmware setup, as this change may cause the system to not boot into Linux or cause other system instability." +msgstr "この変更により、システムが Linux で起動しなくなったり、システムが不安定になったりする可能性があるため、システムファームウェア設定から設定を復元する方法を理解していることを確認してください。" + +#. TRANSLATORS: show the user a warning +msgid "Your distributor may not have verified any of the firmware updates for compatibility with your system or connected devices." +msgstr "ディストリビューターは、ファームウェア更新とシステムや接続されたデバイスとの互換性を検証していない可能性があります。" + +#. TRANSLATORS: %1 is the device vendor name +#, c-format +msgid "Your hardware may be damaged using this firmware, and installing this release may void any warranty with %s." +msgstr "このファームウェアを使用するとハードウェアが損傷する可能性があり、このリリースをインストールすると %s との保証が無効になる場合があります。" + +#. TRANSLATORS: BKC is the industry name for the best known configuration and +#. is a set +#. * of firmware that works together +#, c-format +msgid "Your system is set up to the BKC of %s." +msgstr "このシステムは %s のBKC (既知の最適構成) に設定されています。" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[APPSTREAM_ID]" +msgstr "[APPSTREAM_ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[CHECKSUM]" +msgstr "[CHECKSUM]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID]" +msgstr "[DEVICE-ID|GUID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [BRANCH]" +msgstr "[DEVICE-ID|GUID] [BRANCH]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE-ID|GUID] [VERSION]" +msgstr "[DEVICE-ID|GUID] [VERSION]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[DEVICE]" +msgstr "[DEVICE]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILE FILE_SIG REMOTE-ID]" +msgstr "[FILE FILE_SIG REMOTE-ID]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FILENAME1] [FILENAME2]" +msgstr "[FILENAME1] [FILENAME2]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FWUPD-VERSION]" +msgstr "[FWUPD-VERSION]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[REASON] [TIMEOUT]" +msgstr "[REASON] [TIMEOUT]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SECTION] KEY VALUE" +msgstr "[SECTION] KEY VALUE" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2] [--no-authenticate]" +msgstr "[SETTING1] [SETTING2] [--no-authenticate]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SETTING1] [SETTING2]..." +msgstr "[SETTING1] [SETTING2]..." + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[SMBIOS-FILE|HWIDS-FILE]" +msgstr "[SMBIOS-FILE|HWIDS-FILE]" + +#. TRANSLATORS: this is the default branch name when unset +msgid "default" +msgstr "デフォルト" + +#. TRANSLATORS: program name +msgid "fwupd TPM event log utility" +msgstr "fwupd TPM イベントログユーティリティ" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd プラグイン" diff -Nru fwupd-2.0.8/po/ka.po fwupd-2.0.20/po/ka.po --- fwupd-2.0.8/po/ka.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/ka.po 2026-02-26 11:36:18.000000000 +0000 @@ -3,16 +3,20 @@ # This file is distributed under the same license as the fwupd package. # # Translators: +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Georgian (http://app.transifex.com/freedesktop/fwupd/language/ka/)\n" +"PO-Revision-Date: 2025-10-24 10:21+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Georgian \n" +"Language: ka\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ka\n" -"Plural-Forms: nplurals=2; plural=(n!=1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -33,7 +37,7 @@ msgstr "%s ელემენტის განახლება" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "%s CPU მიკროკოდის განახლება" @@ -570,10 +574,6 @@ msgstr "FILENAME CERTIFICATE PRIVATE-KEY" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "FILENAME DEVICE-ID" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" msgstr "FILENAME OFFSET DATA [FIRMWARE-TYPE]" diff -Nru fwupd-2.0.8/po/kk.po fwupd-2.0.20/po/kk.po --- fwupd-2.0.8/po/kk.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/kk.po 2026-02-26 11:36:18.000000000 +0000 @@ -8,12 +8,15 @@ msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Kazakh (http://www.transifex.com/freedesktop/fwupd/language/kk/)\n" +"PO-Revision-Date: 2025-10-24 10:21+0000\n" +"Last-Translator: Anonymous \n" +"Language-Team: Kazakh \n" +"Language: kk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: kk\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: this is when a device is hotplugged msgid "Added" diff -Nru fwupd-2.0.8/po/ko.po fwupd-2.0.20/po/ko.po --- fwupd-2.0.8/po/ko.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/ko.po 2026-02-26 11:36:18.000000000 +0000 @@ -5,16 +5,21 @@ # Translators: # Seong-ho Cho , 2017,2019,2021-2024 # Shinjo Park , 2018-2021,2023 +# Changwoo Ryu , 2025. +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Korean (http://app.transifex.com/freedesktop/fwupd/language/ko/)\n" +"PO-Revision-Date: 2025-10-24 10:21+0000\n" +"Last-Translator: Changwoo Ryu \n" +"Language-Team: Korean \n" +"Language: ko\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ko\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -34,7 +39,7 @@ msgstr "%s 배터리 업데이트" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "%s CPU 마이크로코드 업데이트" @@ -426,7 +431,7 @@ #. TRANSLATORS: actually sending the update to the hardware msgid "Applying update…" -msgstr "업데이트 적용 중..." +msgstr "업데이트 적용 중…" #. TRANSLATORS: approved firmware has been checked by #. * the domain administrator @@ -951,7 +956,7 @@ #. TRANSLATORS: %1 is a device name #, c-format msgid "Downgrading %s…" -msgstr "%s 다운그레이드 중..." +msgstr "%s 다운그레이드 중…" #. TRANSLATORS: command description msgid "Download a file" @@ -1076,10 +1081,6 @@ msgstr "FILENAME CERTIFICATE PRIVATE-KEY" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "FILENAME DEVICE-ID" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" msgstr "<파일이름> <오프셋> <데이터> [<펌웨어형식>]" @@ -1199,7 +1200,7 @@ #. TRANSLATORS: longer description msgid "Firmware BIOS Region protects device firmware memory from being tampered with." -msgstr "펌웨어 BIOS 영역에서 장치 펌웨어 메모리에 펌웨어를 임의로 변경하는 행위를 막습니다" +msgstr "펌웨어 BIOS 영역에서 장치 펌웨어 메모리에 펌웨어를 임의로 변경하는 행위를 막습니다." #. TRANSLATORS: remote URI msgid "Firmware Base URI" @@ -1238,7 +1239,7 @@ #. TRANSLATORS: longer description msgid "Firmware Write Protection protects device firmware memory from being tampered with." -msgstr "펌웨어 기록 보호 정책에서 장치 펌웨어 메모리에 펌웨어를 임의로 변경하는 행위를 막습니다" +msgstr "펌웨어 기록 보호 정책에서 장치 펌웨어 메모리에 펌웨어를 임의로 변경하는 행위를 막습니다." #. TRANSLATORS: Title: if we can verify the firmware checksums msgid "Firmware attestation" @@ -1345,10 +1346,6 @@ msgstr "설치 방지된 펌웨어 목록 가져오기" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "연결한 하드웨어의 업데이트 목록을 가져옵니다" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "장치 펌웨어 릴리스 가져오기" @@ -1373,7 +1370,6 @@ msgstr "호스트 보안 이벤트" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "호스트 보안 ID(HSI)를 지원하지 않습니다" @@ -1491,7 +1487,7 @@ #. TRANSLATORS: %1 is a device name #, c-format msgid "Installing on %s…" -msgstr "%s에 설치 중..." +msgstr "%s에 설치 중…" #. TRANSLATORS: if it breaks, you get to keep both pieces msgid "Installing this update may also void any device warranty." @@ -1917,10 +1913,6 @@ msgstr "버전 업그레이드만 가능합니다" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "JSON 형식으로 출력" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "기본 ESP 경로 재정의" @@ -1934,11 +1926,11 @@ #. TRANSLATORS: reading new dbx from the update msgid "Parsing dbx update…" -msgstr "dbx 업데이트 해석 중..." +msgstr "dbx 업데이트 해석 중…" #. TRANSLATORS: reading existing dbx from the system msgid "Parsing system dbx…" -msgstr "시스템 dbx 해석 중..." +msgstr "시스템 dbx 해석 중…" #. TRANSLATORS: remote filename base msgid "Password" @@ -2063,7 +2055,7 @@ #. TRANSLATORS: %1 is a device name #, c-format msgid "Reading from %s…" -msgstr "%s에서 읽는 중..." +msgstr "%s에서 읽는 중…" #. TRANSLATORS: reading from the flash chips msgid "Reading…" @@ -2398,11 +2390,6 @@ msgid "Successfully disabled remote" msgstr "원격 저장소를 비활성화함" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "새 메타데이터를 다운로드함:" - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "원격 저장소를 활성화하고 새로 고침" @@ -2886,7 +2873,7 @@ #. TRANSLATORS: command description msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" -msgstr "모든 지정 장치를 최신 펌웨어 버전으로 업데이트합니다. 지정하지 않으면 모든 장치를 업데이트합니다." +msgstr "모든 지정 장치를 최신 펌웨어 버전으로 업데이트합니다. 지정하지 않으면 모든 장치를 업데이트합니다" msgid "Updating" msgstr "업데이트" @@ -2894,7 +2881,7 @@ #. TRANSLATORS: %1 is a device name #, c-format msgid "Updating %s…" -msgstr "%s 업데이트 중..." +msgstr "%s 업데이트 중…" #. TRANSLATORS: message letting the user know an upgrade is available #. * %1 is the device name and %2 and %3 are version strings @@ -2943,7 +2930,7 @@ #. TRANSLATORS: ESP refers to the EFI System Partition msgid "Validating ESP contents…" -msgstr "ESP 내용 검사 중..." +msgstr "ESP 내용 검사 중…" #. TRANSLATORS: one line variant of release (e.g. 'China') msgid "Variant" diff -Nru fwupd-2.0.8/po/ky.po fwupd-2.0.20/po/ky.po --- fwupd-2.0.8/po/ky.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/ky.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,16 +4,20 @@ # # Translators: # Ilyas Bakirov , 2018 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Kyrgyz (http://www.transifex.com/freedesktop/fwupd/language/ky/)\n" +"PO-Revision-Date: 2025-10-24 10:21+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Kyrgyz \n" +"Language: ky\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ky\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: this is when a device is hotplugged msgid "Added" @@ -46,7 +50,7 @@ #. TRANSLATORS: reading from the flash chips msgid "Reading…" -msgstr "Окуулууда..." +msgstr "Окуулууда…" #. TRANSLATORS: device state, i.e. appIDLE msgid "State" @@ -64,7 +68,7 @@ #. TRANSLATORS: writing to the flash chips msgid "Writing…" -msgstr "Жазылууда..." +msgstr "Жазылууда…" msgid "fwupd" msgstr "fwupd" diff -Nru fwupd-2.0.8/po/lt.po fwupd-2.0.20/po/lt.po --- fwupd-2.0.8/po/lt.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/lt.po 2026-02-26 11:36:18.000000000 +0000 @@ -7,16 +7,20 @@ # Moo, 2020-2021 # Moo, 2023-2025 # Rimas Kudelis , 2023 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Lithuanian (http://app.transifex.com/freedesktop/fwupd/language/lt/)\n" +"PO-Revision-Date: 2025-10-24 10:22+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Lithuanian \n" +"Language: lt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: lt\n" "Plural-Forms: nplurals=4; plural=(n % 10 == 1 && (n % 100 > 19 || n % 100 < 11) ? 0 : (n % 10 >= 2 && n % 10 <=9) && (n % 100 > 19 || n % 100 < 11) ? 1 : n % 1 != 0 ? 2: 3);\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -33,7 +37,7 @@ msgstr "„%s“ akumuliatoriaus naujinimas" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "„%s“ procesoriaus mikroprogramos naujinimas" @@ -724,10 +728,6 @@ msgstr "FAILO-VARDAS LIUDIJIMAS PRIVATUSIS-RAKTAS" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "FAILO-VARDAS ĮRENGINIO-ID" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME [DEVICE-ID|GUID]" msgstr "FAILO-VARDAS [ĮRENGINIO-ID|GUID]" @@ -875,10 +875,6 @@ msgstr "Gauna sukonfigūruotas nuotolines saugyklas" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Gauna naujinimų sąrašą prijungtai aparatinei įrangai" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "Gauna laidas įrenginiui" @@ -1134,10 +1130,6 @@ msgstr "Rodyti tik vieną PCR reikšmę" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Išvestis JSON formatu" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Nustelbti numatytąjį ESS (angl. ESP) kelią" @@ -1177,7 +1169,8 @@ msgid "Please enter a number from 0 to %u: " msgstr "Įveskite skaičių nuo 0 iki %u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " msgstr "Įveskite %s arba %s: " @@ -1430,11 +1423,6 @@ msgid "Successfully activated all devices" msgstr "Sėkmingai aktyvuoti visi įrenginiai" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Sėkmingai atsisiųsti nauji metaduomenys: " - #. TRANSLATORS: success message msgid "Successfully installed firmware" msgstr "Aparatinė programinė įranga sėkmingai įdiegta" diff -Nru fwupd-2.0.8/po/meson.build fwupd-2.0.20/po/meson.build --- fwupd-2.0.8/po/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,13 +1,10 @@ -i18n.gettext(meson.project_name(), +i18n.gettext( + meson.project_name(), preset: 'glib', - args: [ - '--default-domain=' + meson.project_name(), - ] + args: ['--default-domain=' + meson.project_name()], ) -run_target('fix-translations', - command: [ - fix_translations, - join_paths(meson.project_source_root(), 'po') - ] +run_target( + 'fix-translations', + command: [fix_translations, join_paths(meson.project_source_root(), 'po')], ) diff -Nru fwupd-2.0.8/po/nl.po fwupd-2.0.20/po/nl.po --- fwupd-2.0.8/po/nl.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/nl.po 2026-02-26 11:36:18.000000000 +0000 @@ -5,16 +5,20 @@ # Translators: # Philip Goto , 2023 # Richard E. van der Luit , 2017,2023 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Dutch (http://app.transifex.com/freedesktop/fwupd/language/nl/)\n" +"PO-Revision-Date: 2025-10-24 10:22+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Dutch \n" +"Language: nl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: nl\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -35,7 +39,7 @@ msgstr "Batterij-update voor %s" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "CPU-microcode-update voor %s" @@ -478,7 +482,7 @@ #. TRANSLATORS: decompressing the firmware file msgid "Decompressing…" -msgstr "Uitpakken..." +msgstr "Uitpakken…" #. TRANSLATORS: description of BIOS setting #. TRANSLATORS: multiline description of device @@ -650,10 +654,6 @@ msgstr "BESTANDSNAAM" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "BESTANDSNAAM APPARAAT-ID" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME [DEVICE-ID|GUID]" msgstr "BESTANDSNAAM [APPARAAT-ID|GUID]" @@ -788,10 +788,6 @@ msgstr "Verkrijgt details over een firmware-bestand" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Verkrijgt een lijst van updates voor verbonden hardware" - -#. TRANSLATORS: command description msgid "Gets the results from the last update" msgstr "Verkrijgt de resultaten van de laatste update" @@ -808,7 +804,6 @@ msgstr "Host-beveiligingsgebeurtenissen" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "Host-beveiligings-ID (HSI) wordt niet ondersteund" @@ -835,7 +830,7 @@ #. TRANSLATORS: daemon is inactive msgid "Idle…" -msgstr "Slaapt..." +msgstr "Slaapt…" #. TRANSLATORS: length of time the update takes to apply msgid "Install Duration" @@ -950,7 +945,7 @@ #. TRANSLATORS: parsing the firmware information msgid "Loading…" -msgstr "Laden..." +msgstr "Laden…" #. TRANSLATORS: Suffix: the HSI result msgid "Locked" @@ -1088,10 +1083,6 @@ msgid "Old version" msgstr "Oude versie" -#. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Uitvoer in JSON-formaat" - #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "PATH" msgstr "PAD" @@ -1218,7 +1209,7 @@ #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" -msgstr "Herstarten apparaat..." +msgstr "Herstarten apparaat…" #. TRANSLATORS: this is the HSI suffix msgid "Runtime Suffix" @@ -1238,7 +1229,7 @@ #. TRANSLATORS: scheduling an update to be done on the next boot msgid "Scheduling…" -msgstr "Inplannen..." +msgstr "Inplannen…" #. TRANSLATORS: HSI event title msgid "Secure Boot disabled" @@ -1560,7 +1551,7 @@ #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" -msgstr "Valideren..." +msgstr "Valideren…" #. TRANSLATORS: the detected version number of the dbx msgid "Version" @@ -1588,7 +1579,7 @@ #. TRANSLATORS: writing to the flash chips msgid "Writing…" -msgstr "Schrijven..." +msgstr "Schrijven…" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "[CHECKSUM]" diff -Nru fwupd-2.0.8/po/oc.po fwupd-2.0.20/po/oc.po --- fwupd-2.0.8/po/oc.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/oc.po 2026-02-26 11:36:18.000000000 +0000 @@ -5,16 +5,20 @@ # Translators: # Cédric Valmary , 2016 # Cédric Valmary , 2016 +# Quentin PAGÈS , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Occitan (post 1500) (http://www.transifex.com/freedesktop/fwupd/language/oc/)\n" +"PO-Revision-Date: 2025-10-24 10:22+0000\n" +"Last-Translator: Anonymous \n" +"Language-Team: Occitan \n" +"Language: oc\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: oc\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: this is when a device is hotplugged msgid "Added" @@ -131,7 +135,7 @@ #. * version numbers e.g. "1.2.3" #, c-format msgid "Updating %s from %s to %s... " -msgstr "Mesa a jorn de %s de %s en %s " +msgstr "Mesa a jorn de %s de %s en %s... " #. TRANSLATORS: the detected version number of the dbx msgid "Version" diff -Nru fwupd-2.0.8/po/pa.po fwupd-2.0.20/po/pa.po --- fwupd-2.0.8/po/pa.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/pa.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,16 +4,29 @@ # # Translators: # A S Alam, 2021 +# A S Alam, 2025 +# Aman Alam , 2025. +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Panjabi (Punjabi) (http://app.transifex.com/freedesktop/fwupd/language/pa/)\n" +"PO-Revision-Date: 2025-10-24 10:23+0000\n" +"Last-Translator: Aman Alam \n" +"Language-Team: Punjabi \n" +"Language: pa\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: pa\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" + +#. TRANSLATORS: more than a minute +#, c-format +msgid "%.0f minute remaining" +msgid_plural "%.0f minutes remaining" +msgstr[0] "%.0f ਮਿੰਟ ਬਾਕੀ" +msgstr[1] "%.0f ਮਿੰਟ ਬਾਕੀ" #. TRANSLATORS: a specific part of hardware, #. * the first %s is the device name, e.g. 'Secure Boot` @@ -41,6 +54,24 @@ msgid "%s Update" msgstr "%s ਅੱਪਡੇਟ" +#. TRANSLATORS: the device has a reason it can't update, e.g. laptop lid +#. closed +#, c-format +msgid "%s is not currently updatable" +msgstr "%s ਇਸ ਵੇਲੇ ਅੱਪਡੇਟ ਕਰਨ ਯੋਗ ਨਹੀਂ ਹੈ" + +#. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT +#, c-format +msgid "%s version" +msgstr "%s ਵਰਜ਼ਨ" + +#. TRANSLATORS: this is shown in the MOTD +#, c-format +msgid "%u device has a firmware upgrade available." +msgid_plural "%u devices have a firmware upgrade available." +msgstr[0] "%u ਡਿਵਾਈਸ ਲਈ ਇੱਕ ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ ਮੌਜੂਦ ਹੈ।" +msgstr[1] "%u ਡਿਵਾਈਸਾਂ ਲਈ ਇੱਕ ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ ਮੌਜੂਦ ਹੈ।" + #. TRANSLATORS: duration in minutes #, c-format msgid "%u minute" @@ -55,9 +86,45 @@ msgstr[0] "%u ਸਕਿੰਟ" msgstr[1] "%u ਸਕਿੰਟ" +#. TRANSLATORS: this is shown as a suffix for obsoleted tests +msgid "(obsoleted)" +msgstr "(ਬਰਤਰਫ਼ ਕੀਤਾ)" + +#. TRANSLATORS: Title: if hardware enforces control of SPI replays +msgid "AMD Firmware Replay Protection" +msgstr "AMD ਫਿਰਮਵੇਅਰ ਰੀਪਲੇਅ ਸੁਰੱਖਿਆ" + +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "AMD ਫਿਰਮਵੇਅਰ ਲਿਖਣ ਸੁਰੱਖਿਆ" + +#. TRANSLATORS: Title: if firmware enforces rollback protection +msgid "AMD Secure Processor Rollback Protection" +msgstr "AMD ਸਕਿਉਰ ਪ੍ਰੋਸੈਸਰ ਰੋਲਬੈਕ ਸੁਰੱਖਿਆ" + +#. TRANSLATORS: the user needs to do something, e.g. remove the device +msgid "Action Required:" +msgstr "ਧਿਆਨ ਦੇਣ ਦੀ ਲੋੜ ਹੈ:" + msgid "Activate the new firmware on the device" msgstr "ਡਿਵਾਈਸ ਉੱਤੇ ਨਵਾਂ ਫਿਰਮਵੇਅਰ ਸਰਗਰਮ ਕਰੋ" +#. TRANSLATORS: should the remote still be enabled +msgid "Agree and enable the remote?" +msgstr "ਸਹਿਮਤ ਹੋ ਅਤੇ ਰਿਮੋਟ ਨੂੰ ਸਮਰੱਥ ਕਰਨਾ ਹੈ?" + +#. TRANSLATORS: command line option +msgid "Apply update files" +msgstr "ਅੱਪਡੇਟ ਫ਼ਾਇਲਾਂ ਨੂੰ ਲਾਗੂ ਕਰੋ" + +#. TRANSLATORS: actually sending the update to the hardware +msgid "Applying update…" +msgstr "…ਅੱਪਡੇਟ ਨੂੰ ਲਾਗੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + +#. TRANSLATORS: waiting for user to authenticate +msgid "Authenticating…" +msgstr "…ਪਰਮਾਣਿਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to downgrade the firmware on a removable device" msgstr "ਹਟਾਉਣਯੋਗ ਡਿਵਾਈਸ ਉੱਤੇ ਫਿਰਮਵੇਅਰ ਡਾਊਨਗਰੇਡ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ" @@ -67,6 +134,18 @@ msgstr "ਇਸ ਮਸ਼ੀਨ ਉੱਤੇ ਫਿਰਮਵੇਅਰ ਨੂੰ ਡਾਊਨਗਰੇਡ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to modify BIOS settings" +msgstr "BIOS ਸੈਟਿੰਗਾਂ ਨੂੰ ਸੋਧਣ ਲਈ ਪਰਮਾਣਿਤ ਹੋਣ ਦੀ ਲੋੜ ਹੈ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to read BIOS settings" +msgstr "BIOS ਸੈਟਿੰਗਾਂ ਨੂੰ ਪੜ੍ਹਨ ਲਈ ਪਰਮਾਣਿਤ ਹੋਣ ਦੀ ਲੋੜ ਹੈ" + +#. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to stop the firmware update service" +msgstr "ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ ਸੇਵਾ ਨੂੰ ਰੋਕਣ ਲਈ ਪਰਮਾਣਿਤ ਹੋਣ ਦੀ ਲੋੜ ਹੈ" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to unlock a device" msgstr "ਡਿਵਾਈਸ ਨੂੰ ਅਣ-ਲਾਕ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ" @@ -78,6 +157,31 @@ msgid "Authentication is required to update the firmware on this machine" msgstr "ਇਸ ਮਸ਼ੀਨ ਉੱਤੇ ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ ਕਰਨ ਲਈ ਪਰਮਾਣਕਿਤਾ ਚਾਹੀਦੀ ਹੈ" +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +msgid "BIOS Firmware Updates" +msgstr "BIOS ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ" + +#. TRANSLATORS: Title: Whether BIOS Firmware updates is enabled +msgid "BIOS firmware updates" +msgstr "BIOS ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ" + +#. TRANSLATORS: description of a BIOS setting +msgid "BIOS updates delivered via LVFS or Windows Update" +msgstr "BIOS ਅੱਪਡੇਟਾਂ ਨੂੰ LVFS ਜਾਂ ਵਿੰਡੋਜ਼ ਅੱਪਡੇਟ ਰਾਹੀਂ ਲਾਗੂ ਕੀਤੇ ਗਏ ਹਨ" + +#. TRANSLATORS: this is to abort the interactive prompt +msgid "Cancel" +msgstr "ਰੱਦ ਕਰੋ" + +#. TRANSLATORS: this is when a device ctrl+c's a watch +#. TRANSLATORS: this is from ctrl+c +msgid "Cancelled" +msgstr "ਰੱਦ ਕੀਤਾ" + +#. TRANSLATORS: get interactive prompt +msgid "Choose device" +msgstr "ਡਿਵਾਈਸ ਨੂੰ ਚੁਣੋ" + #. TRANSLATORS: error message msgid "Command not found" msgstr "ਕਮਾਂਡ ਨਹੀਂ ਲੱਭੀ" @@ -90,6 +194,10 @@ msgid "Critical" msgstr "ਗੰਭੀਰ" +#. TRANSLATORS: decompressing the firmware file +msgid "Decompressing…" +msgstr "…ਡਿ-ਕੰਪਰੈਸ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + #. TRANSLATORS: description of BIOS setting #. TRANSLATORS: multiline description of device msgid "Description" @@ -103,25 +211,118 @@ msgid "Device ID" msgstr "ਡਿਵਾਈਸ ID" +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device added:" +msgstr "ਡਿਵਾਈਸ ਜੋੜਿਆ:" + +#. TRANSLATORS: this is when a device has been updated +msgid "Device changed:" +msgstr "ਡਿਵਾਈਸ ਨੂੰ ਬਦਲਿਆ:" + +#. TRANSLATORS: this is when a device is hotplugged +msgid "Device removed:" +msgstr "ਡਿਵਾਈਸ ਨੂੰ ਹਟਾਇਆ:" + +#. TRANSLATORS: success +msgid "Done!" +msgstr "ਮੁਕੰਮਲ!" + +#. TRANSLATORS: downloading from a remote server +msgid "Downloading…" +msgstr "…ਡਾਊਨਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + #. TRANSLATORS: length of time the update takes to apply msgid "Duration" msgstr "ਲੱਗਣਾ ਸਮਾਂ" +msgid "Enable" +msgstr "ਸਮਰੱਥ" + +#. TRANSLATORS: a remote here is like a 'repo' or software source +msgid "Enable new remote?" +msgstr "ਨਵੇਂ ਰਿਮੋਟ ਨੂੰ ਸਮਰੱਥ ਕਰਨਾ ਹੈ?" + +#. TRANSLATORS: if the remote is enabled +#. TRANSLATORS: Suffix: the HSI result +msgid "Enabled" +msgstr "ਸਮਰੱਥ ਹੈ" + msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." msgstr "ਇਹ ਸਹੂਲਤ ਨੂੰ ਆਪਣੇ ਖਤਰੇ ਉੱਤੇ ਹੀ ਸਮਰੱਥ ਕਰੋ, ਜਿਸ ਦਾ ਅਰਥ ਹੋਵੇਗਾ ਕਿ ਇਹਨਾਂ ਅੱਪਡੇਟਾਂ ਨਾਲ ਆਈ ਕਿਸੇ ਵੀ ਸਮੱਸਿਆ ਵਾਸਤੇ ਤੁਸੀਂ ਅਸਲ ਡਿਵਾਈਸ ਨਿਰਮਾਤਾ ਨਾਲ ਸੰਪਰਕ ਕਰੋਗੇ। ਅੱਪਡੇਟ ਕਰਨ ਦੀ ਕਾਰਵਾਈ ਸੰਬੰਧੀ ਕਿਸੇ ਵੀ ਸਮੱਸਿਆ ਬਾਰੇ $OS_RELEASE:BUG_REPORT_URL$ ਉੱਤੇ ਰਿਪੋਰਟ ਕਰੋ।" +#. TRANSLATORS: erasing contents of the flash chips +msgid "Erasing…" +msgstr "…ਮਿਟਾਇਆ ਜਾ ਰਿਹਾ ਹੈ" + #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME" msgstr "ਫਾਇਲ-ਨਾਂ" +#. TRANSLATORS: dbx file failed to be applied as an update +msgid "Failed to apply update" +msgstr "ਅੱਪਡੇਟ ਲਾਗੂ ਕਰਨ ਲਈ ਅਸਫ਼ਲ" + +#. TRANSLATORS: another fwupdtool instance is already running +msgid "Failed to lock" +msgstr "ਲਾਕ ਕਰਨ ਲਈ ਅਸਫ਼ਲ ਹੈ" + #. TRANSLATORS: filename of the local file msgid "Filename" msgstr "ਫਾਇਲ-ਦਾ-ਨਾਂ" +#. TRANSLATORS: user did not include a filename parameter +msgid "Filename required" +msgstr "ਫਾਇਲ-ਨਾਂ ਚਾਹੀਦਾ ਹੈ" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "Firmware BIOS Region" +msgstr "ਫਿਰਮਵੇਅਰ BIOS ਖੇਤਰ" + +#. TRANSLATORS: program summary +msgid "Firmware Update D-Bus Service" +msgstr "ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ ਡੀ-ਬੱਸ ਸਰਵਿਸ" + +#. TRANSLATORS: program name +msgid "Firmware Update Daemon" +msgstr "ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ ਡੈਮਨ" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "Firmware Updater Verification" +msgstr "ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟਰ ਤਸਦੀਕ" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware Updates" +msgstr "ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection" +msgstr "ਫਿਰਮਵੇਅਰ ਲਿਖਣ ਸੁਰੱਖਿਆ" + +#. TRANSLATORS: Title: firmware refers to the flash chip in the computer +msgid "Firmware Write Protection Lock" +msgstr "ਫਿਰਮਵੇਅਰ ਲਿਖਣ ਸੁਰੱਖਿਆ ਲਾਕ" + +#. TRANSLATORS: Title: if firmware updates are available +msgid "Firmware updates" +msgstr "ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ" + +msgid "Get BIOS settings" +msgstr "BIOS ਸੈਟਿੰਗਾਂ ਉੱਤੇ ਜਾਓ" + #. TRANSLATORS: the release urgency msgid "High" msgstr "ਵੱਧ" +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU" +msgstr "IOMMU" + +#. TRANSLATORS: Title: +#. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit +msgid "IOMMU Protection" +msgstr "IOMMU ਸੁਰੱਖਿਆ" + msgid "Install old version of signed system firmware" msgstr "ਸਾਈਨ ਕੀਤੇ ਸਿਸਟਮ ਫਿਰਮਵੇਅਰ ਦਾ ਪੁਰਾਣਾ ਵਰਜ਼ਨ ਇੰਸਟਾਲ ਕਰੋ" @@ -144,30 +345,73 @@ msgid "Installing firmware update…" msgstr "ਫਿਰਮਵੇਅਰ ਅੱਪਡੇਟ ਇੰਸਟਾਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Installing on %s…" +msgstr "…%s ਉੱਤੇ ਇੰਸਟਾਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + #. TRANSLATORS: issue fixed with the release, e.g. CVE msgid "Issue" msgid_plural "Issues" msgstr[0] "ਮਸਲਾ" msgstr[1] "ਮਸਲੇ" +#. TRANSLATORS: time remaining for completing firmware flash +msgid "Less than one minute remaining" +msgstr "ਇੱਕ ਮਿੰਟ ਤੋਂ ਘੱਟ ਬਾਕੀ ਰਹਿੰਦਾ ਹੈ" + #. TRANSLATORS: e.g. GPLv2+, Proprietary etc msgid "License" msgstr "ਲਸੰਸ" +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux Kernel Lockdown" +msgstr "ਲੀਨਕਸ ਕਰਨਲ ਲਾਕ-ਡਾਊਨ" + +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux Kernel Verification" +msgstr "ਲੀਨਕਸ ਕਰਨਲ ਤਸਦੀਕ" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux Swap" +msgstr "ਲੀਨਕਸ ਸਵੈਪ" + msgid "Linux Vendor Firmware Service (stable firmware)" msgstr "ਲੀਨਕਸ ਵੇਂਡਰ ਫਿਰਮਵੇਅਰ ਸਰਵਿਸ (ਸਟੇਬਲ ਫਿਰਮਵੇਅਰ)" msgid "Linux Vendor Firmware Service (testing firmware)" msgstr "ਲੀਨਕਸ ਵੇਂਡਰ ਫਿਰਮਵੇਅਰ ਸਰਵਿਸ (ਟੈਸਟਿੰਗ ਫਿਰਮਵੇਅਰ)" +#. TRANSLATORS: Title: if it's tainted or not +msgid "Linux kernel" +msgstr "ਲੀਨਕਸ ਕਰਨਲ" + +#. TRANSLATORS: Title: lockdown is a security mode of the kernel +msgid "Linux kernel lockdown" +msgstr "ਲੀਨਕਸ ਕਰਨਲ ਲਾਕ-ਡਾਊਨ" + +#. TRANSLATORS: Title: swap space or swap partition +msgid "Linux swap" +msgstr "ਲੀਨਕਸ ਸਵੈਪ" + +#. TRANSLATORS: parsing the firmware information +msgid "Loading…" +msgstr "…ਲੋਡ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + #. TRANSLATORS: the release urgency msgid "Low" msgstr "ਘੱਟ" +msgid "MEI version" +msgstr "MEI ਵਰਜ਼ਨ" + #. TRANSLATORS: the release urgency msgid "Medium" msgstr "ਠੀਕ-ਠਾਕ" +msgid "Modify a configured remote" +msgstr "ਸੰਰਚਿਤ ਰਿਮੋਟ ਨੂੰ ਸੋਧੋ" + msgid "Modify daemon configuration" msgstr "ਡੈਮਨ ਸੰਰਚਨਾ ਸੋਧੋ" @@ -179,18 +423,87 @@ msgid "New version" msgstr "ਨਵਾਂ ਵਰਜ਼ਨ" +#. TRANSLATORS: user did not tell the tool what to do +msgid "No action specified!" +msgstr "ਕੋਈ ਕਾਰਵਾਈ ਨਹੀਂ ਦਿੱਤੀ ਗਈ!" + +#. TRANSLATORS: Suffix: the HSI result +msgid "Not found" +msgstr "ਨਹੀ ਲੱਭਿਆ" + +#. TRANSLATORS: Suffix: the HSI result +msgid "OK" +msgstr "ਠੀਕ ਹੈ" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "PATH" +msgstr "ਪਾਥ" + #. TRANSLATORS: remote filename base msgid "Password" msgstr "ਪਾਸਵਰਡ" +#. TRANSLATORS: the user isn't reading the question +#, c-format +msgid "Please enter a number from 0 to %u: " +msgstr "0 ਤੋਂ ਲੈ ਕੇ %u ਤੱਕ ਕੋਈ ਨੰਬਰ ਦਿਓ ਜੀ:" + +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' +#, c-format +msgid "Please enter either %s or %s: " +msgstr "%s ਜਾਂ %s ਵਿੱਚੋਂ ਕੋਈ ਇੱਕ ਦਿਓ ਜੀ:" + #. TRANSLATORS: a non-free software license msgid "Proprietary" msgstr "ਪ੍ਰੋਪ੍ਰੇਟਰੀ" +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Reading from %s…" +msgstr "…%s ਤੋਂ ਪੜ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ" + +#. TRANSLATORS: reading from the flash chips +msgid "Reading…" +msgstr "…ਪੜ੍ਹਿਆ ਜਾ ਰਿਹਾ ਹੈ" + +#. TRANSLATORS: Plugin is active and in use +msgid "Ready" +msgstr "ਤਿਆਰ" + +msgid "Reset daemon configuration" +msgstr "ਡੈਮਨ ਸੰਰਚਨਾ ਨੂੰ ਮੁੜ-ਸੈੱਟ ਕਰੋ" + #. TRANSLATORS: reboot to apply the update msgid "Restart now?" msgstr "ਹੁਣੇ ਮੁੜ-ਚਾਲੂ ਕਰਨਾ ਹੈ?" +#. TRANSLATORS: restarting the device to pick up new F/W +msgid "Restarting device…" +msgstr "…ਡਿਵਾਈਸ ਨੂੰ ਮੁੜ-ਚਾਲੂ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr get-upgrades` +#, c-format +msgid "Run `%s` for more information." +msgstr "ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ `%s` ਨੂੰ ਚਲਾਓ" + +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "SMAP" +msgstr "SMAP" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI lock" +msgstr "SPI ਲਾਕ" + +#. TRANSLATORS: Title: SPI refers to the flash chip in the computer +msgid "SPI write" +msgstr "SPI ਲਿਖਣ" + +#. TRANSLATORS: scheduling an update to be done on the next boot +msgid "Scheduling…" +msgstr "…ਸੈਡਿਊਲ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + #. TRANSLATORS: HSI event title msgid "Secure Boot disabled" msgstr "ਸੁਰੱਖਿਅਤ ਬੂਟ ਅਸਮਰੱਥ ਹੈ" @@ -199,6 +512,22 @@ msgid "Secure Boot enabled" msgstr "ਸੁਰੱਖਿਅਤ ਬੂਟ ਸਮਰੱਥ ਹੈ" +#. TRANSLATORS: %s is a link to a website +#, c-format +msgid "See %s for more information." +msgstr "ਹੋਰ ਜਾਣਕਾਰੀ ਲਈ %s ਨੂੰ ਵੇਖੋ।" + +#. TRANSLATORS: device has been chosen by the daemon for the user +msgid "Selected device" +msgstr "ਡਿਵਾਈਸ ਨੂੰ ਚੁਣੋ" + +msgid "Set one or more BIOS settings" +msgstr "ਇੱਕ ਜਾਂ ਵੱਧ BIOS ਸੈਟਿੰਗਾਂ ਨੂੰ ਸੈੱਟ ਕਰੋ" + +#. TRANSLATORS: description of a BIOS setting +msgid "Settings will apply after system reboots" +msgstr "ਸੈਟਿੰਗਾਂ ਸਿਸਟਮ ਦੇ ਮੁੜ-ਚਾਲੂ ਹੋਣ ਦੇ ਬਾਅਦ ਲਾਗੂ ਹੋਣਗੀਆਂ" + #. TRANSLATORS: file size of the download msgid "Size" msgstr "ਆਕਾਰ" @@ -207,6 +536,9 @@ msgid "Source" msgstr "ਸਰੋਤ" +msgid "Stop the fwupd service" +msgstr "fwupd ਸੇਵਾ ਨੂੰ ਰੋਕੋ" + #. TRANSLATORS: the update state of the specific device msgid "Success" msgstr "ਸਫ਼ਲ" @@ -215,10 +547,31 @@ msgid "Summary" msgstr "ਸਾਰ" +#. TRANSLATORS: Suffix: the HSI result +msgid "Supported" +msgstr "ਸਹਾਇਕ ਹੈ" + +#. TRANSLATORS: Title: if fwupd supports HSI on this chip +msgid "Supported CPU" +msgstr "ਸਹਾਇਕ CPU" + +#. TRANSLATORS: Title: sleep state +msgid "Suspend To RAM" +msgstr "ਰੈਮ ਉੱਤੇ ਸਸਪੈਂਡ ਕਰੋ" + #. TRANSLATORS: Must be plugged into an outlet msgid "System requires external power source" msgstr "ਸਿਸਟਮ ਨੂੰ ਬਾਹਰੀ ਪਾਵਰ ਸਰੋਤ ਚਾਹੀਦਾ ਹੈ" +#. TRANSLATORS: Title: PCRs (Platform Configuration Registers) shouldn't be +#. empty +msgid "TPM Platform Configuration" +msgstr "TPM ਪਲੇਟਫਾਰਮ ਸੰਰਚਨਾ" + +#. TRANSLATORS: Title: TPM = Trusted Platform Module +msgid "TPM v2.0" +msgstr "TPM v2.0" + #. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 msgid "Tag" msgid_plural "Tags" @@ -233,6 +586,41 @@ msgid "Type" msgstr "ਕਿਸਮ" +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI Memory Protection" +msgstr "UEFI ਮੈਮੋਰੀ ਸੁਰੱਖਿਆ" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI Platform Key" +msgstr "UEFI ਪਲੇਟਫਾਰਮ ਕੀ" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI Secure Boot" +msgstr "UEFI ਸਕਿਉਰ ਬੂਟ" + +#. TRANSLATORS: Title: is UEFI db up-to-date +msgid "UEFI db" +msgstr "UEFI ਡਾਟਾਬੇਸ" + +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI memory protection" +msgstr "UEFI ਮੈਮੋਰੀ ਸੁਰੱਖਿਆ" + +#. TRANSLATORS: Title: PK is the 'platform key' for the machine +msgid "UEFI platform key" +msgstr "UEFI ਪਲੇਟਫਾਰਮ ਕੀ" + +#. TRANSLATORS: Title: SB is a way of locking down UEFI +msgid "UEFI secure boot" +msgstr "UEFI ਸਕਿਉਰ ਬੂਟ" + +#. TRANSLATORS: current daemon status is unknown +#. TRANSLATORS: we don't know the license of the update +#. TRANSLATORS: unknown release urgency +#. TRANSLATORS: Suffix: the fallback HSI result +msgid "Unknown" +msgstr "ਅਣਪਛਾਤਾ" + #. TRANSLATORS: Name of hardware msgid "Unknown Device" msgstr "ਅਣਪਛਾਤਾ ਡਿਵਾਈਸ" @@ -240,6 +628,14 @@ msgid "Unlock the device to allow access" msgstr "ਪਹੁੰਚ ਦੀ ਇਜਾਜ਼ਤ ਦੇਣ ਲਈ ਡਿਵਾਈਸ ਅਣ-ਲਾਕ ਕਰੋ" +msgid "Update the stored device verification information" +msgstr "ਸਟੋਰ ਕੀਤੀ ਡਿਵਾਈਸ ਤਸਦੀਕ ਜਾਣਕਾਰੀ ਨੂੰ ਅੱਪਡੇਟ ਕਰੋ" + +#. TRANSLATORS: %1 is a device name +#, c-format +msgid "Updating %s…" +msgstr "…%s ਨੂੰ ਅੱਪਡੇਟ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ" + #. TRANSLATORS: how important the release is msgid "Urgency" msgstr "ਜ਼ਰੂਰਤ" @@ -252,10 +648,30 @@ msgid "Vendor" msgstr "ਵੇਂਡਰ" +#. TRANSLATORS: verifying we wrote the firmware correctly +msgid "Verifying…" +msgstr "…ਜਾਂਚਿਆ ਜਾ ਰਿਹਾ ਹੈ" + #. TRANSLATORS: the detected version number of the dbx msgid "Version" msgstr "ਵਰਜ਼ਨ" +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING" +msgstr "ਸਾਵਧਾਨ" + +#. TRANSLATORS: waiting for device to do something +msgid "Waiting…" +msgstr "…ਉਡੀਕ ਕੀਤੀ ਜਾ ਰਹੀ ਹੈ" + +#. TRANSLATORS: writing to the flash chips +msgid "Writing…" +msgstr "…ਲਿਖਿਆ ਜਾ ਰਿਹਾ ਹੈ" + #. TRANSLATORS: this is the default branch name when unset msgid "default" msgstr "ਮੂਲ" + +#. TRANSLATORS: Title: if the fwupd plugins are all present and correct +msgid "fwupd plugins" +msgstr "fwupd ਪਲੱਗਇਨ" diff -Nru fwupd-2.0.8/po/pl.po fwupd-2.0.20/po/pl.po --- fwupd-2.0.8/po/pl.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/pl.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,16 +4,20 @@ # # Translators: # Piotr Drąg , 2015-2025 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Polish (http://app.transifex.com/freedesktop/fwupd/language/pl/)\n" +"PO-Revision-Date: 2025-10-24 10:26+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Polish \n" +"Language: pl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: pl\n" "Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -36,7 +40,7 @@ msgstr "Aktualizacja akumulatora %s" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "Aktualizacja mikrokodu procesora %s" @@ -310,6 +314,26 @@ msgstr[2] "%u urządzeń nie ma najlepszej znanej konfiguracji." msgstr[3] "%u urządzeń nie ma najlepszej znanej konfiguracji." +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#, c-format +msgid "%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "%u devices are supported in the enabled remotes (an update has been published)" +msgstr[0] "%u urządzenie jest obsługiwane we włączonych repozytoriach (aktualizacja została opublikowana)" +msgstr[1] "%u urządzenia są obsługiwane we włączonych repozytoriach (aktualizacja została opublikowana)" +msgstr[2] "%u urządzeń jest obsługiwanych we włączonych repozytoriach (aktualizacja została opublikowana)" +msgstr[3] "%u urządzeń jest obsługiwanych we włączonych repozytoriach (aktualizacja została opublikowana)" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "%u urządzenie można aktualizować" +msgstr[1] "%u urządzenia można aktualizować" +msgstr[2] "%u urządzeń można aktualizować" +msgstr[3] "%u urządzeń można aktualizować" + #. TRANSLATORS: duration in minutes #, c-format msgid "%u hour" @@ -696,6 +720,10 @@ msgstr "Zmieniono" #. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "Sprawdza, czy jakieś urządzenia oczekują na ponowne uruchomienie, aby dokończyć aktualizację" + +#. TRANSLATORS: command description msgid "Checks cryptographic hash matches firmware" msgstr "Sprawdza, czy kryptograficzna suma kontrolna pasuje do oprogramowania sprzętowego" @@ -951,7 +979,7 @@ #. TRANSLATORS: message letting the user there is an update #. * waiting, but there is a reason it cannot be deployed msgid "Devices with firmware updates that need user action: " -msgstr "Urządzenia z aktualizacjami oprogramowania sprzętowego wymagającymi działania użytkownika:" +msgstr "Urządzenia z aktualizacjami oprogramowania sprzętowego wymagającymi działania użytkownika: " #. TRANSLATORS: message letting the user know no device #. * upgrade available due to missing on LVFS @@ -1102,6 +1130,10 @@ msgid "Duration" msgstr "Czas trwania" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "EMULATION-FILE [ARCHIVE-FILE]" +msgstr "PLIK-EMULACJI [PLIK-ARCHIWUM]" + #. TRANSLATORS: command description msgid "Emulate a device using a JSON manifest" msgstr "Emuluje urządzenie za pomocą manifestu JSON" @@ -1213,8 +1245,8 @@ msgstr "NAZWA-PLIKU CERTYFIKAT KLUCZ-PRYWATNY" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "NAZWA-PLIKU IDENTYFIKATOR-URZĄDZENIA" +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "NAZWA-PLIKU IDENTYFIKATOR-URZĄDZENIA [WERSJA]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" @@ -1324,6 +1356,10 @@ msgid "Filter with a set of release flags using a ~ prefix to exclude, e.g. 'trusted-release,~trusted-metadata'" msgstr "Filtruje za pomocą zestawu flag wydań, przedrostek ~ wyklucza, np. „trusted-release,~trusted-metadata”" +#. TRANSLATORS: command description +msgid "Finds firmware releases from the metadata" +msgstr "Znajduje wydania oprogramowania sprzętowego z metadanych" + #. TRANSLATORS: Title: if we can verify the firmware checksums msgid "Firmware Attestation" msgstr "Zaświadczenie oprogramowania sprzętowego" @@ -1500,8 +1536,8 @@ msgstr "Uzyskuje listę zablokowanego oprogramowania sprzętowego" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Uzyskuje listę aktualizacji dla podłączonego sprzętu" +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "Uzyskuje listę aktualizacji dla wszystkich podanych urządzeń, lub wszystkich urządzeń, jeśli nie podano żadnych" #. TRANSLATORS: command description msgid "Gets the releases for a device" @@ -1528,7 +1564,6 @@ msgstr "Zdarzenia zabezpieczeń komputera" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "Identyfikator zabezpieczeń komputera (HSI) jest nieobsługiwany" @@ -2034,6 +2069,10 @@ msgid "No action specified!" msgstr "Nie podano działania." +#. TRANSLATORS: no devices that can be upgraded with new firmware +msgid "No devices are updatable" +msgstr "Nie ma urządzeń, które można aktualizować" + #. TRANSLATORS: message letting the user know no device downgrade available #. * %1 is the device name #, c-format @@ -2053,6 +2092,14 @@ msgstr "Nie wykryto sprzętu z możliwością aktualizacji jego oprogramowania" #. TRANSLATORS: no repositories to download from +msgid "No matching releases for search token" +msgstr "Nie ma pasujących wydań dla szukanego słowa" + +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +msgstr "Ponowne uruchomienie nie jest konieczne" + +#. TRANSLATORS: no repositories to download from msgid "No releases available" msgstr "Brak dostępnych wydań" @@ -2105,6 +2152,10 @@ msgstr "Poprzednia wersja" #. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Instaluje tylko na emulowanych urządzeniach" + +#. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Wyświetla tylko jedną wartość PCR" @@ -2118,8 +2169,8 @@ msgstr "Dozwolone są tylko aktualizacje wersji" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Wyjście w formacie JSON" +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Wyjście w formacie JSON (wyłącza wszystkie interaktywne zapytania)" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -2186,15 +2237,20 @@ msgid "Please enter a number from 0 to %u: " msgstr "Proszę podać liczbę od 0 do %u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " -msgstr "Proszę podać %s lub %s:" +msgstr "Proszę podać %s lub %s: " #. TRANSLATORS: Failed to open plugin, hey Arch users msgid "Plugin dependencies missing" msgstr "Brak zależności wtyczki" +#. TRANSLATORS: The plugin enumeration might change the device current mode +msgid "Plugin enumeration may change device state" +msgstr "Wyliczenie wtyczek może zmienić stan urządzenia" + #. TRANSLATORS: The plugin is only for testing msgid "Plugin is only for testing" msgstr "Wtyczka jest tylko do testowania" @@ -2676,8 +2732,8 @@ #. TRANSLATORS: success message -- where 'metadata' is information #. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Pomyślnie pobrano nowe metadane: " +msgid "Successfully downloaded new metadata:" +msgstr "Pomyślnie pobrano nowe metadane:" #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" @@ -2896,6 +2952,10 @@ msgid "The TPM PCR0 differs from reconstruction." msgstr "PCR0 TPM różni się od rekonstrukcji." +#. TRANSLATORS: HSI event title +msgid "The UEFI certificate store is now up to date" +msgstr "Przechowalnia certyfikatów UEFI jest teraz aktualna" + #. TRANSLATORS: the user is SOL for support... msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" msgstr "Usługa wczytała kod firmy trzeciej i nie jest już wspierana przez jej programistów!" @@ -3059,6 +3119,10 @@ msgid "UEFI capsule updates not available or enabled in firmware setup" msgstr "Aktualizacje kapsułowe UEFI są niedostępne lub wyłączone w konfiguracji oprogramowania sprzętowego" +#. TRANSLATORS: Title: is UEFI db up-to-date +msgid "UEFI db" +msgstr "Baza danych UEFI" + #. TRANSLATORS: program name msgid "UEFI dbx Utility" msgstr "Narzędzie bazy dbx dla UEFI" @@ -3213,15 +3277,6 @@ msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" msgstr "Aktualizuje wszystkie podane urządzenia do najnowszej wersji oprogramowania sprzętowego, lub wszystkie urządzenia, jeśli nie podano żadnych" -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "Aktualizacje zostały opublikowane dla %u lokalnego urządzenia" -msgstr[1] "Aktualizacje zostały opublikowane dla %u z %u lokalnych urządzeń" -msgstr[2] "Aktualizacje zostały opublikowane dla %u z %u lokalnych urządzeń" -msgstr[3] "Aktualizacje zostały opublikowane dla %u z %u lokalnych urządzeń" - msgid "Updating" msgstr "Aktualizowanie" @@ -3320,6 +3375,10 @@ msgid "WARNING" msgstr "OSTRZEŻENIE" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "WORD" +msgstr "SŁOWO" + #. TRANSLATORS: command description msgid "Wait for a device to appear" msgstr "Odczekuje na pojawienie się urządzenia" diff -Nru fwupd-2.0.8/po/pt.po fwupd-2.0.20/po/pt.po --- fwupd-2.0.8/po/pt.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/pt.po 2026-02-26 11:36:18.000000000 +0000 @@ -6,16 +6,20 @@ # Hugo Carvalho , 2021 # Luis Filipe Teixeira , 2023-2024 # Peter J. Mello , 2020 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Portuguese (http://app.transifex.com/freedesktop/fwupd/language/pt/)\n" +"PO-Revision-Date: 2025-10-24 10:26+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Portuguese \n" +"Language: pt\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: pt\n" "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -31,7 +35,7 @@ msgstr "Atualização da bateria para %s" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "Atualização de microcódigo de CPU para %s" @@ -186,7 +190,7 @@ #, c-format msgid "%u device has a firmware upgrade available." msgid_plural "%u devices have a firmware upgrade available." -msgstr[0] " %u dispositivo tem uma atualização de firmware disponível." +msgstr[0] "%u dispositivo tem uma atualização de firmware disponível." msgstr[1] "%u dispositivos têm uma atualização de firmware disponível." msgstr[2] "%u dispositivos têm uma atualização de firmware disponível." @@ -765,10 +769,6 @@ msgstr "NOME-DE-FICHEIRO CERTIFICADO CHAVE-PRIVADA" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "NOME-DE-FICHEIRO ID-DISPOSITIVO" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME [DEVICE-ID|GUID]" msgstr "NOME-DE-FICHEIRO [ID-DISPOSITIVO|GUID]" @@ -946,10 +946,6 @@ msgstr "Obtém a lista de firmwares bloqueados" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Obtém a lista de atualizações para os hardwares conectados" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "Obtém os lançamentos para um dispositivo" @@ -1265,10 +1261,6 @@ msgstr "Apenas atualizações de versão são permitidas" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Saída em formato JSON" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Substitui o caminho padrão da ESP" @@ -1590,11 +1582,6 @@ msgid "Successfully disabled remote" msgstr "Remoto desabilitado com sucesso" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Transferidos com sucesso novos metadados: " - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "Remoto ativado e atualizado com sucesso" diff -Nru fwupd-2.0.8/po/pt_BR.po fwupd-2.0.20/po/pt_BR.po --- fwupd-2.0.8/po/pt_BR.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/pt_BR.po 2026-02-26 11:36:18.000000000 +0000 @@ -6,26 +6,31 @@ # Derek W. Stavis , 2015 # Derek W. Stavis , 2016 # Derek W. Stavis , 2015-2016 -# F Bausch, 2024 -# Rafael Fontenelle , 2017-2018 +# F Bausch, 2024-2025 +# Rafael Fontenelle , 2017-2018, 2025. # Rafael Fontenelle , 2015-2025 +# Richard Hughes , 2025. +# LucasMZ , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Portuguese (Brazil) (http://app.transifex.com/freedesktop/fwupd/language/pt_BR/)\n" +"PO-Revision-Date: 2025-10-26 05:02+0000\n" +"Last-Translator: LucasMZ \n" +"Language-Team: Portuguese (Brazil) \n" +"Language: pt_BR\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: pt_BR\n" "Plural-Forms: nplurals=3; plural=(n == 0 || n == 1) ? 0 : n != 0 && n % 1000000 == 0 ? 1 : 2;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format msgid "%.0f minute remaining" msgid_plural "%.0f minutes remaining" msgstr[0] "%.0f minuto restante" -msgstr[1] "%.0f minutos restantes" +msgstr[1] "%.0f de minutos restantes" msgstr[2] "%.0f minutos restantes" #. TRANSLATORS: BMC refers to baseboard management controller which @@ -40,7 +45,7 @@ msgstr "Atualização da bateria para %s" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "Atualização de microcódigo de CPU para %s" @@ -55,13 +60,13 @@ #. * the first %s is the device name, e.g. 'Secure Boot` #, c-format msgid "%s Configuration Update" -msgstr "Atualização da configuração de %s " +msgstr "Atualização da configuração de %s" #. TRANSLATORS: ME stands for Management Engine, where #. * the first %s is the device name, e.g. 'ThinkPad P50` #, c-format msgid "%s Consumer ME Update" -msgstr "Atualização consumidor-final de ME para %s " +msgstr "Atualização consumidor-final de ME para %s" #. TRANSLATORS: the controller is a device that has other devices #. * plugged into it, for example ThunderBolt, FireWire or USB, @@ -247,13 +252,13 @@ #. closed #, c-format msgid "%s is not currently updatable" -msgstr "%s não é atualmente atualizável:" +msgstr "%s não é atualmente atualizável" #. TRANSLATORS: %1 is a device name, e.g. "ThinkPad Universal #. * ThunderBolt 4 Dock" and %2 is "fwupdmgr activate" #, c-format msgid "%s is pending activation; use %s to complete the update." -msgstr "%s está pendente ativação. use %s para concluir a atualização" +msgstr "%s está pendente ativação, use %s para concluir a atualização." #. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT #, c-format @@ -285,22 +290,22 @@ #. TRANSLATORS: Title: %s is ME kind, e.g. CSME/TXT #, c-format msgid "%s version" -msgstr "versão %s " +msgstr "versão %s" #. TRANSLATORS: duration in days! #, c-format msgid "%u day" msgid_plural "%u days" msgstr[0] "%u dia" -msgstr[1] "%u dias" +msgstr[1] "%u de dias" msgstr[2] "%u dias" #. TRANSLATORS: this is shown in the MOTD #, c-format msgid "%u device has a firmware upgrade available." msgid_plural "%u devices have a firmware upgrade available." -msgstr[0] " %u dispositivo tem uma atualização de firmware disponível." -msgstr[1] "%u dispositivos têm uma atualização de firmware disponível." +msgstr[0] "%u dispositivo tem uma atualização de firmware disponível." +msgstr[1] "%u de dispositivos têm uma atualização de firmware disponível." msgstr[2] "%u dispositivos têm uma atualização de firmware disponível." #. TRANSLATORS: this is shown in the MOTD @@ -308,15 +313,33 @@ msgid "%u device is not the best known configuration." msgid_plural "%u devices are not the best known configuration." msgstr[0] "%u dispositivo não é a configuração mais conhecida." -msgstr[1] "%u dispositivos não são a configuração mais conhecida." +msgstr[1] "%u de dispositivos não são a configuração mais conhecida." msgstr[2] "%u dispositivos não são a configuração mais conhecida." +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#, c-format +msgid "%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "%u devices are supported in the enabled remotes (an update has been published)" +msgstr[0] "%u dispositivo é suportado nos remotos habilitados (uma atualização foi publicada)" +msgstr[1] "%u de dispositivos são suportados nos remotos habilitados (uma atualização foi publicada)" +msgstr[2] "%u dispositivos são suportados nos remotos habilitados (uma atualização foi publicada)" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "%u dispositivo é atualizável" +msgstr[1] "%u de dispositivos são atualizáveis" +msgstr[2] "%u dispositivos são atualizáveis" + #. TRANSLATORS: duration in minutes #, c-format msgid "%u hour" msgid_plural "%u hours" msgstr[0] "%u hora" -msgstr[1] "%u horas" +msgstr[1] "%u de horas" msgstr[2] "%u horas" #. TRANSLATORS: duration in minutes @@ -324,7 +347,7 @@ msgid "%u minute" msgid_plural "%u minutes" msgstr[0] "%u minuto" -msgstr[1] "%u minutos" +msgstr[1] "%u de minutos" msgstr[2] "%u minutos" #. TRANSLATORS: duration in seconds @@ -332,7 +355,7 @@ msgid "%u second" msgid_plural "%u seconds" msgstr[0] "%u segundo" -msgstr[1] "%u segundos" +msgstr[1] "%u de segundos" msgstr[2] "%u segundos" #. TRANSLATORS: device tests can be specific to a CPU type @@ -340,7 +363,7 @@ msgid "%u test was skipped" msgid_plural "%u tests were skipped" msgstr[0] "%u teste foi ignorado" -msgstr[1] "%u testes foram ignorado" +msgstr[1] "%u de testes foram ignorados" msgstr[2] "%u testes foram ignorados" #. TRANSLATORS: first percentage is current value, 2nd percentage is the @@ -696,6 +719,10 @@ msgstr "Alterado" #. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "Verifica se algum dispositivo está aguardando uma reinicialização para concluir a atualização" + +#. TRANSLATORS: command description msgid "Checks cryptographic hash matches firmware" msgstr "Verifica se o hash criptográfico corresponde ao firmware" @@ -970,7 +997,7 @@ #. TRANSLATORS: message letting the user know no #. * device upgrade available due to missing on LVFS msgid "Devices with no available firmware updates: " -msgstr "Dispositivos com nenhuma atualização de firmware disponível:" +msgstr "Dispositivos com nenhuma atualização de firmware disponível: " #. TRANSLATORS: message letting the user know no device upgrade available #. TRANSLATORS: message letting the user know no device @@ -1110,6 +1137,10 @@ msgid "Duration" msgstr "Duração" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "EMULATION-FILE [ARCHIVE-FILE]" +msgstr "ARQUIVO-DE-EMULAÇÃO [ARQUIVO-DE-ARQUIVAMENTO]" + #. TRANSLATORS: longer description msgid "Each system should have tests to ensure firmware security." msgstr "Cada sistema deve ter testes para garantir a segurança do firmware." @@ -1232,8 +1263,8 @@ msgstr "NOME-DE-ARQUIVO CERTIFICADO CHAVE-PRIVADA" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "NOME-DE-ARQUIVO ID-DISPOSITIVO" +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "NOME-DE-ARQUIVO ID-DISPOSITIVO [VERSÃO]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" @@ -1337,12 +1368,16 @@ #. 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\"" +msgstr "Filtra com um conjunto de opções de dispositivo usando um prefixo ~ para excluir, p.ex. \"internal,~needs-reboot\"" #. TRANSLATORS: command line option msgid "Filter with a set of release flags using a ~ prefix to exclude, e.g. 'trusted-release,~trusted-metadata'" msgstr "Filtra com um conjunto de opções de versão usando um prefixo ~ para excluir, p.ex., \"trusted-release,~trusted-metadata\"" +#. TRANSLATORS: command description +msgid "Finds firmware releases from the metadata" +msgstr "Encontra versões de firmware a partir dos metadados" + #. TRANSLATORS: Title: if we can verify the firmware checksums msgid "Firmware Attestation" msgstr "Atestação de firmware" @@ -1423,7 +1458,7 @@ msgid "Firmware metadata has not been updated for %u day and may not be up to date." msgid_plural "Firmware metadata has not been updated for %u days and may not be up to date." msgstr[0] "Os metadados do firmware não foram atualizados a %u dia e podem não estar atualizados." -msgstr[1] "Os metadados do firmware não foram atualizados a %u dias e podem não estar atualizados." +msgstr[1] "Os metadados do firmware não foram atualizados a %u de dias e podem não estar atualizados." msgstr[2] "Os metadados do firmware não foram atualizados a %u dias e podem não estar atualizados." #. TRANSLATORS: Title: if firmware updates are available @@ -1536,8 +1571,8 @@ msgstr "Obtém a lista de firmwares bloqueados" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Obtém a lista de atualizações para os hardwares conectados" +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "Obtém a lista de atualizações para todos os dispositivos especificados ou todos os dispositivos se não especificados" #. TRANSLATORS: command description msgid "Gets the releases for a device" @@ -1564,7 +1599,6 @@ msgstr "Eventos de segurança do host" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "ID de segurança do host (HSI) não suportado" @@ -2098,6 +2132,10 @@ msgid "No action specified!" msgstr "Nenhuma ação especificada!" +#. TRANSLATORS: no devices that can be upgraded with new firmware +msgid "No devices are updatable" +msgstr "Nenhum dispositivo é atualizável" + #. TRANSLATORS: message letting the user know no device downgrade available #. * %1 is the device name #, c-format @@ -2117,6 +2155,14 @@ msgstr "Nenhum periférico com capacidade de atualização de firmware foi detectado" #. TRANSLATORS: no repositories to download from +msgid "No matching releases for search token" +msgstr "Nenhuma versão correspondente para o token pesquisado" + +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +msgstr "Não é necessário reinicializar" + +#. TRANSLATORS: no repositories to download from msgid "No releases available" msgstr "Nenhum lançamento disponível" @@ -2169,6 +2215,10 @@ msgstr "Versão antiga" #. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Só instala em dispositivos emulados" + +#. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Mostra apenas valor único de PCR" @@ -2182,8 +2232,8 @@ msgstr "Apenas atualizações de versão são permitidas" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Saída em formato JSON" +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Saída no formato JSON (desabilita todos os prompts interativos)" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -2254,7 +2304,8 @@ msgid "Please enter a number from 0 to %u: " msgstr "Por favor, insira um número de 0 a %u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " msgstr "Por favor, insira %s ou %s: " @@ -2263,6 +2314,10 @@ msgid "Plugin dependencies missing" msgstr "Dependências de plugin ausentes" +#. TRANSLATORS: The plugin enumeration might change the device current mode +msgid "Plugin enumeration may change device state" +msgstr "A enumeração de plugin pode alterar o estado do dispositivo" + #. TRANSLATORS: The plugin is only for testing msgid "Plugin is only for testing" msgstr "O plugin é apenas para testes" @@ -2752,7 +2807,7 @@ #. TRANSLATORS: success message -- where 'metadata' is information #. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " +msgid "Successfully downloaded new metadata:" msgstr "Baixados com sucesso novos metadados:" #. TRANSLATORS: success message @@ -2800,9 +2855,9 @@ #, c-format msgid "Successfully uploaded %u report" msgid_plural "Successfully uploaded %u reports" -msgstr[0] "Enviado com sucesso %u relatório" -msgstr[1] "Enviados com sucesso %u relatórios" -msgstr[2] "Enviados com sucesso %u relatórios" +msgstr[0] "%u relatório enviado com sucesso" +msgstr[1] "%u de relatórios enviados com sucesso" +msgstr[2] "%u relatórios enviados com sucesso" #. TRANSLATORS: success message when user verified device checksums msgid "Successfully verified device checksums" @@ -3010,6 +3065,14 @@ msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." msgstr "A chave de plataforma UEFI é usada para determinar se o software do dispositivo vem de uma fonte confiável." +#. TRANSLATORS: HSI event title +msgid "The UEFI certificate store is now up to date" +msgstr "O armazenamento de certificados UEFI agora está atualizado" + +#. TRANSLATORS: longer description +msgid "The UEFI db contains the list of valid certificates that can be used to authorize what EFI binaries are allowed to run." +msgstr "O banco de dados UEFI contém a lista de certificados válidos que podem ser usados para autorizar quais binários EFI podem ser executados." + #. TRANSLATORS: longer description msgid "The UEFI system can set up memory attributes at boot which prevent common exploits from running." msgstr "O sistema UEFI pode configurar atributos de memória na inicialização que impedem a execução de exploits comuns." @@ -3103,7 +3166,7 @@ #. TRANSLATORS: CLI description msgid "This tool allows an administrator to use the fwupd plugins without being installed on the host system." -msgstr "Esta ferramenta permite que um administrador use os plugins do fwupd sem estarem instalados no sistema host" +msgstr "Esta ferramenta permite que um administrador use os plugins do fwupd sem estarem instalados no sistema host." #. TRANSLATORS: the %1 is a kernel command line key=value #, c-format @@ -3175,7 +3238,7 @@ #. TRANSLATORS: longer description msgid "UEFI boot service variables should not be readable from runtime mode." -msgstr "As variáveis ​​do serviço de inicialização UEFI não devem ser legíveis no modo de tempo de execução." +msgstr "As variáveis do serviço de inicialização UEFI não devem ser legíveis no modo de tempo de execução." #. TRANSLATORS: Title: Bootservice is when only readable from early-boot msgid "UEFI bootservice variables" @@ -3343,14 +3406,6 @@ msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" msgstr "Atualiza todos os dispositivos especificados para a versão de firmware mais recente ou todos os dispositivos se não especificados" -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "Atualizações foram publicadas para %u dispositivo local" -msgstr[1] "Atualizações foram publicadas para %u de %u dispositivos locais" -msgstr[2] "Atualizações foram publicadas para %u de %u dispositivos locais" - msgid "Updating" msgstr "Atualizando" @@ -3371,7 +3426,7 @@ #. TRANSLATORS: command description msgid "Upload the list of updatable devices to a remote server" -msgstr "Envia a lista de dispositivos atualizáveis ​​para um servidor remoto" +msgstr "Envia a lista de dispositivos atualizáveis para um servidor remoto" #. TRANSLATORS: ask the user to share, %s is something #. * like: "Linux Vendor Firmware Service" @@ -3449,6 +3504,10 @@ msgid "WARNING" msgstr "AVISO" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "WORD" +msgstr "PALAVRA" + #. TRANSLATORS: command description msgid "Wait for a device to appear" msgstr "Aguarda até que um dispositivo apareça" diff -Nru fwupd-2.0.8/po/ro.po fwupd-2.0.20/po/ro.po --- fwupd-2.0.8/po/ro.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/ro.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,16 +4,20 @@ # # Translators: # Remus-Gabriel Chelu , 2024-2025 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Romanian (http://app.transifex.com/freedesktop/fwupd/language/ro/)\n" +"PO-Revision-Date: 2025-10-24 10:32+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Romanian \n" +"Language: ro\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ro\n" -"Plural-Forms: nplurals=3; plural=(n==1?0:(((n%100>19)||((n%100==0)&&(n!=0)))?2:1));\n" +"Plural-Forms: nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -35,7 +39,7 @@ msgstr "Actualizarea bateriei %s" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "Actualizarea microcodului CPU %s" @@ -306,6 +310,24 @@ msgstr[1] "%u dispozitive nu sunt la cea mai bună configurație cunoscută." msgstr[2] "%u de dispozitive nu sunt la cea mai bună configurație cunoscută." +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#, c-format +msgid "%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "%u devices are supported in the enabled remotes (an update has been published)" +msgstr[0] "%u dispozitiv este inclus în depozitele de la distanță activate (a fost publicată o actualizare)" +msgstr[1] "%u dispozitiv sunt incluse în depozitele de la distanță activate (a fost publicată o actualizare)" +msgstr[2] "%u de dispozitiv sunt incluse în depozitele de la distanță activate (a fost publicată o actualizare)" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "%u dispozitiv este actualizabil" +msgstr[1] "%u dispozitive sunt actualizabile" +msgstr[2] "%u de dispozitive sunt actualizabile" + #. TRANSLATORS: duration in minutes #, c-format msgid "%u hour" @@ -585,7 +607,7 @@ #. TRANSLATORS: we can auto-uninhibit after a timeout #, c-format msgid "Automatically uninhibiting in %ums…" -msgstr "Dezinhibare automată în %ums..." +msgstr "Dezinhibare automată în %ums…" #. TRANSLATORS: can we JFDI? msgid "Automatically upload every time?" @@ -691,6 +713,10 @@ msgstr "Modificat" #. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "Verifică dacă există dispozitive care așteaptă o repornire pentru a finaliza actualizarea" + +#. TRANSLATORS: command description msgid "Checks cryptographic hash matches firmware" msgstr "Verifică dacă suma de control criptografică corespunde firmware-ului" @@ -1231,8 +1257,8 @@ msgstr "NUME_FIȘIER CERTIFICAT CHEIE-PRIVATĂ" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "NUME_FIȘIER ID-DISPOZITIV" +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "NUME_FIȘIER ID-DISPOZITIV [VERSIUNE]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" @@ -1342,6 +1368,10 @@ msgid "Filter with a set of release flags using a ~ prefix to exclude, e.g. 'trusted-release,~trusted-metadata'" msgstr "Filtrează cu un set de fanioane de lansare folosind un prefix ~ pentru a exclude, de exemplu „trusted-release,~trusted-metadata”" +#. TRANSLATORS: command description +msgid "Finds firmware releases from the metadata" +msgstr "Găsește versiunile firmware din metadate" + #. TRANSLATORS: Title: if we can verify the firmware checksums msgid "Firmware Attestation" msgstr "Atestarea firmware" @@ -1535,8 +1565,8 @@ msgstr "Obține lista firmware-ului blocat" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Obține lista de actualizări pentru hardware-ul conectat" +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "Obține lista de actualizări pentru toate dispozitivele specificate sau pentru toate dispozitivele dacă nu este specificat niciunul" #. TRANSLATORS: command description msgid "Gets the releases for a device" @@ -1563,7 +1593,6 @@ msgstr "Evenimente de securitate ale gazdei" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "ID-ul de securitate al gazdei (HSI) nu este acceptat" @@ -1787,7 +1816,7 @@ #. consumer #. * boards msgid "Intel Management Engine Override" -msgstr "Intel Management Engine Override" +msgstr "Redefinirea motorului de gestionare Intel" #. TRANSLATORS: longer description msgid "Intel Management Engine Override disables checks for device software tampering." @@ -2097,6 +2126,10 @@ msgid "No action specified!" msgstr "Nicio acțiune specificată!" +#. TRANSLATORS: no devices that can be upgraded with new firmware +msgid "No devices are updatable" +msgstr "Nu există dispozitive actualizabile" + #. TRANSLATORS: message letting the user know no device downgrade available #. * %1 is the device name #, c-format @@ -2116,6 +2149,14 @@ msgstr "Nu a fost detectat niciun hardware cu capacitate de actualizare a firmware-ului" #. TRANSLATORS: no repositories to download from +msgid "No matching releases for search token" +msgstr "Nu există rezultate corespunzătoare pentru termenul de căutare" + +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +msgstr "Nu este necesară repornirea" + +#. TRANSLATORS: no repositories to download from msgid "No releases available" msgstr "Nu există versiuni disponibile" @@ -2168,6 +2209,10 @@ msgstr "Versiunea veche" #. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Se instalează numai pe dispozitive emulate" + +#. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Afișează doar o singură valoare PCR" @@ -2181,8 +2226,8 @@ msgstr "Sunt permise numai actualizările de versiune" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Ieșire în format JSON" +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Ieșire în format JSON (dezactivează toate solicitările interactive)" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -2253,7 +2298,8 @@ msgid "Please enter a number from 0 to %u: " msgstr "Introduceți un număr de la 0 la %u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " msgstr "Introduceți %s sau %s: " @@ -2262,6 +2308,10 @@ msgid "Plugin dependencies missing" msgstr "Lipsesc dependențele modulului" +#. TRANSLATORS: The plugin enumeration might change the device current mode +msgid "Plugin enumeration may change device state" +msgstr "Enumerarea modulelor de extensie poate modifica starea dispozitivului" + #. TRANSLATORS: The plugin is only for testing msgid "Plugin is only for testing" msgstr "Modulul este doar pentru testare" @@ -2751,8 +2801,8 @@ #. TRANSLATORS: success message -- where 'metadata' is information #. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Au fost descărcate cu succes metadatele noi: " +msgid "Successfully downloaded new metadata:" +msgstr "Au fost descărcate cu succes metadatele noi:" #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" @@ -2935,7 +2985,7 @@ #. TRANSLATORS: Title: TPM = Trusted Platform Module msgid "TPM v2.0" -msgstr "TPM v2.0" +msgstr "TPM versiunea 2.0" #. TRANSLATORS: release tag set for release, e.g. lenovo-2021q3 msgid "Tag" @@ -3350,14 +3400,6 @@ msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" msgstr "Actualizează toate dispozitivele specificate la cea mai recentă versiune de firmware sau toate dispozitivele dacă nu se specifică niciunul" -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "Au fost publicate actualizări pentru %u dispozitiv local" -msgstr[1] "Au fost publicate actualizări pentru %u din %u dispozitive locale" -msgstr[2] "Au fost publicate actualizări pentru %u din %u de dispozitive locale" - msgid "Updating" msgstr "Se actualizează" @@ -3456,6 +3498,10 @@ msgid "WARNING" msgstr "AVERTISMENT" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "WORD" +msgstr "CUVÂNT_CĂUTARE" + #. TRANSLATORS: command description msgid "Wait for a device to appear" msgstr "Așteaptă să apară un dispozitiv" diff -Nru fwupd-2.0.8/po/ru.po fwupd-2.0.20/po/ru.po --- fwupd-2.0.8/po/ru.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/ru.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,19 +4,25 @@ # # Translators: # Igor , 2017 +# Sergej A. , 2025 # Serge Vylekzhanin , 2015-2019 # Sergej A. , 2022-2023 # Sergej A. , 2022 +# Aleksandr Melentev , 2025. +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Russian (http://app.transifex.com/freedesktop/fwupd/language/ru/)\n" +"PO-Revision-Date: 2025-10-24 10:33+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Russian \n" +"Language: ru\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: ru\n" "Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -196,6 +202,10 @@ msgid "A TPM PCR is now an invalid value" msgstr "PCR TPM теперь имеет недействительное значение" +#. TRANSLATORS: Title: if hardware enforces control of SPI writes +msgid "AMD Firmware Write Protection" +msgstr "Защита от записи прошивки AMD" + #. TRANSLATORS: the user needs to do something, e.g. remove the device msgid "Action Required:" msgstr "Необходимое действие:" @@ -244,6 +254,10 @@ msgid "Allow switching firmware branch" msgstr "Разрешить переключение веток прошивки" +#. TRANSLATORS: error message +msgid "Already exists, and no --force specified" +msgstr "Уже существует, и не указано --force" + #. TRANSLATORS: is not the main firmware stream msgid "Alternate branch" msgstr "Альтернативная ветка" @@ -298,6 +312,10 @@ msgstr "Для понижения версии прошивки на этой машине требуется аутентификация" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to fix a host security issue" +msgstr "Для устранения проблемы безопасности хоста требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to modify BIOS settings" msgstr "Для изменения настроек BIOS требуется аутентификация" @@ -310,6 +328,10 @@ msgstr "Для изменения настроек фоновой службы требуется аутентификация" #. TRANSLATORS: this is the PolicyKit modal dialog +msgid "Authentication is required to read BIOS settings" +msgstr "Для чтения настроек BIOS требуется аутентификация" + +#. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to set the list of approved firmware" msgstr "Для установки списка одобренных прошивок требуется аутентификация" @@ -339,7 +361,7 @@ #. TRANSLATORS: description of a BIOS setting msgid "BIOS updates delivered via LVFS or Windows Update" -msgstr "Обновления BIOS доставляются через LVFS или Центр обновления Windows." +msgstr "Обновления BIOS доставляются через LVFS или Центр обновления Windows" #. TRANSLATORS: refers to the battery inside the peripheral device msgid "Battery" @@ -395,6 +417,10 @@ msgid "Checksum" msgstr "Контрольная сумма" +#. TRANSLATORS: get interactive prompt +msgid "Choose device" +msgstr "Выберать устройство" + #. TRANSLATORS: command description msgid "Clears the results from the last update" msgstr "Очистить результаты c последнего обновления" @@ -495,7 +521,7 @@ #. TRANSLATORS: message letting the user know no #. * device upgrade available due to missing on LVFS msgid "Devices with no available firmware updates: " -msgstr "Устройства без доступных обновлений прошивки:" +msgstr "Устройства без доступных обновлений прошивки: " #. TRANSLATORS: message letting the user know no device upgrade available #. TRANSLATORS: message letting the user know no device @@ -574,7 +600,7 @@ #. TRANSLATORS: downloading from a remote server msgid "Downloading…" -msgstr "Получение..." +msgstr "Получение…" #. TRANSLATORS: command description msgid "Dump SMBIOS data from a file" @@ -769,6 +795,9 @@ msgid "GUID" msgstr "GUID" +msgid "Get BIOS settings" +msgstr "Получить настройки BIOS" + #. TRANSLATORS: command description msgid "Get all device flags supported by fwupd" msgstr "Получить все флаги устройств, поддерживаемые fwupd" @@ -790,10 +819,6 @@ msgstr "Получить настроенные репозитории" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Получить список обновлений для подключенного оборудования" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "Получить релизы для устройства" @@ -818,6 +843,10 @@ msgid "Ignore SSL strict checks when downloading files" msgstr "Игнорировать строгие проверки SSL при загрузке файлов" +#. TRANSLATORS: show the user a generic image that can be themed +msgid "Image" +msgstr "Изображение" + #. TRANSLATORS: length of time the update takes to apply msgid "Install Duration" msgstr "Продолжительность установки" @@ -957,6 +986,10 @@ msgid "Medium" msgstr "Средний" +#. TRANSLATORS: ask the user to do a simple task which should be translated +msgid "Message" +msgstr "Сообщение" + #. TRANSLATORS: remote URI msgid "Metadata URI" msgstr "URI метаданных" @@ -1005,7 +1038,7 @@ #. TRANSLATORS: user did not tell the tool what to do msgid "No action specified!" -msgstr "Не определено никаких действий." +msgstr "Не определено никаких действий!" #. TRANSLATORS: nothing found msgid "No firmware IDs found" @@ -1067,10 +1100,6 @@ msgstr "Показывать только одно значение PCR" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Вывод в формате JSON" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Переопределить путь ESP по умолчанию" @@ -1113,7 +1142,13 @@ #. TRANSLATORS: the user isn't reading the question #, c-format msgid "Please enter a number from 0 to %u: " -msgstr "Пожалуйста, введите число от 0 до %u:" +msgstr "Пожалуйста, введите число от 0 до %u: " + +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' +#, c-format +msgid "Please enter either %s or %s: " +msgstr "Пожалуйста, введите либо %s, либо %s: " #. TRANSLATORS: Possible values for a bios setting msgid "Possible Values" @@ -1204,6 +1239,18 @@ msgid "Return all the hardware IDs for the machine" msgstr "Показать идентификаторы всех устройств на машине" +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr get-upgrades` +#, c-format +msgid "Run `%s` for more information." +msgstr "Запустите `%s` для получения дополнительной информации." + +#. TRANSLATORS: this is shown in the MOTD -- %1 is the +#. * command name, e.g. `fwupdmgr sync` +#, c-format +msgid "Run `%s` to complete this action." +msgstr "Запустите `%s` для завершения этого действия." + #. TRANSLATORS: command line option msgid "Run the plugin composite cleanup routine when using install-blob" msgstr "Запустить процедуру очистки составного плагина, используя двоичную установку" @@ -1212,6 +1259,10 @@ msgid "Run the plugin composite prepare routine when using install-blob" msgstr "Запустить процедуру подготовки составного плагина, используя двоичную установку" +#. TRANSLATORS: Title: SMAP = Supervisor Mode Access Prevention +msgid "SMAP" +msgstr "SMAP" + #. TRANSLATORS: Title: SPI refers to the flash chip in the computer msgid "SPI BIOS Descriptor" msgstr "Дескриптор SPI BIOS" @@ -1266,6 +1317,9 @@ msgid "Serial Number" msgstr "Серийный номер" +msgid "Set one or more BIOS settings" +msgstr "Установить одну или несколько настроек BIOS" + #. TRANSLATORS: firmware approved by the admin msgid "Sets the list of approved firmware" msgstr "Установить список одобренных прошивок" @@ -1366,11 +1420,6 @@ msgid "Successfully disabled remote" msgstr "Удалённый репозиторий успешно отключен" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Успешно загружены новые метаданные:" - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "Удалённый репозиторий успешно подключен и обновлён" @@ -1636,6 +1685,10 @@ msgid "Version[fwupd]" msgstr "Версия[fwupd]" +#. TRANSLATORS: this is a prefix on the console +msgid "WARNING" +msgstr "ПРЕДУПРЕЖДЕНИЕ" + #. TRANSLATORS: waiting for device to do something msgid "Waiting…" msgstr "Ожидание…" diff -Nru fwupd-2.0.8/po/si.po fwupd-2.0.20/po/si.po --- fwupd-2.0.8/po/si.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/si.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,16 +4,20 @@ # # Translators: # HelaBasa Group , 2021-2022 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Sinhala (http://app.transifex.com/freedesktop/fwupd/language/si/)\n" +"PO-Revision-Date: 2025-10-24 10:33+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Sinhala \n" +"Language: si\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: si\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -34,7 +38,7 @@ msgstr "%s බැටරි යාවත්කාලීන කිරීම" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "%s CPU මයික්‍රොකෝඩ් යාවත්කාලීන කිරීම" @@ -857,10 +861,6 @@ msgstr "ගොනු නාම සහතිකය පුද්ගලික යතුර" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "FILENAME උපාංගය-ID" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" msgstr "FILENAME OFFSET දත්ත [FIRMWARE-TYPE]" @@ -1050,10 +1050,6 @@ msgstr "අවහිර කළ ස්ථිරාංග ලැයිස්තුව ලබා ගනී" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "සම්බන්ධිත දෘඩාංග සඳහා යාවත්කාලීන ලැයිස්තුව ලබා ගනී" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "උපාංගයක් සඳහා නිකුත් කිරීම් ලබා ගනී" @@ -1420,10 +1416,6 @@ msgstr "අනුවාද උත්ශ්‍රේණි කිරීමට පමණක් අවසර ඇත" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "JSON ආකෘතියෙන් ප්‍රතිදානය" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "පෙරනිමි ESP මාර්ගය අභිබවා යන්න" @@ -1793,11 +1785,6 @@ msgid "Successfully disabled remote" msgstr "දුරස්ථ පාලකය සාර්ථකව අබල කරන ලදී" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "නව පාරදත්ත සාර්ථකව බාගන්නා ලදී: " - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "දුරස්ථ පාලකය සාර්ථකව සබල කර නැවුම් කරන ලදී" diff -Nru fwupd-2.0.8/po/sk.po fwupd-2.0.20/po/sk.po --- fwupd-2.0.8/po/sk.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/sk.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,16 +4,20 @@ # # Translators: # Dušan Kazik , 2015-2017 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Slovak (http://app.transifex.com/freedesktop/fwupd/language/sk/)\n" +"PO-Revision-Date: 2025-10-24 10:33+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Slovak \n" +"Language: sk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"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" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: this is a command alias, e.g. 'get-devices' #, c-format @@ -38,7 +42,7 @@ #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on a removable device" -msgstr "Vyžaduje sa overenie totožnosti na aktualizovanie firmvéru vymeniteľného zariadenia " +msgstr "Vyžaduje sa overenie totožnosti na aktualizovanie firmvéru vymeniteľného zariadenia" #. TRANSLATORS: this is the PolicyKit modal dialog msgid "Authentication is required to update the firmware on this machine" @@ -133,10 +137,6 @@ msgstr "Získa podrobnosti o súbore firmvéru" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Získa zoznam aktualizácií pre pripojený hardvér" - -#. TRANSLATORS: command description msgid "Gets the results from the last update" msgstr "Získa výsledky z poslednej aktualizácie" diff -Nru fwupd-2.0.8/po/sl.po fwupd-2.0.20/po/sl.po --- fwupd-2.0.8/po/sl.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/sl.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,16 +4,20 @@ # # Translators: # Martin Srebotnjak , 2021,2024-2025 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Slovenian (http://app.transifex.com/freedesktop/fwupd/language/sl/)\n" +"PO-Revision-Date: 2025-10-24 10:36+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Slovenian \n" +"Language: sl\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: sl\n" -"Plural-Forms: nplurals=4; plural=(n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3);\n" +"Plural-Forms: nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -36,7 +40,7 @@ msgstr "Posodobitev baterije %s" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "Posodobitev mikrokode CPE %s" @@ -93,7 +97,7 @@ #. TRANSLATORS: drive refers to a storage device, e.g. SATA disk #, c-format msgid "%s Drive Update" -msgstr "Posodobitev naprave %s" +msgstr "Posodobitev pogona %s" #. TRANSLATORS: the EC is typically the keyboard controller chip, #. * the first %s is the device name, e.g. 'ThinkPad P50` @@ -305,11 +309,31 @@ #, c-format msgid "%u device is not the best known configuration." msgid_plural "%u devices are not the best known configuration." -msgstr[0] " %u naprave niso najboljša znana konfiguracija." -msgstr[1] " %u naprava ni najboljša znana konfiguracija." +msgstr[0] "%u naprav niso najboljša znana konfiguracija." +msgstr[1] "%u naprava ni najboljša znana konfiguracija." msgstr[2] "%u napravi nista najboljša znana konfiguracija." msgstr[3] "%u naprave niso najboljša znana konfiguracija." +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#, c-format +msgid "%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "%u devices are supported in the enabled remotes (an update has been published)" +msgstr[0] "%u naprav je podprtih v omogočenih oddaljenih (posodobitev je že objavljena)" +msgstr[1] "%u naprava je podprta v omogočenih oddaljenih (posodobitev je že objavljena)" +msgstr[2] "%u napravi sta podprti v omogočenih oddaljenih (posodobitev je že objavljena)" +msgstr[3] "%u naprave so podprte v omogočenih oddaljenih (posodobitev je že objavljena)" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "%u naprav je možno posodobiti" +msgstr[1] "%u napravo je možno posodobiti" +msgstr[2] "%u napravi je možno posodobiti" +msgstr[3] "%u naprave je možno posodobiti" + #. TRANSLATORS: duration in minutes #, c-format msgid "%u hour" @@ -341,9 +365,9 @@ #, c-format msgid "%u test was skipped" msgid_plural "%u tests were skipped" -msgstr[0] "%u preizkusov je preskočenih" -msgstr[1] "%u preizkus je preskočen" -msgstr[2] "%u preizkusa sta preskočena" +msgstr[0] "%u preizkusov je preskočenih" +msgstr[1] "%u preizkus je preskočen" +msgstr[2] "%u preizkusa sta preskočena" msgstr[3] "%u preizkusi so preskočeni" #. TRANSLATORS: first percentage is current value, 2nd percentage is the @@ -501,7 +525,7 @@ #. TRANSLATORS: waiting for user to authenticate msgid "Authenticating…" -msgstr "Preverjanje pristnosti..." +msgstr "Preverjanje pristnosti …" #. TRANSLATORS: user needs to run a command msgid "Authentication details are required" @@ -594,7 +618,7 @@ #. TRANSLATORS: we can auto-uninhibit after a timeout #, c-format msgid "Automatically uninhibiting in %ums…" -msgstr "Samodejno sproščanje sistema čez %u ms ..." +msgstr "Samodejno sproščanje sistema čez %u ms …" #. TRANSLATORS: can we JFDI? msgid "Automatically upload every time?" @@ -700,6 +724,10 @@ msgstr "Spremenjeno" #. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "Preveri, če katera od naprav čaka na ponoven zagon za zaključek posodobitve" + +#. TRANSLATORS: command description msgid "Checks cryptographic hash matches firmware" msgstr "Preverja, da se kriptografska kontrolna vsota ujema z vdelano programsko opremo" @@ -731,7 +759,7 @@ #. TRANSLATORS: command description msgid "Clears the results from the last update" -msgstr "Počisti rezultate zadnje posodobitve." +msgstr "Počisti rezultate zadnje posodobitve" #. TRANSLATORS: error message msgid "Command not found" @@ -799,7 +827,7 @@ #. TRANSLATORS: decompressing the firmware file msgid "Decompressing…" -msgstr "Razširjanje ..." +msgstr "Razširjanje …" #. TRANSLATORS: command description msgid "Delete an EFI boot entry" @@ -963,7 +991,7 @@ #. TRANSLATORS: message letting the user there is an update #. * waiting, but there is a reason it cannot be deployed msgid "Devices with firmware updates that need user action: " -msgstr "Naprave s posodobitvami vdelane programske opreme, ki zahtevajo ukrepanje:" +msgstr "Naprave s posodobitvami vdelane programske opreme, ki zahtevajo ukrepanje: " #. TRANSLATORS: message letting the user know no device #. * upgrade available due to missing on LVFS @@ -1096,7 +1124,7 @@ #. TRANSLATORS: %1 is a device name #, c-format msgid "Downgrading %s…" -msgstr "Povrnitev na starejšo različico %s ..." +msgstr "Povrnitev na starejšo različico %s …" #. TRANSLATORS: command description msgid "Download a file" @@ -1114,6 +1142,10 @@ msgid "Duration" msgstr "Trajanje" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "EMULATION-FILE [ARCHIVE-FILE]" +msgstr "DATOTEKA-EMULACIJE [ARHIVSKA-DATOTEKA]" + #. TRANSLATORS: longer description msgid "Each system should have tests to ensure firmware security." msgstr "Vsak sistem mora imeti preizkuse za zagotovitev varnosti vdelane programske opreme." @@ -1197,7 +1229,7 @@ #. TRANSLATORS: erasing contents of the flash chips msgid "Erasing…" -msgstr "Poteka brisanje..." +msgstr "Poteka brisanje …" #. TRANSLATORS: exit after we've started up, used for user profiling msgid "Exit after a small delay" @@ -1236,8 +1268,8 @@ msgstr "IME-DATOTEKE POTRDILO OSEBNI-KLJUČ" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "IMEDATOTEKE ID-NAPRAVE" +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "IME-DATOTEKE ID-NAPRAVE [RAZLIČICA]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" @@ -1345,7 +1377,11 @@ #. TRANSLATORS: command line option msgid "Filter with a set of release flags using a ~ prefix to exclude, e.g. 'trusted-release,~trusted-metadata'" -msgstr "Filtriraj z naborom zastavic za izdajo s predpono ~, da se izključijo npr. »zaupanja-vredna-izdaja,~zaupanja-vredni-metapodatki« oz. 'trusted-release,~trusted-metadata'" +msgstr "Filtriraj z naborom zastavic za izdajo s predpono ~, da se izključijo npr. »zaupanja-vredna-izdaja,~zaupanja-vredni-metapodatki« oz. 'trusted-release,~trusted-metadata'" + +#. TRANSLATORS: command description +msgid "Finds firmware releases from the metadata" +msgstr "Poišče izdaje strojne programske opreme iz metapodatkov" #. TRANSLATORS: Title: if we can verify the firmware checksums msgid "Firmware Attestation" @@ -1542,8 +1578,8 @@ msgstr "Pridobi seznam blokirane vdelane programske opreme" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Pridobi seznam posodobitev za priključeno strojno opremo" +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "Pridobi seznam posodobitev za vse navedene naprave ali za vse naprave, če nobena ni izrecno navedena" #. TRANSLATORS: command description msgid "Gets the releases for a device" @@ -1570,7 +1606,6 @@ msgstr "Dogodki varnosti gostitelja" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "Varnostni ID gostitelja (HSI) ni podprt" @@ -1592,7 +1627,7 @@ #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "INDEX NAME TARGET [MOUNTPOINT]" -msgstr "INDEKS IME CILJ [TOČKAPRIKLOPA]" +msgstr "INDEKS IME CILJ [TOČKAPRIKLOPA]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "INDEX1,INDEX2" @@ -1711,12 +1746,12 @@ #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" -msgstr "Nameščanje posodobitve strojne programske opreme ..." +msgstr "Nameščanje posodobitve strojne programske opreme …" #. TRANSLATORS: %1 is a device name #, c-format msgid "Installing on %s…" -msgstr "Nameščanje na %s ..." +msgstr "Nameščanje na %s …" #. TRANSLATORS: if it breaks, you get to keep both pieces msgid "Installing this update may also void any device warranty." @@ -2105,6 +2140,10 @@ msgid "No action specified!" msgstr "Nobeno dejanje ni določeno!" +#. TRANSLATORS: no devices that can be upgraded with new firmware +msgid "No devices are updatable" +msgstr "Nobene naprave ni mogoče posodobiti" + #. TRANSLATORS: message letting the user know no device downgrade available #. * %1 is the device name #, c-format @@ -2124,6 +2163,14 @@ msgstr "Zaznana ni nobena strojna oprema z možnostjo posodobitve vdelane programske opreme" #. TRANSLATORS: no repositories to download from +msgid "No matching releases for search token" +msgstr "Ni zadetkov med izdajami za dani žeton" + +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +msgstr "Ponoven zagon ni potreben" + +#. TRANSLATORS: no repositories to download from msgid "No releases available" msgstr "Izdaje niso na voljo" @@ -2176,6 +2223,10 @@ msgstr "Stara različica" #. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Namesti samo na emulirane naprave" + +#. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Pokaži samo eno vrednost PCR" @@ -2189,8 +2240,8 @@ msgstr "Dovoljene so samo nadgradnje različic" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Izhod v formatu JSON" +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Izpis v zapisu JSON (onemogoči vsa vprašanja uporabniku)" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -2214,11 +2265,11 @@ #. TRANSLATORS: reading new dbx from the update msgid "Parsing dbx update…" -msgstr "Razčlenjevanje posodobitve dbx ..." +msgstr "Razčlenjevanje posodobitve dbx …" #. TRANSLATORS: reading existing dbx from the system msgid "Parsing system dbx…" -msgstr "Razčlenjevanje sistemskega dbx ..." +msgstr "Razčlenjevanje sistemskega dbx …" #. TRANSLATORS: remote filename base msgid "Password" @@ -2261,15 +2312,20 @@ msgid "Please enter a number from 0 to %u: " msgstr "Vnesite številko od 0 do %u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " -msgstr "Vnesite %s ali %s:" +msgstr "Vnesite %s ali %s: " #. TRANSLATORS: Failed to open plugin, hey Arch users msgid "Plugin dependencies missing" msgstr "Manjkajo odvisnosti vstavkov" +#. TRANSLATORS: The plugin enumeration might change the device current mode +msgid "Plugin enumeration may change device state" +msgstr "Oštevilčenje vstavka lahko spremeni stanje naprave" + #. TRANSLATORS: The plugin is only for testing msgid "Plugin is only for testing" msgstr "Vstavek je namenjen le preizkušanju" @@ -2352,11 +2408,11 @@ #. TRANSLATORS: %1 is a device name #, c-format msgid "Reading from %s…" -msgstr "Branje iz %s ..." +msgstr "Branje iz %s …" #. TRANSLATORS: reading from the flash chips msgid "Reading…" -msgstr "Poteka branje ..." +msgstr "Poteka branje …" #. TRANSLATORS: Plugin is active and in use msgid "Ready" @@ -2446,7 +2502,7 @@ #. TRANSLATORS: restarting the device to pick up new F/W msgid "Restarting device…" -msgstr "Ponovni zagon naprave ..." +msgstr "Ponovni zagon naprave …" #. TRANSLATORS: command description msgid "Retrieve BIOS settings. If no arguments are passed all settings are returned" @@ -2501,6 +2557,10 @@ msgid "Runtime Suffix" msgstr "Pripona izvajalne datoteke" +#. TRANSLATORS: Software Bill of Materials link +msgid "SBOM" +msgstr "SBOM" + #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "SECTION" msgstr "RAZDELEK" @@ -2567,7 +2627,7 @@ #. TRANSLATORS: scheduling an update to be done on the next boot msgid "Scheduling…" -msgstr "Časovno načrtovanje procesov ..." +msgstr "Časovno načrtovanje procesov …" #. TRANSLATORS: HSI event title msgid "Secure Boot disabled" @@ -2755,8 +2815,8 @@ #. TRANSLATORS: success message -- where 'metadata' is information #. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Uspešno preneseni novi metapodatki: " +msgid "Successfully downloaded new metadata:" +msgstr "Uspešno preneseni novi metapodatki:" #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" @@ -3015,6 +3075,18 @@ msgid "The UEFI Platform Key is used to determine if device software comes from a trusted source." msgstr "Ključ platforme UEFI se uporablja za ugotavljanje, ali programska oprema naprave prihaja iz zaupanja vrednega vira." +#. TRANSLATORS: HSI event title +msgid "The UEFI certificate store is now up to date" +msgstr "Shramba potrdil UEFI je zdaj posodobljena" + +#. TRANSLATORS: longer description +msgid "The UEFI db contains the list of valid certificates that can be used to authorize what EFI binaries are allowed to run." +msgstr "Zbirka podatkov UEFI vsebuje seznam veljavnih potrdil, ki jih lahko uporabite za overjanje, katere izvršne datoteke EFI je dovoljeno izvajati." + +#. TRANSLATORS: longer description +msgid "The UEFI system can set up memory attributes at boot which prevent common exploits from running." +msgstr "Sistem UEFI lahko nastavi atribute pomnilnika ob zagonu, ki preprečujejo pogosta vsiljena izvajanja pred izvršitvijo." + #. TRANSLATORS: the user is SOL for support... msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" msgstr "Prikriti proces je naložil kodo tretje strani in razvijalci novih različic je ne podpirajo več!" @@ -3034,7 +3106,7 @@ #. TRANSLATORS: warning message shown after update has been scheduled msgid "The update will continue when the device USB cable has been unplugged and then re-inserted." -msgstr "Posodobitev se bo nadaljevala, ko bo kabel naprave USB izključen in nato znova vstavljen." +msgstr "Posodobitev se bo nadaljevala, ko bo kabel naprave USB izključen in nato znova vstavljen." #. TRANSLATORS: warning message shown after update has been scheduled msgid "The update will continue when the device USB cable has been unplugged." @@ -3158,6 +3230,10 @@ msgid "UEFI ESP partition not detected or configured" msgstr "Razdelek UEFI ESP ni zaznan ali konfiguriran" +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI Memory Protection" +msgstr "Zaščita pomnilnika UEFI" + #. TRANSLATORS: Title: PK is the 'platform key' for the machine msgid "UEFI Platform Key" msgstr "Ključ platforme UEFI" @@ -3182,6 +3258,10 @@ msgid "UEFI capsule updates not available or enabled in firmware setup" msgstr "Posodobitve kapsul UEFI niso na voljo ali omogočene pri nastavitvi vdelane programske opreme" +#. TRANSLATORS: Title: is UEFI db up-to-date +msgid "UEFI db" +msgstr "Db UEFI" + #. TRANSLATORS: program name msgid "UEFI dbx Utility" msgstr "Pripomoček UEFI dbx" @@ -3190,6 +3270,26 @@ msgid "UEFI firmware can not be updated in legacy BIOS mode" msgstr "Vdelane programske opreme UEFI ni mogoče posodobiti v zastarelem načinu BIOS-a" +#. TRANSLATORS: Title: is UEFI early-boot memory protection turned on +msgid "UEFI memory protection" +msgstr "Zaščita pomnilnika UEFI" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled and locked" +msgstr "Zaščita pomnilnika UEFI je omogočena in zaklenjena" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection enabled but not locked" +msgstr "Zaščita pomnilnika UEFI je omogočena, vendar ni zaklenjena" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now locked" +msgstr "Zaščita pomnilnika UEFI je zdaj zaklenjena" + +#. TRANSLATORS: HSI event title +msgid "UEFI memory protection is now unlocked" +msgstr "Zaščita pomnilnika UEFI je zdaj odklenjena" + #. TRANSLATORS: Title: PK is the 'platform key' for the machine msgid "UEFI platform key" msgstr "Ključ platforme UEFI" @@ -3316,22 +3416,13 @@ msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" msgstr "Posodobi vse navedene naprave na najnovejšo različico vdelane programske opreme ali vse naprave, če niso navedene" -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "Posodobitve so objavljene za %u krajevnih naprav" -msgstr[1] "Posodobitve so objavljene za %u od %u krajevnih naprav" -msgstr[2] "Posodobitve so objavljene za %u od %u krajevnih naprav" -msgstr[3] "Posodobitve so objavljene za %u od %u krajevnih naprav" - msgid "Updating" msgstr "Posodabljanje" #. TRANSLATORS: %1 is a device name #, c-format msgid "Updating %s…" -msgstr "Posodabljanje %s ..." +msgstr "Posodabljanje %s …" #. TRANSLATORS: message letting the user know an upgrade is available #. * %1 is the device name and %2 and %3 are version strings @@ -3397,7 +3488,7 @@ #. TRANSLATORS: ESP refers to the EFI System Partition msgid "Validating ESP contents…" -msgstr "Preverjanje vsebine ESP ..." +msgstr "Preverjanje vsebine ESP …" #. TRANSLATORS: one line variant of release (e.g. 'China') msgid "Variant" @@ -3409,7 +3500,7 @@ #. TRANSLATORS: verifying we wrote the firmware correctly msgid "Verifying…" -msgstr "Poteka preverjanje ..." +msgstr "Poteka preverjanje …" #. TRANSLATORS: the detected version number of the dbx msgid "Version" @@ -3423,6 +3514,10 @@ msgid "WARNING" msgstr "OPOZORILO" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "WORD" +msgstr "BESEDA" + #. TRANSLATORS: command description msgid "Wait for a device to appear" msgstr "Počakajte, da se naprava prikaže" @@ -3446,7 +3541,7 @@ #. TRANSLATORS: writing to the flash chips msgid "Writing…" -msgstr "Poteka zapisovanje ..." +msgstr "Poteka zapisovanje …" #. TRANSLATORS: the user has to manually recover; we can't do it msgid "You should ensure you are comfortable restoring the setting from a recovery or installation disk, as this change may cause the system to not boot into Linux or cause other system instability." @@ -3505,6 +3600,10 @@ msgstr "[IMEDATOTEKE1] [IMEDATOTEKE2]" #. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "[FWUPD-VERSION]" +msgstr "[FWUPD-RAZLIČICA]" + +#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "[REASON] [TIMEOUT]" msgstr "[REASON] [TIMEOUT]" diff -Nru fwupd-2.0.8/po/sr.po fwupd-2.0.20/po/sr.po --- fwupd-2.0.8/po/sr.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/sr.po 2026-02-26 11:36:18.000000000 +0000 @@ -10,12 +10,15 @@ msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Serbian (http://app.transifex.com/freedesktop/fwupd/language/sr/)\n" +"PO-Revision-Date: 2025-10-24 10:36+0000\n" +"Last-Translator: Anonymous \n" +"Language-Team: Serbian \n" +"Language: sr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"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" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: the age of the metadata msgid "Age" @@ -228,10 +231,6 @@ msgstr "Добавља подешена удаљена места" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Добави списак свих ажурирања за повезани уређај" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "Добавља издања за уређај" diff -Nru fwupd-2.0.8/po/sv.po fwupd-2.0.20/po/sv.po --- fwupd-2.0.8/po/sv.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/sv.po 2026-02-26 11:36:18.000000000 +0000 @@ -10,16 +10,21 @@ # Luna Jernberg , 2020-2021 # Sebastian Rasmussen , 2018-2020 # Sebastian Rasmussen , 2018 +# Anders Jonsson , 2025. +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Swedish (http://app.transifex.com/freedesktop/fwupd/language/sv/)\n" +"PO-Revision-Date: 2025-10-24 10:39+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Swedish \n" +"Language: sv\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: sv\n" -"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"Plural-Forms: nplurals=2; plural=n != 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -40,7 +45,7 @@ msgstr "%s-batteriuppdatering" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "%s CPU-mikrokodsuppdatering" @@ -308,6 +313,22 @@ msgstr[0] "%u enhet har inte den mest kända konfigurationen." msgstr[1] "%u enheter har inte den mest kända konfigurationen." +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#, c-format +msgid "%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "%u devices are supported in the enabled remotes (an update has been published)" +msgstr[0] "%u enhet stöds i de aktiverade fjärrarna (en uppdatering har publicerats)" +msgstr[1] "%u enheter stöds i de aktiverade fjärrarna (en uppdatering har publicerats)" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "%u enhet kan uppdateras" +msgstr[1] "%u enheter kan uppdateras" + #. TRANSLATORS: duration in minutes #, c-format msgid "%u hour" @@ -688,6 +709,10 @@ msgstr "Ändrad" #. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "Kontrollera om några enheter väntar på en omstart för att slutföra uppdatering" + +#. TRANSLATORS: command description msgid "Checks cryptographic hash matches firmware" msgstr "Kontrollerar att kryptografisk hash matchar fast programvara" @@ -847,7 +872,7 @@ #. TRANSLATORS: lid means "laptop top cover" msgid "Device cannot be updated while the lid is closed" -msgstr "Enheten kan inte uppdateras medan locket är stängt." +msgstr "Enheten kan inte uppdateras medan locket är stängt" #. TRANSLATORS: this is when a device has been updated msgid "Device changed:" @@ -1228,8 +1253,8 @@ msgstr "FILNAMN CERTIFIKAT PRIVAT-NYCKEL" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "FILNAMN ENHETS-ID" +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "FILNAMN ENHETS-ID [VERSION]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" @@ -1339,6 +1364,10 @@ msgid "Filter with a set of release flags using a ~ prefix to exclude, e.g. 'trusted-release,~trusted-metadata'" msgstr "Filtrera med en uppsättning utgåveflaggor och prefixet ~ för att exkludera, t.ex. 'trusted-release,~trusted-metadata'" +#. TRANSLATORS: command description +msgid "Finds firmware releases from the metadata" +msgstr "Hittar fast programvaruutgåvor från metadata" + #. TRANSLATORS: Title: if we can verify the firmware checksums msgid "Firmware Attestation" msgstr "Attestering av fast programvara" @@ -1428,7 +1457,7 @@ #. TRANSLATORS: user needs to run a command, %1 is 'fwupdmgr unlock' #, c-format msgid "Firmware updates disabled; run '%s' to enable" -msgstr "Uppdateringar av fast programvara inaktiverade; kör ”%s” för att aktivera" +msgstr "Uppdateringar av fast programvara inaktiverade; kör ”%s” för att aktivera" #. TRANSLATORS: command description msgid "Fix a specific host security attribute" @@ -1530,8 +1559,8 @@ msgstr "Hämtar listan över blockerad 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" +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "Hämtar listan med uppdateringar för alla angivna enheter, eller alla enheter om ingen angivits" #. TRANSLATORS: command description msgid "Gets the releases for a device" @@ -1558,7 +1587,6 @@ msgstr "Säkerhetshändelser för värd" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "Värdsäkerhets-ID (HSI) stöds inte" @@ -1757,7 +1785,7 @@ #. TRANSLATORS: longer description msgid "Intel BootGuard prevents unauthorized device software from operating when the device is started." -msgstr "Intel BootGuard förhindrar ej auktoriserad enhetsprogramvara från att köras då enheten startas." +msgstr "Intel BootGuard förhindrar ej auktoriserad enhetsprogramvara från att köras då enheten startas." #. TRANSLATORS: Title: BootGuard is a trademark from Intel, #. * verified boot refers to the way the boot process is verified @@ -2091,6 +2119,10 @@ msgid "No action specified!" msgstr "Ingen åtgärd angiven!" +#. TRANSLATORS: no devices that can be upgraded with new firmware +msgid "No devices are updatable" +msgstr "Inga enheter kan uppdateras" + #. TRANSLATORS: message letting the user know no device downgrade available #. * %1 is the device name #, c-format @@ -2110,6 +2142,14 @@ msgstr "Ingen uppdateringsbar hårdvara upptäcktes" #. TRANSLATORS: no repositories to download from +msgid "No matching releases for search token" +msgstr "Inga matchande utgåvor för söktoken" + +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +msgstr "Ingen omstart krävs" + +#. TRANSLATORS: no repositories to download from msgid "No releases available" msgstr "Inga utgåvor tillgängliga" @@ -2162,6 +2202,10 @@ msgstr "Gammal version" #. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Installera endast till emulerade enheter" + +#. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Visa endast enkelt PCR-värde" @@ -2175,8 +2219,8 @@ msgstr "Endast versionsuppgraderingar är tillåtna" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Utmatning i JSON-format" +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Utdata i JSON-format (inaktiverar alla interaktiva frågor)" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -2247,7 +2291,8 @@ msgid "Please enter a number from 0 to %u: " msgstr "Ange en siffra mellan 0 och %u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " msgstr "Ange antingen %s eller %s:" @@ -2256,6 +2301,10 @@ msgid "Plugin dependencies missing" msgstr "Beroenden för insticksmodul saknas" +#. TRANSLATORS: The plugin enumeration might change the device current mode +msgid "Plugin enumeration may change device state" +msgstr "Insticksmoduluppräkning kan ändra enhetens tillstånd" + #. TRANSLATORS: The plugin is only for testing msgid "Plugin is only for testing" msgstr "Insticksmodulen är endast för tester" @@ -2745,8 +2794,8 @@ #. TRANSLATORS: success message -- where 'metadata' is information #. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Hämtade framgångsrikt ny metadata: " +msgid "Successfully downloaded new metadata:" +msgstr "Hämtade nya metadata:" #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" @@ -2873,7 +2922,7 @@ #. TRANSLATORS: this CLI tool is now preventing system updates msgid "System Update Inhibited" -msgstr "Systemuppdatering hindrad." +msgstr "Systemuppdatering hindrad" #. TRANSLATORS: longer description msgid "System management mode is used by the firmware to access resident BIOS code and data." @@ -3342,13 +3391,6 @@ msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" msgstr "Uppdaterar alla angivna enheter till senaste version av fast programvara, eller alla enheter om ej angivet" -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "Uppdateringar har publicerats för %u lokal enhet" -msgstr[1] "Uppdateringar har publicerats för %u av %u lokala enheter" - msgid "Updating" msgstr "Uppdaterar" @@ -3447,6 +3489,10 @@ msgid "WARNING" msgstr "VARNING" +#. TRANSLATORS: command argument: uppercase, spaces->dashes +msgid "WORD" +msgstr "ORD" + #. TRANSLATORS: command description msgid "Wait for a device to appear" msgstr "Vänta på att en enhet ska dyka upp" diff -Nru fwupd-2.0.8/po/test-deps fwupd-2.0.20/po/test-deps --- fwupd-2.0.8/po/test-deps 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/test-deps 2026-02-26 11:36:18.000000000 +0000 @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # SPDX-License-Identifier: LGPL-2.1-or-later -""" Check dependencies needed for rasterization """ +"""Check dependencies needed for rasterization""" import sys import os diff -Nru fwupd-2.0.8/po/tr.po fwupd-2.0.20/po/tr.po --- fwupd-2.0.8/po/tr.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/tr.po 2026-02-26 11:36:18.000000000 +0000 @@ -10,16 +10,20 @@ # Sabri Ünal , 2020 # Serdar Sağlam , 2020 # yunus kaba , 2021 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Turkish (http://app.transifex.com/freedesktop/fwupd/language/tr/)\n" +"PO-Revision-Date: 2025-10-24 10:39+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Turkish \n" +"Language: tr\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: tr\n" -"Plural-Forms: nplurals=2; plural=(n > 1);\n" +"Plural-Forms: nplurals=2; plural=n > 1;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -567,10 +571,6 @@ msgstr "DOSYA-ADI" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "DOSYAADI AYGIT-KİMLİĞİ" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME [DEVICE-ID|GUID]" msgstr "DOSYAADI [AYGIT-KİMLİĞİ|GUID]" @@ -723,10 +723,6 @@ msgstr "Donanım yazılımı dosyasıyla ilgili ayrıntıları al" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Bağlı donanım için güncelleme listesini al" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "Bir aygıt için sürümleri alır" @@ -1077,10 +1073,6 @@ msgstr "Sadece sürüm yükseltmelerine izin veriliyor" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "JSON formatında dışarı aktar" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "Öntanımlı ESP yolunu geçersiz kıl" @@ -1166,7 +1158,7 @@ #. * %1 is the device name and %2 is a version string #, c-format msgid "Reinstall %s to %s?" -msgstr " %s %s yeniden kurulsun mu?" +msgstr "%s %s yeniden kurulsun mu?" #. TRANSLATORS: command description msgid "Reinstall current firmware on the device" @@ -1255,7 +1247,7 @@ #. TRANSLATORS: %s is a link to a website #, c-format msgid "See %s for more information." -msgstr "Daha fazlası için tıklayınız. %s " +msgstr "Daha fazlası için tıklayınız. %s" #. TRANSLATORS: device has been chosen by the daemon for the user msgid "Selected device" @@ -1356,11 +1348,6 @@ msgid "Successfully activated all devices" msgstr "Tüm aygıtlar başarıyla etkinleştirildi" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "Yeni üst veri başarıyla indirildi:" - #. TRANSLATORS: success message msgid "Successfully installed firmware" msgstr "Donanım yazılımı başarıyla kuruldu" @@ -1590,7 +1577,7 @@ #. * %1 is the device name and %2 and %3 are version strings #, c-format msgid "Upgrade %s from %s to %s?" -msgstr " %s, %s sürümünden %s sürümüne yükseltilsin mi?" +msgstr "%s, %s sürümünden %s sürümüne yükseltilsin mi?" #. TRANSLATORS: User has been notified msgid "User has been notified" diff -Nru fwupd-2.0.8/po/uk.po fwupd-2.0.20/po/uk.po --- fwupd-2.0.8/po/uk.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/uk.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,16 +4,20 @@ # # Translators: # Yuri Chornoivan , 2015-2025 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Ukrainian (http://app.transifex.com/freedesktop/fwupd/language/uk/)\n" +"PO-Revision-Date: 2025-10-26 05:02+0000\n" +"Last-Translator: Yuri Chornoivan \n" +"Language-Team: Ukrainian \n" +"Language: uk\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: uk\n" "Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n % 10 == 1 && n % 100 != 11 ? 0 : n % 1 == 0 && n % 10 >= 2 && n % 10 <= 4 && (n % 100 < 12 || n % 100 > 14) ? 1 : n % 1 == 0 && (n % 10 ==0 || (n % 10 >=5 && n % 10 <=9) || (n % 100 >=11 && n % 100 <=14 )) ? 2: 3);\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -36,7 +40,7 @@ msgstr "Оновлення для акумуляторів %s" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "Оновлення мікропрограми процесора %s" @@ -310,6 +314,26 @@ msgstr[2] "%u пристрої не є найкращою відомою конфігурацією." msgstr[3] "%u пристрій не є найкращою відомою конфігурацією." +#. TRANSLATORS: how many devices have published updates on +#. something like the LVFS +#, c-format +msgid "%u device is supported in the enabled remotes (an update has been published)" +msgid_plural "%u devices are supported in the enabled remotes (an update has been published)" +msgstr[0] "в увімкнених віддалених сховищах передбачено підтримку %u пристрою (оприлюднено оновлення)" +msgstr[1] "в увімкнених віддалених сховищах передбачено підтримку %u пристроїв (оприлюднено оновлення)" +msgstr[2] "в увімкнених віддалених сховищах передбачено підтримку %u пристроїв (оприлюднено оновлення)" +msgstr[3] "в увімкнених віддалених сховищах передбачено підтримку %u пристрою (оприлюднено оновлення)" + +#. TRANSLATORS: how many devices could be updated in theory if +#. we had the firmware locally +#, c-format +msgid "%u device is updatable" +msgid_plural "%u devices are updatable" +msgstr[0] "до оновлення придатний %u пристрій" +msgstr[1] "до оновлення придатні %u пристрої" +msgstr[2] "до оновлення придатні %u пристроїв" +msgstr[3] "до оновлення придатний %u пристрій" + #. TRANSLATORS: duration in minutes #, c-format msgid "%u hour" @@ -700,6 +724,10 @@ msgstr "Змінено" #. TRANSLATORS: command description +msgid "Check if any devices are pending a reboot to complete update" +msgstr "Перевірити, чи повністю оновлено усі пристрої, які очікують у черзі на перезавантаження" + +#. TRANSLATORS: command description msgid "Checks cryptographic hash matches firmware" msgstr "Перевірити відповідність криптографічних хешів мікропрограми" @@ -963,7 +991,7 @@ #. TRANSLATORS: message letting the user there is an update #. * waiting, but there is a reason it cannot be deployed msgid "Devices with firmware updates that need user action: " -msgstr "Пристрої із оновленнями мікропрограм, які потребують дій від користувача:" +msgstr "Пристрої із оновленнями мікропрограм, які потребують дій від користувача: " #. TRANSLATORS: message letting the user know no device #. * upgrade available due to missing on LVFS @@ -974,7 +1002,7 @@ #. TRANSLATORS: message letting the user know no #. * device upgrade available due to missing on LVFS msgid "Devices with no available firmware updates: " -msgstr "Пристрої, для яких немає оновлень мікропрограми:" +msgstr "Пристрої, для яких немає оновлень мікропрограми: " #. TRANSLATORS: message letting the user know no device upgrade available #. TRANSLATORS: message letting the user know no device @@ -1170,7 +1198,7 @@ msgstr "Вмикання оновлень мікропрограми для BIOS уможливлює виправлення проблем із захистом." msgid "Enabling this functionality is done at your own risk, which means you have to contact your original equipment manufacturer regarding any problems caused by these updates. Only problems with the update process itself should be filed at $OS_RELEASE:BUG_REPORT_URL$." -msgstr "Наслідки вмикання цієї можливості покладаються на вас. Це означає, що усі проблеми, пов'язані із цими оновленнями, ви маєте вирішувати із виробником обладнання. Повідомляти розробникам дистрибутива за адресою $OS_RELEASE:BUG_REPORT_URL$ слід лише про помилки у процесі оновлення." +msgstr "Наслідки вмикання цієї можливості покладаються на вас. Це означає, що усі проблеми, пов'язані із цими оновленнями, ви маєте вирішувати із виробником обладнання. Повідомляти розробникам дистрибутива за адресою $OS_RELEASE:BUG_REPORT_URL$ слід лише про помилки у процесі оновлення." #. TRANSLATORS: show the user a warning msgid "Enabling this remote is done at your own risk." @@ -1240,8 +1268,8 @@ msgstr "НАЗВА-ФАЙЛА СЕРТИФІКАТ ЗАКРИТИЙ-КЛЮЧ" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "НАЗВА_ФАЙЛА ІД_ПРИСТРОЮ" +msgid "FILENAME DEVICE-ID [VERSION]" +msgstr "НАЗВА_ФАЙЛА ІД_ПРИСТРОЮ [ВЕРСІЯ]" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME OFFSET DATA [FIRMWARE-TYPE]" @@ -1546,8 +1574,8 @@ msgstr "Отримує список заблокованих мікропрограм" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "Отримує список оновлень для з’єднаного обладнання" +msgid "Gets the list of updates for all specified devices, or all devices if unspecified" +msgstr "Отримує список оновлення для усіх вказаних пристроїв або усіх пристроїв, якщо пристрої не вказано" #. TRANSLATORS: command description msgid "Gets the releases for a device" @@ -1574,7 +1602,6 @@ msgstr "Події захисту вузла" #. TRANSLATORS: error message for unsupported feature -#, c-format msgid "Host Security ID (HSI) is not supported" msgstr "Підтримки Host Security ID (HSI) не передбачено" @@ -2109,6 +2136,10 @@ msgid "No action specified!" msgstr "Не вказано дії!" +#. TRANSLATORS: no devices that can be upgraded with new firmware +msgid "No devices are updatable" +msgstr "Немає придатних до оновлення пристроїв" + #. TRANSLATORS: message letting the user know no device downgrade available #. * %1 is the device name #, c-format @@ -2127,6 +2158,10 @@ msgid "No hardware detected with firmware update capability" msgstr "Не виявлено обладнання із передбаченою можливістю оновлення мікропрограми" +#. TRANSLATORS: no rebooting needed +msgid "No reboot is necessary" +msgstr "Перезавантаження не потрібне" + #. TRANSLATORS: no repositories to download from msgid "No releases available" msgstr "Немає доступних випусків" @@ -2180,6 +2215,10 @@ msgstr "Стара версія" #. TRANSLATORS: command line option +msgid "Only install onto emulated devices" +msgstr "Встановити лише на емульовані пристрої" + +#. TRANSLATORS: command line option msgid "Only show single PCR value" msgstr "Показувати лише одне значення PCR" @@ -2193,8 +2232,8 @@ msgstr "Дозволено лише оновлення версії" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "Виведені дані у форматі JSON" +msgid "Output in JSON format (disables all interactive prompts)" +msgstr "Вивести дані у форматі JSON (вимикає усі інтерактивні запити)" #. TRANSLATORS: command line option msgid "Override the default ESP path" @@ -2265,7 +2304,8 @@ msgid "Please enter a number from 0 to %u: " msgstr "Будь ласка, введіть число від 0 до %u: " -#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' +#. TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is +#. * 'N' #, c-format msgid "Please enter either %s or %s: " msgstr "Будь ласка, введіть %s або %s: " @@ -2274,6 +2314,10 @@ msgid "Plugin dependencies missing" msgstr "Не встановлено залежності додатка" +#. TRANSLATORS: The plugin enumeration might change the device current mode +msgid "Plugin enumeration may change device state" +msgstr "Нумерування додатків може змінити стан пристрою" + #. TRANSLATORS: The plugin is only for testing msgid "Plugin is only for testing" msgstr "Додаток призначено лише для тестування" @@ -2763,7 +2807,7 @@ #. TRANSLATORS: success message -- where 'metadata' is information #. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " +msgid "Successfully downloaded new metadata:" msgstr "Успішно отримано нові метадані:" #. TRANSLATORS: success message @@ -3050,7 +3094,7 @@ #. TRANSLATORS: warning message shown after update has been scheduled msgid "The update will continue when the device USB cable has been re-inserted." -msgstr "Оновлення буде продовжено після того, як кабель USB буде повторно вставлено до пристрою" +msgstr "Оновлення буде продовжено після того, як кабель USB буде повторно вставлено до пристрою." #. TRANSLATORS: warning message shown after update has been scheduled msgid "The update will continue when the device USB cable has been unplugged and then re-inserted." @@ -3362,16 +3406,7 @@ #. TRANSLATORS: command description msgid "Updates all specified devices to latest firmware version, or all devices if unspecified" -msgstr "Оновлює усі вказані пристрої до найсвіжішої версії мікропрограми. Якщо пристрої не вказано, оновлює усі пристрої." - -#. TRANSLATORS: how many local devices can expect updates now -#, c-format -msgid "Updates have been published for %u local device" -msgid_plural "Updates have been published for %u of %u local devices" -msgstr[0] "Оприлюднено оновлення для %u локального пристрою" -msgstr[1] "Оприлюднено оновлення для %u з %u локальних пристроїв" -msgstr[2] "Оприлюднено оновлення для %u з %u локальних пристроїв" -msgstr[3] "Оприлюднено оновлення для %u з %u локального пристрою" +msgstr "Оновлює усі вказані пристрої до найсвіжішої версії мікропрограми. Якщо пристрої не вказано, оновлює усі пристрої" msgid "Updating" msgstr "Оновлення" diff -Nru fwupd-2.0.8/po/zh_CN.po fwupd-2.0.20/po/zh_CN.po --- fwupd-2.0.8/po/zh_CN.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/zh_CN.po 2026-02-26 11:36:18.000000000 +0000 @@ -11,16 +11,21 @@ # Mingye Wang , 2016 # Mingye Wang , 2015 # Richard Hughes , 2023 +# Richard Hughes , 2025. +# 大王叫我来巡山 , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Chinese (China) (http://app.transifex.com/freedesktop/fwupd/language/zh_CN/)\n" +"PO-Revision-Date: 2025-11-05 12:51+0000\n" +"Last-Translator: 大王叫我来巡山 \n" +"Language-Team: Chinese (Simplified Han script) \n" +"Language: zh_CN\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: zh_CN\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -34,7 +39,7 @@ msgstr "%s 电池更新" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "%s CPU 微码更新" @@ -365,7 +370,7 @@ #. TRANSLATORS: description of a BIOS setting msgid "BIOS updates delivered via LVFS or Windows Update" -msgstr "BIOS 更新通过 LVFS 或 Windows Update 提供。" +msgstr "BIOS 更新通过 LVFS 或 Windows Update 提供" #. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "BUILDER-XML FILENAME-DST" @@ -565,7 +570,7 @@ #. TRANSLATORS: message letting the user know no #. * device upgrade available due to missing on LVFS msgid "Devices with no available firmware updates: " -msgstr "无固件更新可用的设备:" +msgstr "无固件更新可用的设备: " #. TRANSLATORS: message letting the user know no device upgrade available #. TRANSLATORS: message letting the user know no device @@ -658,7 +663,7 @@ #. TRANSLATORS: downloading from a remote server msgid "Downloading…" -msgstr "正在下载..." +msgstr "正在下载…" #. TRANSLATORS: command description msgid "Dump SMBIOS data from a file" @@ -673,7 +678,7 @@ #. TRANSLATORS: a remote here is like a 'repo' or software source msgid "Enable new remote?" -msgstr "£启用新的远程库" +msgstr "启用新的远程库?" #. TRANSLATORS: Turn on the remote msgid "Enable this remote?" @@ -744,10 +749,6 @@ msgstr "文件名称 证书 私钥" #. TRANSLATORS: command argument: uppercase, spaces->dashes -msgid "FILENAME DEVICE-ID" -msgstr "文件名 设备ID" - -#. TRANSLATORS: command argument: uppercase, spaces->dashes msgid "FILENAME [DEVICE-ID|GUID]" msgstr "文件名 [设备ID|GUID]" @@ -926,10 +927,6 @@ msgstr "获取受阻固件的列表" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "获取已连接硬件的可用更新列表" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "获取用于设备的发行版本" @@ -951,7 +948,7 @@ #. TRANSLATORS: this is a string like 'HSI:2-U' msgid "Host Security ID:" -msgstr "主机安全 ID" +msgstr "主机安全 ID:" #. TRANSLATORS: Title: #. * https://en.wikipedia.org/wiki/Input%E2%80%93output_memory_management_unit @@ -1014,7 +1011,7 @@ #. TRANSLATORS: this is shown when updating the firmware after the reboot msgid "Installing firmware update…" -msgstr "正在安装固件更新..." +msgstr "正在安装固件更新…" #. TRANSLATORS: %1 is a device name #, c-format @@ -1251,10 +1248,6 @@ msgstr "只允许版本升级" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "以 JSON 格式输出" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "覆盖默认的 ESP 路径" @@ -1296,7 +1289,7 @@ #. TRANSLATORS: the user isn't reading the question #, c-format msgid "Please enter a number from 0 to %u: " -msgstr "请输入一个 0 到 %u 之间的数字:" +msgstr "请输入一个 0 到 %u 之间的数字: " #. TRANSLATORS: Failed to open plugin, hey Arch users msgid "Plugin dependencies missing" @@ -1574,11 +1567,6 @@ msgid "Successfully disabled remote" msgstr "禁用远程源成功" -#. TRANSLATORS: success message -- where 'metadata' is information -#. * about available firmware on the remote server -msgid "Successfully downloaded new metadata: " -msgstr "已成功下载新元数据:" - #. TRANSLATORS: success message msgid "Successfully enabled and refreshed remote" msgstr "已成功启用和刷新远程库" @@ -1695,7 +1683,7 @@ #. TRANSLATORS: the user is SOL for support... msgid "The daemon has loaded 3rd party code and is no longer supported by the upstream developers!" -msgstr "守护进程已经加载了第三方代码,并且上游开发者不再支持。" +msgstr "守护进程已经加载了第三方代码,并且上游开发者不再支持!" #. TRANSLATORS: %1 is the firmware vendor, %2 is the device vendor name #, c-format @@ -1928,7 +1916,7 @@ #. TRANSLATORS: waiting for device to do something msgid "Waiting…" -msgstr "等待中..." +msgstr "等待中…" #. TRANSLATORS: command description msgid "Watch for hardware changes" diff -Nru fwupd-2.0.8/po/zh_TW.po fwupd-2.0.20/po/zh_TW.po --- fwupd-2.0.8/po/zh_TW.po 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/po/zh_TW.po 2026-02-26 11:36:18.000000000 +0000 @@ -4,16 +4,20 @@ # # Translators: # Cheng-Chia Tseng , 2017-2018,2024 +# Richard Hughes , 2025. msgid "" msgstr "" "Project-Id-Version: fwupd\n" "Report-Msgid-Bugs-To: \n" -"Language-Team: Chinese (Taiwan) (http://app.transifex.com/freedesktop/fwupd/language/zh_TW/)\n" +"PO-Revision-Date: 2025-10-24 10:42+0000\n" +"Last-Translator: Richard Hughes \n" +"Language-Team: Chinese (Traditional Han script) \n" +"Language: zh_TW\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" -"Language: zh_TW\n" "Plural-Forms: nplurals=1; plural=0;\n" +"X-Generator: Weblate 5.14.1-dev\n" #. TRANSLATORS: more than a minute #, c-format @@ -33,7 +37,7 @@ msgstr "「%s」電池更新" #. TRANSLATORS: the CPU microcode is firmware loaded onto the CPU -#. * at system bootup +#. * at system boot-up #, c-format msgid "%s CPU Microcode Update" msgstr "「%s」CPU 微碼更新" @@ -889,10 +893,6 @@ msgstr "取得已批准韌體的清單" #. TRANSLATORS: command description -msgid "Gets the list of updates for connected hardware" -msgstr "取得連接硬體的更新清單" - -#. TRANSLATORS: command description msgid "Gets the releases for a device" msgstr "取得裝置的發行版本" @@ -1243,10 +1243,6 @@ msgstr "僅允許版本升級" #. TRANSLATORS: command line option -msgid "Output in JSON format" -msgstr "以 JSON 格式輸出" - -#. TRANSLATORS: command line option msgid "Override the default ESP path" msgstr "凌駕預設 ESP 路徑" diff -Nru fwupd-2.0.8/policy/meson.build fwupd-2.0.20/policy/meson.build --- fwupd-2.0.8/policy/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/policy/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,5 +1,8 @@ -install_data('org.freedesktop.fwupd.rules', - install_dir: join_paths(datadir, 'polkit-1', 'rules.d')) +install_data( + 'org.freedesktop.fwupd.rules', + install_tag: 'runtime', + install_dir: join_paths(datadir, 'polkit-1', 'rules.d'), +) #newer polkit has the ITS rules included if polkit.version().version_compare('>0.113') @@ -7,19 +10,21 @@ input: 'org.freedesktop.fwupd.policy.in', output: 'org.freedesktop.fwupd.policy', install: true, - install_dir: join_paths(datadir, 'polkit-1', 'actions') , + install_tag: 'runtime', + install_dir: join_paths(datadir, 'polkit-1', 'actions'), type: 'xml', - po_dir: join_paths(meson.project_source_root(), 'po') + po_dir: join_paths(meson.project_source_root(), 'po'), ) -#older polkit is missing ITS rules and will fail + #older polkit is missing ITS rules and will fail else i18n.merge_file( input: 'org.freedesktop.fwupd.policy.in', output: 'org.freedesktop.fwupd.policy', install: true, - install_dir: join_paths(datadir, 'polkit-1', 'actions') , + install_tag: 'runtime', + install_dir: join_paths(datadir, 'polkit-1', 'actions'), type: 'xml', data_dirs: join_paths(meson.project_source_root(), 'policy'), - po_dir: join_paths(meson.project_source_root(), 'po') + po_dir: join_paths(meson.project_source_root(), 'po'), ) endif diff -Nru fwupd-2.0.8/policy/org.freedesktop.fwupd.policy.in fwupd-2.0.20/policy/org.freedesktop.fwupd.policy.in --- fwupd-2.0.8/policy/org.freedesktop.fwupd.policy.in 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/policy/org.freedesktop.fwupd.policy.in 2026-02-26 11:36:18.000000000 +0000 @@ -183,6 +183,18 @@ no auth_admin_keep + org.freedesktop.fwupd.clean-remote + + + + Clean a configured remote + + Authentication is required to delete metadata from a remote + + auth_admin + no + auth_admin_keep + diff -Nru fwupd-2.0.8/src/fu-cabinet.c fwupd-2.0.20/src/fu-cabinet.c --- fwupd-2.0.8/src/fu-cabinet.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-cabinet.c 2026-02-26 11:36:18.000000000 +0000 @@ -79,28 +79,32 @@ * @self: a #FuCabinet * @basename: filename * @data: file data + * @error: (nullable): optional return location for an error * * Adds a file to the silo. * - * Since: 1.6.0 + * Returns: %TRUE for success **/ -void -fu_cabinet_add_file(FuCabinet *self, const gchar *basename, GBytes *data) +gboolean +fu_cabinet_add_file(FuCabinet *self, const gchar *basename, GBytes *data, GError **error) { g_autoptr(FuCabImage) img = fu_cab_image_new(); - g_return_if_fail(FU_IS_CABINET(self)); - g_return_if_fail(basename != NULL); - g_return_if_fail(data != NULL); + g_return_val_if_fail(FU_IS_CABINET(self), FALSE); + g_return_val_if_fail(basename != NULL, FALSE); + g_return_val_if_fail(data != NULL, FALSE); fu_firmware_set_bytes(FU_FIRMWARE(img), data); fu_firmware_set_id(FU_FIRMWARE(img), basename); - fu_firmware_add_image(FU_FIRMWARE(self), FU_FIRMWARE(img)); + return fu_firmware_add_image(FU_FIRMWARE(self), FU_FIRMWARE(img), error); } /* sets the firmware and signature blobs on XbNode */ static gboolean -fu_cabinet_parse_release(FuCabinet *self, XbNode *release, GError **error) +fu_cabinet_parse_release(FuCabinet *self, + XbNode *release, + FuFirmwareParseFlags flags, + GError **error) { const gchar *csum_filename = NULL; gsize streamsz = 0; @@ -116,6 +120,20 @@ g_autoptr(GBytes) release_flags_blob = NULL; g_autoptr(GBytes) filename_blob = NULL; FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE; + JcatVerifyFlags jcat_flags = JCAT_VERIFY_FLAG_DISABLE_TIME_CHECKS; + + /* distrusting RSA? */ + if (flags & FU_FIRMWARE_PARSE_FLAG_ONLY_TRUST_PQ_SIGNATURES) { +#if JCAT_CHECK_VERSION(0, 2, 4) + jcat_flags |= JCAT_VERIFY_FLAG_ONLY_PQ; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "only trusting PQ signatures requires libjcat >= 0.2.4"); + return FALSE; +#endif + } /* we set this with XbBuilderSource before the silo was created */ metadata_trust = xb_node_query_first(release, "../../info/metadata_trust", NULL); @@ -232,12 +250,13 @@ blob_target_sha512 = jcat_blob_new_utf8(JCAT_BLOB_KIND_SHA512, checksum_sha512); jcat_item_add_blob(item_target, blob_target_sha512); - results = jcat_context_verify_target(self->jcat_context, - item_target, - item, - JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM | - JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE, - &error_local); + results = + jcat_context_verify_target(self->jcat_context, + item_target, + item, + jcat_flags | JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM | + JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE, + &error_local); if (results == NULL) { g_info("failed to verify indirect payload %s: %s", basename, @@ -258,7 +277,7 @@ results = jcat_context_verify_item(self->jcat_context, blob, item, - JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM | + jcat_flags | JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM | JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE, &error_local); if (results == NULL) { @@ -291,11 +310,12 @@ if (data_sig == NULL) return FALSE; jcat_blob = jcat_blob_new(JCAT_BLOB_KIND_GPG, data_sig); - jcat_result = jcat_context_verify_blob(self->jcat_context, - blob, - jcat_blob, - JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE, - &error_local); + jcat_result = jcat_context_verify_blob( + self->jcat_context, + blob, + jcat_blob, + jcat_flags | JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE, + &error_local); if (jcat_result == NULL) { g_info("failed to verify payload %s using detached: %s", basename, @@ -500,11 +520,29 @@ } static gboolean -fu_cabinet_build_silo_metainfo(FuCabinet *self, FuFirmware *img, GError **error) +fu_cabinet_build_silo_metainfo(FuCabinet *self, + FuFirmware *img, + FuFirmwareParseFlags flags, + GError **error) { FwupdReleaseFlags release_flags = FWUPD_RELEASE_FLAG_NONE; const gchar *fn = fu_firmware_get_id(img); g_autoptr(JcatItem) item = NULL; + JcatVerifyFlags jcat_flags = + JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM | JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE; + + /* distrusting RSA? */ + if (flags & FU_FIRMWARE_PARSE_FLAG_ONLY_TRUST_PQ_SIGNATURES) { +#if JCAT_CHECK_VERSION(0, 2, 4) + jcat_flags |= JCAT_VERIFY_FLAG_ONLY_PQ; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "only trusting PQ signatures requires libjcat >= 0.2.4"); + return FALSE; +#endif + } /* validate against the Jcat file */ item = jcat_file_get_item_by_id(self->jcat_file, fn, NULL); @@ -521,8 +559,7 @@ results = jcat_context_verify_item(self->jcat_context, blob, item, - JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM | - JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE, + jcat_flags, &error_local); if (results == NULL) { g_info("failed to verify %s: %s", fn, error_local->message); @@ -568,7 +605,7 @@ JCAT_IMPORT_FLAG_NONE, NULL, error)) { - g_prefix_error(error, "failed to import JCat stream: "); + g_prefix_error_literal(error, "failed to import JCat stream: "); return FALSE; } } @@ -577,7 +614,10 @@ /* adds each image to the silo */ static gboolean -fu_cabinet_build_silo_folder(FuCabinet *self, FuFirmware *img, GError **error) +fu_cabinet_build_silo_folder(FuCabinet *self, + FuFirmware *img, + FuFirmwareParseFlags flags, + GError **error) { const gchar *fn = fu_firmware_get_id(img); if (fn == NULL) { @@ -588,20 +628,21 @@ return FALSE; } if (g_str_has_suffix(fn, ".metainfo.xml")) { - if (!fu_cabinet_build_silo_metainfo(self, img, error)) + if (!fu_cabinet_build_silo_metainfo(self, img, flags, error)) return FALSE; } return TRUE; } static gboolean -fu_cabinet_build_silo(FuCabinet *self, GError **error) +fu_cabinet_build_silo(FuCabinet *self, FuFirmwareParseFlags flags, GError **error) { g_autoptr(GPtrArray) imgs = NULL; g_autoptr(XbBuilderFixup) fixup1 = NULL; g_autoptr(XbBuilderFixup) fixup2 = NULL; g_autoptr(XbBuilderFixup) fixup3 = NULL; g_autoptr(XbBuilderFixup) fixup4 = NULL; + g_autoptr(XbNode) guid1 = NULL; /* verbose profiling */ if (g_getenv("FWUPD_XMLB_VERBOSE") != NULL) { @@ -623,7 +664,7 @@ /* adds each metainfo file to the silo */ for (guint i = 0; i < imgs->len; i++) { FuFirmware *img = g_ptr_array_index(imgs, i); - if (!fu_cabinet_build_silo_folder(self, img, error)) + if (!fu_cabinet_build_silo_folder(self, img, flags, error)) return FALSE; } @@ -660,6 +701,23 @@ return FALSE; } + /* verify there's at least one GUID */ + guid1 = xb_silo_query_first(self->silo, + "components/component[@type='firmware']/" + "provides/firmware[@type='flashed']", + error); + if (guid1 == NULL) { + fwupd_error_convert(error); + return FALSE; + } + if (xb_node_get_text(guid1) == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no data"); + return FALSE; + } + /* build the index */ if (!xb_silo_query_build_index(self->silo, "components/component[@type='firmware']/provides/firmware", @@ -878,14 +936,13 @@ if (!jcat_file_export_stream(jcat_file, ostr, JCAT_EXPORT_FLAG_NONE, NULL, error)) return FALSE; new_bytes = g_memory_output_stream_steal_as_bytes(G_MEMORY_OUTPUT_STREAM(ostr)); - fu_cabinet_add_file(self, "firmware.jcat", new_bytes); - return TRUE; + return fu_cabinet_add_file(self, "firmware.jcat", new_bytes, error); } static gboolean fu_cabinet_parse(FuFirmware *firmware, GInputStream *stream, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { FuCabinet *self = FU_CABINET(firmware); @@ -900,21 +957,31 @@ /* decompress and calculate container hashes */ if (stream != NULL) { + if ((flags & FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM) == 0 && + (flags & FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB) == 0) { + g_set_error_literal( + error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "FuCabinet requires FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM or " + "FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB for accurate checksums"); + return FALSE; + } if (!FU_FIRMWARE_CLASS(fu_cabinet_parent_class) ->parse(firmware, stream, flags, error)) return FALSE; self->container_checksum = - fu_firmware_get_checksum(firmware, G_CHECKSUM_SHA1, error); + fu_input_stream_compute_checksum(stream, G_CHECKSUM_SHA1, error); if (self->container_checksum == NULL) return FALSE; self->container_checksum_alt = - fu_firmware_get_checksum(firmware, G_CHECKSUM_SHA256, error); + fu_input_stream_compute_checksum(stream, G_CHECKSUM_SHA256, error); if (self->container_checksum_alt == NULL) return FALSE; } /* build xmlb silo */ - if (!fu_cabinet_build_silo(self, error)) + if (!fu_cabinet_build_silo(self, flags, error)) return FALSE; /* sanity check */ @@ -954,7 +1021,7 @@ for (guint j = 0; j < releases->len; j++) { XbNode *rel = g_ptr_array_index(releases, j); g_info("processing release: %s", xb_node_get_attr(rel, "version")); - if (!fu_cabinet_parse_release(self, rel, error)) + if (!fu_cabinet_parse_release(self, rel, flags, error)) return FALSE; } } diff -Nru fwupd-2.0.8/src/fu-cabinet.h fwupd-2.0.20/src/fu-cabinet.h --- fwupd-2.0.8/src/fu-cabinet.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-cabinet.h 2026-02-26 11:36:18.000000000 +0000 @@ -37,8 +37,9 @@ GBytes *privkey, FuCabinetSignFlags flags, GError **error) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1, 2, 3); -void -fu_cabinet_add_file(FuCabinet *self, const gchar *basename, GBytes *data) G_GNUC_NON_NULL(1, 2, 3); +gboolean +fu_cabinet_add_file(FuCabinet *self, const gchar *basename, GBytes *data, GError **error) + G_GNUC_NON_NULL(1, 2, 3); XbSilo * fu_cabinet_get_silo(FuCabinet *self, GError **error) G_GNUC_NON_NULL(1); GPtrArray * diff -Nru fwupd-2.0.8/src/fu-client.c fwupd-2.0.20/src/fu-client.c --- fwupd-2.0.8/src/fu-client.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-client.c 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,7 @@ gchar *sender; GHashTable *hints; /* str:str */ FwupdFeatureFlags feature_flags; - FuClientFlag flags; + FuClientFlags flags; }; G_DEFINE_TYPE(FuClient, fu_client, G_TYPE_OBJECT) @@ -61,7 +61,7 @@ } static void -fu_client_add_flag(FuClient *self, FuClientFlag flag) +fu_client_add_flag(FuClient *self, FuClientFlags flag) { g_return_if_fail(FU_IS_CLIENT(self)); g_return_if_fail(flag != FU_CLIENT_FLAG_NONE); @@ -72,7 +72,7 @@ } void -fu_client_remove_flag(FuClient *self, FuClientFlag flag) +fu_client_remove_flag(FuClient *self, FuClientFlags flag) { g_return_if_fail(FU_IS_CLIENT(self)); g_return_if_fail(flag != FU_CLIENT_FLAG_NONE); @@ -83,7 +83,7 @@ } gboolean -fu_client_has_flag(FuClient *self, FuClientFlag flag) +fu_client_has_flag(FuClient *self, FuClientFlags flag) { g_return_val_if_fail(FU_IS_CLIENT(self), FALSE); g_return_val_if_fail(flag != FU_CLIENT_FLAG_NONE, FALSE); @@ -116,7 +116,7 @@ self->sender = g_value_dup_string(value); break; case PROP_FLAGS: - fu_client_add_flag(self, (FuClientFlag)g_value_get_uint64(value)); + fu_client_add_flag(self, (FuClientFlags)g_value_get_uint64(value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec); diff -Nru fwupd-2.0.8/src/fu-client.h fwupd-2.0.20/src/fu-client.h --- fwupd-2.0.8/src/fu-client.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-client.h 2026-02-26 11:36:18.000000000 +0000 @@ -27,6 +27,6 @@ FwupdFeatureFlags fu_client_get_feature_flags(FuClient *self) G_GNUC_NON_NULL(1); void -fu_client_remove_flag(FuClient *self, FuClientFlag flag) G_GNUC_NON_NULL(1); +fu_client_remove_flag(FuClient *self, FuClientFlags flag) G_GNUC_NON_NULL(1); gboolean -fu_client_has_flag(FuClient *self, FuClientFlag flag) G_GNUC_NON_NULL(1); +fu_client_has_flag(FuClient *self, FuClientFlags flag) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/src/fu-console.c fwupd-2.0.20/src/fu-console.c --- fwupd-2.0.8/src/fu-console.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-console.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,6 +11,11 @@ #include #include +#ifdef HAVE_READLINE +#include +#include +#endif + #include "fu-console.h" #ifdef _WIN32 @@ -41,12 +46,12 @@ fu_console_setup(FuConsole *self, GError **error) { #ifdef _WIN32 - HANDLE hOut; - DWORD dwMode = 0; + HANDLE out; + DWORD mode = 0; /* enable VT sequences */ - hOut = GetStdHandle(STD_OUTPUT_HANDLE); - if (hOut == INVALID_HANDLE_VALUE) { + out = GetStdHandle(STD_OUTPUT_HANDLE); + if (out == INVALID_HANDLE_VALUE) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -54,7 +59,7 @@ (guint)GetLastError()); return FALSE; } - if (!GetConsoleMode(hOut, &dwMode)) { + if (!GetConsoleMode(out, &mode)) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -62,8 +67,8 @@ (guint)GetLastError()); return FALSE; } - dwMode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; - if (!SetConsoleMode(hOut, dwMode)) { + mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING; + if (!SetConsoleMode(out, mode)) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -92,6 +97,11 @@ g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not a TTY"); return FALSE; } + if (isatty(fileno(stdout)) == 0) { + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "not a TTY"); + return FALSE; + } + #endif /* success */ return TRUE; @@ -137,6 +147,21 @@ } } +#ifndef HAVE_READLINE +static gchar * +readline(const gchar *prompt) /* nocheck:name */ +{ + char buffer[64] = {0}; + + if (prompt != NULL) + g_print("%s\n", prompt); + if (!fgets(buffer, sizeof(buffer), stdin)) + return NULL; + g_strdelimit(buffer, "\n", '\0'); + return g_strndup(buffer, sizeof(buffer)); +} +#endif + guint fu_console_input_uint(FuConsole *self, guint maxnum, const gchar *format, ...) { @@ -144,6 +169,7 @@ guint answer = 0; va_list args; g_autofree gchar *tmp = NULL; + g_autofree gchar *prompt = NULL; va_start(args, format); tmp = g_strdup_vprintf(format, args); @@ -151,13 +177,10 @@ fu_console_print_full(self, FU_CONSOLE_PRINT_FLAG_NONE, "%s [0-%u]: ", tmp, maxnum); do { - char buffer[64]; + g_autofree gchar *buffer = readline(prompt); - /* swallow the \n at end of line too */ - if (!fgets(buffer, sizeof(buffer), stdin)) + if (buffer == NULL) break; - if (strlen(buffer) == sizeof(buffer) - 1) - continue; /* get a number */ retval = sscanf(buffer, "%u", &answer); @@ -166,11 +189,16 @@ if (retval == 1 && answer <= maxnum) break; - fu_console_print_full(self, - FU_CONSOLE_PRINT_FLAG_NONE, - /* TRANSLATORS: the user isn't reading the question */ - _("Please enter a number from 0 to %u: "), - maxnum); + if (prompt == NULL) { + g_autoptr(GString) str = g_string_new(NULL); + g_string_append_printf( + str, + /* TRANSLATORS: the user isn't reading the question */ + _("Please enter a number from 0 to %u:"), + maxnum); + g_string_append(str, " "); + prompt = g_string_free(g_steal_pointer(&str), FALSE); + } } while (TRUE); return answer; } @@ -180,6 +208,7 @@ { va_list args; g_autofree gchar *tmp = NULL; + g_autofree gchar *prompt = NULL; va_start(args, format); tmp = g_strdup_vprintf(format, args); @@ -190,22 +219,29 @@ "%s [%s]: ", tmp, def ? "Y|n" : "y|N"); + do { - char buffer[4]; - if (!fgets(buffer, sizeof(buffer), stdin)) - continue; - if (strlen(buffer) == sizeof(buffer) - 1) - continue; - if (g_strcmp0(buffer, "\n") == 0) + g_autofree gchar *buffer = readline(prompt); + + if (buffer == NULL || !strlen(buffer)) return def; buffer[0] = g_ascii_toupper(buffer[0]); - if (g_strcmp0(buffer, "Y\n") == 0) + if (g_strcmp0(buffer, "Y") == 0) return TRUE; - if (g_strcmp0(buffer, "N\n") == 0) + if (g_strcmp0(buffer, "N") == 0) return FALSE; - /* TRANSLATORS: the user isn't reading the question -- %1 is 'Y' and %2 is 'N' */ - fu_console_print(self, _("Please enter either %s or %s: "), "Y", "N"); + if (prompt == NULL) { + g_autoptr(GString) str = g_string_new(NULL); + g_string_append_printf(str, + /* TRANSLATORS: the user isn't reading the question + -- %1 is 'Y' and %2 is 'N' */ + _("Please enter either %s or %s:"), + "Y", + "N"); + g_string_append(str, " "); + prompt = g_string_free(g_steal_pointer(&str), FALSE); + } } while (TRUE); return FALSE; } @@ -523,6 +559,9 @@ g_string_append_vprintf(str, format, args); va_end(args); + if (flags & FU_CONSOLE_PRINT_FLAG_LIST_ITEM) + g_string_prepend(str, " • "); + if (flags & FU_CONSOLE_PRINT_FLAG_WARNING) { /* TRANSLATORS: this is a prefix on the console */ g_autofree gchar *fmt = fu_console_color_format(_("WARNING"), FU_CONSOLE_COLOR_RED); @@ -531,6 +570,9 @@ flags |= FU_CONSOLE_PRINT_FLAG_STDERR; } + if (flags & FU_CONSOLE_PRINT_FLAG_NEWLINE) + g_string_append(str, "\n"); + fu_console_reset_line(self); if (flags & FU_CONSOLE_PRINT_FLAG_STDERR) { g_printerr("%s", str->str); diff -Nru fwupd-2.0.8/src/fu-console.h fwupd-2.0.20/src/fu-console.h --- fwupd-2.0.8/src/fu-console.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-console.h 2026-02-26 11:36:18.000000000 +0000 @@ -26,7 +26,9 @@ FU_CONSOLE_PRINT_FLAG_NONE = 0, FU_CONSOLE_PRINT_FLAG_STDERR = 1 << 0, FU_CONSOLE_PRINT_FLAG_WARNING = 1 << 1, -} FuConsolePrintFlags; + FU_CONSOLE_PRINT_FLAG_LIST_ITEM = 1 << 2, + FU_CONSOLE_PRINT_FLAG_NEWLINE = 1 << 3, +} G_GNUC_FLAG_ENUM FuConsolePrintFlags; gchar * fu_console_color_format(const gchar *text, FuConsoleColor fg_color) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/src/fu-daemon.c fwupd-2.0.20/src/fu-daemon.c --- fwupd-2.0.8/src/fu-daemon.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-daemon.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,7 +21,6 @@ typedef struct { FuEngine *engine; GMainLoop *loop; - FuDaemonMachineKind machine_kind; guint housekeeping_id; gboolean update_in_progress; gboolean pending_stop; @@ -42,22 +41,6 @@ } void -fu_daemon_set_machine_kind(FuDaemon *self, FuDaemonMachineKind machine_kind) -{ - FuDaemonPrivate *priv = GET_PRIVATE(self); - g_return_if_fail(FU_IS_DAEMON(self)); - priv->machine_kind = machine_kind; -} - -FuDaemonMachineKind -fu_daemon_get_machine_kind(FuDaemon *self) -{ - FuDaemonPrivate *priv = GET_PRIVATE(self); - g_return_val_if_fail(FU_IS_DAEMON(self), 0); - return priv->machine_kind; -} - -void fu_daemon_set_update_in_progress(FuDaemon *self, gboolean update_in_progress) { FuDaemonPrivate *priv = GET_PRIVATE(self); @@ -166,9 +149,7 @@ fu_daemon_setup(FuDaemon *self, const gchar *socket_address, GError **error) { FuDaemonClass *klass = FU_DAEMON_GET_CLASS(self); - FuDaemonPrivate *priv = GET_PRIVATE(self); FuEngine *engine = fu_daemon_get_engine(self); - const gchar *machine_kind = g_getenv("FWUPD_MACHINE_KIND"); guint timer_max_ms; g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); g_autoptr(GTimer) timer = g_timer_new(); @@ -181,19 +162,6 @@ if (!fu_daemon_check_syscall_filtering(error)) return FALSE; - /* allow overriding for development */ - if (machine_kind != NULL) { - priv->machine_kind = fu_daemon_machine_kind_from_string(machine_kind); - if (priv->machine_kind == FU_DAEMON_MACHINE_KIND_UNKNOWN) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, - "invalid machine kind specified: %s", - machine_kind); - return FALSE; - } - } - /* proxy on */ if (!klass->setup(self, socket_address, progress, error)) return FALSE; diff -Nru fwupd-2.0.8/src/fu-daemon.h fwupd-2.0.20/src/fu-daemon.h --- fwupd-2.0.8/src/fu-daemon.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-daemon.h 2026-02-26 11:36:18.000000000 +0000 @@ -6,7 +6,6 @@ #pragma once -#include "fu-daemon-struct.h" #include "fu-engine.h" #define FU_TYPE_DAEMON (fu_daemon_get_type()) @@ -39,10 +38,6 @@ FuEngine * fu_daemon_get_engine(FuDaemon *self) G_GNUC_NON_NULL(1); -FuDaemonMachineKind -fu_daemon_get_machine_kind(FuDaemon *self) G_GNUC_NON_NULL(1); -void -fu_daemon_set_machine_kind(FuDaemon *self, FuDaemonMachineKind machine_kind) G_GNUC_NON_NULL(1); void fu_daemon_set_update_in_progress(FuDaemon *self, gboolean update_in_progress) G_GNUC_NON_NULL(1); gboolean diff -Nru fwupd-2.0.8/src/fu-daemon.rs fwupd-2.0.20/src/fu-daemon.rs --- fwupd-2.0.8/src/fu-daemon.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-daemon.rs 1970-01-01 00:00:00.000000000 +0000 @@ -1,10 +0,0 @@ -// Copyright 2023 Richard Hughes -// SPDX-License-Identifier: LGPL-2.1-or-later - -#[derive(FromString)] -enum FuDaemonMachineKind { - Unknown, - Physical, - Virtual, - Container, -} diff -Nru fwupd-2.0.8/src/fu-dbus-daemon.c fwupd-2.0.20/src/fu-dbus-daemon.c --- fwupd-2.0.8/src/fu-dbus-daemon.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-dbus-daemon.c 2026-02-26 11:36:18.000000000 +0000 @@ -16,14 +16,10 @@ #include #include -#include "fwupd-device-private.h" #include "fwupd-enums-private.h" -#include "fwupd-remote-private.h" -#include "fwupd-request-private.h" -#include "fwupd-security-attr-private.h" -#include "fu-bios-settings-private.h" #include "fu-client-list.h" +#include "fu-context-private.h" #include "fu-dbus-daemon.h" #include "fu-device-private.h" #include "fu-engine-helper.h" @@ -52,11 +48,6 @@ G_DEFINE_TYPE(FuDbusDaemon, fu_dbus_daemon, FU_TYPE_DAEMON) -#define FU_DAEMON_INSTALL_FLAG_MASK_SAFE \ - (FWUPD_INSTALL_FLAG_ALLOW_OLDER | FWUPD_INSTALL_FLAG_ALLOW_REINSTALL | \ - FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH | FWUPD_INSTALL_FLAG_FORCE | \ - FWUPD_INSTALL_FLAG_NO_HISTORY | FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS) - static void fu_dbus_daemon_engine_changed_cb(FuEngine *engine, FuDbusDaemon *self) { @@ -186,7 +177,7 @@ return; self->status = status; - g_debug("Emitting PropertyChanged('Status'='%s')", fwupd_status_to_string(status)); + g_debug("emitting PropertyChanged('Status'='%s')", fwupd_status_to_string(status)); fu_dbus_daemon_emit_property_changed(self, "Status", g_variant_new_uint32(status)); } @@ -226,6 +217,13 @@ } /* are we root and therefore trusted? */ + if (self->proxy_uid == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "org.freedesktop.DBus is not available"); + return NULL; + } value = g_dbus_proxy_call_sync(self->proxy_uid, "GetConnectionUnixUser", g_variant_new("(s)", sender), @@ -234,7 +232,7 @@ NULL, error); if (value == NULL) { - g_prefix_error(error, "failed to read user id of caller: "); + g_prefix_error_literal(error, "failed to read user id of caller: "); return NULL; } g_variant_get(value, "(u)", &calling_uid); @@ -323,7 +321,7 @@ static void fu_dbus_daemon_method_invocation_return_gerror(GDBusMethodInvocation *invocation, GError *error) { - fu_error_convert(&error); + fwupd_error_convert(&error); g_dbus_method_invocation_return_gerror(invocation, error); } @@ -573,7 +571,7 @@ return; self->percentage = percentage; - g_debug("Emitting PropertyChanged('Percentage'='%u%%')", percentage); + g_debug("emitting PropertyChanged('Percentage'='%u%%')", percentage); fu_dbus_daemon_emit_property_changed(self, "Percentage", g_variant_new_uint32(percentage)); } @@ -682,6 +680,29 @@ g_dbus_method_invocation_return_value(helper->invocation, NULL); } +static void +fu_dbus_daemon_authorize_clean_remote_cb(GObject *source, GAsyncResult *res, gpointer user_data) +{ + g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; + g_autoptr(GError) error = NULL; + FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(helper->self)); + + /* get result */ + if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { + fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); + return; + } + + /* authenticated */ + if (!fu_engine_clean_remote(engine, helper->remote_id, &error)) { + fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); + return; + } + + /* success */ + g_dbus_method_invocation_return_value(helper->invocation, NULL); +} + static FuPolkitAuthorityCheckFlags fu_dbus_daemon_engine_request_get_authority_check_flags(FuEngineRequest *request) { @@ -760,7 +781,7 @@ &error); fu_daemon_set_update_in_progress(FU_DAEMON(self), FALSE); if (fu_daemon_get_pending_stop(FU_DAEMON(self))) { - g_set_error_literal(&error, + g_set_error_literal(&error, /* nocheck:error-false-return */ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "daemon was stopped"); @@ -954,6 +975,14 @@ XbNode *component = g_ptr_array_index(components, i); for (guint j = 0; j < devices_possible->len; j++) { FuDevice *device = g_ptr_array_index(devices_possible, j); + + /* emulating */ + if ((helper->flags & FWUPD_INSTALL_FLAG_ONLY_EMULATED) && + !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) { + g_debug("skipping non-emulated %s", fu_device_get_id(device)); + continue; + } + g_debug("testing device %u [%s] with component %u", j, fu_device_get_id(device), @@ -983,17 +1012,6 @@ } #endif /* HAVE_GIO_UNIX */ -static gboolean -fu_dbus_daemon_device_id_valid(const gchar *device_id, GError **error) -{ - if (g_strcmp0(device_id, FWUPD_DEVICE_ID_ANY) == 0) - return TRUE; - if (device_id != NULL && strlen(device_id) >= 4) - return TRUE; - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid device ID: %s", device_id); - return FALSE; -} - typedef struct { gchar *id; gchar *sender; @@ -1065,8 +1083,19 @@ /* get the fd */ message = g_dbus_method_invocation_get_message(invocation); fd_list = g_dbus_message_get_unix_fd_list(message); - if (fd_list == NULL || g_unix_fd_list_get_length(fd_list) != 1) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid handle"); + if (fd_list == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no file descriptors are associated"); + return NULL; + } + if (g_unix_fd_list_get_length(fd_list) != 1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "wrong number of file descriptors: %i", + g_unix_fd_list_get_length(fd_list)); return NULL; } fd = g_unix_fd_list_get(fd_list, 0, error); @@ -1081,7 +1110,7 @@ } return g_steal_pointer(&stream); #else - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature"); return NULL; #endif } @@ -1098,8 +1127,19 @@ /* get the fd */ message = g_dbus_method_invocation_get_message(invocation); fd_list = g_dbus_message_get_unix_fd_list(message); - if (fd_list == NULL || g_unix_fd_list_get_length(fd_list) != 1) { - g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid handle"); + if (fd_list == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no file descriptors are associated"); + return NULL; + } + if (g_unix_fd_list_get_length(fd_list) != 1) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "wrong number of file descriptors: %i", + g_unix_fd_list_get_length(fd_list)); return NULL; } fd = g_unix_fd_list_get(fd_list, 0, error); @@ -1114,7 +1154,7 @@ } return g_steal_pointer(&stream); #else - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature"); + g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "unsupported feature"); return NULL; #endif } @@ -1123,39 +1163,38 @@ fu_dbus_daemon_hsi_supported(FuDbusDaemon *self, GError **error) { #ifdef HAVE_HSI - g_autofree gchar *sysfsfwdir = NULL; - g_autofree gchar *xen_privileged_fn = NULL; + FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); + FuContext *ctx = fu_engine_get_context(engine); if (g_getenv("UMOCKDEV_DIR") != NULL) return TRUE; - if (fu_daemon_get_machine_kind(FU_DAEMON(self)) == FU_DAEMON_MACHINE_KIND_PHYSICAL) - return TRUE; - sysfsfwdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR_FW_ATTRIB); - /* privileged xen can access most hardware */ - xen_privileged_fn = - g_build_filename(sysfsfwdir, "hypervisor", "start_flags", "privileged", NULL); - if (g_file_test(xen_privileged_fn, G_FILE_TEST_EXISTS)) { - g_autofree gchar *contents = NULL; - - if (g_file_get_contents(xen_privileged_fn, &contents, NULL, NULL)) { - if (g_strcmp0(contents, "1") == 0) - return TRUE; - } + if (fu_context_has_flag(ctx, FU_CONTEXT_FLAG_IS_CONTAINER)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "HSI unavailable for container"); + return FALSE; + } + if (fu_context_has_flag(ctx, FU_CONTEXT_FLAG_IS_HYPERVISOR) && + !fu_context_has_flag(ctx, FU_CONTEXT_FLAG_IS_HYPERVISOR_PRIVILEGED)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "HSI unavailable for unprivileged hypervisor"); + return FALSE; } - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "HSI unavailable for hypervisor"); + /* success */ + return TRUE; #else g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "HSI support not enabled"); -#endif return FALSE; +#endif } static void @@ -1207,10 +1246,6 @@ g_autoptr(GPtrArray) releases = NULL; g_variant_get(parameters, "(&s)", &device_id); - if (!fu_dbus_daemon_device_id_valid(device_id, &error)) { - fu_dbus_daemon_method_invocation_return_gerror(invocation, error); - return; - } releases = fu_engine_get_releases(engine, request, device_id, &error); if (releases == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); @@ -1307,7 +1342,7 @@ fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); helper = g_new0(FuMainAuthHelper, 1); helper->self = self; - helper->flags = FWUPD_INSTALL_FLAG_NO_SEARCH; + helper->flags = FU_FIRMWARE_PARSE_FLAG_NO_SEARCH; helper->request = g_object_ref(request); helper->invocation = g_object_ref(invocation); helper->checksums = g_ptr_array_new_with_free_func(g_free); @@ -1454,10 +1489,6 @@ g_autoptr(GPtrArray) releases = NULL; g_variant_get(parameters, "(&s)", &device_id); - if (!fu_dbus_daemon_device_id_valid(device_id, &error)) { - fu_dbus_daemon_method_invocation_return_gerror(invocation, error); - return; - } releases = fu_engine_get_downgrades(engine, request, device_id, &error); if (releases == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); @@ -1480,10 +1511,6 @@ g_autoptr(GPtrArray) releases = NULL; g_variant_get(parameters, "(&s)", &device_id); - if (!fu_dbus_daemon_device_id_valid(device_id, &error)) { - fu_dbus_daemon_method_invocation_return_gerror(invocation, error); - return; - } releases = fu_engine_get_upgrades(engine, request, device_id, &error); if (releases == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); @@ -1603,6 +1630,28 @@ } static void +fu_dbus_daemon_method_search(FuDbusDaemon *self, + GVariant *parameters, + FuEngineRequest *request, + GDBusMethodInvocation *invocation) +{ + FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); + const gchar *token; + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) releases = NULL; + + g_variant_get(parameters, "(&s)", &token); + releases = fu_engine_search(engine, token, &error); + if (releases == NULL) { + fu_dbus_daemon_method_invocation_return_gerror(invocation, error); + return; + } + g_dbus_method_invocation_return_value( + invocation, + fwupd_codec_array_to_variant(releases, FWUPD_CODEC_FLAG_NONE)); +} + +static void fu_dbus_daemon_authorize_emulation_load_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; @@ -1739,6 +1788,13 @@ fu_dbus_daemon_authorize_modify_device_cb(GObject *source, GAsyncResult *res, gpointer user_data) { g_autoptr(FuMainAuthHelper) helper = (FuMainAuthHelper *)user_data; + g_autoptr(GError) error = NULL; + + /* get result */ + if (!fu_polkit_authority_check_finish(FU_POLKIT_AUTHORITY(source), res, &error)) { + fu_dbus_daemon_method_invocation_return_gerror(helper->invocation, error); + return; + } fu_dbus_daemon_authorize_modify_device_internal( fu_daemon_get_engine(FU_DAEMON(helper->self)), @@ -1805,21 +1861,17 @@ { FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); GVariant *val; + const gchar *device_id = NULL; + g_autoptr(FwupdDevice) device = NULL; g_autoptr(GError) error = NULL; - const gchar *device_id = NULL; - g_autoptr(FwupdDevice) result = NULL; g_variant_get(parameters, "(&s)", &device_id); - if (!fu_dbus_daemon_device_id_valid(device_id, &error)) { - fu_dbus_daemon_method_invocation_return_gerror(invocation, error); - return; - } - result = fu_engine_get_results(engine, device_id, &error); - if (result == NULL) { + device = fu_engine_get_results(engine, device_id, &error); + if (device == NULL) { fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } - val = fwupd_codec_to_variant(FWUPD_CODEC(result), FWUPD_CODEC_FLAG_TRUSTED); + val = fwupd_codec_to_variant(FWUPD_CODEC(device), FWUPD_CODEC_FLAG_TRUSTED); g_dbus_method_invocation_return_value(invocation, g_variant_new_tuple(&val, 1)); } @@ -1843,8 +1895,20 @@ /* update the metadata store */ message = g_dbus_method_invocation_get_message(invocation); fd_list = g_dbus_message_get_unix_fd_list(message); - if (fd_list == NULL || g_unix_fd_list_get_length(fd_list) != 2) { - g_set_error(&error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "invalid handle"); + if (fd_list == NULL) { + g_set_error_literal(&error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "no file descriptors are associated"); + fu_dbus_daemon_method_invocation_return_gerror(invocation, error); + return; + } + if (g_unix_fd_list_get_length(fd_list) != 2) { + g_set_error(&error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "wrong number of file descriptors: %i", + g_unix_fd_list_get_length(fd_list)); fu_dbus_daemon_method_invocation_return_gerror(invocation, error); return; } @@ -1882,13 +1946,8 @@ { const gchar *device_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; - g_autoptr(GError) error = NULL; g_variant_get(parameters, "(&s)", &device_id); - if (!fu_dbus_daemon_device_id_valid(device_id, &error)) { - fu_dbus_daemon_method_invocation_return_gerror(invocation, error); - return; - } /* authenticate */ fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); @@ -1914,13 +1973,8 @@ { const gchar *device_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; - g_autoptr(GError) error = NULL; g_variant_get(parameters, "(&s)", &device_id); - if (!fu_dbus_daemon_device_id_valid(device_id, &error)) { - fu_dbus_daemon_method_invocation_return_gerror(invocation, error); - return; - } /* authenticate */ fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); @@ -2029,6 +2083,36 @@ } static void +fu_dbus_daemon_method_clean_remote(FuDbusDaemon *self, + GVariant *parameters, + FuEngineRequest *request, + GDBusMethodInvocation *invocation) +{ + const gchar *remote_id = NULL; + g_autoptr(FuMainAuthHelper) helper = NULL; + + /* check the id exists */ + g_variant_get(parameters, "(&s)", &remote_id); + + /* create helper object */ + helper = g_new0(FuMainAuthHelper, 1); + helper->request = g_object_ref(request); + helper->invocation = g_object_ref(invocation); + helper->remote_id = g_strdup(remote_id); + helper->self = self; + + /* authenticate */ + fu_dbus_daemon_set_status(self, FWUPD_STATUS_WAITING_FOR_AUTH); + fu_polkit_authority_check(self->authority, + fu_engine_request_get_sender(request), + "org.freedesktop.fwupd.clean-remote", + fu_dbus_daemon_engine_request_get_authority_check_flags(request), + NULL, + fu_dbus_daemon_authorize_clean_remote_cb, + g_steal_pointer(&helper)); +} + +static void fu_dbus_daemon_method_verify_update(FuDbusDaemon *self, GVariant *parameters, FuEngineRequest *request, @@ -2036,16 +2120,10 @@ { const gchar *device_id = NULL; g_autoptr(FuMainAuthHelper) helper = NULL; - g_autoptr(GError) error = NULL; /* check the id exists */ g_variant_get(parameters, "(&s)", &device_id); - if (!fu_dbus_daemon_device_id_valid(device_id, &error)) { - fu_dbus_daemon_method_invocation_return_gerror(invocation, error); - return; - } - /* create helper object */ helper = g_new0(FuMainAuthHelper, 1); helper->request = g_object_ref(request); @@ -2076,10 +2154,6 @@ g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); g_variant_get(parameters, "(&s)", &device_id); - if (!fu_dbus_daemon_device_id_valid(device_id, &error)) { - fu_dbus_daemon_method_invocation_return_gerror(invocation, error); - return; - } /* progress */ fu_progress_set_profile(progress, g_getenv("FWUPD_VERBOSE") != NULL); @@ -2183,10 +2257,6 @@ /* check the id exists */ g_variant_get(parameters, "(&sha{sv})", &device_id, &fd_handle, &iter); - if (!fu_dbus_daemon_device_id_valid(device_id, &error)) { - fu_dbus_daemon_method_invocation_return_gerror(invocation, error); - return; - } /* create helper object */ helper = g_new0(FuMainAuthHelper, 1); @@ -2216,19 +2286,6 @@ g_variant_unref(prop_value); } - /* verify the client didn't send "internal" flags like no-search */ - if (helper->flags & ~FU_DAEMON_INSTALL_FLAG_MASK_SAFE) { - FwupdInstallFlags flags_unsafe = helper->flags & ~FU_DAEMON_INSTALL_FLAG_MASK_SAFE; - g_set_error(&error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "client sent unsupported flag: 0x%x [%s]", - (guint)flags_unsafe, - fwupd_install_flags_to_string(flags_unsafe)); - fu_dbus_daemon_method_invocation_return_gerror(invocation, error); - return; - } - /* get stream */ helper->stream = fu_dbus_daemon_invocation_get_input_stream(invocation, &error); if (helper->stream == NULL) { @@ -2502,6 +2559,7 @@ {"ClearResults", fu_dbus_daemon_method_clear_results}, {"EmulationLoad", fu_dbus_daemon_method_emulation_load}, {"EmulationSave", fu_dbus_daemon_method_emulation_save}, + {"Search", fu_dbus_daemon_method_search}, {"ModifyDevice", fu_dbus_daemon_method_modify_device}, {"GetResults", fu_dbus_daemon_method_get_results}, {"UpdateMetadata", fu_dbus_daemon_method_update_metadata}, @@ -2510,6 +2568,7 @@ {"ModifyConfig", fu_dbus_daemon_method_modify_config}, {"ResetConfig", fu_dbus_daemon_method_reset_config}, {"ModifyRemote", fu_dbus_daemon_method_modify_remote}, + {"CleanRemote", fu_dbus_daemon_method_clean_remote}, {"VerifyUpdate", fu_dbus_daemon_method_verify_update}, {"Verify", fu_dbus_daemon_method_verify}, {"SetFeatureFlags", fu_dbus_daemon_method_set_feature_flags}, @@ -2536,7 +2595,7 @@ /* be helpful */ parameters_str = g_variant_print_string(parameters, NULL, TRUE); - g_debug("Called %s%s", method_name, parameters_str->str); + g_debug("called %s%s", method_name, parameters_str->str); /* call the correct vfunc */ for (guint i = 0; i < G_N_ELEMENTS(method_funcs); i++) { @@ -2553,6 +2612,43 @@ } static GVariant * +fu_dbus_daemon_get_property_hwids(FuDbusDaemon *self) +{ + FuEngine *engine = fu_daemon_get_engine(FU_DAEMON(self)); + FuContext *ctx = fu_engine_get_context(engine); + FuHwids *hwids = fu_context_get_hwids(ctx); + GVariantBuilder builder; + g_autoptr(GPtrArray) chid_keys = fu_hwids_get_chid_keys(hwids); + g_autoptr(GPtrArray) hwid_keys = fu_hwids_get_keys(hwids); + + g_variant_builder_init(&builder, G_VARIANT_TYPE("a(ss)")); + for (guint i = 0; i < hwid_keys->len; i++) { + const gchar *hwid_key = g_ptr_array_index(hwid_keys, i); + const gchar *value = fu_hwids_get_value(hwids, hwid_key); + if (value == NULL) + continue; + g_variant_builder_add(&builder, "(ss)", hwid_key, value); + } + for (guint i = 0; i < chid_keys->len; i++) { + const gchar *key = g_ptr_array_index(chid_keys, i); + const gchar *keys = NULL; + g_autofree gchar *guid = NULL; + + /* get the GUID */ + keys = fu_hwids_get_replace_keys(hwids, key); + if (keys == NULL) + continue; + guid = fu_hwids_get_guid(hwids, key, NULL); + if (guid == NULL) + continue; + g_variant_builder_add(&builder, "(ss)", keys, guid); + } + + /* done */ + return g_variant_builder_end(&builder); +} + +static GVariant * fu_dbus_daemon_get_property(GDBusConnection *connection_, const gchar *sender, const gchar *object_path, @@ -2601,7 +2697,7 @@ if (g_strcmp0(property_name, "HostMachineId") == 0) { const gchar *tmp = fu_engine_get_host_machine_id(engine); if (tmp == NULL) { - g_set_error(error, + g_set_error(error, /* nocheck:error */ G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "failed to get daemon property %s", @@ -2616,7 +2712,7 @@ g_autofree gchar *tmp = fu_engine_get_host_security_id(engine, NULL); return g_variant_new_string(tmp); #else - g_set_error(error, + g_set_error(error, /* nocheck:error */ G_DBUS_ERROR, G_DBUS_ERROR_NOT_SUPPORTED, "failed to get daemon property %s", @@ -2632,9 +2728,11 @@ return g_variant_new_boolean( fu_engine_config_get_only_trusted(fu_engine_get_config(engine))); } + if (g_strcmp0(property_name, "Hwids") == 0) + return fu_dbus_daemon_get_property_hwids(self); /* return an error */ - g_set_error(error, + g_set_error(error, /* nocheck:error */ G_DBUS_ERROR, G_DBUS_ERROR_UNKNOWN_PROPERTY, "failed to get daemon property %s", @@ -2727,14 +2825,8 @@ FuDbusDaemon *self = FU_DBUS_DAEMON(user_data); g_autoptr(GError) error = NULL; - fu_dbus_daemon_set_connection(self, connection); - if (!fu_dbus_daemon_register_object(self, &error)) { - g_warning("cannot register object: %s", error->message); - return; - } - /* connect to D-Bus directly */ - self->proxy_uid = g_dbus_proxy_new_sync(self->connection, + self->proxy_uid = g_dbus_proxy_new_sync(connection, G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS, NULL, @@ -2747,6 +2839,12 @@ g_warning("cannot connect to DBus: %s", error->message); return; } + + fu_dbus_daemon_set_connection(self, connection); + if (!fu_dbus_daemon_register_object(self, &error)) { + g_warning("cannot register object: %s", error->message); + return; + } } static void @@ -2855,7 +2953,7 @@ FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to load engine: "); + g_prefix_error_literal(error, "failed to load engine: "); return FALSE; } fu_progress_step_done(progress); @@ -2864,7 +2962,7 @@ self->introspection_daemon = fu_dbus_daemon_load_introspection(FWUPD_DBUS_INTERFACE ".xml", error); if (self->introspection_daemon == NULL) { - g_prefix_error(error, "failed to load introspection: "); + g_prefix_error_literal(error, "failed to load introspection: "); return FALSE; } fu_progress_step_done(progress); @@ -2887,7 +2985,7 @@ NULL, error); if (server == NULL) { - g_prefix_error(error, "failed to create D-Bus server: "); + g_prefix_error_literal(error, "failed to create D-Bus server: "); return FALSE; } g_message("using socket address: %s", g_dbus_server_get_client_address(server)); diff -Nru fwupd-2.0.8/src/fu-debug.c fwupd-2.0.20/src/fu-debug.c --- fwupd-2.0.8/src/fu-debug.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-debug.c 2026-02-26 11:36:18.000000000 +0000 @@ -220,7 +220,7 @@ self->log_level = G_LOG_LEVEL_DEBUG; return TRUE; } - g_set_error_literal(error, + g_set_error_literal(error, /* nocheck:error */ G_OPTION_ERROR, G_OPTION_ERROR_FAILED, "No further debug level supported"); @@ -304,7 +304,7 @@ { HKEY key; gchar msgfile[MAX_PATH]; - DWORD dwData = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; + DWORD data = EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE; if (RegCreateKeyExA(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\" @@ -326,7 +326,7 @@ REG_EXPAND_SZ, (BYTE *)msgfile, strlen(msgfile) + 1); - RegSetValueExA(key, "TypesSupported", 0, REG_DWORD, (BYTE *)&dwData, sizeof(dwData)); + RegSetValueExA(key, "TypesSupported", 0, REG_DWORD, (BYTE *)&data, sizeof(data)); RegCloseKey(key); /* good to go */ diff -Nru fwupd-2.0.8/src/fu-device-list.c fwupd-2.0.20/src/fu-device-list.c --- fwupd-2.0.8/src/fu-device-list.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-device-list.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,7 +12,6 @@ #include "fu-device-list.h" #include "fu-device-private.h" -#include "fu-engine.h" /** * FuDeviceList: @@ -40,6 +39,13 @@ static guint signals[SIGNAL_LAST] = {0}; +enum { + QUARK_UNCONNECTED, + QUARK_LAST, +}; + +static guint quarks[QUARK_LAST] = {0}; + typedef struct { FuDevice *device; FuDevice *device_old; @@ -59,21 +65,24 @@ static void fu_device_list_emit_device_added(FuDeviceList *self, FuDevice *device) { - g_info("::added %s [%s]", fu_device_get_id(device), fu_device_get_name(device)); + g_autofree gchar *id_display = fu_device_get_id_display(device); + g_info("::added %s", id_display); g_signal_emit(self, signals[SIGNAL_ADDED], 0, device); } static void fu_device_list_emit_device_removed(FuDeviceList *self, FuDevice *device) { - g_info("::removed %s [%s]", fu_device_get_id(device), fu_device_get_name(device)); + g_autofree gchar *id_display = fu_device_get_id_display(device); + g_info("::removed %s", id_display); g_signal_emit(self, signals[SIGNAL_REMOVED], 0, device); } static void fu_device_list_emit_device_changed(FuDeviceList *self, FuDevice *device) { - g_info("::changed %s [%s]", fu_device_get_id(device), fu_device_get_name(device)); + g_autofree gchar *id_display = fu_device_get_id_display(device); + g_info("::changed %s", id_display); g_signal_emit(self, signals[SIGNAL_CHANGED], 0, device); } @@ -120,7 +129,7 @@ g_rw_lock_reader_lock(&self->devices_mutex); for (guint i = 0; i < self->devices->len; i++) { FuDeviceItem *item = g_ptr_array_index(self->devices, i); - if (device == fu_device_get_parent(item->device)) + if (device == fu_device_get_parent_internal(item->device)) g_ptr_array_add(devices, g_object_ref(item->device)); } g_rw_lock_reader_unlock(&self->devices_mutex); @@ -140,6 +149,8 @@ for (guint i = 0; i < children->len; i++) { FuDevice *child = g_ptr_array_index(children, i); if (fu_device_has_private_flag(child, + FU_DEVICE_PRIVATE_FLAG_INSTALL_PARENT_FIRST) || + fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_INSTALL_PARENT_FIRST)) { fu_device_list_depsolve_order_full(self, child, depth + 1); } else { @@ -223,7 +234,7 @@ g_rw_lock_reader_lock(&self->devices_mutex); for (guint i = 0; i < self->devices->len; i++) { FuDeviceItem *item = g_ptr_array_index(self->devices, i); - if (fu_device_has_private_flag(item->device, FU_DEVICE_PRIVATE_FLAG_UNCONNECTED)) + if (fu_device_has_private_flag_quark(item->device, quarks[QUARK_UNCONNECTED])) continue; if (fu_device_has_inhibit(item->device, "hidden")) continue; @@ -322,6 +333,14 @@ /* support abbreviated hashes */ device_id_len = strlen(device_id); + if (device_id_len < 4) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "invalid device ID: %s", + device_id); + return NULL; + } g_rw_lock_reader_lock(&self->devices_mutex); for (guint i = 0; i < self->devices->len; i++) { FuDeviceItem *item_tmp = g_ptr_array_index(self->devices, i); @@ -499,16 +518,18 @@ static void fu_device_list_remove_with_delay(FuDeviceItem *item) { + g_autofree gchar *id_display = fu_device_get_id_display(item->device); /* give the hardware time to re-enumerate or the user time to * re-insert the device with a magic button pressed */ g_info("waiting %ums for %s device removal", fu_device_get_remove_delay(item->device), - fu_device_get_name(item->device)); + id_display); item->remove_id = g_timeout_add(fu_device_get_remove_delay(item->device), fu_device_list_device_delayed_remove_cb, item); } +/* nocheck:name */ static gboolean fu_device_list_should_remove_with_delay(FuDevice *device) { @@ -698,9 +719,10 @@ /* debug */ str = fwupd_codec_to_string(FWUPD_CODEC(self)); - g_debug("\n%s", str); + g_debug("%s", str); } +/* nocheck:name */ static void _fu_device_incorporate_problem_update_in_progress(FuDevice *self, FuDevice *donor) { @@ -754,6 +776,9 @@ fu_device_set_version_raw(device, raw); } + /* allow another plugin to handle the write too */ + fu_device_incorporate_flag(device, item->device, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED); + /* seems like a sane assumption if we've tagged the runtime mode as signed */ fu_device_incorporate_flag(device, item->device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD); fu_device_incorporate_flag(device, item->device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); @@ -766,10 +791,11 @@ fu_device_incorporate_flag(device, item->device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR); /* copy the parent if not already set */ - if (fu_device_get_parent(item->device) != NULL && - fu_device_get_parent(item->device) != device && - fu_device_get_parent(device) != item->device && fu_device_get_parent(device) == NULL) { - FuDevice *parent = fu_device_get_parent(item->device); + if (fu_device_get_parent_internal(item->device) != NULL && + fu_device_get_parent_internal(item->device) != device && + fu_device_get_parent_internal(device) != item->device && + fu_device_get_parent_internal(device) == NULL) { + FuDevice *parent = fu_device_get_parent_internal(item->device); g_info("copying parent %s to new device", fu_device_get_id(parent)); fu_device_set_parent(device, parent); } @@ -788,7 +814,7 @@ /* debug */ str = fwupd_codec_to_string(FWUPD_CODEC(self)); - g_debug("\n%s", str); + g_debug("%s", str); /* we were waiting for this... */ fu_device_list_clear_wait_for_replug(self, item); @@ -1016,7 +1042,9 @@ do { g_autoptr(GPtrArray) devices_wfr_tmp = NULL; g_usleep(1000); - g_main_context_iteration(NULL, FALSE); + while (g_main_context_iteration(NULL, FALSE)) { + /* nothing needs to be done here */ + }; devices_wfr_tmp = fu_device_list_get_wait_for_replug(self); if (devices_wfr_tmp->len == 0) break; @@ -1031,7 +1059,7 @@ /* dump to console */ str = fwupd_codec_to_string(FWUPD_CODEC(self)); - g_debug("\n%s", str); + g_debug("%s", str); /* unset and build error string */ for (guint i = 0; i < devices_wfr2->len; i++) { @@ -1119,6 +1147,9 @@ object_class->dispose = fu_device_list_dispose; object_class->finalize = fu_device_list_finalize; + /* used as device flags */ + quarks[QUARK_UNCONNECTED] = g_quark_from_static_string(FU_DEVICE_PRIVATE_FLAG_UNCONNECTED); + /** * FuDeviceList::added: * @self: the #FuDeviceList instance that emitted the signal diff -Nru fwupd-2.0.8/src/fu-engine-config.c fwupd-2.0.20/src/fu-engine-config.c --- fwupd-2.0.8/src/fu-engine-config.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-engine-config.c 2026-02-26 11:36:18.000000000 +0000 @@ -177,10 +177,16 @@ if (host_bkc != NULL && host_bkc[0] != '\0') self->host_bkc = g_steal_pointer(&host_bkc); - /* fetch hardcoded ESP mountpoint */ + /* fetch hardcoded ESP mountpoint, removing trailing slash as required */ esp_location = fu_config_get_value(FU_CONFIG(self), "fwupd", "EspLocation"); - if (esp_location != NULL && esp_location[0] != '\0') - self->esp_location = g_steal_pointer(&esp_location); + if (esp_location != NULL && esp_location[0] != '\0') { + g_autoptr(GString) esp_location_tmp = g_string_new(esp_location); + if (g_str_has_suffix(esp_location_tmp->str, "/")) { + g_warning("removing trailing slash from EspLocation"); + g_string_truncate(esp_location_tmp, esp_location_tmp->len - 1); + } + self->esp_location = g_string_free(g_steal_pointer(&esp_location_tmp), FALSE); + } /* get trusted uids */ g_array_set_size(self->trusted_uids, 0); @@ -330,6 +336,18 @@ } gboolean +fu_engine_config_get_ignore_efivars_free_space(FuEngineConfig *self) +{ + return fu_config_get_value_bool(FU_CONFIG(self), "fwupd", "IgnoreEfivarsFreeSpace"); +} + +gboolean +fu_engine_config_get_only_trust_pq_signatures(FuEngineConfig *self) +{ + return fu_config_get_value_bool(FU_CONFIG(self), "fwupd", "OnlyTrustPostQuantumSignatures"); +} + +gboolean fu_engine_config_get_release_dedupe(FuEngineConfig *self) { return fu_config_get_value_bool(FU_CONFIG(self), "fwupd", "ReleaseDedupe"); @@ -359,6 +377,12 @@ return fu_config_get_value_bool(FU_CONFIG(self), "fwupd", "EnumerateAllDevices"); } +gboolean +fu_engine_config_get_require_immutable_enumeration(FuEngineConfig *self) +{ + return fu_config_get_value_bool(FU_CONFIG(self), "fwupd", "RequireImmutableEnumeration"); +} + const gchar * fu_engine_config_get_host_bkc(FuEngineConfig *self) { @@ -413,12 +437,15 @@ fu_engine_config_set_default(self, "HostBkc", NULL); fu_engine_config_set_default(self, "IdleTimeout", "300"); /* s */ fu_engine_config_set_default(self, "IdleInhibitStartupThreshold", "500"); /* ms */ + fu_engine_config_set_default(self, "IgnoreEfivarsFreeSpace", "false"); fu_engine_config_set_default(self, "IgnorePower", "false"); fu_engine_config_set_default(self, "IgnoreRequirements", "false"); fu_engine_config_set_default(self, "OnlyTrusted", "true"); fu_engine_config_set_default(self, "P2pPolicy", FU_DEFAULT_P2P_POLICY); fu_engine_config_set_default(self, "ReleaseDedupe", "true"); fu_engine_config_set_default(self, "ReleasePriority", "local"); + fu_engine_config_set_default(self, "RequireImmutableEnumeration", "false"); + fu_engine_config_set_default(self, "OnlyTrustPostQuantumSignatures", "false"); fu_engine_config_set_default(self, "ShowDevicePrivate", "true"); fu_engine_config_set_default(self, "TestDevices", "false"); fu_engine_config_set_default(self, "TrustedReports", "VendorId=$OEM"); diff -Nru fwupd-2.0.8/src/fu-engine-config.h fwupd-2.0.20/src/fu-engine-config.h --- fwupd-2.0.8/src/fu-engine-config.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-engine-config.h 2026-02-26 11:36:18.000000000 +0000 @@ -39,6 +39,8 @@ gboolean fu_engine_config_get_enumerate_all_devices(FuEngineConfig *self) G_GNUC_NON_NULL(1); gboolean +fu_engine_config_get_require_immutable_enumeration(FuEngineConfig *self); +gboolean fu_engine_config_get_ignore_power(FuEngineConfig *self) G_GNUC_NON_NULL(1); gboolean fu_engine_config_get_only_trusted(FuEngineConfig *self) G_GNUC_NON_NULL(1); @@ -49,6 +51,10 @@ gboolean fu_engine_config_get_ignore_requirements(FuEngineConfig *self) G_GNUC_NON_NULL(1); gboolean +fu_engine_config_get_ignore_efivars_free_space(FuEngineConfig *self) G_GNUC_NON_NULL(1); +gboolean +fu_engine_config_get_only_trust_pq_signatures(FuEngineConfig *self) G_GNUC_NON_NULL(1); +gboolean fu_engine_config_get_release_dedupe(FuEngineConfig *self) G_GNUC_NON_NULL(1); FuReleasePriority fu_engine_config_get_release_priority(FuEngineConfig *self) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/src/fu-engine-emulator.c fwupd-2.0.20/src/fu-engine-emulator.c --- fwupd-2.0.8/src/fu-engine-emulator.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-engine-emulator.c 2026-02-26 11:36:18.000000000 +0000 @@ -23,12 +23,20 @@ enum { PROP_0, PROP_ENGINE, PROP_LAST }; +/* [composite_cnt:]{phase}[-write_cnt].json */ static gchar * -fu_engine_emulator_phase_to_filename(FuEngineEmulatorPhase phase, guint write_cnt) +fu_engine_emulator_phase_to_filename(guint composite_cnt, + FuEngineEmulatorPhase phase, + guint write_cnt) { - if (write_cnt == FU_ENGINE_EMULATOR_WRITE_COUNT_DEFAULT) - return g_strdup_printf("%s.json", fu_engine_emulator_phase_to_string(phase)); - return g_strdup_printf("%s-%u.json", fu_engine_emulator_phase_to_string(phase), write_cnt); + g_autoptr(GString) fn = g_string_new(NULL); + if (composite_cnt != 0) + g_string_append_printf(fn, "%u:", composite_cnt); + g_string_append(fn, fu_engine_emulator_phase_to_string(phase)); + if (write_cnt != FU_ENGINE_EMULATOR_WRITE_COUNT_DEFAULT) + g_string_append_printf(fn, "-%u", write_cnt); + g_string_append(fn, ".json"); + return g_string_free(g_steal_pointer(&fn), FALSE); } gboolean @@ -39,6 +47,7 @@ gpointer key; gpointer value; g_autoptr(GByteArray) buf = NULL; + g_autoptr(GBytes) blob = NULL; g_autoptr(FuArchive) archive = fu_archive_new(NULL, FU_ARCHIVE_FLAG_NONE, NULL); g_return_val_if_fail(FU_IS_ENGINE_EMULATOR(self), FALSE); @@ -63,12 +72,11 @@ buf = fu_archive_write(archive, FU_ARCHIVE_FORMAT_ZIP, FU_ARCHIVE_COMPRESSION_GZIP, error); if (buf == NULL) return FALSE; - if (!g_output_stream_write_all(stream, buf->data, buf->len, NULL, NULL, error)) { - fu_error_convert(error); + blob = g_byte_array_free_to_bytes(g_steal_pointer(&buf)); /* nocheck:blocked */ + if (!fu_output_stream_write_bytes(stream, blob, NULL, error)) return FALSE; - } if (!g_output_stream_flush(stream, NULL, error)) { - fu_error_convert(error); + fwupd_error_convert(error); return FALSE; } @@ -105,16 +113,21 @@ gboolean fu_engine_emulator_load_phase(FuEngineEmulator *self, + guint composite_cnt, FuEngineEmulatorPhase phase, guint write_cnt, GError **error) { GBytes *json_blob; - g_autofree gchar *fn = fu_engine_emulator_phase_to_filename(phase, write_cnt); + g_autofree gchar *fn = NULL; + fn = fu_engine_emulator_phase_to_filename(composite_cnt, phase, write_cnt); json_blob = g_hash_table_lookup(self->phase_blobs, fn); - if (json_blob == NULL) + if (json_blob == NULL) { + g_debug("emulator not loading %s, as not found", fn); return TRUE; + } + g_debug("emulator loading %s", fn); return fu_engine_emulator_load_json_blob(self, json_blob, error); } @@ -133,7 +146,7 @@ if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATION_TAG)) continue; json_builder_begin_object(json_builder); - fwupd_codec_to_json(FWUPD_CODEC(device), json_builder, FWUPD_CODEC_FLAG_NONE); + fu_device_add_json(device, json_builder, FWUPD_CODEC_FLAG_NONE); json_builder_end_object(json_builder); } json_builder_end_array(json_builder); @@ -150,13 +163,14 @@ gboolean fu_engine_emulator_save_phase(FuEngineEmulator *self, + guint composite_cnt, FuEngineEmulatorPhase phase, guint write_cnt, GError **error) { GBytes *blob_old; g_autofree gchar *blob_new_safe = NULL; - g_autofree gchar *fn = fu_engine_emulator_phase_to_filename(phase, write_cnt); + g_autofree gchar *fn = NULL; g_autoptr(GBytes) blob_new = NULL; g_autoptr(GPtrArray) devices = NULL; g_autoptr(GOutputStream) ostream = g_memory_output_stream_new_resizable(); @@ -175,6 +189,8 @@ json_generator_set_pretty(json_generator, TRUE); json_generator_set_root(json_generator, json_root); + fn = fu_engine_emulator_phase_to_filename(composite_cnt, phase, write_cnt); + g_debug("saving %s", fn); blob_old = g_hash_table_lookup(self->phase_blobs, fn); if (!json_generator_to_stream(json_generator, ostream, NULL, error)) return FALSE; @@ -195,7 +211,7 @@ return TRUE; } blob_new_safe = fu_strsafe_bytes(blob_new, 8000); - g_info("JSON %s for phase %s [%u]: %s...", + g_info("JSON %s for phase %s [%u]: %s…", blob_old == NULL ? "added" : "changed", fu_engine_emulator_phase_to_string(phase), write_cnt, @@ -209,6 +225,7 @@ static gboolean fu_engine_emulator_load_phases(FuEngineEmulator *self, FuArchive *archive, + guint composite_cnt, guint write_cnt, gboolean *got_json, GError **error) @@ -216,10 +233,11 @@ for (FuEngineEmulatorPhase phase = FU_ENGINE_EMULATOR_PHASE_SETUP; phase < FU_ENGINE_EMULATOR_PHASE_LAST; phase++) { - g_autofree gchar *fn = fu_engine_emulator_phase_to_filename(phase, write_cnt); + g_autofree gchar *fn = NULL; g_autoptr(GBytes) blob = NULL; /* not found */ + fn = fu_engine_emulator_phase_to_filename(composite_cnt, phase, write_cnt); blob = fu_archive_lookup_by_fn(archive, fn, NULL); if (blob == NULL || g_bytes_get_size(blob) == 0) continue; @@ -227,7 +245,7 @@ g_info("emulation for phase %s [%u]", fu_engine_emulator_phase_to_string(phase), write_cnt); - if (write_cnt == FU_ENGINE_EMULATOR_WRITE_COUNT_DEFAULT && + if (composite_cnt == 0 && write_cnt == FU_ENGINE_EMULATOR_WRITE_COUNT_DEFAULT && phase == FU_ENGINE_EMULATOR_PHASE_SETUP) { if (!fu_engine_emulator_load_json_blob(self, blob, error)) return FALSE; @@ -272,9 +290,18 @@ } /* load JSON files from archive */ - for (guint write_cnt = 0; write_cnt < FU_ENGINE_EMULATOR_WRITE_COUNT_MAX; write_cnt++) { - if (!fu_engine_emulator_load_phases(self, archive, write_cnt, &got_json, error)) - return FALSE; + for (guint composite_cnt = 0; composite_cnt < FU_ENGINE_EMULATOR_COMPOSITE_MAX; + composite_cnt++) { + for (guint write_cnt = 0; write_cnt < FU_ENGINE_EMULATOR_WRITE_COUNT_MAX; + write_cnt++) { + if (!fu_engine_emulator_load_phases(self, + archive, + composite_cnt, + write_cnt, + &got_json, + error)) + return FALSE; + } } if (!got_json) { g_set_error_literal(error, diff -Nru fwupd-2.0.8/src/fu-engine-emulator.h fwupd-2.0.20/src/fu-engine-emulator.h --- fwupd-2.0.8/src/fu-engine-emulator.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-engine-emulator.h 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,9 @@ /* the maximum times a device can use FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED */ #define FU_ENGINE_EMULATOR_WRITE_COUNT_MAX 5 +/* the maximum number of composite devices */ +#define FU_ENGINE_EMULATOR_COMPOSITE_MAX 10 + FuEngineEmulator * fu_engine_emulator_new(FuEngine *engine) G_GNUC_NON_NULL(1); gboolean @@ -28,11 +31,13 @@ G_GNUC_NON_NULL(1, 2); gboolean fu_engine_emulator_load_phase(FuEngineEmulator *self, + guint composite_cnt, FuEngineEmulatorPhase phase, guint write_cnt, GError **error) G_GNUC_NON_NULL(1); gboolean fu_engine_emulator_save_phase(FuEngineEmulator *self, + guint composite_cnt, FuEngineEmulatorPhase phase, guint write_cnt, GError **error) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/src/fu-engine-helper.c fwupd-2.0.20/src/fu-engine-helper.c --- fwupd-2.0.8/src/fu-engine-helper.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-engine-helper.c 2026-02-26 11:36:18.000000000 +0000 @@ -13,10 +13,68 @@ #include -#include "fwupd-device-private.h" - +#include "fu-cabinet.h" +#include "fu-context-private.h" #include "fu-engine-helper.h" #include "fu-engine.h" +#include "fu-usb-device-fw-ds20.h" +#include "fu-usb-device-ms-ds20.h" + +void +fu_engine_add_firmware_gtypes(FuEngine *self) +{ + FuContext *ctx = fu_engine_get_context(self); + fu_context_add_firmware_gtype(ctx, "raw", FU_TYPE_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "cab", FU_TYPE_CAB_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "cabinet", FU_TYPE_CABINET); + fu_context_add_firmware_gtype(ctx, "dfu", FU_TYPE_DFU_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "fdt", FU_TYPE_FDT_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "csv", FU_TYPE_CSV_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "fit", FU_TYPE_FIT_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "dfuse", FU_TYPE_DFUSE_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "ifwi-cpd", FU_TYPE_IFWI_CPD_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "ifwi-fpt", FU_TYPE_IFWI_FPT_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "oprom", FU_TYPE_OPROM_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "fmap", FU_TYPE_FMAP_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "ihex", FU_TYPE_IHEX_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "linear", FU_TYPE_LINEAR_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "srec", FU_TYPE_SREC_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "hid-descriptor", FU_TYPE_HID_DESCRIPTOR); + fu_context_add_firmware_gtype(ctx, "archive", FU_TYPE_ARCHIVE_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "smbios", FU_TYPE_SMBIOS); + fu_context_add_firmware_gtype(ctx, "acpi-table", FU_TYPE_ACPI_TABLE); + fu_context_add_firmware_gtype(ctx, "sbatlevel", FU_TYPE_SBATLEVEL_SECTION); + fu_context_add_firmware_gtype(ctx, "edid", FU_TYPE_EDID); + fu_context_add_firmware_gtype(ctx, "efi-file", FU_TYPE_EFI_FILE); + fu_context_add_firmware_gtype(ctx, "efi-signature", FU_TYPE_EFI_SIGNATURE); + fu_context_add_firmware_gtype(ctx, "efi-signature-list", FU_TYPE_EFI_SIGNATURE_LIST); + fu_context_add_firmware_gtype(ctx, + "efi-variable-authentication2", + FU_TYPE_EFI_VARIABLE_AUTHENTICATION2); + fu_context_add_firmware_gtype(ctx, "efi-load-option", FU_TYPE_EFI_LOAD_OPTION); + fu_context_add_firmware_gtype(ctx, "efi-device-path-list", FU_TYPE_EFI_DEVICE_PATH_LIST); + fu_context_add_firmware_gtype(ctx, "efi-filesystem", FU_TYPE_EFI_FILESYSTEM); + fu_context_add_firmware_gtype(ctx, "efi-section", FU_TYPE_EFI_SECTION); + fu_context_add_firmware_gtype(ctx, "efi-volume", FU_TYPE_EFI_VOLUME); + fu_context_add_firmware_gtype(ctx, "efi-ftw-store", FU_TYPE_EFI_FTW_STORE); + fu_context_add_firmware_gtype(ctx, + "efi-vss2-variable-store", + FU_TYPE_EFI_VSS2_VARIABLE_STORE); + fu_context_add_firmware_gtype(ctx, "json", FU_TYPE_JSON_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "ifd-bios", FU_TYPE_IFD_BIOS); + fu_context_add_firmware_gtype(ctx, "ifd-firmware", FU_TYPE_IFD_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "cfu-offer", FU_TYPE_CFU_OFFER); + fu_context_add_firmware_gtype(ctx, "cfu-payload", FU_TYPE_CFU_PAYLOAD); + fu_context_add_firmware_gtype(ctx, "uswid", FU_TYPE_USWID_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "coswid", FU_TYPE_COSWID_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "pefile", FU_TYPE_PEFILE_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "elf", FU_TYPE_ELF_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "x509-certificate", FU_TYPE_X509_CERTIFICATE); + fu_context_add_firmware_gtype(ctx, "intel-thunderbolt", FU_TYPE_INTEL_THUNDERBOLT_FIRMWARE); + fu_context_add_firmware_gtype(ctx, "intel-thunderbolt-nvm", FU_TYPE_INTEL_THUNDERBOLT_NVM); + fu_context_add_firmware_gtype(ctx, "usb-device-fw-ds20", FU_TYPE_USB_DEVICE_FW_DS20); + fu_context_add_firmware_gtype(ctx, "usb-device-ms-ds20", FU_TYPE_USB_DEVICE_MS_DS20); +} static FwupdRelease * fu_engine_get_release_with_tag(FuEngine *self, @@ -54,6 +112,7 @@ const gchar *host_bkc = fu_engine_get_host_bkc(self); guint upgrade_count = 0; guint sync_count = 0; + guint reboot_count = 0; g_autoptr(FuEngineRequest) request = NULL; g_autoptr(GPtrArray) devices = NULL; g_autoptr(GString) str = g_string_new(NULL); @@ -72,6 +131,19 @@ FwupdDevice *dev = g_ptr_array_index(devices, i); g_autoptr(GPtrArray) rels = NULL; + /* check if device needs reboot to complete update */ + if (fwupd_device_get_update_state(dev) == FWUPD_UPDATE_STATE_NEEDS_REBOOT) { + reboot_count++; + continue; + } + + /* skip devices with failed updates */ + if (fwupd_device_get_update_state(dev) == FWUPD_UPDATE_STATE_FAILED || + fwupd_device_get_update_state(dev) == + FWUPD_UPDATE_STATE_FAILED_TRANSIENT) { + continue; + } + /* get the releases for this device */ if (!fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)) continue; @@ -85,6 +157,15 @@ for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices, i); g_autoptr(FwupdRelease) rel = NULL; + + /* skip devices with failed updates */ + if (fwupd_device_get_update_state(dev) == + FWUPD_UPDATE_STATE_FAILED || + fwupd_device_get_update_state(dev) == + FWUPD_UPDATE_STATE_FAILED_TRANSIENT) { + continue; + } + if (!fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)) continue; rel = fu_engine_get_release_with_tag(self, @@ -101,21 +182,33 @@ } } - /* If running under systemd unit, use the directory as a base */ + /* if running under systemd unit, use the directory as a base */ if (g_getenv("RUNTIME_DIRECTORY") != NULL) { target = g_build_filename(g_getenv("RUNTIME_DIRECTORY"), MOTD_FILE, NULL); /* otherwise use the cache directory */ } else { - g_autofree gchar *directory = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); - target = g_build_filename(directory, MOTD_DIR, MOTD_FILE, NULL); + target = fu_path_build(FU_PATH_KIND_CACHEDIR_PKG, MOTD_DIR, MOTD_FILE, NULL); } /* create the directory and file, even if zero devices; we want an empty file then */ if (!fu_path_mkdir_parent(target, error)) return FALSE; - /* nag about syncing or updating, but never both */ - if (sync_count > 0) { + /* nag about reboot first, then syncing or updating, but never both */ + if (reboot_count > 0) { + g_string_append(str, "\n"); + g_string_append_printf(str, + /* TRANSLATORS: this is shown in the MOTD */ + ngettext("%u device has been updated and needs a reboot.", + "%u devices have been updated and need a reboot.", + reboot_count), + reboot_count); + g_string_append(str, "\n"); + g_string_append_printf(str, + /* TRANSLATORS: this is shown in the MOTD */ + _("Reboot to complete the update.")); + g_string_append(str, "\n\n"); + } else if (sync_count > 0) { g_string_append(str, "\n"); g_string_append_printf(str, /* TRANSLATORS: this is shown in the MOTD */ @@ -162,7 +255,6 @@ g_autoptr(JsonNode) root = NULL; g_autoptr(GPtrArray) devices = NULL; g_autofree gchar *data = NULL; - g_autofree gchar *directory = NULL; g_autofree gchar *target = NULL; if (fu_engine_config_get_show_device_private(fu_engine_get_config(self))) @@ -188,8 +280,7 @@ return FALSE; } - directory = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); - target = g_build_filename(directory, "devices.json", NULL); + target = fu_path_build(FU_PATH_KIND_CACHEDIR_PKG, "devices.json", NULL); return g_file_set_contents(target, data, (gssize)len, error); } @@ -203,7 +294,6 @@ static void fu_engine_integrity_measure_acpi(FuContext *ctx, GHashTable *self) { - g_autofree gchar *path = fu_path_from_kind(FU_PATH_KIND_ACPI_TABLES); const gchar *tables[] = { "SLIC", "MSDM", @@ -211,7 +301,7 @@ }; for (guint i = 0; i < G_N_ELEMENTS(tables); i++) { - g_autofree gchar *fn = g_build_filename(path, tables[i], NULL); + g_autofree gchar *fn = fu_path_build(FU_PATH_KIND_ACPI_TABLES, tables[i], NULL); g_autoptr(GBytes) blob = NULL; blob = fu_bytes_get_contents(fn, NULL); @@ -257,7 +347,7 @@ } } - /* Boot#### */ + /* UEFI Boot#### */ for (guint i = 0; i < 0xFF; i++) { g_autoptr(GBytes) blob = fu_efivars_get_boot_data(efivars, i, NULL); if (blob != NULL && g_bytes_get_size(blob) > 0) { diff -Nru fwupd-2.0.8/src/fu-engine-helper.h fwupd-2.0.20/src/fu-engine-helper.h --- fwupd-2.0.8/src/fu-engine-helper.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-engine-helper.h 2026-02-26 11:36:18.000000000 +0000 @@ -22,3 +22,6 @@ fu_engine_error_array_get_best(GPtrArray *errors); gchar * fu_engine_build_machine_id(const gchar *salt, GError **error); + +void +fu_engine_add_firmware_gtypes(FuEngine *self) G_GNUC_NON_NULL(1); diff -Nru fwupd-2.0.8/src/fu-engine-request.c fwupd-2.0.20/src/fu-engine-request.c --- fwupd-2.0.8/src/fu-engine-request.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-engine-request.c 2026-02-26 11:36:18.000000000 +0000 @@ -14,7 +14,7 @@ struct _FuEngineRequest { GObject parent_instance; - FuEngineRequestFlag flags; + FuEngineRequestFlags flags; FwupdFeatureFlags feature_flags; FwupdCodecFlags converter_flags; gchar *sender; @@ -34,7 +34,7 @@ { FuEngineRequest *self = FU_ENGINE_REQUEST(codec); if (self->flags != FU_ENGINE_REQUEST_FLAG_NONE) { - g_autofree gchar *flags = fu_engine_request_flag_to_string(self->flags); + g_autofree gchar *flags = fu_engine_request_flags_to_string(self->flags); fwupd_codec_string_append(str, idt, "Flags", flags); } fwupd_codec_string_append_hex(str, idt, "FeatureFlags", self->feature_flags); @@ -70,16 +70,16 @@ } void -fu_engine_request_add_flag(FuEngineRequest *self, FuEngineRequestFlag flag) +fu_engine_request_add_flag(FuEngineRequest *self, FuEngineRequestFlags flag) { g_return_if_fail(FU_IS_ENGINE_REQUEST(self)); self->flags |= flag; } gboolean -fu_engine_request_has_flag(FuEngineRequest *self, FuEngineRequestFlag flag) +fu_engine_request_has_flag(FuEngineRequest *self, FuEngineRequestFlags flag) { - g_return_val_if_fail(FU_IS_ENGINE_REQUEST(self), FU_ENGINE_REQUEST_FLAG_NONE); + g_return_val_if_fail(FU_IS_ENGINE_REQUEST(self), FALSE); return (self->flags & flag) > 0; } diff -Nru fwupd-2.0.8/src/fu-engine-request.h fwupd-2.0.20/src/fu-engine-request.h --- fwupd-2.0.8/src/fu-engine-request.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-engine-request.h 2026-02-26 11:36:18.000000000 +0000 @@ -18,9 +18,10 @@ const gchar * fu_engine_request_get_sender(FuEngineRequest *self) G_GNUC_NON_NULL(1); void -fu_engine_request_add_flag(FuEngineRequest *self, FuEngineRequestFlag flag) G_GNUC_NON_NULL(1); +fu_engine_request_add_flag(FuEngineRequest *self, FuEngineRequestFlags flag) G_GNUC_NON_NULL(1); gboolean -fu_engine_request_has_flag(FuEngineRequest *self, FuEngineRequestFlag flag) G_GNUC_NON_NULL(1); +fu_engine_request_has_flag(FuEngineRequest *self, + FuEngineRequestFlags flag) G_GNUC_WARN_UNUSED_RESULT G_GNUC_NON_NULL(1); FwupdFeatureFlags fu_engine_request_get_feature_flags(FuEngineRequest *self) G_GNUC_NON_NULL(1); void diff -Nru fwupd-2.0.8/src/fu-engine-requirements.c fwupd-2.0.20/src/fu-engine-requirements.c --- fwupd-2.0.8/src/fu-engine-requirements.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-engine-requirements.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,40 +8,42 @@ #include "config.h" +#include "fwupd-remote-private.h" + +#include "fu-device-private.h" #include "fu-engine-requirements.h" static gboolean -fu_engine_requirements_require_vercmp(XbNode *req, - const gchar *version, - FwupdVersionFormat fmt, - GError **error) +fu_engine_requirements_require_vercmp_part(const gchar *compare, + const gchar *version_req, + const gchar *version, + FwupdVersionFormat fmt, + GError **error) { gboolean ret = FALSE; gint rc = 0; - const gchar *tmp = xb_node_get_attr(req, "compare"); - const gchar *version_req = xb_node_get_attr(req, "version"); - if (g_strcmp0(tmp, "eq") == 0) { + if (g_strcmp0(compare, "eq") == 0) { rc = fu_version_compare(version, version_req, fmt); ret = rc == 0; - } else if (g_strcmp0(tmp, "ne") == 0) { + } else if (g_strcmp0(compare, "ne") == 0) { rc = fu_version_compare(version, version_req, fmt); ret = rc != 0; - } else if (g_strcmp0(tmp, "lt") == 0) { + } else if (g_strcmp0(compare, "lt") == 0) { rc = fu_version_compare(version, version_req, fmt); ret = rc < 0; - } else if (g_strcmp0(tmp, "gt") == 0) { + } else if (g_strcmp0(compare, "gt") == 0) { rc = fu_version_compare(version, version_req, fmt); ret = rc > 0; - } else if (g_strcmp0(tmp, "le") == 0) { + } else if (g_strcmp0(compare, "le") == 0) { rc = fu_version_compare(version, version_req, fmt); ret = rc <= 0; - } else if (g_strcmp0(tmp, "ge") == 0) { + } else if (g_strcmp0(compare, "ge") == 0) { rc = fu_version_compare(version, version_req, fmt); ret = rc >= 0; - } else if (g_strcmp0(tmp, "glob") == 0) { + } else if (g_strcmp0(compare, "glob") == 0) { ret = g_pattern_match_simple(version_req, version); - } else if (g_strcmp0(tmp, "regex") == 0) { + } else if (g_strcmp0(compare, "regex") == 0) { ret = g_regex_match_simple(version_req, version, 0, 0); } else { g_set_error(error, @@ -60,16 +62,98 @@ FWUPD_ERROR_INTERNAL, "failed predicate [%s %s %s]", version_req, - tmp, + compare, version); + return FALSE; + } + + /* success */ + return TRUE; +} + +typedef struct { + FuRelease *release; + FwupdInstallFlags install_flags; + gchar *fwupd_version; + gboolean has_hardware_req; + gboolean has_not_hardware_req; + gboolean has_id_requirement_glob; + gboolean has_client_id_requirement_glob; +} FuEngineRequirementsHelper; + +static void +fu_engine_requirements_helper_free(FuEngineRequirementsHelper *helper) +{ + g_object_unref(helper->release); + g_free(helper->fwupd_version); + g_free(helper); +} + +static gboolean +fu_engine_requirements_check_fwupd_version(FuEngineRequirementsHelper *helper, + const gchar *fwupd_version_req, + GError **error) +{ + if (fu_version_compare(helper->fwupd_version, + fwupd_version_req, + FWUPD_VERSION_FORMAT_UNKNOWN) < 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "needs %s >= %s", + FWUPD_DBUS_SERVICE, + fwupd_version_req); + return FALSE; + } + return TRUE; +} + +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuEngineRequirementsHelper, fu_engine_requirements_helper_free) + +static gboolean +fu_engine_requirements_require_vercmp(XbNode *req, + const gchar *version, + FwupdVersionFormat fmt, + FuEngineRequirementsHelper *helper, + GError **error) +{ + const gchar *compare = xb_node_get_attr(req, "compare"); + const gchar *version_req = xb_node_get_attr(req, "version"); + g_auto(GStrv) split = NULL; + + /* parse globbed version, e.g. `1.9.*=1.9.7|1.8.*=1.8.23|2.0.15`, or just `2.0.5` */ + split = g_strsplit(version_req, "|", 0); + for (guint i = 0; split[i] != NULL; i++) { + g_auto(GStrv) kv = g_strsplit(split[i], "=", 2); + if (g_strv_length(kv) > 1) { + helper->has_id_requirement_glob = TRUE; + if (!g_pattern_match_simple(kv[0], version)) { + g_debug("skipping vercmp %s as version %s", kv[0], version); + continue; + } + g_debug("checking vercmp %s as version %s", kv[1], version); + return fu_engine_requirements_require_vercmp_part(compare, + kv[1], + version, + fmt, + error); + } + return fu_engine_requirements_require_vercmp_part(compare, + kv[0], + version, + fmt, + error); } - return ret; + + /* success */ + return TRUE; } static gboolean fu_engine_requirements_check_not_child(FuEngine *self, XbNode *req, FuDevice *device, + FuEngineRequirementsHelper *helper, GError **error) { GPtrArray *children = fu_device_get_children(device); @@ -89,17 +173,20 @@ FuDevice *child = g_ptr_array_index(children, i); const gchar *version = fu_device_get_version(child); if (version == NULL) { + g_autofree gchar *id_display = fu_device_get_id_display(device); + g_autofree gchar *id_display_child = fu_device_get_id_display(child); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no version provided by %s, child of %s", - fu_device_get_name(child), - fu_device_get_name(device)); + id_display_child, + id_display); return FALSE; } if (fu_engine_requirements_require_vercmp(req, version, fu_device_get_version_format(child), + helper, NULL)) { g_set_error(error, FWUPD_ERROR, @@ -159,6 +246,7 @@ return TRUE; } +/* nocheck:name */ static gboolean _fu_device_has_guids_any(FuDevice *self, gchar **guids) { @@ -174,11 +262,10 @@ static gboolean fu_engine_requirements_check_firmware(FuEngine *self, XbNode *req, - FuDevice *device, - const gchar *fwupd_version, - FwupdInstallFlags flags, + FuEngineRequirementsHelper *helper, GError **error) { + FuDevice *device = fu_release_get_device(helper->release); const gchar *version; const gchar *depth_str; gint64 depth = G_MAXINT64; @@ -186,20 +273,26 @@ g_autoptr(GError) error_local = NULL; g_auto(GStrv) guids = NULL; + /* self tests */ + if (device == NULL) + return TRUE; + /* look at the parent device */ depth_str = xb_node_get_attr(req, "depth"); if (depth_str != NULL) { if (!fu_strtoll(depth_str, &depth, -1, 10, FU_INTEGER_BASE_AUTO, error)) return FALSE; for (gint64 i = 0; i < depth; i++) { - FuDevice *device_tmp = fu_device_get_parent(device_actual); + FuDevice *device_tmp = fu_device_get_parent_internal(device_actual); if (device_tmp == NULL) { + g_autofree gchar *id_display = + fu_device_get_id_display(device_actual); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "No parent device for %s " "(%" G_GINT64_FORMAT "/%" G_GINT64_FORMAT ")", - fu_device_get_name(device_actual), + id_display, i, depth); return FALSE; @@ -210,30 +303,8 @@ /* check fwupd version requirement */ if (depth < 0) { - if (fu_version_compare(fwupd_version, "1.9.7", FWUPD_VERSION_FORMAT_UNKNOWN) < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "requirement 'child firmware' also needs %s >= 1.9.7", - FWUPD_DBUS_SERVICE); - return FALSE; - } - } else if (depth == 0) { - if (fu_version_compare(fwupd_version, "1.6.1", FWUPD_VERSION_FORMAT_UNKNOWN) < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "requirement 'sibling firmware' also needs %s >= 1.6.1", - FWUPD_DBUS_SERVICE); - return FALSE; - } - } else if (depth == 1) { - if (fu_version_compare(fwupd_version, "1.3.4", FWUPD_VERSION_FORMAT_UNKNOWN) < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "requirement 'parent firmware' also needs %s >= 1.3.4", - FWUPD_DBUS_SERVICE); + if (!fu_engine_requirements_check_fwupd_version(helper, "1.9.7", error)) { + g_prefix_error_literal(error, "requirement child firmware: "); return FALSE; } } @@ -245,6 +316,7 @@ req, version, fu_device_get_version_format(device_actual), + helper, &error_local)) { if (g_strcmp0(xb_node_get_attr(req, "compare"), "ge") == 0) { g_set_error( @@ -254,13 +326,13 @@ "Not compatible with firmware version %s, requires >= %s", version, xb_node_get_attr(req, "version")); - } else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Not compatible with firmware version: %s", - error_local->message); + return FALSE; } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Not compatible with firmware version: %s", + error_local->message); return FALSE; } return TRUE; @@ -273,6 +345,7 @@ req, version, fu_device_get_version_format(device_actual), + helper, &error_local)) { if (g_strcmp0(xb_node_get_attr(req, "compare"), "ge") == 0) { g_set_error( @@ -282,14 +355,12 @@ "Not compatible with bootloader version %s, requires >= %s", version, xb_node_get_attr(req, "version")); - - } else { - g_debug("Bootloader is not compatible: %s", error_local->message); - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Bootloader is not compatible"); + return FALSE; } + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Bootloader is not compatible"); return FALSE; } return TRUE; @@ -297,14 +368,18 @@ /* vendor ID */ if (g_strcmp0(xb_node_get_text(req), "vendor-id") == 0) { - if (flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) + if (helper->install_flags & FWUPD_INSTALL_FLAG_IGNORE_VID_PID) return TRUE; return fu_engine_requirements_check_vendor_id(self, req, device_actual, error); } /* child version */ if (g_strcmp0(xb_node_get_text(req), "not-child") == 0) - return fu_engine_requirements_check_not_child(self, req, device_actual, error); + return fu_engine_requirements_check_not_child(self, + req, + device_actual, + helper, + error); /* another device, specified by GUID|GUID|GUID */ guids = g_strsplit(xb_node_get_text(req), "|", -1); @@ -365,18 +440,20 @@ /* look for a sibling */ } else if (depth == 0) { FuDevice *child = NULL; - FuDevice *parent = fu_device_get_parent(device_actual); + FuDevice *parent = fu_device_get_parent_internal(device_actual); GPtrArray *children; /* no parent, so look for GUIDs on this device */ if (parent == NULL) { if (!_fu_device_has_guids_any(device_actual, guids)) { + g_autofree gchar *id_display = + fu_device_get_id_display(device_actual); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "No GUID of %s on device %s", xb_node_get_text(req), - fu_device_get_name(device_actual)); + id_display); return FALSE; } return TRUE; @@ -401,34 +478,13 @@ /* verify the parent device has the GUID */ } else { if (!_fu_device_has_guids_any(device_actual, guids)) { + g_autofree gchar *id_display = fu_device_get_id_display(device_actual); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "No GUID of %s on parent device %s", xb_node_get_text(req), - fu_device_get_name(device_actual)); - return FALSE; - } - } - - /* check fwupd version requirement */ - if (depth == G_MAXINT64 && fu_device_get_version(device_actual) != NULL) { - if (fu_version_compare(fwupd_version, "1.1.0", FWUPD_VERSION_FORMAT_UNKNOWN) < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "requirement 'firmware with version' also needs %s >= 1.1.0", - FWUPD_DBUS_SERVICE); - return FALSE; - } - } - if (depth == G_MAXINT64 && fu_device_get_version(device_actual) == NULL) { - if (fu_version_compare(fwupd_version, "1.2.11", FWUPD_VERSION_FORMAT_UNKNOWN) < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "requirement 'firmware no version' also needs %s >= 1.2.11", - FWUPD_DBUS_SERVICE); + id_display); return FALSE; } } @@ -439,6 +495,7 @@ !fu_engine_requirements_require_vercmp(req, version, fu_device_get_version_format(device_actual), + helper, &error_local)) { if (g_strcmp0(xb_node_get_attr(req, "compare"), "ge") == 0) { g_set_error(error, @@ -448,14 +505,14 @@ fu_device_get_name(device_actual), version, xb_node_get_attr(req, "version")); - } else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Not compatible with %s: %s", - fu_device_get_name(device_actual), - error_local->message); + return FALSE; } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Not compatible with %s: %s", + fu_device_get_name(device_actual), + error_local->message); return FALSE; } @@ -464,7 +521,10 @@ } static gboolean -fu_engine_requirements_check_id(FuEngine *self, XbNode *req, GError **error) +fu_engine_requirements_check_id(FuEngine *self, + XbNode *req, + FuEngineRequirementsHelper *helper, + GError **error) { FuContext *ctx = fu_engine_get_context(self); g_autoptr(GError) error_local = NULL; @@ -490,6 +550,7 @@ if (!fu_engine_requirements_require_vercmp(req, version, FWUPD_VERSION_FORMAT_UNKNOWN, + helper, &error_local)) { if (g_strcmp0(xb_node_get_attr(req, "compare"), "ge") == 0) { g_set_error(error, @@ -499,14 +560,14 @@ xb_node_get_text(req), version, xb_node_get_attr(req, "version")); - } else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Not compatible with %s version: %s", - xb_node_get_text(req), - error_local->message); + return FALSE; } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Not compatible with %s version: %s", + xb_node_get_text(req), + error_local->message); return FALSE; } @@ -521,13 +582,17 @@ static gboolean fu_engine_requirements_check_hardware(FuEngine *self, XbNode *req, - const gchar *fwupd_version, + FuEngineRequirementsHelper *helper, GError **error) { FuContext *ctx = fu_engine_get_context(self); - const gchar *fwupd_required = "1.0.1"; + FuDevice *device = fu_release_get_device(helper->release); g_auto(GStrv) hwid_split = NULL; + /* skip for tests */ + if (device == NULL || fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) + return TRUE; + /* sanity check */ if (xb_node_get_text(req) == NULL) { g_set_error_literal(error, @@ -537,19 +602,6 @@ return FALSE; } - /* check fwupd version requirement */ - if (g_strstr_len(xb_node_get_text(req), -1, "|") != NULL) - fwupd_required = "1.0.8"; - if (fu_version_compare(fwupd_version, fwupd_required, FWUPD_VERSION_FORMAT_UNKNOWN) < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "requirement 'hardware' also needs %s >= %s", - FWUPD_DBUS_SERVICE, - fwupd_required); - return FALSE; - } - /* split and treat as OR */ hwid_split = g_strsplit(xb_node_get_text(req), "|", -1); for (guint i = 0; hwid_split[i] != NULL; i++) { @@ -571,19 +623,15 @@ static gboolean fu_engine_requirements_check_not_hardware(FuEngine *self, XbNode *req, - const gchar *fwupd_version, + FuEngineRequirementsHelper *helper, GError **error) { FuContext *ctx = fu_engine_get_context(self); g_auto(GStrv) hwid_split = NULL; /* check fwupd version requirement */ - if (fu_version_compare(fwupd_version, "1.9.10", FWUPD_VERSION_FORMAT_UNKNOWN) < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "requirement 'not_hardware' also needs %s >= 1.9.10", - FWUPD_DBUS_SERVICE); + if (!fu_engine_requirements_check_fwupd_version(helper, "1.9.10", error)) { + g_prefix_error_literal(error, "requirement not_hardware: "); return FALSE; } @@ -614,13 +662,97 @@ } static gboolean +fu_engine_requirements_check_phased_update(FuEngine *self, + XbNode *req, + FuEngineRequirementsHelper *helper, + GError **error) +{ + FwupdRemote *remote; + const gchar *archive_filename; + const gchar *host_machine_id; + gint32 seed; + guint64 phased_update = 0; + + /* i'm feeling lucky */ + if (helper->install_flags & FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS) { + g_info("ignoring phased_update requirement"); + return TRUE; + } + + /* check fwupd version requirement */ + if (!fu_engine_requirements_check_fwupd_version(helper, "2.0.17", error)) { + g_prefix_error_literal(error, "requirement phased_update: "); + return FALSE; + } + + /* sanity check */ + remote = fu_release_get_remote(helper->release); + if (remote == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no assigned remote for firmware release"); + return FALSE; + } + if (fwupd_remote_has_flag(remote, FWUPD_REMOTE_FLAG_NO_PHASED_UPDATES)) { + g_info("ignoring phased update requirement due to remote policy"); + return TRUE; + } + if (xb_node_get_text(req) == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no phased_update value supplied"); + return FALSE; + } + if (!fu_strtoull(xb_node_get_text(req), + &phased_update, + 2, + 1024, + FU_INTEGER_BASE_AUTO, + error)) { + g_prefix_error_literal(error, "expected integer for phased_update: "); + return FALSE; + } + + /* use multiple seeds, but return uniformly distributed random numbers */ + archive_filename = fu_release_get_filename(helper->release); + host_machine_id = fu_engine_get_host_machine_id(self); + if (host_machine_id == NULL || archive_filename == NULL) { + seed = (gint32)fwupd_remote_get_mtime(remote); + } else { + guint32 seed_buf[4] = {0}; + guint seed_bufsz = 0; + g_autoptr(GRand) rand = NULL; + + seed_buf[seed_bufsz++] = g_str_hash(host_machine_id); + seed_buf[seed_bufsz++] = g_str_hash(archive_filename); + seed_buf[seed_bufsz++] = (guint32)fwupd_remote_get_mtime(remote); + rand = g_rand_new_with_seed_array(seed_buf, seed_bufsz); + seed = g_rand_int_range(rand, 0, G_MAXINT); + } + + /* does seed divide perfectly into the phased update factor */ + if (seed % phased_update != 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "remote mtime meant that deployment was delayed"); + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean fu_engine_requirements_check_client(FuEngine *self, - FuEngineRequest *request, XbNode *req, - const gchar *fwupd_version, + FuEngineRequirementsHelper *helper, GError **error) { - FwupdFeatureFlags flags; + FuEngineRequest *request = fu_release_get_request(helper->release); + FwupdFeatureFlags feature_flags; g_auto(GStrv) feature_split = NULL; /* sanity check */ @@ -632,41 +764,43 @@ return FALSE; } - /* check fwupd version requirement */ - if (fu_version_compare(fwupd_version, "1.4.5", FWUPD_VERSION_FORMAT_UNKNOWN) < 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "requirement 'client' also needs %s >= 1.4.5", - FWUPD_DBUS_SERVICE); - return FALSE; - } - /* split and treat as AND */ feature_split = g_strsplit(xb_node_get_text(req), "|", -1); - flags = fu_engine_request_get_feature_flags(request); + feature_flags = fu_engine_request_get_feature_flags(request); for (guint i = 0; feature_split[i] != NULL; i++) { - FwupdFeatureFlags flag = fwupd_feature_flag_from_string(feature_split[i]); + FuEngineCapabilityFlags capability_flag; + FwupdFeatureFlags feature_flag; - /* not recognized */ - if (flag == FWUPD_FEATURE_FLAG_UNKNOWN) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "client requirement %s unknown", - feature_split[i]); - return FALSE; + /* client feature */ + feature_flag = fwupd_feature_flag_from_string(feature_split[i]); + if (feature_flag != FWUPD_FEATURE_FLAG_UNKNOWN) { + if ((feature_flags & feature_flag) == 0) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "client feature requirement %s not supported", + feature_split[i]); + return FALSE; + } + continue; } - /* not supported */ - if ((flags & flag) == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "client requirement %s not supported", - feature_split[i]); - return FALSE; + /* assumed by the daemon version, see https://github.com/fwupd/fwupd/pull/8949 */ + capability_flag = fu_engine_capability_flags_from_string(feature_split[i]); + if (capability_flag != FU_ENGINE_CAPABILITY_FLAG_UNKNOWN) { + if (capability_flag == FU_ENGINE_CAPABILITY_FLAG_ID_REQUIREMENT_GLOB) { + helper->has_client_id_requirement_glob = TRUE; + continue; + } } + + /* not recognized */ + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "client requirement %s unknown", + feature_split[i]); + return FALSE; } /* success */ @@ -675,54 +809,41 @@ static gboolean fu_engine_requirements_check_hard(FuEngine *self, - FuRelease *release, XbNode *req, - const gchar *fwupd_version, - FwupdInstallFlags flags, + FuEngineRequirementsHelper *helper, GError **error) { FuContext *ctx = fu_engine_get_context(self); - FuDevice *device = fu_release_get_device(release); - FuEngineRequest *request = fu_release_get_request(release); /* ensure component requirement */ if (g_strcmp0(xb_node_get_element(req), "id") == 0) - return fu_engine_requirements_check_id(self, req, error); + return fu_engine_requirements_check_id(self, req, helper, error); /* ensure firmware requirement */ - if (g_strcmp0(xb_node_get_element(req), "firmware") == 0) { - if (device == NULL) - return TRUE; - return fu_engine_requirements_check_firmware(self, - req, - device, - fwupd_version, - flags, - error); - } + if (g_strcmp0(xb_node_get_element(req), "firmware") == 0) + return fu_engine_requirements_check_firmware(self, req, helper, error); /* ensure hardware requirement */ if (g_strcmp0(xb_node_get_element(req), "hardware") == 0) { - if (device == NULL || fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) - return TRUE; + helper->has_hardware_req = TRUE; if (!fu_context_has_flag(ctx, FU_CONTEXT_FLAG_LOADED_HWINFO)) return TRUE; - return fu_engine_requirements_check_hardware(self, req, fwupd_version, error); + return fu_engine_requirements_check_hardware(self, req, helper, error); } if (g_strcmp0(xb_node_get_element(req), "not_hardware") == 0) { + helper->has_not_hardware_req = TRUE; if (!fu_context_has_flag(ctx, FU_CONTEXT_FLAG_LOADED_HWINFO)) return TRUE; - return fu_engine_requirements_check_not_hardware(self, req, fwupd_version, error); + return fu_engine_requirements_check_not_hardware(self, req, helper, error); } + /* support slowing down deployments */ + if (g_strcmp0(xb_node_get_element(req), "phased_update") == 0) + return fu_engine_requirements_check_phased_update(self, req, helper, error); + /* ensure client requirement */ - if (g_strcmp0(xb_node_get_element(req), "client") == 0) { - return fu_engine_requirements_check_client(self, - request, - req, - fwupd_version, - error); - } + if (g_strcmp0(xb_node_get_element(req), "client") == 0) + return fu_engine_requirements_check_client(self, req, helper, error); /* not supported */ g_set_error(error, @@ -735,20 +856,13 @@ static gboolean fu_engine_requirements_check_soft(FuEngine *self, - FuRelease *release, XbNode *req, - const gchar *fwupd_version, - FwupdInstallFlags flags, + FuEngineRequirementsHelper *helper, GError **error) { g_autoptr(GError) error_local = NULL; - if (!fu_engine_requirements_check_hard(self, - release, - req, - fwupd_version, - flags, - &error_local)) { - if (flags & FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS) { + if (!fu_engine_requirements_check_hard(self, req, helper, &error_local)) { + if (helper->install_flags & FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS) { g_info("ignoring soft-requirement: %s", error_local->message); return TRUE; } @@ -772,12 +886,12 @@ static gchar * fu_engine_requirements_get_newest_fwupd_version(FuEngine *self, FuRelease *release, GError **error) { - const gchar *newest_version = "1.0.0"; GPtrArray *reqs = fu_release_get_hard_reqs(release); + g_autoptr(GString) newest_version = g_string_new("1.0.0"); /* trivial case */ if (reqs == NULL) - return g_strdup(newest_version); + return g_string_free(g_steal_pointer(&newest_version), FALSE); /* find the newest fwupd requirement */ for (guint i = 0; i < reqs->len; i++) { @@ -785,6 +899,8 @@ if (g_strcmp0(xb_node_get_text(req), FWUPD_DBUS_SERVICE) == 0 && g_strcmp0(xb_node_get_attr(req, "compare"), "ge") == 0) { const gchar *version = xb_node_get_attr(req, "version"); + g_auto(GStrv) split = NULL; + if (version == NULL) { g_set_error(error, FWUPD_ERROR, @@ -793,16 +909,24 @@ xb_node_get_text(req)); return NULL; } - /* is this unique, or newer than what we have */ - if (newest_version == NULL || - fu_version_compare(version, - newest_version, - FWUPD_VERSION_FORMAT_UNKNOWN) > 0) { - newest_version = version; + + /* only care about the fallback version if using globs */ + split = g_strsplit(version, "|", -1); + for (guint j = 0; split[j] != NULL; j++) { + if (g_strstr_len(split[j], -1, "=") != NULL) + continue; + /* is this newer than what we have */ + if (fu_version_compare(split[j], + newest_version->str, + FWUPD_VERSION_FORMAT_UNKNOWN) > 0) { + g_string_assign(newest_version, split[j]); + } } } } - return g_strdup(newest_version); + + /* success */ + return g_string_free(g_steal_pointer(&newest_version), FALSE); } gboolean @@ -813,25 +937,32 @@ { FuDevice *device = fu_release_get_device(release); GPtrArray *reqs; - gboolean has_hardware_req = FALSE; - gboolean has_not_hardware_req = FALSE; gboolean has_specific_requirement = FALSE; - g_autofree gchar *fwupd_version = NULL; + g_autoptr(FuEngineRequirementsHelper) helper = g_new0(FuEngineRequirementsHelper, 1); + + g_return_val_if_fail(FU_IS_ENGINE(self), FALSE); + g_return_val_if_fail(FU_IS_RELEASE(release), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* create a small helper with common data */ + helper->release = g_object_ref(release); + helper->install_flags = flags; /* get the newest fwupd version requirement */ - fwupd_version = fu_engine_requirements_get_newest_fwupd_version(self, release, error); - if (fwupd_version == NULL) + helper->fwupd_version = + fu_engine_requirements_get_newest_fwupd_version(self, release, error); + if (helper->fwupd_version == NULL) return FALSE; /* sanity check */ if (device != NULL && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE) && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)) { + g_autofree gchar *id_display = fu_device_get_id_display(device); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "%s [%s] is not updatable", - fu_device_get_name(device), - fu_device_get_id(device)); + "%s is not updatable", + id_display); return FALSE; } @@ -853,24 +984,15 @@ if (reqs != NULL) { for (guint i = 0; i < reqs->len; i++) { XbNode *req = g_ptr_array_index(reqs, i); - if (!fu_engine_requirements_check_hard(self, - release, - req, - fwupd_version, - flags, - error)) + if (!fu_engine_requirements_check_hard(self, req, helper, error)) return FALSE; if (fu_engine_requirements_is_specific_req(req)) has_specific_requirement = TRUE; - if (g_strcmp0(xb_node_get_element(req), "hardware") == 0) - has_hardware_req = TRUE; - else if (g_strcmp0(xb_node_get_element(req), "not_hardware") == 0) - has_not_hardware_req = TRUE; } } /* it does not make sense to allowlist and denylist at the same time */ - if (has_hardware_req && has_not_hardware_req) { + if (helper->has_hardware_req && helper->has_not_hardware_req) { g_set_error_literal( error, FWUPD_ERROR, @@ -879,6 +1001,16 @@ return FALSE; } + /* if we're using ID requirements with globs we have to have a client requirement */ + if (helper->has_id_requirement_glob && !helper->has_client_id_requirement_glob) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "using version requirements with globs also needs " + "id-requirement-glob"); + return FALSE; + } + /* if a device uses a generic ID (i.e. not matching the OEM) then check to make sure the * firmware is specific enough, e.g. by using a CHID or depth requirement */ if (device != NULL && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED) && @@ -909,12 +1041,7 @@ if (reqs != NULL) { for (guint i = 0; i < reqs->len; i++) { XbNode *req = g_ptr_array_index(reqs, i); - if (!fu_engine_requirements_check_soft(self, - release, - req, - fwupd_version, - flags, - error)) + if (!fu_engine_requirements_check_soft(self, req, helper, error)) return FALSE; } } diff -Nru fwupd-2.0.8/src/fu-engine.c fwupd-2.0.20/src/fu-engine.c --- fwupd-2.0.8/src/fu-engine.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-engine.c 2026-02-26 11:36:18.000000000 +0000 @@ -16,7 +16,6 @@ #ifdef HAVE_PASSIM #include #endif -#include #include #ifdef HAVE_UTSNAME_H #include @@ -24,7 +23,6 @@ #ifdef HAVE_AUXV_H #include #endif -#include #ifdef _WIN32 #include @@ -34,20 +32,15 @@ #include -#include "fwupd-common-private.h" -#include "fwupd-device-private.h" #include "fwupd-enums-private.h" #include "fwupd-remote-private.h" #include "fwupd-resources.h" #include "fwupd-security-attr-private.h" -#include "fu-backend-private.h" #include "fu-bios-setting.h" #include "fu-bios-settings-private.h" #include "fu-config-private.h" #include "fu-context-private.h" -#include "fu-coswid-firmware.h" -#include "fu-debug.h" #include "fu-device-list.h" #include "fu-device-private.h" #include "fu-device-progress.h" @@ -69,9 +62,6 @@ #include "fu-udev-device-private.h" #include "fu-uefi-backend.h" #include "fu-usb-backend.h" -#include "fu-usb-device-fw-ds20.h" -#include "fu-usb-device-ms-ds20.h" -#include "fu-usb-device-private.h" #ifdef HAVE_GIO_UNIX #include "fu-unix-seekable-input-stream.h" @@ -105,13 +95,14 @@ fu_engine_ensure_security_attrs(FuEngine *self); static void fu_engine_md_refresh_device(FuEngine *self, FuDevice *device); +static void +fu_engine_metadata_changed(FuEngine *self); struct _FuEngine { GObject parent_instance; FuEngineConfig *config; FuRemoteList *remote_list; FuDeviceList *device_list; - gboolean only_trusted; gboolean write_history; gboolean host_emulation; guint percentage; @@ -122,7 +113,7 @@ XbQuery *query_container_checksum1; /* container checksum -> release */ XbQuery *query_container_checksum2; /* artifact checksum -> release */ XbQuery *query_tag_by_guid_version; - guint coldplug_id; + GPtrArray *search_queries; /* (element-type XbQuery) */ FuPluginList *plugin_list; GPtrArray *plugin_filter; FuContext *ctx; @@ -132,7 +123,6 @@ GHashTable *device_changed_allowlist; /* (element-type str int) */ gchar *host_machine_id; JcatContext *jcat_context; - gboolean loaded; FuSecurityAttrs *host_security_attrs; GPtrArray *local_monitors; /* (element-type GFileMonitor) */ GMainLoop *acquiesce_loop; @@ -141,6 +131,8 @@ guint update_motd_id; FuEngineEmulatorPhase emulator_phase; guint emulator_write_cnt; + guint emulator_composite_cnt; + FuEngineLoadFlags load_flags; #ifdef HAVE_PASSIM PassimClient *passim_client; #endif @@ -160,12 +152,23 @@ static guint signals[SIGNAL_LAST] = {0}; +enum { + QUARK_AUTO_PARENT_CHILDREN, + QUARK_HOST_FIRMWARE, + QUARK_HOST_FIRMWARE_CHILD, + QUARK_HOST_CPU, + QUARK_HOST_CPU_CHILD, + QUARK_LAST, +}; + +static guint quarks[QUARK_LAST] = {0}; + G_DEFINE_TYPE(FuEngine, fu_engine, G_TYPE_OBJECT) gboolean fu_engine_get_loaded(FuEngine *self) { - return self->loaded; + return (self->load_flags & FU_ENGINE_LOAD_FLAG_READY) > 0; } static gboolean @@ -202,7 +205,7 @@ g_autoptr(GError) error = NULL; /* do nothing */ - if (!self->loaded) + if ((self->load_flags & FU_ENGINE_LOAD_FLAG_READY) == 0) return; g_signal_emit(self, signals[SIGNAL_CHANGED], 0); @@ -221,7 +224,7 @@ fu_engine_emit_device_changed_safe(FuEngine *self, FuDevice *device) { /* do nothing */ - if (!self->loaded) + if ((self->load_flags & FU_ENGINE_LOAD_FLAG_READY) == 0) return; /* invalidate host security attributes */ @@ -275,6 +278,11 @@ FuDevice *device, FuDevice *device_tmp) { + /* not important */ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE) || + !fu_device_has_flag(device_tmp, FWUPD_DEVICE_FLAG_UPDATABLE)) + return; + /* not a match */ if (g_strcmp0(fu_device_get_id(device_tmp), fu_device_get_equivalent_id(device)) != 0 && g_strcmp0(fu_device_get_equivalent_id(device_tmp), fu_device_get_id(device)) != 0) @@ -447,6 +455,19 @@ } static void +fu_engine_ensure_device_maybe_remove_affects_fde(FuEngine *self, FuDevice *device) +{ + if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_AFFECTS_FDE)) + return; + if (!fu_context_has_flag(self->ctx, FU_CONTEXT_FLAG_FDE_BITLOCKER) && + !fu_context_has_flag(self->ctx, FU_CONTEXT_FLAG_FDE_SNAPD)) { + g_debug("removing affects-fde from %s as no FDE detected", + fu_device_get_id(device)); + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_AFFECTS_FDE); + } +} + +static void fu_engine_ensure_device_system_inhibit(FuEngine *self, FuDevice *device) { if (fu_context_has_flag(self->ctx, FU_CONTEXT_FLAG_SYSTEM_INHIBIT) && @@ -510,6 +531,7 @@ fu_engine_ensure_device_lid_inhibit(self, device); fu_engine_ensure_device_display_required_inhibit(self, device); fu_engine_ensure_device_system_inhibit(self, device); + fu_engine_ensure_device_maybe_remove_affects_fde(self, device); fu_engine_acquiesce_reset(self); g_signal_emit(self, signals[SIGNAL_DEVICE_ADDED], 0, device); } @@ -604,14 +626,16 @@ static void fu_engine_release_remote_id_changed_cb(FuRelease *release, GParamSpec *pspec, FuEngine *self) { - FwupdRemote *remote; - const gchar *remote_id = fwupd_release_get_remote_id(FWUPD_RELEASE(release)); + const gchar *remote_id; + g_autoptr(FwupdRemote) remote = NULL; + g_autoptr(GError) error_local = NULL; + remote_id = fwupd_release_get_remote_id(FWUPD_RELEASE(release)); if (remote_id == NULL) return; - remote = fu_remote_list_get_by_id(self->remote_list, remote_id); + remote = fu_remote_list_get_by_id(self->remote_list, remote_id, &error_local); if (remote == NULL) { - g_warning("no remote found for %s", remote_id); + g_warning("%s", error_local->message); return; } fu_release_set_remote(release, remote); @@ -740,30 +764,30 @@ return TRUE; } -/* finds the release for the first firmware in the silo that matches this +/* finds the releases for all firmware in the silo that matches this * container or artifact checksum */ -static XbNode * -fu_engine_get_release_for_checksum(FuEngine *self, const gchar *csum) +static GPtrArray * +fu_engine_get_releases_for_container_checksum(FuEngine *self, const gchar *csum) { g_auto(XbQueryContext) context = XB_QUERY_CONTEXT_INIT(); xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, csum, NULL); if (self->query_container_checksum1 != NULL) { - g_autoptr(XbNode) rel = - xb_silo_query_first_with_context(self->silo, - self->query_container_checksum1, - &context, - NULL); - if (rel != NULL) - return g_steal_pointer(&rel); + g_autoptr(GPtrArray) rels = + xb_silo_query_with_context(self->silo, + self->query_container_checksum1, + &context, + NULL); + if (rels != NULL) + return g_steal_pointer(&rels); } if (self->query_container_checksum2 != NULL) { - g_autoptr(XbNode) rel = - xb_silo_query_first_with_context(self->silo, - self->query_container_checksum2, - &context, - NULL); - if (rel != NULL) - return g_steal_pointer(&rel); + g_autoptr(GPtrArray) rels = + xb_silo_query_with_context(self->silo, + self->query_container_checksum2, + &context, + NULL); + if (rels != NULL) + return g_steal_pointer(&rels); } /* failed */ @@ -781,11 +805,16 @@ for (guint i = 0; checksum_types[i] != 0; i++) { g_autofree gchar *csum = NULL; - g_autoptr(XbNode) rel = NULL; + g_autoptr(GPtrArray) rels = NULL; + csum = fu_input_stream_compute_checksum(stream, checksum_types[i], NULL); - if (csum != NULL) - rel = fu_engine_get_release_for_checksum(self, csum); - if (rel != NULL) { + if (csum == NULL) + continue; + rels = fu_engine_get_releases_for_container_checksum(self, csum); + if (rels == NULL) + continue; + for (guint j = 0; j < rels->len; j++) { + XbNode *rel = g_ptr_array_index(rels, j); const gchar *remote_id = xb_node_query_text(rel, "../../../custom/value[@key='fwupd::RemoteId']", @@ -863,17 +892,29 @@ /* check keys are valid */ if (g_strcmp0(section, "fwupd") == 0) { const gchar *keys[] = { - "ArchiveSizeMax", "ApprovedFirmware", - "BlockedFirmware", "DisabledDevices", - "DisabledPlugins", "EnumerateAllDevices", - "EspLocation", "HostBkc", - "IdleTimeout", "IgnorePower", - "OnlyTrusted", "P2pPolicy", - "ReleaseDedupe", "ReleasePriority", - "ShowDevicePrivate", "TestDevices", - "TrustedReports", "TrustedUids", - "UpdateMotd", "UriSchemes", - "VerboseDomains", NULL, + "ArchiveSizeMax", + "ApprovedFirmware", + "BlockedFirmware", + "DisabledDevices", + "DisabledPlugins", + "EnumerateAllDevices", + "EspLocation", + "HostBkc", + "IdleTimeout", + "IgnorePower", + "OnlyTrusted", + "P2pPolicy", + "ReleaseDedupe", + "ReleasePriority", + "RequireImmutableEnumeration", + "ShowDevicePrivate", + "TestDevices", + "TrustedReports", + "TrustedUids", + "UpdateMotd", + "UriSchemes", + "VerboseDomains", + NULL, }; if (!g_strv_contains(keys, key)) { g_set_error(error, @@ -947,6 +988,34 @@ return fu_remote_list_set_key_value(self->remote_list, remote_id, key, value, error); } +/** + * fu_engine_clean_remote: + * @self: a #FuEngine + * @remote_id: a remote ID + * @error: (nullable): optional return location for an error + * + * Cleans a remote, deleting downloaded metadata files. + * + * Returns: %TRUE for success + **/ +gboolean +fu_engine_clean_remote(FuEngine *self, const gchar *remote_id, GError **error) +{ + g_autoptr(FwupdRemote) remote = NULL; + + g_return_val_if_fail(FU_IS_ENGINE(self), FALSE); + g_return_val_if_fail(remote_id != NULL, FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + remote = fu_remote_list_get_by_id(self->remote_list, remote_id, error); + if (remote == NULL) + return FALSE; + if (!fu_remote_clean(remote, error)) + return FALSE; + fu_engine_metadata_changed(self); + return TRUE; +} + static gboolean fu_engine_modify_single_bios_setting(FuEngine *self, const gchar *key, @@ -1471,7 +1540,7 @@ XB_QUERY_FLAG_OPTIMIZE | XB_QUERY_FLAG_USE_INDEXES, error); if (query == NULL) { - fu_error_convert(error); + fwupd_error_convert(error); return NULL; } @@ -1613,6 +1682,7 @@ g_autoptr(GString) checksums_metadata = g_string_new(NULL); g_autoptr(GPtrArray) csums = NULL; g_autoptr(GString) xpath = g_string_new(NULL); + g_autofree gchar *id_display = fu_device_get_id_display(device); /* get all checksums to display a useful error */ xb_string_append_union(xpath, "checksum[@target='device']"); @@ -1637,7 +1707,7 @@ FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "For %s %s expected %s, got %s", - fu_device_get_name(device), + id_display, fu_device_get_version(device), checksums_metadata->str, checksums_device); @@ -1656,8 +1726,8 @@ g_debug("checking trust of %s", str); if (fu_engine_config_get_only_trusted(self->config) && !fu_release_has_flag(release, FWUPD_RELEASE_FLAG_TRUSTED_PAYLOAD)) { - g_autofree gchar *sysconfdir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); - g_autofree gchar *fn = g_build_filename(sysconfdir, "fwupd.conf", NULL); + g_autofree gchar *fn = + fu_path_build(FU_PATH_KIND_SYSCONFDIR_PKG, "fwupd.conf", NULL); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, @@ -1708,7 +1778,7 @@ g_autoptr(GPtrArray) devices = fu_device_list_get_active(self->device_list); for (guint i = 0; i < devices->len; i++) { FuDevice *device = g_ptr_array_index(devices, i); - if (fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_HOST_CPU)) + if (fu_device_has_private_flag_quark(device, quarks[QUARK_HOST_CPU])) return g_object_ref(device); } return NULL; @@ -1868,6 +1938,27 @@ return TRUE; } +static gboolean +fu_engine_get_report_metadata_selinux(GHashTable *hash, GError **error) +{ + g_autofree gchar *buf = NULL; + g_autofree gchar *filename = + fu_path_build(FU_PATH_KIND_SYSFSDIR, "fs", "selinux", "enforce", NULL); + + if (!g_file_test(filename, G_FILE_TEST_EXISTS)) { + g_debug("no %s, skipping", filename); + return TRUE; + } + if (!g_file_get_contents(filename, &buf, NULL, error)) + return FALSE; + if (g_strcmp0(buf, "1") == 0) { + g_hash_table_insert(hash, g_strdup("SELinux"), g_strdup("enforcing")); + return TRUE; + } + g_hash_table_insert(hash, g_strdup("SELinux"), g_strdup("permissive")); + return TRUE; +} + static void fu_engine_add_report_metadata_bool(GHashTable *hash, const gchar *key, gboolean value) { @@ -1902,12 +1993,14 @@ GHashTable * fu_engine_get_report_metadata(FuEngine *self, GError **error) { + FuEfivars *efivars = fu_context_get_efivars(self->ctx); GHashTable *compile_versions = fu_context_get_compile_versions(self->ctx); GHashTable *runtime_versions = fu_context_get_runtime_versions(self->ctx); const gchar *tmp; gchar *btime; + guint64 nvram_total; #ifdef HAVE_UTSNAME_H - struct utsname name_tmp; + struct utsname name_tmp = {0}; #endif g_autoptr(GHashTable) hash = NULL; g_autoptr(GList) compile_keys = g_hash_table_get_keys(compile_versions); @@ -1936,6 +2029,22 @@ return NULL; if (!fu_engine_get_report_metadata_kernel_cmdline(hash, error)) return NULL; + if (!fu_engine_get_report_metadata_selinux(hash, error)) + return NULL; + + /* useful for almost all plugins */ + nvram_total = fu_efivars_space_used(efivars, NULL); + if (nvram_total != G_MAXUINT64) { + g_hash_table_insert(hash, + g_strdup("EfivarsNvramUsed"), + g_strdup_printf("%" G_GUINT64_FORMAT, nvram_total)); + } + nvram_total = fu_efivars_space_free(efivars, NULL); + if (nvram_total != G_MAXUINT64) { + g_hash_table_insert(hash, + g_strdup("EfivarsNvramFree"), + g_strdup_printf("%" G_GUINT64_FORMAT, nvram_total)); + } /* these affect the report credibility */ #ifdef SUPPORTED_BUILD @@ -1987,19 +2096,34 @@ /* kernel version is often important for debugging failures */ #ifdef HAVE_UTSNAME_H - memset(&name_tmp, 0, sizeof(struct utsname)); if (uname(&name_tmp) >= 0) { - g_hash_table_insert(hash, g_strdup("CpuArchitecture"), g_strdup(name_tmp.machine)); - g_hash_table_insert(hash, g_strdup("KernelName"), g_strdup(name_tmp.sysname)); - g_hash_table_insert(hash, g_strdup("KernelVersion"), g_strdup(name_tmp.release)); + if (name_tmp.machine[0] != '\0') { + g_hash_table_insert(hash, + g_strdup("CpuArchitecture"), + g_strdup(name_tmp.machine)); + } + if (name_tmp.sysname[0] != '\0') { + g_hash_table_insert(hash, + g_strdup("KernelName"), + g_strdup(name_tmp.sysname)); + } + if (name_tmp.release[0] != '\0') { + g_hash_table_insert(hash, + g_strdup("KernelVersion"), + g_strdup(name_tmp.release)); + } } #endif -#ifdef HAVE_AUXV_H +#if defined(HAVE_AUXV_H) && !defined(__FreeBSD__) /* this is the architecture of the userspace, e.g. i686 would be returned for * glibc-2.40-17.fc41.i686 on kernel-6.12.9-200.fc41.x86_64 */ - g_hash_table_insert(hash, - g_strdup("PlatformArchitecture"), - g_strdup((const gchar *)getauxval(AT_PLATFORM))); + tmp = (const gchar *)getauxval(AT_PLATFORM); + if (tmp == NULL) { + tmp = name_tmp.machine; + g_debug("no AT_PLATFORM, so using CpuArchitecture (%s) for platform", tmp); + } + if (tmp != NULL) + g_hash_table_insert(hash, g_strdup("PlatformArchitecture"), g_strdup(tmp)); #endif /* add the kernel boot time so we can detect a reboot */ @@ -2008,23 +2132,35 @@ g_hash_table_insert(hash, g_strdup("BootTime"), btime); /* add context information */ - g_hash_table_insert( - hash, - g_strdup("PowerState"), - g_strdup(fu_power_state_to_string(fu_context_get_power_state(self->ctx)))); - g_hash_table_insert( - hash, - g_strdup("DisplayState"), - g_strdup(fu_display_state_to_string(fu_context_get_display_state(self->ctx)))); - g_hash_table_insert(hash, - g_strdup("LidState"), - g_strdup(fu_lid_state_to_string(fu_context_get_lid_state(self->ctx)))); - g_hash_table_insert(hash, - g_strdup("BatteryLevel"), - g_strdup_printf("%u", fu_context_get_battery_level(self->ctx))); - g_hash_table_insert(hash, - g_strdup("BatteryThreshold"), - g_strdup_printf("%u", fu_context_get_battery_threshold(self->ctx))); + if (fu_context_get_power_state(self->ctx) != FU_POWER_STATE_UNKNOWN) { + g_hash_table_insert( + hash, + g_strdup("PowerState"), + g_strdup(fu_power_state_to_string(fu_context_get_power_state(self->ctx)))); + } + if (fu_context_get_display_state(self->ctx) != FU_DISPLAY_STATE_UNKNOWN) { + g_hash_table_insert( + hash, + g_strdup("DisplayState"), + g_strdup(fu_display_state_to_string(fu_context_get_display_state(self->ctx)))); + } + if (fu_context_get_lid_state(self->ctx) != FU_LID_STATE_UNKNOWN) { + g_hash_table_insert( + hash, + g_strdup("LidState"), + g_strdup(fu_lid_state_to_string(fu_context_get_lid_state(self->ctx)))); + } + if (fu_context_get_battery_level(self->ctx) != FWUPD_BATTERY_LEVEL_INVALID) { + g_hash_table_insert(hash, + g_strdup("BatteryLevel"), + g_strdup_printf("%u", fu_context_get_battery_level(self->ctx))); + } + if (fu_context_get_battery_threshold(self->ctx) != FWUPD_BATTERY_LEVEL_INVALID) { + g_hash_table_insert( + hash, + g_strdup("BatteryThreshold"), + g_strdup_printf("%u", fu_context_get_battery_threshold(self->ctx))); + } return g_steal_pointer(&hash); } @@ -2055,6 +2191,7 @@ } if (any_emulated) { if (!fu_engine_emulator_load_phase(self->emulation, + self->emulator_composite_cnt, self->emulator_phase, FU_ENGINE_EMULATOR_WRITE_COUNT_DEFAULT, error)) @@ -2070,6 +2207,7 @@ /* save to emulated phase */ if (fu_context_has_flag(self->ctx, FU_CONTEXT_FLAG_SAVE_EVENTS) && !any_emulated) { if (!fu_engine_emulator_save_phase(self->emulation, + self->emulator_composite_cnt, self->emulator_phase, FU_ENGINE_EMULATOR_WRITE_COUNT_DEFAULT, error)) @@ -2078,7 +2216,7 @@ /* wait for any device to disconnect and reconnect */ if (!fu_device_list_wait_for_replug(self->device_list, error)) { - g_prefix_error(error, "failed to wait for composite prepare: "); + g_prefix_error_literal(error, "failed to wait for composite prepare: "); return FALSE; } @@ -2110,6 +2248,7 @@ } if (any_emulated) { if (!fu_engine_emulator_load_phase(self->emulation, + self->emulator_composite_cnt, self->emulator_phase, FU_ENGINE_EMULATOR_WRITE_COUNT_DEFAULT, error)) @@ -2125,6 +2264,7 @@ /* save to emulated phase */ if (fu_context_has_flag(self->ctx, FU_CONTEXT_FLAG_SAVE_EVENTS) && !any_emulated) { if (!fu_engine_emulator_save_phase(self->emulation, + self->emulator_composite_cnt, self->emulator_phase, FU_ENGINE_EMULATOR_WRITE_COUNT_DEFAULT, error)) @@ -2133,7 +2273,7 @@ /* wait for any device to disconnect and reconnect */ if (!fu_device_list_wait_for_replug(self->device_list, error)) { - g_prefix_error(error, "failed to wait for composite cleanup: "); + g_prefix_error_literal(error, "failed to wait for composite cleanup: "); return FALSE; } @@ -2291,7 +2431,7 @@ } fu_engine_set_emulator_phase(self, FU_ENGINE_EMULATOR_PHASE_COMPOSITE_PREPARE); if (!fu_engine_composite_prepare(self, devices, error)) { - g_prefix_error(error, "failed to prepare composite action: "); + g_prefix_error_literal(error, "failed to prepare composite action: "); return FALSE; } @@ -2300,17 +2440,9 @@ fu_progress_set_steps(progress, releases->len); for (guint i = 0; i < releases->len; i++) { FuRelease *release = g_ptr_array_index(releases, i); - GInputStream *stream = fu_release_get_stream(release); - if (stream == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no stream for release"); - return FALSE; - } + self->emulator_composite_cnt = i; if (!fu_engine_install_release(self, release, - stream, fu_progress_get_child(progress), flags, error)) { @@ -2351,7 +2483,8 @@ /* notify the plugins about the composite action */ fu_engine_set_emulator_phase(self, FU_ENGINE_EMULATOR_PHASE_COMPOSITE_CLEANUP); if (!fu_engine_composite_cleanup(self, devices_new, error)) { - g_prefix_error(error, "failed to cleanup composite action: "); + g_prefix_error_literal(error, "failed to cleanup composite action: "); + fu_engine_update_motd_reset(self); return FALSE; } @@ -2463,16 +2596,15 @@ static gboolean fu_engine_save_into_backup_remote(FuEngine *self, GBytes *fw, GError **error) { - FwupdRemote *remote_tmp = fu_remote_list_get_by_id(self->remote_list, "backup"); - g_autofree gchar *localstatepkg = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); - g_autofree gchar *backupdir = g_build_filename(localstatepkg, "backup", NULL); + g_autofree gchar *backupdir = fu_path_build(FU_PATH_KIND_LOCALSTATEDIR_PKG, "backup", NULL); g_autofree gchar *backupdir_uri = g_strdup_printf("file://%s", backupdir); - g_autofree gchar *remotes_path = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_REMOTES); - g_autofree gchar *remotes_fn = g_build_filename(remotes_path, "backup.conf", NULL); + g_autofree gchar *remotes_fn = + fu_path_build(FU_PATH_KIND_LOCALSTATEDIR_REMOTES, "backup.conf", NULL); g_autofree gchar *archive_checksum = g_compute_checksum_for_bytes(G_CHECKSUM_SHA256, fw); g_autofree gchar *archive_basename = g_strdup_printf("%s.cab", archive_checksum); g_autofree gchar *archive_fn = g_build_filename(backupdir, archive_basename, NULL); g_autoptr(FwupdRemote) remote = fwupd_remote_new(); + g_autoptr(FwupdRemote) remote_tmp = NULL; /* save archive if required */ if (!g_file_test(archive_fn, G_FILE_TEST_EXISTS)) { @@ -2482,6 +2614,7 @@ } /* already exists as an enabled remote */ + remote_tmp = fu_remote_list_get_by_id(self->remote_list, "backup", NULL); if (remote_tmp != NULL && fwupd_remote_has_flag(remote_tmp, FWUPD_REMOTE_FLAG_ENABLED)) return TRUE; @@ -2500,11 +2633,44 @@ return fu_remote_save_to_filename(remote, remotes_fn, NULL, error); } +static gboolean +fu_engine_create_reboot_required_file(GError **error) +{ + g_autofree gchar *rundir = fu_path_from_kind(FU_PATH_KIND_RUNDIR); + g_autofree gchar *reboot_required_path = g_build_filename(rundir, "reboot-required", NULL); + g_autofree gchar *reboot_required_pkgs_path = + g_build_filename(rundir, "reboot-required.pkgs", NULL); + g_autoptr(GString) new_content = g_string_new(NULL); + + if (!g_file_test(rundir, G_FILE_TEST_IS_DIR)) + return TRUE; + + if (!g_file_set_contents(reboot_required_path, "", -1, error)) + return FALSE; + + if (g_file_test(reboot_required_pkgs_path, G_FILE_TEST_EXISTS)) { + g_autofree gchar *existing_content = NULL; + + if (!g_file_get_contents(reboot_required_pkgs_path, &existing_content, NULL, error)) + return FALSE; + if (existing_content != NULL) { + g_auto(GStrv) lines = g_strsplit(existing_content, "\n", -1); + for (guint i = 0; lines[i] != NULL; i++) { + if (g_strcmp0(lines[i], "fwupd") == 0) + return TRUE; + } + g_string_append(new_content, existing_content); + } + } + + g_string_append(new_content, "fwupd\n"); + return g_file_set_contents(reboot_required_pkgs_path, new_content->str, -1, error); +} + /** * fu_engine_install_release: * @self: a #FuEngine * @release: a #FuRelease - * @stream: the #GInputStream of the .cab file * @progress: a #FuProgress * @flags: install flags, e.g. %FWUPD_INSTALL_FLAG_ALLOW_OLDER * @error: (nullable): optional return location for an error @@ -2520,7 +2686,6 @@ gboolean fu_engine_install_release(FuEngine *self, FuRelease *release, - GInputStream *stream, FuProgress *progress, FwupdInstallFlags flags, GError **error) @@ -2529,7 +2694,7 @@ FuEngineRequest *request = fu_release_get_request(release); FuPlugin *plugin; FwupdFeatureFlags feature_flags = FWUPD_FEATURE_FLAG_NONE; - GInputStream *stream_fw; + GInputStream *stream = fu_release_get_stream(release); const gchar *tmp; g_autoptr(FuDevice) device = NULL; g_autoptr(FuDevice) device_tmp = NULL; @@ -2538,9 +2703,17 @@ g_return_val_if_fail(FU_IS_ENGINE(self), FALSE); g_return_val_if_fail(FU_IS_RELEASE(release), FALSE); g_return_val_if_fail(FU_IS_PROGRESS(progress), FALSE); - g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + /* sanity check */ + if (stream == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no stream for release"); + return FALSE; + } + /* optional for tests */ if (request != NULL) feature_flags = fu_engine_request_get_feature_flags(request); @@ -2582,16 +2755,6 @@ /* set this for the callback */ self->write_history = (flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0; - /* get per-release firmware blob */ - stream_fw = fu_release_get_stream(release); - if (stream_fw == NULL) { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "Failed to get firmware stream from release"); - return FALSE; - } - /* get the plugin */ plugin = fu_plugin_list_find_by_name(self->plugin_list, fu_device_get_plugin(device), error); @@ -2611,7 +2774,7 @@ /* install firmware blob */ if (!fu_engine_install_blob(self, device, - stream_fw, + release, progress, flags, feature_flags, @@ -2630,7 +2793,7 @@ /* the device may have changed */ device_tmp = fu_device_list_get_by_id(self->device_list, fu_device_get_id(device), error); if (device_tmp == NULL) { - g_prefix_error(error, "failed to get device after install: "); + g_prefix_error_literal(error, "failed to get device after install: "); return FALSE; } g_set_object(&device, device_tmp); @@ -2638,7 +2801,13 @@ /* update state (which updates the database if required) */ if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT) || fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) { - fu_device_set_update_state(device_orig, FWUPD_UPDATE_STATE_NEEDS_REBOOT); + if (!fu_engine_create_reboot_required_file(error)) + return FALSE; + if (g_strcmp0(fu_device_get_plugin(device), "test") == 0) { + g_debug("not setting needs-reboot for test device"); + } else { + fu_device_set_update_state(device_orig, FWUPD_UPDATE_STATE_NEEDS_REBOOT); + } return TRUE; } @@ -2696,9 +2865,23 @@ gboolean fu_engine_emulation_load(FuEngine *self, GInputStream *stream, GError **error) { + gsize streamsz = 0; + g_return_val_if_fail(FU_IS_ENGINE(self), FALSE); g_return_val_if_fail(G_IS_INPUT_STREAM(stream), FALSE); g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + /* sanity check */ + if (!fu_input_stream_size(stream, &streamsz, error)) + return FALSE; + if (streamsz > fu_common_get_memory_size() / 10) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "skipping large emulation due to memory constraint"); + return FALSE; + } + return fu_engine_emulator_load(self->emulation, stream, error); } @@ -2730,6 +2913,7 @@ if (device_old != NULL && fu_device_has_flag(device_old, FWUPD_DEVICE_FLAG_EMULATED)) { if (!fu_engine_emulator_load_phase(self->emulation, + self->emulator_composite_cnt, self->emulator_phase, self->emulator_write_cnt, error)) @@ -2739,7 +2923,7 @@ /* wait for any device to disconnect and reconnect */ if (!fu_device_list_wait_for_replug(self->device_list, error)) { - g_prefix_error(error, "failed to wait for device: "); + g_prefix_error_literal(error, "failed to wait for device: "); return NULL; } @@ -2760,11 +2944,8 @@ FwupdInstallFlags flags, GError **error) { - g_autoptr(FuDeviceLocker) locker = fu_device_locker_new(device, error); - if (locker == NULL) { - g_prefix_error(error, "failed to open device for prepare: "); - return FALSE; - } + FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(device); + g_autoptr(FuDeviceLocker) locker = NULL; /* check battery level is sane */ if (fu_device_get_battery_level(device) > 0 && @@ -2777,6 +2958,15 @@ return FALSE; } + /* nothing to do */ + if (device_class->prepare == NULL) + return TRUE; + + locker = fu_device_locker_new(device, error); + if (locker == NULL) { + g_prefix_error_literal(error, "failed to open device for prepare: "); + return FALSE; + } return fu_device_prepare(device, progress, flags, error); } @@ -2788,6 +2978,7 @@ FwupdInstallFlags flags, GError **error) { + FuDeviceClass *device_class = FU_DEVICE_GET_CLASS(device); g_autoptr(FuDeviceLocker) locker = NULL; if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_WILL_DISAPPEAR)) { @@ -2795,9 +2986,13 @@ return TRUE; } + /* nothing to do */ + if (device_class->cleanup == NULL) + return TRUE; + locker = fu_device_locker_new(device, error); if (locker == NULL) { - g_prefix_error(error, "failed to open device for cleanup: "); + g_prefix_error_literal(error, "failed to open device for cleanup: "); return FALSE; } return fu_device_cleanup(device, progress, flags, error); @@ -2813,7 +3008,8 @@ return TRUE; /* not charging */ - if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REQUIRE_AC) && + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + fu_device_has_flag(device, FWUPD_DEVICE_FLAG_REQUIRE_AC) && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED) && !fu_power_state_is_ac(fu_context_get_power_state(self->ctx))) { g_set_error_literal(error, @@ -2825,7 +3021,8 @@ } /* not enough just in case */ - if (!fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_IGNORE_SYSTEM_POWER) && + if ((flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + !fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_IGNORE_SYSTEM_POWER) && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED) && fu_context_get_battery_level(self->ctx) != FWUPD_BATTERY_LEVEL_INVALID && fu_context_get_battery_threshold(self->ctx) != FWUPD_BATTERY_LEVEL_INVALID && @@ -2848,7 +3045,7 @@ const gchar *device_id, GInputStream *stream, FuProgress *progress, - FwupdInstallFlags flags, + FuFirmwareParseFlags flags, GError **error) { g_autoptr(FuDevice) device = NULL; @@ -2856,7 +3053,7 @@ /* the device and plugin both may have changed */ device = fu_engine_get_device(self, device_id, error); if (device == NULL) { - g_prefix_error(error, "failed to get device before prepare firmware: "); + g_prefix_error_literal(error, "failed to get device before prepare firmware: "); return NULL; } return fu_device_prepare_firmware(device, stream, progress, flags, error); @@ -2876,13 +3073,15 @@ /* the device and plugin both may have changed */ device = fu_engine_get_device(self, device_id, error); if (device == NULL) { - g_prefix_error(error, "failed to get device before update prepare: "); + g_prefix_error_literal(error, "failed to get device before update prepare: "); return FALSE; } fu_device_add_problem(device, FWUPD_DEVICE_PROBLEM_UPDATE_IN_PROGRESS); - if (!fu_engine_device_check_power(self, device, flags, error)) + if (!fu_engine_device_check_power(self, device, flags, error)) { + fu_device_set_update_state(device, FWUPD_UPDATE_STATE_FAILED_TRANSIENT); return FALSE; + } str = fu_device_to_string(device); g_info("prepare -> %s", str); @@ -2898,6 +3097,7 @@ if (fu_context_has_flag(self->ctx, FU_CONTEXT_FLAG_SAVE_EVENTS) && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) { if (!fu_engine_emulator_save_phase(self->emulation, + self->emulator_composite_cnt, self->emulator_phase, FU_ENGINE_EMULATOR_WRITE_COUNT_DEFAULT, error)) @@ -2906,7 +3106,7 @@ /* wait for any device to disconnect and reconnect */ if (!fu_device_list_wait_for_replug(self->device_list, error)) { - g_prefix_error(error, "failed to wait for prepare replug: "); + g_prefix_error_literal(error, "failed to wait for prepare replug: "); return FALSE; } @@ -2928,7 +3128,7 @@ /* the device and plugin both may have changed */ device = fu_engine_get_device(self, device_id, error); if (device == NULL) { - g_prefix_error(error, "failed to get device before update cleanup: "); + g_prefix_error_literal(error, "failed to get device before update cleanup: "); return FALSE; } fu_device_remove_problem(device, FWUPD_DEVICE_PROBLEM_UPDATE_IN_PROGRESS); @@ -2946,6 +3146,7 @@ if (fu_context_has_flag(self->ctx, FU_CONTEXT_FLAG_SAVE_EVENTS) && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) { if (!fu_engine_emulator_save_phase(self->emulation, + self->emulator_composite_cnt, self->emulator_phase, FU_ENGINE_EMULATOR_WRITE_COUNT_DEFAULT, error)) @@ -2954,7 +3155,7 @@ /* wait for any device to disconnect and reconnect */ if (!fu_device_list_wait_for_replug(self->device_list, error)) { - g_prefix_error(error, "failed to wait for cleanup replug: "); + g_prefix_error_literal(error, "failed to wait for cleanup replug: "); return FALSE; } @@ -2978,7 +3179,7 @@ /* the device and plugin both may have changed */ device = fu_engine_get_device(self, device_id, error); if (device == NULL) { - g_prefix_error(error, "failed to get device before update detach: "); + g_prefix_error_literal(error, "failed to get device before update detach: "); return FALSE; } device_progress = fu_device_progress_new(device, progress); @@ -3022,6 +3223,7 @@ if (fu_context_has_flag(self->ctx, FU_CONTEXT_FLAG_SAVE_EVENTS) && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) { if (!fu_engine_emulator_save_phase(self->emulation, + self->emulator_composite_cnt, self->emulator_phase, self->emulator_write_cnt, error)) @@ -3030,7 +3232,7 @@ /* wait for any device to disconnect and reconnect */ if (!fu_device_list_wait_for_replug(self->device_list, error)) { - g_prefix_error(error, "failed to wait for detach replug: "); + g_prefix_error_literal(error, "failed to wait for detach replug: "); return FALSE; } @@ -3050,7 +3252,7 @@ /* the device and plugin both may have changed */ device = fu_engine_get_device(self, device_id, error); if (device == NULL) { - g_prefix_error(error, "failed to get device before update attach: "); + g_prefix_error_literal(error, "failed to get device before update attach: "); return FALSE; } device_progress = fu_device_progress_new(device, progress); @@ -3075,6 +3277,7 @@ if (fu_context_has_flag(self->ctx, FU_CONTEXT_FLAG_SAVE_EVENTS) && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) { if (!fu_engine_emulator_save_phase(self->emulation, + self->emulator_composite_cnt, self->emulator_phase, self->emulator_write_cnt, error)) @@ -3083,7 +3286,7 @@ /* wait for any device to disconnect and reconnect */ if (!fu_device_list_wait_for_replug(self->device_list, error)) { - g_prefix_error(error, "failed to wait for attach replug: "); + g_prefix_error_literal(error, "failed to wait for attach replug: "); return FALSE; } @@ -3099,7 +3302,7 @@ /* the device and plugin both may have changed */ device = fu_engine_get_device(self, device_id, error); if (device == NULL) { - g_prefix_error(error, "failed to get device before setting progress: "); + g_prefix_error_literal(error, "failed to get device before setting progress: "); return FALSE; } fu_device_set_progress(device, progress); @@ -3146,7 +3349,7 @@ /* the device and plugin both may have changed */ device = fu_engine_get_device(self, device_id, error); if (device == NULL) { - g_prefix_error(error, "failed to get device before update reload: "); + g_prefix_error_literal(error, "failed to get device before update reload: "); return FALSE; } str = fu_device_to_string(device); @@ -3162,7 +3365,7 @@ } if (!fu_plugin_runner_reload(plugin, device, error)) { - g_prefix_error(error, "failed to reload device: "); + g_prefix_error_literal(error, "failed to reload device: "); return FALSE; } @@ -3173,6 +3376,7 @@ if (fu_context_has_flag(self->ctx, FU_CONTEXT_FLAG_SAVE_EVENTS) && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) { if (!fu_engine_emulator_save_phase(self->emulation, + self->emulator_composite_cnt, self->emulator_phase, self->emulator_write_cnt, error)) @@ -3181,7 +3385,7 @@ /* wait for any device to disconnect and reconnect */ if (!fu_device_list_wait_for_replug(self->device_list, error)) { - g_prefix_error(error, "failed to wait for reload replug: "); + g_prefix_error_literal(error, "failed to wait for reload replug: "); return FALSE; } @@ -3190,6 +3394,28 @@ } static gboolean +fu_engine_composite_peek_firmware(FuEngine *self, + FuDevice *device, + FuFirmware *firmware, + FuProgress *progress, + FwupdInstallFlags flags, + GError **error) +{ + GPtrArray *plugins = fu_plugin_list_get_all(self->plugin_list); + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(plugins, i); + if (!fu_plugin_runner_composite_peek_firmware(plugin, + device, + firmware, + progress, + flags, + error)) + return FALSE; + } + return TRUE; +} + +static gboolean fu_engine_write_firmware(FuEngine *self, const gchar *device_id, FuFirmware *firmware, @@ -3207,12 +3433,16 @@ /* the device and plugin both may have changed */ device = fu_engine_get_device(self, device_id, error); if (device == NULL) { - g_prefix_error(error, "failed to get device before update: "); + g_prefix_error_literal(error, "failed to get device before update: "); return FALSE; } device_progress = fu_device_progress_new(device, progress); g_return_val_if_fail(device_progress != NULL, FALSE); + /* notify all the other plugins */ + if (!fu_engine_composite_peek_firmware(self, device, firmware, progress, flags, error)) + return FALSE; + /* pause the polling */ poll_locker = fu_device_poll_locker_new(device, error); if (poll_locker == NULL) @@ -3273,6 +3503,7 @@ if (fu_context_has_flag(self->ctx, FU_CONTEXT_FLAG_SAVE_EVENTS) && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) { if (!fu_engine_emulator_save_phase(self->emulation, + self->emulator_composite_cnt, self->emulator_phase, self->emulator_write_cnt, error)) @@ -3285,7 +3516,7 @@ /* wait for any device to disconnect and reconnect */ if (!fu_device_list_wait_for_replug(self->device_list, error)) { - g_prefix_error(error, "failed to wait for write-firmware replug: "); + g_prefix_error_literal(error, "failed to wait for write-firmware replug: "); return FALSE; } @@ -3311,7 +3542,7 @@ /* open, read, close */ locker = fu_device_locker_new(device, error); if (locker == NULL) { - g_prefix_error(error, "failed to open device for firmware read: "); + g_prefix_error_literal(error, "failed to open device for firmware read: "); return NULL; } return fu_device_dump_firmware(device, progress, error); @@ -3335,26 +3566,47 @@ /* open, read, close */ locker = fu_device_locker_new(device, error); if (locker == NULL) { - g_prefix_error(error, "failed to open device for firmware read: "); + g_prefix_error_literal(error, "failed to open device for firmware read: "); return NULL; } - return fu_device_read_firmware(device, progress, error); + return fu_device_read_firmware(device, progress, FU_FIRMWARE_PARSE_FLAG_NONE, error); } static gboolean fu_engine_install_loop(FuEngine *self, const gchar *device_id, - GInputStream *stream_fw, + FuRelease *release, FwupdInstallFlags flags, FwupdFeatureFlags feature_flags, gboolean *write_complete, FuProgress *progress, GError **error) { + GInputStream *stream_fw; + gsize streamsz = 0; g_autoptr(FuDevice) device = NULL; g_autoptr(FuDevice) device_tmp = NULL; g_autoptr(FuFirmware) firmware = NULL; + /* test the firmware is not an empty blob */ + stream_fw = fu_release_get_stream(release); + if (stream_fw == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "Failed to get firmware stream from release"); + return FALSE; + } + if (!fu_input_stream_size(stream_fw, &streamsz, error)) + return FALSE; + if (streamsz == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "Firmware is invalid as has zero size"); + return FALSE; + } + /* not ideal; but best we can do as we don't know how many writes are required */ fu_progress_reset(progress); @@ -3381,7 +3633,7 @@ /* some emulations are storing events on the bootloader device */ device = fu_engine_get_device(self, device_id, error); if (device == NULL) { - g_prefix_error(error, "failed to get device for flags: "); + g_prefix_error_literal(error, "failed to get device for flags: "); return FALSE; } @@ -3396,15 +3648,15 @@ } /* detach->parse->install or parse->detach->install */ + fu_engine_set_emulator_phase(self, FU_ENGINE_EMULATOR_PHASE_DETACH); if (fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_DETACH_PREPARE_FIRMWARE)) { /* detach to bootloader mode */ - fu_engine_set_emulator_phase(self, FU_ENGINE_EMULATOR_PHASE_DETACH); if (!fu_engine_detach(self, device_id, fu_progress_get_child(progress), feature_flags, error)) { - g_prefix_error(error, "failed to detach: "); + g_prefix_error_literal(error, "failed to detach: "); return FALSE; } fu_progress_step_done(progress); @@ -3415,10 +3667,16 @@ device_id, stream_fw, fu_progress_get_child(progress), - flags, + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, error); if (firmware == NULL) return FALSE; + if (fu_firmware_get_version(firmware) == NULL) + fu_firmware_set_version(firmware, fu_release_get_version(release)); + if (fu_firmware_get_filename(firmware) == NULL) { + fu_firmware_set_filename(firmware, + fu_release_get_firmware_basename(release)); + } fu_progress_step_done(progress); /* install */ @@ -3428,7 +3686,7 @@ fu_progress_get_child(progress), flags, error)) { - g_prefix_error(error, "failed to write-firmware: "); + g_prefix_error_literal(error, "failed to write-firmware: "); return FALSE; } fu_progress_step_done(progress); @@ -3438,20 +3696,25 @@ device_id, stream_fw, fu_progress_get_child(progress), - flags, + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, error); if (firmware == NULL) return FALSE; + if (fu_firmware_get_version(firmware) == NULL) + fu_firmware_set_version(firmware, fu_release_get_version(release)); + if (fu_firmware_get_filename(firmware) == NULL) { + fu_firmware_set_filename(firmware, + fu_release_get_firmware_basename(release)); + } fu_progress_step_done(progress); /* detach to bootloader mode */ - fu_engine_set_emulator_phase(self, FU_ENGINE_EMULATOR_PHASE_DETACH); if (!fu_engine_detach(self, device_id, fu_progress_get_child(progress), feature_flags, error)) { - g_prefix_error(error, "failed to detach: "); + g_prefix_error_literal(error, "failed to detach: "); return FALSE; } fu_progress_step_done(progress); @@ -3464,7 +3727,7 @@ fu_progress_get_child(progress), flags, error)) { - g_prefix_error(error, "failed to write-firmware: "); + g_prefix_error_literal(error, "failed to write-firmware: "); return FALSE; } fu_progress_step_done(progress); @@ -3479,7 +3742,7 @@ /* attach into runtime mode */ fu_engine_set_emulator_phase(self, FU_ENGINE_EMULATOR_PHASE_ATTACH); if (!fu_engine_attach(self, device_id, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to attach: "); + g_prefix_error_literal(error, "failed to attach: "); return FALSE; } fu_progress_step_done(progress); @@ -3493,7 +3756,7 @@ /* get the new version number */ fu_engine_set_emulator_phase(self, FU_ENGINE_EMULATOR_PHASE_RELOAD); if (!fu_engine_reload(self, device_id, error)) { - g_prefix_error(error, "failed to reload: "); + g_prefix_error_literal(error, "failed to reload: "); return FALSE; } fu_progress_step_done(progress); @@ -3501,7 +3764,7 @@ /* abort loop */ device_tmp = fu_engine_get_device(self, device_id, error); if (device_tmp == NULL) { - g_prefix_error(error, "failed to get device after reload: "); + g_prefix_error_literal(error, "failed to get device after reload: "); return FALSE; } if (fu_device_has_flag(device_tmp, FWUPD_DEVICE_FLAG_ANOTHER_WRITE_REQUIRED)) { @@ -3517,15 +3780,15 @@ gboolean fu_engine_install_blob(FuEngine *self, FuDevice *device, - GInputStream *stream_fw, + FuRelease *release, FuProgress *progress, FwupdInstallFlags flags, FwupdFeatureFlags feature_flags, GError **error) { gboolean write_complete = FALSE; - gsize streamsz = 0; g_autofree gchar *device_id = NULL; + g_autofree gchar *id_display = fu_device_get_id_display(device); g_autoptr(GTimer) timer = g_timer_new(); g_autoptr(FuDeviceProgress) device_progress = fu_device_progress_new(device, progress); @@ -3538,17 +3801,6 @@ fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_WRITE, 98, NULL); fu_progress_add_step(progress, FWUPD_STATUS_DEVICE_BUSY, 1, "cleanup"); - /* test the firmware is not an empty blob */ - if (!fu_input_stream_size(stream_fw, &streamsz, error)) - return FALSE; - if (streamsz == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_FILE, - "Firmware is invalid as has zero size"); - return FALSE; - } - /* mark this as modified even if we actually fail to do the update */ fu_device_set_modified_usec(device, g_get_real_time()); @@ -3566,7 +3818,7 @@ self->emulator_write_cnt++) { if (!fu_engine_install_loop(self, device_id, - stream_fw, + release, flags, feature_flags, &write_complete, @@ -3590,7 +3842,7 @@ fu_device_set_install_duration(device, g_timer_elapsed(timer, NULL)); if ((flags & FWUPD_INSTALL_FLAG_NO_HISTORY) == 0) { if (!fu_history_modify_device(self->history, device, error)) { - g_prefix_error(error, "failed to set success: "); + g_prefix_error_literal(error, "failed to set success: "); return FALSE; } } @@ -3603,9 +3855,7 @@ /* make the UI update */ fu_engine_emit_device_changed(self, device_id); - g_info("Updating %s took %f seconds", - fu_device_get_name(device), - g_timer_elapsed(timer, NULL)); + g_info("updating %s took %f seconds", id_display, g_timer_elapsed(timer, NULL)); return TRUE; } @@ -3666,6 +3916,71 @@ } static gboolean +fu_engine_search_query_append(FuEngine *self, const gchar *xpath, GError **error) +{ + g_autoptr(GError) error_local = NULL; + g_autoptr(XbQuery) query = NULL; + + /* prepare tag query with bound GUID parameter */ + query = xb_query_new_full(self->silo, xpath, XB_QUERY_FLAG_OPTIMIZE, &error_local); + if (query == NULL) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) || + g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) { + g_debug("ignoring prepared query %s: %s", xpath, error_local->message); + return TRUE; + } + g_propagate_error(error, g_steal_pointer(&error_local)); + fwupd_error_convert(error); + return FALSE; + } + g_ptr_array_add(self->search_queries, g_steal_pointer(&query)); + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_search_query_create(FuEngine *self, GError **error) +{ + /* invalidate everything */ + g_ptr_array_set_size(self->search_queries, 0); + + /* we get one for free, add build the others */ + g_ptr_array_add(self->search_queries, g_object_ref(self->query_component_by_guid)); + if (!fu_engine_search_query_append(self, "components/component/id[text()=?]/..", error)) + return FALSE; + if (!fu_engine_search_query_append(self, "components/component/name[text()~=?]/..", error)) + return FALSE; + if (!fu_engine_search_query_append(self, + "components/component/developer_name[text()~=?]/..", + error)) + return FALSE; + if (!fu_engine_search_query_append(self, + "components/component/releases/release/artifacts/" + "artifact/filename[text()=?]/../../../../..", + error)) + return FALSE; + if (!fu_engine_search_query_append(self, + "components/component/releases/release/artifacts/" + "artifact/checksum[text()=?]/../../../../..", + error)) + return FALSE; + if (!fu_engine_search_query_append( + self, + "components/component/releases/release/issues/issue[text()=?]/../../../..", + error)) + return FALSE; + if (!fu_engine_search_query_append( + self, + "components/component/custom/value[@key='LVFS::UpdateProtocol'][text()=?]/../..", + error)) + return FALSE; + + /* success */ + return TRUE; +} + +static gboolean fu_engine_create_silo_index(FuEngine *self, GError **error) { g_autoptr(GPtrArray) components = NULL; @@ -3720,7 +4035,7 @@ XB_QUERY_FLAG_OPTIMIZE, error); if (self->query_component_by_guid == NULL) { - g_prefix_error(error, "failed to prepare query: "); + g_prefix_error_literal(error, "failed to prepare query: "); return FALSE; } @@ -3754,6 +4069,10 @@ if (self->query_tag_by_guid_version == NULL) g_debug("ignoring prepared query: %s", error_tag_by_guid_version->message); + /* build all the search queries */ + if (!fu_engine_search_query_create(self, error)) + return FALSE; + /* success */ return TRUE; } @@ -3915,9 +4234,8 @@ if (releases == NULL) { if (!g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO) && !g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { - g_warning("failed to get releases for %s: %s", - fu_device_get_name(device), - error->message); + g_autofree gchar *id_display = fu_device_get_id_display(device); + g_warning("failed to get releases for %s: %s", id_display, error->message); } } else { if (releases->len > 0) @@ -3983,8 +4301,7 @@ FuPathKind path_kind, GError **error) { - g_autofree gchar *fn = fu_path_from_kind(path_kind); - g_autofree gchar *metadata_path = g_build_filename(fn, "local.d", NULL); + g_autofree gchar *metadata_path = fu_path_build(path_kind, "local.d", NULL); g_autoptr(GError) error_local = NULL; g_autoptr(GPtrArray) metadata_fns = NULL; @@ -4017,9 +4334,9 @@ static gboolean fu_engine_load_metadata_store(FuEngine *self, FuEngineLoadFlags flags, GError **error) { - GPtrArray *remotes; XbBuilderCompileFlags compile_flags = XB_BUILDER_COMPILE_FLAG_IGNORE_INVALID; g_autoptr(GFile) xmlb = NULL; + g_autoptr(GPtrArray) remotes = NULL; g_autoptr(XbBuilder) builder = xb_builder_new(); /* clear existing silo */ @@ -4126,13 +4443,13 @@ if (xmlb == NULL) return FALSE; } else { - g_autofree gchar *cachedirpkg = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); - g_autofree gchar *xmlbfn = g_build_filename(cachedirpkg, "metadata.xmlb", NULL); + g_autofree gchar *xmlbfn = + fu_path_build(FU_PATH_KIND_CACHEDIR_PKG, "metadata.xmlb", NULL); xmlb = g_file_new_for_path(xmlbfn); } self->silo = xb_builder_ensure(builder, xmlb, compile_flags, NULL, error); if (self->silo == NULL) { - g_prefix_error(error, "cannot create metadata.xmlb: "); + g_prefix_error_literal(error, "cannot create metadata.xmlb: "); return FALSE; } @@ -4159,7 +4476,7 @@ static void fu_engine_config_changed_cb(FuEngineConfig *config, FuEngine *self) { - GPtrArray *remotes = fu_remote_list_get_all(self->remote_list); + g_autoptr(GPtrArray) remotes = fu_remote_list_get_all(self->remote_list); fu_idle_set_timeout(self->idle, fu_engine_config_get_idle_timeout(config)); @@ -4258,6 +4575,9 @@ g_autoptr(GPtrArray) results = NULL; g_autoptr(JcatItem) jcat_item = NULL; g_autoptr(JcatFile) jcat_file = jcat_file_new(); + JcatVerifyFlags jcat_flags = JCAT_VERIFY_FLAG_DISABLE_TIME_CHECKS | + JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM | + JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE; blob = fu_bytes_get_contents(fwupd_remote_get_filename_cache(remote), error); if (blob == NULL) @@ -4266,22 +4586,30 @@ if (istream == NULL) return NULL; if (!jcat_file_import_stream(jcat_file, istream, JCAT_IMPORT_FLAG_NONE, NULL, error)) { - fu_error_convert(error); + fwupd_error_convert(error); return NULL; } jcat_item = jcat_file_get_item_default(jcat_file, error); if (jcat_item == NULL) { - fu_error_convert(error); + fwupd_error_convert(error); + return NULL; + } + + /* distrusting RSA? */ + if (fu_engine_config_get_only_trust_pq_signatures(self->config)) { +#if JCAT_CHECK_VERSION(0, 2, 4) + jcat_flags |= JCAT_VERIFY_FLAG_ONLY_PQ; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "only trusting PQ signatures requires libjcat >= 0.2.4"); return NULL; +#endif } - results = jcat_context_verify_item(self->jcat_context, - blob, - jcat_item, - JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM | - JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE, - error); + results = jcat_context_verify_item(self->jcat_context, blob, jcat_item, jcat_flags, error); if (results == NULL) { - fu_error_convert(error); + fwupd_error_convert(error); return NULL; } @@ -4300,13 +4628,23 @@ g_return_val_if_fail(JCAT_IS_RESULT(jcat_result_old), FALSE); if (jcat_result_get_timestamp(jcat_result) == 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE, "no signing timestamp"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_FILE, + "no signing timestamp"); return FALSE; } if (jcat_result_get_timestamp(jcat_result_old) > 0) { delta = jcat_result_get_timestamp(jcat_result) - jcat_result_get_timestamp(jcat_result_old); } + if (delta == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "signing timestamp was not newer"); + return FALSE; + } if (delta < 0) { g_set_error(error, FWUPD_ERROR, @@ -4339,7 +4677,7 @@ GBytes *bytes_sig, GError **error) { - FwupdRemote *remote; + g_autoptr(FwupdRemote) remote = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(GInputStream) istream = NULL; g_autoptr(GPtrArray) results = NULL; @@ -4347,6 +4685,8 @@ g_autoptr(JcatItem) jcat_item = NULL; g_autoptr(JcatResult) jcat_result = NULL; g_autoptr(JcatResult) jcat_result_old = NULL; + JcatVerifyFlags jcat_flags = + JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM | JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE; g_return_val_if_fail(FU_IS_ENGINE(self), FALSE); g_return_val_if_fail(remote_id != NULL, FALSE); @@ -4355,15 +4695,9 @@ g_return_val_if_fail(error == NULL || *error == NULL, FALSE); /* check remote is valid */ - remote = fu_remote_list_get_by_id(self->remote_list, remote_id); - if (remote == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "remote %s not found", - remote_id); + remote = fu_remote_list_get_by_id(self->remote_list, remote_id, error); + if (remote == NULL) return FALSE; - } if (!fwupd_remote_has_flag(remote, FWUPD_REMOTE_FLAG_ENABLED)) { g_set_error(error, FWUPD_ERROR, @@ -4378,16 +4712,25 @@ if (!jcat_file_import_stream(jcat_file, istream, JCAT_IMPORT_FLAG_NONE, NULL, error)) return FALSE; + /* distrusting RSA? */ + if (fu_engine_config_get_only_trust_pq_signatures(self->config)) { +#if JCAT_CHECK_VERSION(0, 2, 4) + jcat_flags |= JCAT_VERIFY_FLAG_ONLY_PQ; +#else + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "only trusting PQ signatures requires libjcat >= 0.2.4"); + return FALSE; +#endif + } + /* this should only be signing one thing */ jcat_item = jcat_file_get_item_default(jcat_file, error); if (jcat_item == NULL) return FALSE; - results = jcat_context_verify_item(self->jcat_context, - bytes_raw, - jcat_item, - JCAT_VERIFY_FLAG_REQUIRE_SIGNATURE | - JCAT_VERIFY_FLAG_REQUIRE_CHECKSUM, - error); + results = + jcat_context_verify_item(self->jcat_context, bytes_raw, jcat_item, jcat_flags, error); if (results == NULL) return FALSE; @@ -4414,6 +4757,8 @@ /* save XML and signature to remotes.d */ if (!fu_bytes_set_contents(fwupd_remote_get_filename_cache(remote), bytes_raw, error)) return FALSE; + if (!fwupd_remote_ensure_mtime(remote, error)) + return FALSE; #ifdef HAVE_PASSIM /* lazy load */ @@ -4512,10 +4857,10 @@ /* update with blobs */ return fu_engine_update_metadata_bytes(self, remote_id, bytes_raw, bytes_sig, error); #else - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Not supported as is unavailable"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Not supported as is unavailable"); return FALSE; #endif } @@ -4534,21 +4879,22 @@ fu_engine_build_cabinet_from_stream(FuEngine *self, GInputStream *stream, GError **error) { g_autoptr(FuCabinet) cabinet = fu_cabinet_new(); + FuFirmwareParseFlags flags = FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM; g_return_val_if_fail(FU_IS_ENGINE(self), NULL); g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); + /* distrusting RSA? */ + if (fu_engine_config_get_only_trust_pq_signatures(self->config)) + flags |= FU_FIRMWARE_PARSE_FLAG_ONLY_TRUST_PQ_SIGNATURES; + /* load file */ fu_engine_set_status(self, FWUPD_STATUS_DECOMPRESSING); fu_firmware_set_size_max(FU_FIRMWARE(cabinet), fu_engine_config_get_archive_size_max(self->config)); fu_cabinet_set_jcat_context(cabinet, self->jcat_context); - if (!fu_firmware_parse_stream(FU_FIRMWARE(cabinet), - stream, - 0x0, - FWUPD_INSTALL_FLAG_NONE, - error)) + if (!fu_firmware_parse_stream(FU_FIRMWARE(cabinet), stream, 0x0, flags, error)) return NULL; return g_steal_pointer(&cabinet); } @@ -4695,7 +5041,7 @@ g_autoptr(GPtrArray) details = NULL; g_autoptr(GPtrArray) checksums = g_ptr_array_new_with_free_func(g_free); g_autoptr(FuCabinet) cabinet = NULL; - g_autoptr(XbNode) rel_by_csum = NULL; + g_autoptr(GPtrArray) rels_by_csum = NULL; g_return_val_if_fail(FU_IS_ENGINE(self), NULL); g_return_val_if_fail(G_IS_INPUT_STREAM(stream), NULL); @@ -4703,7 +5049,7 @@ cabinet = fu_engine_build_cabinet_from_stream(self, stream, error); if (cabinet == NULL) { - g_prefix_error(error, "failed to load file: "); + g_prefix_error_literal(error, "failed to load file: "); return NULL; } components = fu_cabinet_get_components(cabinet, error); @@ -4722,8 +5068,8 @@ /* does this exist in any enabled remote */ for (guint i = 0; i < checksums->len; i++) { const gchar *csum = g_ptr_array_index(checksums, i); - rel_by_csum = fu_engine_get_release_for_checksum(self, csum); - if (rel_by_csum != NULL) + rels_by_csum = fu_engine_get_releases_for_container_checksum(self, csum); + if (rels_by_csum != NULL) break; } @@ -4739,7 +5085,8 @@ return NULL; fu_device_add_release(dev, FWUPD_RELEASE(rel)); - if (rel_by_csum != NULL) { + for (guint j = 0; rels_by_csum != NULL && j < rels_by_csum->len; j++) { + XbNode *rel_by_csum = g_ptr_array_index(rels_by_csum, j); const gchar *remote_id = xb_node_query_text(rel_by_csum, "../../../custom/value[@key='fwupd::RemoteId']", @@ -4916,8 +5263,79 @@ } #endif -static void -fu_engine_fixup_history_device(FuEngine *self, FuDevice *device) +static gboolean +fu_engine_fixup_history_device_for_rel(FuEngine *self, + FuDevice *device, + XbNode *rel, + GError **error) +{ + FwupdRelease *release = fu_device_get_release_default(device); + const gchar *appstream_id; + g_autoptr(XbNode) component = NULL; + + component = xb_node_query_first(rel, "../..", error); + if (component == NULL) { + fwupd_error_convert(error); + g_prefix_error_literal(error, "failed to load component: "); + return FALSE; + } + + /* check appstream ID is the same */ + appstream_id = xb_node_query_text(component, "id", NULL); + if (appstream_id == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "no appstream ID for component"); + return FALSE; + } + if (g_strcmp0(appstream_id, fwupd_release_get_appstream_id(release)) != 0) { + g_debug("ignoring %s as expecting %s", + appstream_id, + fwupd_release_get_appstream_id(release)); + return TRUE; + } + + if (!fu_release_load(FU_RELEASE(release), + NULL, + component, + rel, + FWUPD_INSTALL_FLAG_NONE, + error)) { + g_prefix_error_literal(error, "failed to load release: "); + return FALSE; + } + + /* success */ + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SUPPORTED); + return TRUE; +} + +static gboolean +fu_engine_fixup_history_device_for_csum(FuEngine *self, + FuDevice *device, + const gchar *csum, + GError **error) +{ + g_autoptr(GPtrArray) rels = NULL; + + rels = fu_engine_get_releases_for_container_checksum(self, csum); + if (rels == NULL) + return TRUE; + for (guint i = 0; i < rels->len; i++) { + XbNode *rel = g_ptr_array_index(rels, i); + if (!fu_engine_fixup_history_device_for_rel(self, device, rel, error)) + return FALSE; + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SUPPORTED)) + break; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_engine_fixup_history_device(FuEngine *self, FuDevice *device, GError **error) { FwupdRelease *release; GPtrArray *csums; @@ -4925,39 +5343,33 @@ /* get the checksums */ release = fu_device_get_release_default(device); if (release == NULL) { - g_warning("no checksums from release history"); - return; + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "no default release for device"); + return FALSE; } /* find the checksum that matches */ csums = fwupd_release_get_checksums(release); - for (guint j = 0; j < csums->len; j++) { - const gchar *csum = g_ptr_array_index(csums, j); - g_autoptr(XbNode) rel = fu_engine_get_release_for_checksum(self, csum); - if (rel != NULL) { - g_autoptr(GError) error_local = NULL; - g_autoptr(XbNode) component = NULL; - - component = xb_node_query_first(rel, "../..", &error_local); - if (component == NULL) { - g_warning("failed to load component: %s", error_local->message); - continue; - } - fu_release_set_device(FU_RELEASE(release), device); - - if (!fu_release_load(FU_RELEASE(release), - NULL, - component, - rel, - FWUPD_INSTALL_FLAG_NONE, - &error_local)) { - g_warning("failed to load release: %s", error_local->message); - continue; - } - fu_device_add_flag(device, FWUPD_DEVICE_FLAG_SUPPORTED); + if (csums->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_DATA, + "no checksums from release"); + return FALSE; + } + for (guint i = 0; i < csums->len; i++) { + const gchar *csum = g_ptr_array_index(csums, i); + g_debug("finding release checksum %s", csum); + if (!fu_engine_fixup_history_device_for_csum(self, device, csum, error)) + return FALSE; + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SUPPORTED)) break; - } } + + /* no device was matched, which is fine... */ + return TRUE; } /** @@ -4997,7 +5409,7 @@ /* if this is the system firmware device, add the HSI attrs */ for (guint i = 0; i < devices->len; i++) { FuDevice *dev = g_ptr_array_index(devices, i); - if (fu_device_has_private_flag(dev, FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE)) + if (fu_device_has_private_flag_quark(dev, quarks[QUARK_HOST_FIRMWARE])) fu_engine_get_history_set_hsi_attrs(self, dev); } #endif @@ -5005,7 +5417,8 @@ /* try to set the remote ID for each device */ for (guint i = 0; i < devices->len; i++) { FuDevice *dev = g_ptr_array_index(devices, i); - fu_engine_fixup_history_device(self, dev); + if (!fu_engine_fixup_history_device(self, dev, error)) + return NULL; } return g_steal_pointer(&devices); @@ -5023,14 +5436,17 @@ GPtrArray * fu_engine_get_remotes(FuEngine *self, GError **error) { - GPtrArray *remotes; + g_autoptr(GPtrArray) remotes = NULL; g_return_val_if_fail(FU_IS_ENGINE(self), NULL); g_return_val_if_fail(error == NULL || *error == NULL, NULL); remotes = fu_remote_list_get_all(self->remote_list); if (remotes->len == 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "No remotes configured"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "No remotes configured"); return NULL; } @@ -5190,7 +5606,7 @@ checksums = fu_release_get_checksums(release); if (checksums->len == 0) { g_autofree gchar *str = fwupd_codec_to_string(FWUPD_CODEC(release)); - g_debug("no locations for %s", str); + g_debug("no checksums for %s", str); continue; } @@ -5239,13 +5655,11 @@ /* add update message if exists but device doesn't already have one */ update_message = fwupd_release_get_update_message(FWUPD_RELEASE(release)); - if (fu_device_get_update_message(device) == NULL && update_message != NULL) { + if (fu_device_get_update_message(device) == NULL && update_message != NULL) fu_device_set_update_message(device, update_message); - } update_image = fwupd_release_get_update_image(FWUPD_RELEASE(release)); - if (fu_device_get_update_image(device) == NULL && update_image != NULL) { + if (fu_device_get_update_image(device) == NULL && update_image != NULL) fu_device_set_update_image(device, update_image); - } update_request_id = fu_release_get_update_request_id(release); if (fu_device_get_update_request_id(device) == NULL && update_request_id != NULL) { fu_device_add_request_flag(device, @@ -5286,7 +5700,10 @@ /* no components in silo */ if (self->query_component_by_guid == NULL) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "no components in silo"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no components in silo"); return NULL; } @@ -5295,10 +5712,10 @@ !fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS)) { const gchar *version = fu_device_get_version(device); if (version == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "no version set"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "no version set"); return NULL; } } @@ -5383,13 +5800,72 @@ /* return the compound error */ if (releases->len == 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, "No releases found"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No releases found"); return NULL; } return g_steal_pointer(&releases); } /** + * fu_engine_search: + * @self: a #FuEngine + * @token: a search term + * @error: (nullable): optional return location for an error + * + * Gets all the releases that match a specific token. + * + * Returns: (transfer container) (element-type FwupdResult): results + **/ +GPtrArray * +fu_engine_search(FuEngine *self, const gchar *token, GError **error) +{ + g_autoptr(GPtrArray) releases = + g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + g_auto(XbQueryContext) context = XB_QUERY_CONTEXT_INIT(); + + g_return_val_if_fail(FU_IS_ENGINE(self), NULL); + g_return_val_if_fail(token != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); + + /* bind search token and then query */ + xb_value_bindings_bind_str(xb_query_context_get_bindings(&context), 0, token, NULL); + for (guint i = 0; i < self->search_queries->len; i++) { + XbQuery *query_tmp = g_ptr_array_index(self->search_queries, i); + g_autoptr(GError) error_local = NULL; + g_autoptr(GPtrArray) components = NULL; + + components = + xb_silo_query_with_context(self->silo, query_tmp, &context, &error_local); + if (components == NULL) { + if (g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_NOT_FOUND) || + g_error_matches(error_local, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT)) + continue; + g_propagate_error(error, g_steal_pointer(&error_local)); + fwupd_error_convert(error); + return NULL; + } + for (guint j = 0; j < components->len; j++) { + g_autoptr(FuRelease) rel = fu_release_new(); + XbNode *component = g_ptr_array_index(components, j); + if (!fu_release_load(rel, + NULL, + component, + NULL, + FWUPD_INSTALL_FLAG_FORCE, + error)) + return NULL; + g_ptr_array_add(releases, g_steal_pointer(&rel)); + } + } + + /* success */ + return g_steal_pointer(&releases); +} + +/** * fu_engine_get_releases: * @self: a #FuEngine * @request: a #FuEngineRequest @@ -5563,13 +6039,13 @@ "current version is %s: %s", fu_device_get_version(device), error_str->str); - } else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "current version is %s", - fu_device_get_version(device)); + return NULL; } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "current version is %s", + fu_device_get_version(device)); return NULL; } g_ptr_array_sort_with_data(releases, fu_engine_sort_releases_cb, device); @@ -5795,13 +6271,13 @@ "current version is %s: %s", fu_device_get_version(device), error_str->str); - } else { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "current version is %s", - fu_device_get_version(device)); + return NULL; } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "current version is %s", + fu_device_get_version(device)); return NULL; } g_ptr_array_sort_with_data(releases, fu_engine_sort_releases_cb, device); @@ -5886,17 +6362,18 @@ /* the notification has already been shown to the user */ if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NOTIFIED)) { + g_autofree gchar *id_display = fu_device_get_id_display(device); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, - "User has already been notified about %s [%s]", - fu_device_get_name(device), - fu_device_get_id(device)); + "User has already been notified about %s", + id_display); return NULL; } /* try to set some release properties for the UI */ - fu_engine_fixup_history_device(self, device); + if (!fu_engine_fixup_history_device(self, device, error)) + return NULL; /* we did not either record or find the AppStream ID */ rel = fu_device_get_release_default(device); @@ -5924,9 +6401,8 @@ FuPlugin *plugin = g_ptr_array_index(plugins, i); if (!fu_plugin_runner_startup(plugin, fu_progress_get_child(progress), &error)) { fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED); - if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_NO_HARDWARE); - } g_info("disabling plugin because: %s", error->message); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_CHILD_FINISHED); } @@ -5944,9 +6420,8 @@ g_autoptr(GError) error = NULL; FuPlugin *plugin = g_ptr_array_index(plugins, i); if (!fu_plugin_runner_ready(plugin, fu_progress_get_child(progress), &error)) { - if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_NO_HARDWARE); - } g_info("disabling plugin because: %s", error->message); fu_progress_add_flag(progress, FU_PROGRESS_FLAG_CHILD_FINISHED); } @@ -6035,26 +6510,26 @@ static void fu_engine_adopt_children_device(FuEngine *self, FuDevice *device, FuDevice *device_tmp) { - if (fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD) && - fu_device_has_private_flag(device_tmp, FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE)) { + if (fu_device_has_private_flag_quark(device, quarks[QUARK_HOST_FIRMWARE_CHILD]) && + fu_device_has_private_flag_quark(device_tmp, quarks[QUARK_HOST_FIRMWARE])) { fu_device_set_parent(device, device_tmp); fu_engine_ensure_device_supported(self, device_tmp); return; } - if (fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE) && - fu_device_has_private_flag(device_tmp, FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD)) { + if (fu_device_has_private_flag_quark(device, quarks[QUARK_HOST_FIRMWARE]) && + fu_device_has_private_flag_quark(device_tmp, quarks[QUARK_HOST_FIRMWARE_CHILD])) { fu_device_set_parent(device_tmp, device); fu_engine_ensure_device_supported(self, device_tmp); return; } - if (fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_HOST_CPU_CHILD) && - fu_device_has_private_flag(device_tmp, FU_DEVICE_PRIVATE_FLAG_HOST_CPU)) { + if (fu_device_has_private_flag_quark(device, quarks[QUARK_HOST_CPU_CHILD]) && + fu_device_has_private_flag_quark(device_tmp, quarks[QUARK_HOST_CPU])) { fu_device_set_parent(device, device_tmp); fu_engine_ensure_device_supported(self, device_tmp); return; } - if (fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_HOST_CPU) && - fu_device_has_private_flag(device_tmp, FU_DEVICE_PRIVATE_FLAG_HOST_CPU_CHILD)) { + if (fu_device_has_private_flag_quark(device, quarks[QUARK_HOST_CPU]) && + fu_device_has_private_flag_quark(device_tmp, quarks[QUARK_HOST_CPU_CHILD])) { fu_device_set_parent(device_tmp, device); fu_engine_ensure_device_supported(self, device_tmp); return; @@ -6076,16 +6551,15 @@ g_autoptr(GPtrArray) devices = fu_device_list_get_active(self->device_list); /* find the parent in any existing device */ - for (guint i = 0; fu_device_get_parent(device) == NULL && i < devices->len; i++) { + for (guint i = 0; fu_device_get_parent_internal(device) == NULL && i < devices->len; i++) { FuDevice *device_tmp = g_ptr_array_index(devices, i); fu_engine_adopt_children_device(self, device, device_tmp); } - if (fu_device_get_parent(device) == NULL) { + if (fu_device_get_parent_internal(device) == NULL) { for (guint i = 0; i < devices->len; i++) { FuDevice *device_tmp = g_ptr_array_index(devices, i); - if (!fu_device_has_private_flag( - device_tmp, - FU_DEVICE_PRIVATE_FLAG_AUTO_PARENT_CHILDREN)) + if (!fu_device_has_private_flag_quark(device_tmp, + quarks[QUARK_AUTO_PARENT_CHILDREN])) continue; if (fu_device_get_physical_id(device_tmp) == NULL) continue; @@ -6097,12 +6571,11 @@ } } } - if (fu_device_get_parent(device) == NULL) { + if (fu_device_get_parent_internal(device) == NULL) { for (guint i = 0; i < devices->len; i++) { FuDevice *device_tmp = g_ptr_array_index(devices, i); - if (!fu_device_has_private_flag( - device_tmp, - FU_DEVICE_PRIVATE_FLAG_AUTO_PARENT_CHILDREN)) + if (!fu_device_has_private_flag_quark(device_tmp, + quarks[QUARK_AUTO_PARENT_CHILDREN])) continue; if (fu_device_get_backend_id(device_tmp) == NULL) continue; @@ -6113,7 +6586,7 @@ } } } - if (fu_device_get_parent(device) == NULL) { + if (fu_device_get_parent_internal(device) == NULL) { guids = fu_device_get_parent_guids(device); for (guint j = 0; j < guids->len; j++) { const gchar *guid = g_ptr_array_index(guids, j); @@ -6131,7 +6604,7 @@ for (guint j = 0; j < devices->len; j++) { GPtrArray *parent_physical_ids = NULL; FuDevice *device_tmp = g_ptr_array_index(devices, j); - if (fu_device_get_parent(device_tmp) != NULL) + if (fu_device_get_parent_internal(device_tmp) != NULL) continue; parent_physical_ids = fu_device_get_parent_physical_ids(device_tmp); if (parent_physical_ids == NULL) @@ -6145,7 +6618,7 @@ for (guint j = 0; j < devices->len; j++) { GPtrArray *parent_backend_ids = NULL; FuDevice *device_tmp = g_ptr_array_index(devices, j); - if (fu_device_get_parent(device_tmp) != NULL) + if (fu_device_get_parent_internal(device_tmp) != NULL) continue; parent_backend_ids = fu_device_get_parent_backend_ids(device_tmp); if (parent_backend_ids == NULL) @@ -6161,7 +6634,7 @@ const gchar *guid = g_ptr_array_index(guids, j); for (guint i = 0; i < devices->len; i++) { FuDevice *device_tmp = g_ptr_array_index(devices, i); - if (fu_device_get_parent(device_tmp) != NULL) + if (fu_device_get_parent_internal(device_tmp) != NULL) continue; if (fu_device_has_parent_guid(device_tmp, guid)) fu_engine_set_device_parent(self, device_tmp, device); @@ -6176,7 +6649,7 @@ g_autoptr(FuDevice) proxy = NULL; g_autoptr(GPtrArray) devices = NULL; - if (fu_device_get_proxy(device) != NULL) + if (fu_device_get_proxy_internal(device) != NULL) return; if (fu_device_get_proxy_guid(device) == NULL) return; @@ -6250,8 +6723,9 @@ if (fu_version_compare(fu_device_get_version(device), fwupd_release_get_version(release), fu_device_get_version_format(device)) != 0) { + g_autofree gchar *id_display = fu_device_get_id_display(device); g_info("inheriting needs-activation for %s as version %s != %s", - fu_device_get_name(device), + id_display, fu_device_get_version(device), fwupd_release_get_version(release)); fu_device_add_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION); @@ -6290,10 +6764,10 @@ fu_device_convert_instance_ids(device); /* device still has no GUIDs set! */ - if (device_guids->len == 0 && fu_device_get_children(device)->len == 0) { - g_warning("no GUIDs for device %s [%s]", - fu_device_get_name(device), - fu_device_get_id(device)); + if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE) && device_guids->len == 0 && + fu_device_get_children(device)->len == 0) { + g_autofree gchar *id_display = fu_device_get_id_display(device); + g_warning("no GUIDs for device %s", id_display); return; } @@ -6304,9 +6778,9 @@ for (guint j = 0; j < device_guids->len; j++) { const gchar *device_guid = g_ptr_array_index(device_guids, j); if (g_strcmp0(disabled_guid, device_guid) == 0) { - g_info("%s [%s] is disabled [%s], ignoring from %s", - fu_device_get_name(device), - fu_device_get_id(device), + g_autofree gchar *id_display = fu_device_get_id_display(device); + g_info("%s is disabled [%s], ignoring from %s", + id_display, device_guid, fu_device_get_plugin(device)); return; @@ -6317,9 +6791,8 @@ /* does the device not have an assigned protocol */ if (fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE) && fu_device_get_protocols(device)->len == 0) { - g_warning("device %s [%s] does not define an update protocol", - fu_device_get_id(device), - fu_device_get_name(device)); + g_autofree gchar *id_display = fu_device_get_id_display(device); + g_warning("device %s does not define an update protocol", id_display); } /* if this device is locked get some metadata from AppStream */ @@ -6378,9 +6851,8 @@ } /* no vendor-id, and so no way to lock it down! */ - if (fu_device_is_updatable(device) && fu_device_get_vendor_ids(device)->len == 0) { + if (fu_device_is_updatable(device) && fu_device_get_vendor_ids(device)->len == 0) fu_device_inhibit(device, "vendor-id", "No vendor ID set"); - } /* create new device */ fu_device_list_add(self->device_list, device); @@ -6391,10 +6863,10 @@ !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD) && !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD) && !fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_MD_SET_SIGNED)) { - g_critical("%s [%s] does not declare signed/unsigned payload -- perhaps add " + g_autofree gchar *id_display = fu_device_get_id_display(device); + g_critical("%s does not declare signed/unsigned payload -- perhaps add " "fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD);", - fu_device_get_plugin(device), - fu_device_get_id(device)); + id_display); } #endif @@ -6411,6 +6883,7 @@ !fu_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) { g_autoptr(GError) error_local = NULL; if (!fu_engine_emulator_save_phase(self->emulation, + self->emulator_composite_cnt, self->emulator_phase, self->emulator_write_cnt, &error_local)) @@ -6497,6 +6970,14 @@ return FALSE; } +gboolean +fu_engine_plugin_allows_enumeration(FuEngine *self, FuPlugin *plugin) +{ + if (!fu_engine_config_get_require_immutable_enumeration(self->config)) + return TRUE; + return !fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); +} + static gboolean fu_engine_is_test_plugin_disabled(FuEngine *self, FuPlugin *plugin) { @@ -6707,14 +7188,14 @@ FWUPD_CODEC_FLAG_NONE, error); if (json == NULL) { - g_prefix_error(error, "cannot convert current attrs to string: "); + g_prefix_error_literal(error, "cannot convert current attrs to string: "); return FALSE; } /* check that we did not store this already last boot */ attrs_array = fu_history_get_security_attrs(self->history, 1, error); if (attrs_array == NULL) { - g_prefix_error(error, "failed to get historical attr: "); + g_prefix_error_literal(error, "failed to get historical attr: "); return FALSE; } if (attrs_array->len > 0) { @@ -6727,7 +7208,7 @@ /* write new values */ if (!fu_history_add_security_attribute(self->history, json, host_security_id, error)) { - g_prefix_error(error, "failed to write to DB: "); + g_prefix_error_literal(error, "failed to write to DB: "); return FALSE; } @@ -7322,7 +7803,8 @@ /* is disabled */ if (fu_engine_is_plugin_name_disabled(self, name) || fu_engine_is_test_plugin_disabled(self, plugin) || - !fu_engine_is_plugin_name_enabled(self, name)) { + !fu_engine_is_plugin_name_enabled(self, name) || + !fu_engine_plugin_allows_enumeration(self, plugin)) { g_ptr_array_add(plugins_disabled, g_strdup(name)); fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED); fu_progress_step_done(progress); @@ -7405,8 +7887,8 @@ fu_engine_apply_default_bios_settings_policy(FuEngine *self, GError **error) { const gchar *tmp; - g_autofree gchar *base = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR_PKG); - g_autofree gchar *dirname = g_build_filename(base, "bios-settings.d", NULL); + g_autofree gchar *dirname = + fu_path_build(FU_PATH_KIND_SYSCONFDIR_PKG, "bios-settings.d", NULL); g_autoptr(FuBiosSettings) new_bios_settings = fu_bios_settings_new(); g_autoptr(GHashTable) hashtable = NULL; g_autoptr(GDir) dir = NULL; @@ -7488,11 +7970,11 @@ if (g_strcmp0(fu_device_get_backend_id(device_tmp), fu_device_get_backend_id(device)) == 0) { FuPlugin *plugin; + g_autofree gchar *id_display = fu_device_get_id_display(device_tmp); if (fu_device_has_private_flag(device_tmp, FU_DEVICE_PRIVATE_FLAG_NO_AUTO_REMOVE)) { - g_info("not auto-removing backend device %s [%s] due to flags", - fu_device_get_name(device_tmp), - fu_device_get_id(device_tmp)); + g_info("not auto-removing backend device %s due to flags", + id_display); continue; } plugin = fu_plugin_list_find_by_name(self->plugin_list, @@ -7500,9 +7982,7 @@ NULL); if (plugin == NULL) continue; - g_info("auto-removing backend device %s [%s]", - fu_device_get_name(device_tmp), - fu_device_get_id(device_tmp)); + g_info("auto-removing backend device %s", id_display); fu_plugin_device_remove(plugin, device_tmp); } } @@ -7527,7 +8007,7 @@ if (!fu_plugin_runner_backend_device_added(plugin, device, progress, error)) { #ifdef SUPPORTED_BUILD /* sanity check */ - if (*error == NULL) { + if (*error == NULL) { /* nocheck:error */ g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, @@ -7548,6 +8028,23 @@ { g_autoptr(GPtrArray) possible_plugins = fu_device_get_possible_plugins(device); + /* useful for fwupdtool get-devices --show-all --force */ + if ((self->load_flags & FU_ENGINE_LOAD_FLAG_COLDPLUG_FORCE) > 0 && + possible_plugins->len == 0) { + g_autoptr(GError) error_local = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + locker = fu_device_locker_new(device, &error_local); + if (locker == NULL) { + g_debug("ignoring: %s", error_local->message); + return; + } + if (fu_device_get_instance_ids(device)->len > 0) { + fu_device_remove_flag(device, FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG); + fu_engine_add_device(self, device); + } + return; + } + /* progress */ fu_progress_set_id(progress, G_STRLOC); fu_progress_set_steps(progress, possible_plugins->len); @@ -7661,6 +8158,8 @@ devices = fu_device_list_get_active(self->device_list); for (guint i = 0; i < devices->len; i++) { FuDevice *device_tmp = g_ptr_array_index(devices, i); + if (fu_device_has_flag(device_tmp, FWUPD_DEVICE_FLAG_EMULATED)) + continue; if (!FU_IS_UDEV_DEVICE(device_tmp) || !FU_IS_UDEV_DEVICE(device)) continue; if (g_strcmp0(fu_udev_device_get_sysfs_path(FU_UDEV_DEVICE(device_tmp)), @@ -7771,8 +8270,13 @@ /* if it needed reboot then, it also needs it now... */ if (fu_device_get_update_state(dev_history) == FWUPD_UPDATE_STATE_NEEDS_REBOOT) { - g_info("inheriting needs-reboot for %s", fu_device_get_name(dev)); - fu_device_set_update_state(dev, FWUPD_UPDATE_STATE_NEEDS_REBOOT); + g_autofree gchar *id_display = fu_device_get_id_display(dev); + if (g_strcmp0(fu_device_get_plugin(dev_history), "test") == 0) { + g_debug("ignoring needs-reboot for %s", id_display); + } else { + g_info("inheriting needs-reboot for %s", id_display); + fu_device_set_update_state(dev, FWUPD_UPDATE_STATE_NEEDS_REBOOT); + } } return TRUE; } @@ -7785,7 +8289,7 @@ dev_history, rel_history, error)) { - g_prefix_error(error, "failed to set metadata: "); + g_prefix_error_literal(error, "failed to set metadata: "); return FALSE; } } @@ -7799,7 +8303,7 @@ /* do any late-cleanup actions */ if (!fu_plugin_runner_reboot_cleanup(plugin, dev, error)) { - g_prefix_error(error, "failed to do post-reboot cleanup: "); + g_prefix_error_literal(error, "failed to do post-reboot cleanup: "); return FALSE; } @@ -7979,8 +8483,7 @@ GFileMonitor *monitor; g_autoptr(GFile) file = NULL; g_autoptr(GError) error_local = NULL; - g_autofree gchar *base = fu_path_from_kind(path_kinds[i]); - g_autofree gchar *fn = g_build_filename(base, "local.d", NULL); + g_autofree gchar *fn = fu_path_build(path_kinds[i], "local.d", NULL); file = g_file_new_for_path(fn); monitor = g_file_monitor_directory(file, G_FILE_MONITOR_NONE, NULL, &error_local); @@ -8164,7 +8667,7 @@ g_return_val_if_fail(error == NULL || *error == NULL, FALSE); /* avoid re-loading a second time if fu-tool or fu-util request to */ - if (self->loaded) + if (self->load_flags & FU_ENGINE_LOAD_FLAG_READY) return TRUE; /* progress */ @@ -8216,8 +8719,11 @@ return FALSE; /* read config file */ - if (!fu_config_load(FU_CONFIG(self->config), error)) { - g_prefix_error(error, "Failed to load config: "); + if (!fu_config_load(FU_CONFIG(self->config), + FU_CONFIG_LOAD_FLAG_FIX_PERMISSIONS | FU_CONFIG_LOAD_FLAG_WATCH_FILES | + FU_CONFIG_LOAD_FLAG_MIGRATE_FILES, + error)) { + g_prefix_error_literal(error, "failed to load config: "); return FALSE; } fu_progress_step_done(progress); @@ -8239,7 +8745,7 @@ remote_list_flags |= FU_REMOTE_LIST_LOAD_FLAG_NO_CACHE; fu_remote_list_set_lvfs_metadata_format(self->remote_list, FU_LVFS_METADATA_FORMAT); if (!fu_remote_list_load(self->remote_list, remote_list_flags, error)) { - g_prefix_error(error, "Failed to load remotes: "); + g_prefix_error_literal(error, "failed to load remotes: "); return FALSE; } } @@ -8281,7 +8787,7 @@ /* load plugins early, as we have to call ->load() *before* building quirk silo */ if (!fu_engine_load_plugins(self, flags, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to load plugins: "); + g_prefix_error_literal(error, "failed to load plugins: "); return FALSE; } fu_progress_step_done(progress); @@ -8303,7 +8809,7 @@ } /* set up idle exit */ - if ((flags & FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES) == 0) + if (!fu_context_has_flag(self->ctx, FU_CONTEXT_FLAG_NO_IDLE_SOURCES)) fu_idle_set_timeout(self->idle, fu_engine_config_get_idle_timeout(self->config)); /* on a read-only filesystem don't care about the cache GUID */ @@ -8319,11 +8825,17 @@ if (flags & FU_ENGINE_LOAD_FLAG_READONLY) fu_context_add_flag(self->ctx, FU_CONTEXT_FLAG_INHIBIT_VOLUME_MOUNT); + /* required on Linux kernel < 6.4, or when `RT->QueryVariableInfo` is not supported */ + if (fu_engine_config_get_ignore_efivars_free_space(self->config)) + fu_context_add_flag(self->ctx, FU_CONTEXT_FLAG_IGNORE_EFIVARS_FREE_SPACE); + /* load SMBIOS and the hwids */ if (flags & FU_ENGINE_LOAD_FLAG_HWINFO) { if (!fu_context_load_hwinfo(self->ctx, fu_progress_get_child(progress), - FU_CONTEXT_HWID_FLAG_LOAD_ALL, + FU_CONTEXT_HWID_FLAG_LOAD_ALL | + FU_CONTEXT_HWID_FLAG_FIX_PERMISSIONS | + FU_CONTEXT_HWID_FLAG_WATCH_FILES, error)) return FALSE; } @@ -8331,7 +8843,7 @@ /* load AppStream metadata */ if (!fu_engine_load_metadata_store(self, flags, error)) { - g_prefix_error(error, "Failed to load AppStream data: "); + g_prefix_error_literal(error, "failed to load AppStream data: "); return FALSE; } fu_progress_step_done(progress); @@ -8341,52 +8853,7 @@ return FALSE; /* add the "built-in" firmware types */ - fu_context_add_firmware_gtype(self->ctx, "raw", FU_TYPE_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "cab", FU_TYPE_CAB_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "dfu", FU_TYPE_DFU_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "fdt", FU_TYPE_FDT_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "csv", FU_TYPE_CSV_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "fit", FU_TYPE_FIT_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "dfuse", FU_TYPE_DFUSE_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "ifwi-cpd", FU_TYPE_IFWI_CPD_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "ifwi-fpt", FU_TYPE_IFWI_FPT_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "oprom", FU_TYPE_OPROM_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "fmap", FU_TYPE_FMAP_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "ihex", FU_TYPE_IHEX_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "linear", FU_TYPE_LINEAR_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "srec", FU_TYPE_SREC_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "hid-descriptor", FU_TYPE_HID_DESCRIPTOR); - fu_context_add_firmware_gtype(self->ctx, "archive", FU_TYPE_ARCHIVE_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "smbios", FU_TYPE_SMBIOS); - fu_context_add_firmware_gtype(self->ctx, "acpi-table", FU_TYPE_ACPI_TABLE); - fu_context_add_firmware_gtype(self->ctx, "sbatlevel", FU_TYPE_SBATLEVEL_SECTION); - fu_context_add_firmware_gtype(self->ctx, "edid", FU_TYPE_EDID); - fu_context_add_firmware_gtype(self->ctx, "efi-file", FU_TYPE_EFI_FILE); - fu_context_add_firmware_gtype(self->ctx, "efi-signature", FU_TYPE_EFI_SIGNATURE); - fu_context_add_firmware_gtype(self->ctx, "efi-signature-list", FU_TYPE_EFI_SIGNATURE_LIST); - fu_context_add_firmware_gtype(self->ctx, "efi-load-option", FU_TYPE_EFI_LOAD_OPTION); - fu_context_add_firmware_gtype(self->ctx, - "efi-device-path-list", - FU_TYPE_EFI_DEVICE_PATH_LIST); - fu_context_add_firmware_gtype(self->ctx, "efi-filesystem", FU_TYPE_EFI_FILESYSTEM); - fu_context_add_firmware_gtype(self->ctx, "efi-section", FU_TYPE_EFI_SECTION); - fu_context_add_firmware_gtype(self->ctx, "efi-volume", FU_TYPE_EFI_VOLUME); - fu_context_add_firmware_gtype(self->ctx, "ifd-bios", FU_TYPE_IFD_BIOS); - fu_context_add_firmware_gtype(self->ctx, "ifd-firmware", FU_TYPE_IFD_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "cfu-offer", FU_TYPE_CFU_OFFER); - fu_context_add_firmware_gtype(self->ctx, "cfu-payload", FU_TYPE_CFU_PAYLOAD); - fu_context_add_firmware_gtype(self->ctx, "uswid", FU_TYPE_USWID_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "coswid", FU_TYPE_COSWID_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "pefile", FU_TYPE_PEFILE_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, "elf", FU_TYPE_ELF_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, - "intel-thunderbolt", - FU_TYPE_INTEL_THUNDERBOLT_FIRMWARE); - fu_context_add_firmware_gtype(self->ctx, - "intel-thunderbolt-nvm", - FU_TYPE_INTEL_THUNDERBOLT_NVM); - fu_context_add_firmware_gtype(self->ctx, "usb-device-fw-ds20", FU_TYPE_USB_DEVICE_FW_DS20); - fu_context_add_firmware_gtype(self->ctx, "usb-device-ms-ds20", FU_TYPE_USB_DEVICE_MS_DS20); + fu_engine_add_firmware_gtypes(self); /* we are emulating a different host */ if (host_emulate != NULL) { @@ -8396,11 +8863,13 @@ if (g_file_test(host_emulate, G_FILE_TEST_EXISTS)) { fn = g_strdup(host_emulate); } else { - g_autofree gchar *datadir = fu_path_from_kind(FU_PATH_KIND_DATADIR_PKG); - fn = g_build_filename(datadir, "host-emulate.d", host_emulate, NULL); + fn = fu_path_build(FU_PATH_KIND_DATADIR_PKG, + "host-emulate.d", + host_emulate, + NULL); } if (!fu_engine_load_host_emulation(self, fn, error)) { - g_prefix_error(error, "failed to load emulated host: "); + g_prefix_error_literal(error, "failed to load emulated host: "); return FALSE; } @@ -8410,6 +8879,7 @@ } /* set up backends */ + self->load_flags = flags; if (flags & FU_ENGINE_LOAD_FLAG_COLDPLUG) { FuBackendSetupFlags backend_flags = FU_BACKEND_SETUP_FLAG_NONE; if (flags & FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG) @@ -8432,13 +8902,13 @@ /* delete old data files */ if (!fu_engine_cleanup_state(error)) { - g_prefix_error(error, "Failed to clean up: "); + g_prefix_error_literal(error, "failed to clean up: "); return FALSE; } /* init plugins, adding device and firmware GTypes */ if (!fu_engine_plugins_init(self, fu_progress_get_child(progress), error)) { - g_prefix_error(error, "failed to init plugins: "); + g_prefix_error_literal(error, "failed to init plugins: "); return FALSE; } fu_progress_step_done(progress); @@ -8523,7 +8993,7 @@ g_info("failed to update list of devices: %s", error_json_devices->message); fu_engine_set_status(self, FWUPD_STATUS_IDLE); - self->loaded = TRUE; + self->load_flags |= FU_ENGINE_LOAD_FLAG_READY; /* let clients know engine finished starting up */ fu_engine_emit_changed(self); @@ -8597,12 +9067,24 @@ { GParamSpec *pspec; GObjectClass *object_class = G_OBJECT_CLASS(klass); + const gchar *quark_flags[] = { + FU_DEVICE_PRIVATE_FLAG_AUTO_PARENT_CHILDREN, + FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE, + FU_DEVICE_PRIVATE_FLAG_HOST_FIRMWARE_CHILD, + FU_DEVICE_PRIVATE_FLAG_HOST_CPU, + FU_DEVICE_PRIVATE_FLAG_HOST_CPU_CHILD, + }; + object_class->dispose = fu_engine_dispose; object_class->finalize = fu_engine_finalize; object_class->get_property = fu_engine_get_property; object_class->set_property = fu_engine_set_property; object_class->constructed = fu_engine_constructed; + /* used as device flags, order is important! */ + for (guint i = 0; i < G_N_ELEMENTS(quark_flags); i++) + quarks[i] = g_quark_from_static_string(quark_flags[i]); + pspec = g_param_spec_object("context", NULL, NULL, @@ -8770,12 +9252,11 @@ { FuEngine *self = FU_ENGINE(obj); #ifdef HAVE_UTSNAME_H - struct utsname uname_tmp; + struct utsname uname_tmp = {0}; #endif g_autofree gchar *keyring_path = NULL; g_autofree gchar *pkidir_fw = NULL; g_autofree gchar *pkidir_md = NULL; - g_autofree gchar *sysconfdir = NULL; g_signal_connect(FU_CONTEXT(self->ctx), "security-changed", @@ -8861,10 +9342,9 @@ jcat_context_blob_kind_allow(self->jcat_context, JCAT_BLOB_KIND_GPG); keyring_path = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); jcat_context_set_keyring_path(self->jcat_context, keyring_path); - sysconfdir = fu_path_from_kind(FU_PATH_KIND_SYSCONFDIR); - pkidir_fw = g_build_filename(sysconfdir, "pki", "fwupd", NULL); + pkidir_fw = fu_path_build(FU_PATH_KIND_SYSCONFDIR, "pki", "fwupd", NULL); jcat_context_add_public_keys(self->jcat_context, pkidir_fw); - pkidir_md = g_build_filename(sysconfdir, "pki", "fwupd-metadata", NULL); + pkidir_md = fu_path_build(FU_PATH_KIND_SYSCONFDIR, "pki", "fwupd-metadata", NULL); jcat_context_add_public_keys(self->jcat_context, pkidir_md); /* add some runtime versions of things the daemon depends on */ @@ -8874,7 +9354,6 @@ /* optional kernel version */ #ifdef HAVE_UTSNAME_H - memset(&uname_tmp, 0, sizeof(uname_tmp)); if (uname(&uname_tmp) >= 0) fu_engine_add_runtime_version(self, "org.kernel", uname_tmp.release); #endif @@ -8931,6 +9410,7 @@ self->plugin_filter = g_ptr_array_new_with_free_func(g_free); self->host_security_attrs = fu_security_attrs_new(); self->local_monitors = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + self->search_queries = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); self->acquiesce_loop = g_main_loop_new(NULL, FALSE); self->device_changed_allowlist = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); @@ -8962,8 +9442,6 @@ g_object_unref(self->query_container_checksum2); if (self->query_tag_by_guid_version != NULL) g_object_unref(self->query_tag_by_guid_version); - if (self->coldplug_id != 0) - g_source_remove(self->coldplug_id); if (self->approved_firmware != NULL) g_hash_table_unref(self->approved_firmware); if (self->blocked_firmware != NULL) @@ -8990,6 +9468,7 @@ g_object_unref(self->jcat_context); g_ptr_array_unref(self->plugin_filter); g_ptr_array_unref(self->local_monitors); + g_ptr_array_unref(self->search_queries); g_hash_table_unref(self->device_changed_allowlist); g_object_unref(self->plugin_list); diff -Nru fwupd-2.0.8/src/fu-engine.h fwupd-2.0.20/src/fu-engine.h --- fwupd-2.0.8/src/fu-engine.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-engine.h 2026-02-26 11:36:18.000000000 +0000 @@ -15,43 +15,12 @@ #include "fu-cabinet.h" #include "fu-engine-config.h" +#include "fu-engine-struct.h" #include "fu-release.h" #define FU_TYPE_ENGINE (fu_engine_get_type()) G_DECLARE_FINAL_TYPE(FuEngine, fu_engine, FU, ENGINE, GObject) -/** - * FuEngineLoadFlags: - * @FU_ENGINE_LOAD_FLAG_NONE: No flags set - * @FU_ENGINE_LOAD_FLAG_READONLY: Ignore readonly filesystem errors - * @FU_ENGINE_LOAD_FLAG_COLDPLUG: Enumerate devices - * @FU_ENGINE_LOAD_FLAG_REMOTES: Enumerate remotes - * @FU_ENGINE_LOAD_FLAG_HWINFO: Load details about the hardware - * @FU_ENGINE_LOAD_FLAG_NO_CACHE: Do not save persistent xmlb silos - * @FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES:Do not load idle sources - * @FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS: Load built-in plugins - * @FU_ENGINE_LOAD_FLAG_ENSURE_CLIENT_CERT: Ensure the client certificate exists - * @FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS: Load external dload'ed plugins such as flashrom - * @FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG: Set up device hotplug - * - * The flags to use when loading the engine. - **/ -typedef enum { - FU_ENGINE_LOAD_FLAG_NONE = 0, - FU_ENGINE_LOAD_FLAG_READONLY = 1 << 0, - FU_ENGINE_LOAD_FLAG_COLDPLUG = 1 << 1, - FU_ENGINE_LOAD_FLAG_REMOTES = 1 << 2, - FU_ENGINE_LOAD_FLAG_HWINFO = 1 << 3, - FU_ENGINE_LOAD_FLAG_NO_CACHE = 1 << 4, - FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES = 1 << 5, - FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS = 1 << 6, - FU_ENGINE_LOAD_FLAG_ENSURE_CLIENT_CERT = 1 << 7, - FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS = 1 << 8, - FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG = 1 << 9, - /*< private >*/ - FU_ENGINE_LOAD_FLAG_LAST -} FuEngineLoadFlags; - FuEngine * fu_engine_new(FuContext *ctx) G_GNUC_NON_NULL(1); gboolean @@ -113,6 +82,8 @@ const gchar *device_id, GError **error) G_GNUC_NON_NULL(1, 2, 3); GPtrArray * +fu_engine_search(FuEngine *self, const gchar *token, GError **error) G_GNUC_NON_NULL(1, 2); +GPtrArray * fu_engine_get_downgrades(FuEngine *self, FuEngineRequest *request, const gchar *device_id, @@ -174,6 +145,9 @@ const gchar *value, GError **error) G_GNUC_NON_NULL(1, 2, 3, 4); gboolean +fu_engine_clean_remote(FuEngine *self, const gchar *remote_id, GError **error) + G_GNUC_NON_NULL(1, 2); +gboolean fu_engine_modify_device(FuEngine *self, const gchar *device_id, const gchar *key, @@ -188,14 +162,13 @@ gboolean fu_engine_install_release(FuEngine *self, FuRelease *release, - GInputStream *stream, FuProgress *progress, FwupdInstallFlags flags, - GError **error) G_GNUC_NON_NULL(1, 2, 3, 4); + GError **error) G_GNUC_NON_NULL(1, 2, 3); gboolean fu_engine_install_blob(FuEngine *self, FuDevice *device, - GInputStream *stream_fw, + FuRelease *release, FuProgress *progress, FwupdInstallFlags flags, FwupdFeatureFlags feature_flags, @@ -289,3 +262,5 @@ gboolean fu_engine_undo_host_security_attr(FuEngine *self, const gchar *appstream_id, GError **error) G_GNUC_NON_NULL(1, 2); +gboolean +fu_engine_plugin_allows_enumeration(FuEngine *self, FuPlugin *plugin) G_GNUC_NON_NULL(1, 2); diff -Nru fwupd-2.0.8/src/fu-engine.rs fwupd-2.0.20/src/fu-engine.rs --- fwupd-2.0.8/src/fu-engine.rs 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-engine.rs 2026-02-26 11:36:18.000000000 +0000 @@ -28,25 +28,46 @@ CompositeCleanup, } -#[derive(ToBitString)] -enum FuEngineRequestFlag { +#[derive(ToString)] +enum FuEngineRequestFlags { None = 0, NoRequirements = 1 << 0, AnyRelease = 1 << 1, } -#[derive(ToBitString)] +enum FuEngineLoadFlags { + None = 0, + Readonly = 1 << 0, + Coldplug = 1 << 1, + Remotes = 1 << 2, + Hwinfo = 1 << 3, + NoCache = 1 << 4, + BuiltinPlugins = 1 << 6, + EnsureClientCert = 1 << 7, + ExternalPlugins = 1 << 8, // dload'ed plugins such as flashrom + DeviceHotplug = 1 << 9, + ColdplugForce = 1 << 10, // even without a matched plugin + Ready = 1 << 11, +} + +#[derive(ToString)] enum FuIdleInhibit { None = 0, Timeout = 1 << 0, Signals = 1 << 1, } -enum FuClientFlag { +enum FuClientFlags { None = 0, Active = 1 << 0, } +#[derive(FromString)] +enum FuEngineCapabilityFlags { + Unknown = 0, + IdRequirementGlob = 1 << 0, +} + #[derive(ParseBytes, Default)] #[repr(C, packed)] struct FuStructUdevMonitorNetlinkHeader { diff -Nru fwupd-2.0.8/src/fu-history.c fwupd-2.0.20/src/fu-history.c --- fwupd-2.0.8/src/fu-history.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-history.c 2026-02-26 11:36:18.000000000 +0000 @@ -11,14 +11,11 @@ #include #include #include -#include - -#include "fwupd-security-attr-private.h" #include "fu-device-private.h" #include "fu-history.h" #include "fu-release.h" -#include "fu-security-attr-common.h" +#include "fu-security-attrs-private.h" /* * v1 legacy schema @@ -688,7 +685,7 @@ return g_string_free(str, FALSE); } -/* unset some flags we don't want to store */ +/* nocheck:name unset some flags we don't want to store */ static FwupdDeviceFlags fu_history_get_device_flags_filtered(FuDevice *device) { @@ -713,6 +710,7 @@ fu_history_modify_device(FuHistory *self, FuDevice *device, GError **error) { gint rc; + g_autofree gchar *id_display = fu_device_get_id_display(device); g_autoptr(sqlite3_stmt) stmt = NULL; g_return_val_if_fail(FU_IS_HISTORY(self), FALSE); @@ -723,7 +721,7 @@ return FALSE; /* overwrite entry if it exists */ - g_debug("modifying device %s [%s]", fu_device_get_name(device), fu_device_get_id(device)); + g_debug("modifying device %s", id_display); rc = sqlite3_prepare_v2(self->db, "UPDATE history SET " "update_state = ?1, " @@ -792,6 +790,7 @@ GError **error) { gint rc; + g_autofree gchar *id_display = fu_device_get_id_display(device); g_autofree gchar *metadata = NULL; g_autoptr(sqlite3_stmt) stmt = NULL; @@ -806,7 +805,7 @@ metadata = fu_history_convert_hash_to_string(fu_release_get_metadata(release)); /* overwrite entry if it exists */ - g_debug("modifying device %s [%s]", fu_device_get_name(device), fu_device_get_id(device)); + g_debug("modifying device %s", id_display); rc = sqlite3_prepare_v2(self->db, "UPDATE history SET " "update_state = ?1, " @@ -864,6 +863,7 @@ const gchar *checksum_device; const gchar *checksum = NULL; gint rc; + g_autofree gchar *id_display = fu_device_get_id_display(device); g_autofree gchar *metadata = NULL; g_autoptr(sqlite3_stmt) stmt = NULL; @@ -881,7 +881,7 @@ /* ensure all old device(s) with this ID are removed */ if (!fu_history_remove_device(self, device, error)) return FALSE; - g_debug("add device %s [%s]", fu_device_get_name(device), fu_device_get_id(device)); + g_debug("add device %s", id_display); checksum = fwupd_checksum_get_by_kind(fu_release_get_checksums(release), G_CHECKSUM_SHA1); checksum_device = fwupd_checksum_get_by_kind(fu_device_get_checksums(device), G_CHECKSUM_SHA1); @@ -1002,6 +1002,7 @@ fu_history_remove_device(FuHistory *self, FuDevice *device, GError **error) { gint rc; + g_autofree gchar *id_display = fu_device_get_id_display(device); g_autoptr(sqlite3_stmt) stmt = NULL; g_return_val_if_fail(FU_IS_HISTORY(self), FALSE); @@ -1011,7 +1012,7 @@ if (!fu_history_load(self, error)) return FALSE; - g_debug("remove device %s [%s]", fu_device_get_name(device), fu_device_get_id(device)); + g_debug("remove device %s", id_display); rc = sqlite3_prepare_v2(self->db, "DELETE FROM history WHERE device_id = ?1;", -1, diff -Nru fwupd-2.0.8/src/fu-main-windows.c fwupd-2.0.20/src/fu-main-windows.c --- fwupd-2.0.8/src/fu-main-windows.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-main-windows.c 2026-02-26 11:36:18.000000000 +0000 @@ -18,6 +18,7 @@ #include "fu-daemon.h" #include "fu-debug.h" +/* nocheck:static */ static SERVICE_STATUS gSvcStatus = {.dwServiceType = SERVICE_WIN32_OWN_PROCESS, .dwServiceSpecificExitCode = 0}; static SERVICE_STATUS_HANDLE gSvcStatusHandle = 0; @@ -26,7 +27,7 @@ static void fu_main_svc_report_status(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) { - static DWORD dwCheckPoint = 1; + static DWORD check_point = 1; /* nocheck:static */ gSvcStatus.dwCurrentState = dwCurrentState; gSvcStatus.dwWin32ExitCode = dwWin32ExitCode; @@ -40,7 +41,7 @@ if (dwCurrentState == SERVICE_RUNNING || dwCurrentState == SERVICE_STOPPED) gSvcStatus.dwCheckPoint = 0; else - gSvcStatus.dwCheckPoint = dwCheckPoint++; + gSvcStatus.dwCheckPoint = check_point++; SetServiceStatus(gSvcStatusHandle, &gSvcStatus); } @@ -129,7 +130,7 @@ } /* success */ - g_message("Daemon ready for requests"); + g_message("daemon ready for requests"); return EXIT_SUCCESS; } diff -Nru fwupd-2.0.8/src/fu-main.c fwupd-2.0.20/src/fu-main.c --- fwupd-2.0.8/src/fu-main.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-main.c 2026-02-26 11:36:18.000000000 +0000 @@ -72,35 +72,6 @@ g_warning("failed to stop daemon, will wait: %s\n", error->message); } -static gboolean -fu_main_is_hypervisor(void) -{ - const gchar *flags; - g_autoptr(GHashTable) cpu_attrs = NULL; - - cpu_attrs = fu_cpu_get_attrs(NULL); - if (cpu_attrs == NULL) - return FALSE; - flags = g_hash_table_lookup(cpu_attrs, "flags"); - if (flags == NULL) - return FALSE; - return g_strstr_len(flags, -1, "hypervisor") != NULL; -} - -static gboolean -fu_main_is_container(void) -{ - g_autofree gchar *buf = NULL; - gsize bufsz = 0; - if (!g_file_get_contents("/proc/1/cgroup", &buf, &bufsz, NULL)) - return FALSE; - if (g_strstr_len(buf, (gssize)bufsz, "docker") != NULL) - return TRUE; - if (g_strstr_len(buf, (gssize)bufsz, "lxc") != NULL) - return TRUE; - return FALSE; -} - int main(int argc, char *argv[]) { @@ -151,15 +122,6 @@ return EXIT_FAILURE; } - /* detect the machine kind */ - if (fu_main_is_hypervisor()) { - fu_daemon_set_machine_kind(daemon, FU_DAEMON_MACHINE_KIND_VIRTUAL); - } else if (fu_main_is_container()) { - fu_daemon_set_machine_kind(daemon, FU_DAEMON_MACHINE_KIND_CONTAINER); - } else { - fu_daemon_set_machine_kind(daemon, FU_DAEMON_MACHINE_KIND_PHYSICAL); - } - #ifdef FWUPD_DBUS_SOCKET_ADDRESS /* this is set for macOS and Windows */ if (socket_filename == NULL) diff -Nru fwupd-2.0.8/src/fu-polkit-agent.c fwupd-2.0.20/src/fu-polkit-agent.c --- fwupd-2.0.8/src/fu-polkit-agent.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-polkit-agent.c 2026-02-26 11:36:18.000000000 +0000 @@ -24,7 +24,6 @@ #include #include #include -#include #include #include @@ -37,6 +36,8 @@ G_DEFINE_TYPE(FuPolkitAgent, fu_polkit_agent, G_TYPE_OBJECT) +#pragma GCC diagnostic ignored "-Wanalyzer-fd-leak" + static int fu_polkit_agent_fork_agent(FuPolkitAgent *self, const char *path, ...) { @@ -209,7 +210,7 @@ FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "failed to create pipe: %s", - g_strerror(errno)); + fwupd_strerror(errno)); return FALSE; } @@ -227,7 +228,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed to fork TTY ask password agent: %s", - g_strerror(-r)); + fwupd_strerror(-r)); fu_polkit_agent_close_nointr_nofail(pipe_fd[1]); fu_polkit_agent_close_nointr_nofail(pipe_fd[0]); return FALSE; diff -Nru fwupd-2.0.8/src/fu-polkit-authority.c fwupd-2.0.20/src/fu-polkit-authority.c --- fwupd-2.0.8/src/fu-polkit-authority.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-polkit-authority.c 2026-02-26 11:36:18.000000000 +0000 @@ -54,10 +54,10 @@ /* did not auth */ if (!polkit_authorization_result_get_is_authorized(auth)) { - g_task_return_new_error(task, - FWUPD_ERROR, - FWUPD_ERROR_AUTH_FAILED, - "Failed to obtain auth"); + g_task_return_new_error_literal(task, + FWUPD_ERROR, + FWUPD_ERROR_AUTH_FAILED, + "Failed to obtain auth"); return; } @@ -104,10 +104,10 @@ /* fallback to the caller being euid=0 */ if ((flags & FU_POLKIT_AUTHORITY_CHECK_FLAG_USER_IS_TRUSTED) == 0) { - g_task_return_new_error(task, - FWUPD_ERROR, - FWUPD_ERROR_AUTH_FAILED, - "Failed to obtain auth as not trusted user"); + g_task_return_new_error_literal(task, + FWUPD_ERROR, + FWUPD_ERROR_AUTH_FAILED, + "Failed to obtain auth as not trusted user"); return; } @@ -126,7 +126,7 @@ #ifdef HAVE_POLKIT self->pkauthority = polkit_authority_get_sync(NULL, error); if (self->pkauthority == NULL) { - g_prefix_error(error, "failed to load authority: "); + g_prefix_error_literal(error, "failed to load authority: "); return FALSE; } #endif diff -Nru fwupd-2.0.8/src/fu-polkit-authority.h fwupd-2.0.20/src/fu-polkit-authority.h --- fwupd-2.0.8/src/fu-polkit-authority.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-polkit-authority.h 2026-02-26 11:36:18.000000000 +0000 @@ -15,7 +15,7 @@ FU_POLKIT_AUTHORITY_CHECK_FLAG_NONE = 0, FU_POLKIT_AUTHORITY_CHECK_FLAG_ALLOW_USER_INTERACTION = 1 << 0, FU_POLKIT_AUTHORITY_CHECK_FLAG_USER_IS_TRUSTED = 1 << 1, -} FuPolkitAuthorityCheckFlags; +} G_GNUC_FLAG_ENUM FuPolkitAuthorityCheckFlags; FuPolkitAuthority * fu_polkit_authority_new(void); diff -Nru fwupd-2.0.8/src/fu-release.c fwupd-2.0.20/src/fu-release.c --- fwupd-2.0.8/src/fu-release.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-release.c 2026-02-26 11:36:18.000000000 +0000 @@ -29,6 +29,7 @@ GInputStream *stream; gchar *update_request_id; gchar *device_version_old; + gchar *firmware_basename; GPtrArray *soft_reqs; /* nullable, element-type XbNode */ GPtrArray *hard_reqs; /* nullable, element-type XbNode */ guint64 priority; @@ -56,6 +57,7 @@ if (self->device != NULL) fwupd_codec_string_append(str, idt, "Device", fu_device_get_id(self->device)); fwupd_codec_string_append(str, idt, "DeviceVersionOld", self->device_version_old); + fwupd_codec_string_append(str, idt, "FirmwareBasename", self->firmware_basename); if (self->remote != NULL) fwupd_codec_string_append(str, idt, "Remote", fwupd_remote_get_id(self->remote)); fwupd_codec_string_append_bool(str, idt, "HasConfig", self->config != NULL); @@ -113,6 +115,34 @@ return self->device_version_old; } +/** + * fu_release_get_firmware_basename: + * @self: a #FuRelease + * + * Gets the name of the update binary, typically `firmware.bin` + * + * Returns: a string value, or %NULL if never set. + **/ +const gchar * +fu_release_get_firmware_basename(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->firmware_basename; +} + +void +fu_release_set_firmware_basename(FuRelease *self, const gchar *firmware_basename) +{ + g_return_if_fail(FU_IS_RELEASE(self)); + + /* not changed */ + if (g_strcmp0(self->firmware_basename, firmware_basename) == 0) + return; + + g_free(self->firmware_basename); + self->firmware_basename = g_strdup(firmware_basename); +} + static void fu_release_set_device_version_old(FuRelease *self, const gchar *device_version_old) { @@ -175,6 +205,15 @@ return self->stream; } +/* private: for tests */ +void +fu_release_set_stream(FuRelease *self, GInputStream *stream) +{ + g_return_if_fail(FU_IS_RELEASE(self)); + g_return_if_fail(G_IS_INPUT_STREAM(stream)); + g_set_object(&self->stream, stream); +} + /** * fu_release_get_soft_reqs: * @self: a #FuRelease @@ -250,6 +289,21 @@ } /** + * fu_release_get_remote: + * @self: a #FuRelease + * + * Gets the remote this release should use when loading. + * + * Returns: (transfer none): a #FwupdRemote, or %NULL if never set + **/ +FwupdRemote * +fu_release_get_remote(FuRelease *self) +{ + g_return_val_if_fail(FU_IS_RELEASE(self), NULL); + return self->remote; +} + +/** * fu_release_set_config: * @self: a #FuRelease * @config: (nullable): a #FuEngineConfig @@ -287,31 +341,26 @@ static gchar * fu_release_get_release_version(FuRelease *self, const gchar *version, GError **error) { - FwupdVersionFormat fmt = fu_device_get_version_format(self->device); - guint64 ver_uint32; - g_autoptr(GError) error_local = NULL; - /* already dotted notation */ if (g_strstr_len(version, -1, ".") != NULL) return g_strdup(version); - /* don't touch my version! */ - if (fmt == FWUPD_VERSION_FORMAT_PLAIN || fmt == FWUPD_VERSION_FORMAT_UNKNOWN) - return g_strdup(version); - - /* parse as integer */ - if (!fu_strtoull(version, - &ver_uint32, - 1, - G_MAXUINT32, - FU_INTEGER_BASE_AUTO, - &error_local)) { - g_warning("invalid release version %s: %s", version, error_local->message); - return g_strdup(version); + /* fallback for ESRT-derived UEFI devices */ + if (fu_device_has_private_flag(self->device, FU_DEVICE_PRIVATE_FLAG_LAZY_VERFMT)) { + guint64 version_raw = 0; + if (!fu_strtoull(version, + &version_raw, + 1, + G_MAXUINT64, + FU_INTEGER_BASE_AUTO, + error)) { + return NULL; + } + return fu_device_convert_version(self->device, version_raw, error); } - /* convert to dotted decimal */ - return fu_version_from_uint32((guint32)ver_uint32, fmt); + /* fallback */ + return g_strdup(version); } static gboolean @@ -398,11 +447,14 @@ /* filename */ filename = xb_node_query_text(artifact, "filename", NULL); - if (filename != NULL && !g_str_has_suffix(filename, ".cab")) { - /* some firmware archives was signed with where the - * checksums were the *content* checksums, not the *container* checksum */ - g_debug("ignoring non-binary artifact entry: %s", filename); - return TRUE; + if (filename != NULL) { + g_autofree gchar *fn_lowercase = g_ascii_strdown(filename, -1); + if (!g_str_has_suffix(fn_lowercase, ".cab")) { + /* some firmware archives was signed with where the + * checksums were the *content* checksums, not the *container* checksum */ + g_debug("ignoring non-binary artifact entry: %s", filename); + return TRUE; + } } if (filename != NULL) fu_release_set_filename(self, filename); @@ -615,7 +667,8 @@ /* device requires a version check */ if (fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_VERSION_CHECK_REQUIRED)) { if (!fu_release_check_requirements_version_check(self, error)) { - g_prefix_error(error, "device requires firmware with a version check: "); + g_prefix_error_literal(error, + "device requires firmware with a version check: "); return FALSE; } } @@ -626,12 +679,14 @@ !fu_device_has_protocol(self->device, protocol) && (install_flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { g_autofree gchar *str = NULL; + g_autofree gchar *id_display = fu_device_get_id_display(self->device); + str = fu_strjoin("|", fu_device_get_protocols(self->device)); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "Device %s does not support %s, only %s", - fu_device_get_name(self->device), + id_display, protocol, str); return FALSE; @@ -639,12 +694,12 @@ /* check the device is not locked */ if (fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_LOCKED)) { + g_autofree gchar *id_display = fu_device_get_id_display(self->device); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "Device %s [%s] is locked", - fu_device_get_name(self->device), - fu_device_get_id(self->device)); + "Device %s is locked", + id_display); return FALSE; } @@ -653,12 +708,12 @@ branch_old = fu_device_get_branch(self->device); if ((install_flags & FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH) == 0 && g_strcmp0(branch_old, branch_new) != 0) { + g_autofree gchar *id_display = fu_device_get_id_display(self->device); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, - "Device %s [%s] would switch firmware branch from %s to %s", - fu_device_get_name(self->device), - fu_device_get_id(self->device), + "Device %s would switch firmware branch from %s to %s", + id_display, branch_old != NULL ? branch_old : "default", branch_new != NULL ? branch_new : "default"); return FALSE; @@ -667,11 +722,11 @@ /* no update abilities */ if (!fu_engine_request_has_feature_flag(self->request, FWUPD_FEATURE_FLAG_SHOW_PROBLEMS) && !fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_UPDATABLE)) { + g_autofree gchar *id_display = fu_device_get_id_display(self->device); g_autoptr(GString) str = g_string_new(NULL); g_string_append_printf(str, - "Device %s [%s] does not currently allow updates", - fu_device_get_name(self->device), - fu_device_get_id(self->device)); + "Device %s does not currently allow updates", + id_display); if (fu_device_get_update_error(self->device) != NULL) { g_string_append_printf(str, ": %s", @@ -719,15 +774,30 @@ return TRUE; } + /* allow using no-version-expected on emulated devices, or when not build as supported */ + if (fu_device_has_private_flag(self->device, FU_DEVICE_PRIVATE_FLAG_NO_VERSION_EXPECTED)) { +#ifdef SUPPORTED_BUILD + if (!fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_EMULATED)) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "only emulated devices can install releases with no " + "version when -Dsupported_build"); + return FALSE; + } +#endif + return TRUE; + } + /* ensure device has a version */ version = fu_device_get_version(self->device); if (version == NULL) { + g_autofree gchar *id_display = fu_device_get_id_display(self->device); g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "Device %s [%s] has no firmware version", - fu_device_get_name(self->device), - fu_device_get_id(self->device)); + "Device %s has no firmware version", + id_display); return FALSE; } @@ -764,11 +834,11 @@ fu_release_get_version(self), fu_device_get_version_format(self->device)); if (fu_device_has_flag(self->device, FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE) && - vercmp > 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - "Device only supports version upgrades"); + vercmp >= 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + "Device only supports version upgrades"); return FALSE; } if (vercmp == 0 && (install_flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0) { @@ -814,7 +884,7 @@ /** * fu_release_load: * @self: a #FuRelease - * @cabinet: a #FuCabinet + * @cabinet: (nullable): a #FuCabinet * @component: (not nullable): a #XbNode * @rel_optional: (nullable): a #XbNode * @install_flags: a #FwupdInstallFlags, e.g. %FWUPD_INSTALL_FLAG_FORCE @@ -940,7 +1010,9 @@ return FALSE; fwupd_release_set_version(FWUPD_RELEASE(self), version_rel); } else { - fwupd_release_set_version(FWUPD_RELEASE(self), tmp); + /* historical releases have ->convert_version() already done */ + if (fu_release_get_version(self) == NULL) + fu_release_set_version(self, tmp); } /* optional release ID -- currently a integer but maybe namespaced in the future */ @@ -1134,11 +1206,14 @@ /* get per-release firmware stream */ blob_basename = xb_node_get_data(rel, "fwupd::FirmwareBasename"); if (cabinet != NULL && blob_basename != NULL) { - const gchar *basename = (const gchar *)g_bytes_get_data(blob_basename, NULL); g_autoptr(FuFirmware) img = NULL; - img = fu_firmware_get_image_by_id(FU_FIRMWARE(cabinet), basename, error); + + self->firmware_basename = fu_strsafe_bytes(blob_basename, G_MAXSIZE); + img = fu_firmware_get_image_by_id(FU_FIRMWARE(cabinet), + self->firmware_basename, + error); if (img == NULL) { - g_prefix_error(error, "failed to find %s: ", basename); + g_prefix_error(error, "failed to find %s: ", self->firmware_basename); return FALSE; } self->stream = fu_firmware_get_stream(img, error); @@ -1318,6 +1393,7 @@ g_free(self->update_request_id); g_free(self->device_version_old); + g_free(self->firmware_basename); if (self->request != NULL) g_object_unref(self->request); if (self->device != NULL) diff -Nru fwupd-2.0.8/src/fu-release.h fwupd-2.0.20/src/fu-release.h --- fwupd-2.0.8/src/fu-release.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-release.h 2026-02-26 11:36:18.000000000 +0000 @@ -52,8 +52,12 @@ fu_release_to_string(FuRelease *self) G_GNUC_NON_NULL(1); FuDevice * fu_release_get_device(FuRelease *self) G_GNUC_NON_NULL(1); +FwupdRemote * +fu_release_get_remote(FuRelease *self) G_GNUC_NON_NULL(1); GInputStream * fu_release_get_stream(FuRelease *self) G_GNUC_NON_NULL(1); +void +fu_release_set_stream(FuRelease *self, GInputStream *stream) G_GNUC_NON_NULL(1, 2); FuEngineRequest * fu_release_get_request(FuRelease *self) G_GNUC_NON_NULL(1); GPtrArray * @@ -64,6 +68,8 @@ fu_release_get_update_request_id(FuRelease *self) G_GNUC_NON_NULL(1); const gchar * fu_release_get_device_version_old(FuRelease *self) G_GNUC_NON_NULL(1); +const gchar * +fu_release_get_firmware_basename(FuRelease *self) G_GNUC_NON_NULL(1); void fu_release_set_request(FuRelease *self, FuEngineRequest *request) G_GNUC_NON_NULL(1); @@ -73,6 +79,9 @@ fu_release_set_remote(FuRelease *self, FwupdRemote *remote) G_GNUC_NON_NULL(1); void fu_release_set_config(FuRelease *self, FuEngineConfig *config) G_GNUC_NON_NULL(1); +void +fu_release_set_firmware_basename(FuRelease *self, const gchar *firmware_basename) + G_GNUC_NON_NULL(1); gboolean fu_release_load(FuRelease *self, diff -Nru fwupd-2.0.8/src/fu-remote-list.c fwupd-2.0.20/src/fu-remote-list.c --- fwupd-2.0.8/src/fu-remote-list.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-remote-list.c 2026-02-26 11:36:18.000000000 +0000 @@ -84,30 +84,11 @@ fu_remote_list_emit_changed(self); } -static guint64 -_fwupd_remote_get_mtime(FwupdRemote *remote) -{ - g_autoptr(GFile) file = NULL; - g_autoptr(GFileInfo) info = NULL; - - file = g_file_new_for_path(fwupd_remote_get_filename_cache(remote)); - if (!g_file_query_exists(file, NULL)) - return G_MAXUINT64; - info = g_file_query_info(file, - G_FILE_ATTRIBUTE_TIME_MODIFIED, - G_FILE_QUERY_INFO_NONE, - NULL, - NULL); - if (info == NULL) - return G_MAXUINT64; - return g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED); -} - /* GLib only returns the very unhelpful "Unable to find default local file monitor type" * when /proc/sys/fs/inotify/max_user_instances is set too low; detect this and set a proper * error prefix to aid debugging when the daemon fails to start */ static void -fu_remote_list_fixup_inotify_error(GError **error) +fu_remote_list_fixup_inotify_error(GError **error) /* nocheck:error */ { #ifdef HAVE_INOTIFY_H int fd; @@ -116,13 +97,18 @@ fd = inotify_init(); if (fd == -1) { - g_prefix_error(error, "Could not initialize inotify, check %s: ", fn); + g_prefix_error(error, /* nocheck:error */ + "Could not initialize inotify, check %s: ", + fn); return; } wd = inotify_add_watch(fd, fn, IN_MODIFY); if (wd < 0) { - if (errno == ENOSPC) - g_prefix_error(error, "No space for inotify, check %s: ", fn); + if (errno == ENOSPC) { + g_prefix_error(error, /* nocheck:error */ + "No space for inotify, check %s: ", + fn); + } } else { inotify_rm_watch(fd, wd); } @@ -291,9 +277,9 @@ static gboolean fu_remote_list_add_for_file(FuRemoteList *self, const gchar *filename, GError **error) { - FwupdRemote *remote_tmp; g_autofree gchar *remotesdir = NULL; g_autoptr(FwupdRemote) remote = fu_remote_new(); + g_autoptr(FwupdRemote) remote_tmp = NULL; /* set directory to store data */ remotesdir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_METADATA); @@ -307,7 +293,7 @@ } /* does it already exist */ - remote_tmp = fu_remote_list_get_by_id(self, fwupd_remote_get_id(remote)); + remote_tmp = fu_remote_list_get_by_id(self, fwupd_remote_get_id(remote), NULL); if (remote_tmp != NULL) { g_debug("remote %s already added from %s", fwupd_remote_get_id(remote), @@ -398,7 +384,8 @@ } /* set mtime */ - fwupd_remote_set_mtime(remote, _fwupd_remote_get_mtime(remote)); + if (!fwupd_remote_ensure_mtime(remote, error)) + return FALSE; fu_remote_list_add_remote(self, remote); /* success */ @@ -438,23 +425,17 @@ const gchar *value, GError **error) { - FwupdRemote *remote; const gchar *filename; g_autofree gchar *filename_new = NULL; g_autofree gchar *value_old = NULL; + g_autoptr(FwupdRemote) remote = NULL; g_autoptr(GError) error_local = NULL; g_autoptr(GKeyFile) keyfile = g_key_file_new(); /* check remote is valid */ - remote = fu_remote_list_get_by_id(self, remote_id); - if (remote == NULL) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_FOUND, - "remote %s not found", - remote_id); + remote = fu_remote_list_get_by_id(self, remote_id, error); + if (remote == NULL) return FALSE; - } /* modify the remote */ filename = fwupd_remote_get_filename_source(remote); @@ -471,11 +452,11 @@ if (!g_key_file_save_to_file(keyfile, filename, &error_local)) { if (g_error_matches(error_local, G_FILE_ERROR, G_FILE_ERROR_PERM)) { g_autofree gchar *basename = g_path_get_basename(filename); - g_autofree gchar *remotesdir_mut = - fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); - filename_new = - g_build_filename(remotesdir_mut, "remotes.d", basename, NULL); + filename_new = fu_path_build(FU_PATH_KIND_LOCALSTATEDIR_PKG, + "remotes.d", + basename, + NULL); if (!fu_path_mkdir_parent(filename_new, error)) return FALSE; g_info("falling back from %s to %s", filename, filename_new); @@ -525,12 +506,12 @@ if (order == NULL) continue; for (guint j = 0; order[j] != NULL; j++) { - FwupdRemote *remote2; + g_autoptr(FwupdRemote) remote2 = NULL; if (g_strcmp0(order[j], fwupd_remote_get_id(remote)) == 0) { g_debug("ignoring self-dep remote %s", order[j]); continue; } - remote2 = fu_remote_list_get_by_id(self, order[j]); + remote2 = fu_remote_list_get_by_id(self, order[j], NULL); if (remote2 == NULL) { g_debug("ignoring unfound remote %s", order[j]); continue; @@ -558,12 +539,12 @@ if (order == NULL) continue; for (guint j = 0; order[j] != NULL; j++) { - FwupdRemote *remote2; + g_autoptr(FwupdRemote) remote2 = NULL; if (g_strcmp0(order[j], fwupd_remote_get_id(remote)) == 0) { g_debug("ignoring self-dep remote %s", order[j]); continue; } - remote2 = fu_remote_list_get_by_id(self, order[j]); + remote2 = fu_remote_list_get_by_id(self, order[j], NULL); if (remote2 == NULL) { g_debug("ignoring unfound remote %s", order[j]); continue; @@ -645,13 +626,11 @@ fu_remote_list_load_metainfos(XbBuilder *builder, GError **error) { const gchar *fn; - g_autofree gchar *datadir = NULL; g_autofree gchar *metainfo_path = NULL; g_autoptr(GDir) dir = NULL; /* pkg metainfo dir */ - datadir = fu_path_from_kind(FU_PATH_KIND_DATADIR_PKG); - metainfo_path = g_build_filename(datadir, "metainfo", NULL); + metainfo_path = fu_path_build(FU_PATH_KIND_DATADIR_PKG, "metainfo", NULL); if (!g_file_test(metainfo_path, G_FILE_TEST_EXISTS)) return TRUE; @@ -739,8 +718,8 @@ if (xmlb == NULL) return FALSE; } else { - g_autofree gchar *cachedirpkg = fu_path_from_kind(FU_PATH_KIND_CACHEDIR_PKG); - g_autofree gchar *xmlbfn = g_build_filename(cachedirpkg, "metainfo.xmlb", NULL); + g_autofree gchar *xmlbfn = + fu_path_build(FU_PATH_KIND_CACHEDIR_PKG, "metainfo.xmlb", NULL); xmlb = g_file_new_for_path(xmlbfn); } self->silo = xb_builder_ensure(builder, xmlb, compile_flags, NULL, error); @@ -755,18 +734,21 @@ fu_remote_list_get_all(FuRemoteList *self) { g_return_val_if_fail(FU_IS_REMOTE_LIST(self), NULL); - return self->array; + return g_ptr_array_ref(self->array); } FwupdRemote * -fu_remote_list_get_by_id(FuRemoteList *self, const gchar *remote_id) +fu_remote_list_get_by_id(FuRemoteList *self, const gchar *remote_id, GError **error) { g_return_val_if_fail(FU_IS_REMOTE_LIST(self), NULL); + g_return_val_if_fail(remote_id != NULL, NULL); + g_return_val_if_fail(error == NULL || *error == NULL, NULL); for (guint i = 0; i < self->array->len; i++) { FwupdRemote *remote = g_ptr_array_index(self->array, i); if (g_strcmp0(remote_id, fwupd_remote_get_id(remote)) == 0) - return remote; + return g_object_ref(remote); } + g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "remote %s not found", remote_id); return NULL; } diff -Nru fwupd-2.0.8/src/fu-remote-list.h fwupd-2.0.20/src/fu-remote-list.h --- fwupd-2.0.8/src/fu-remote-list.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-remote-list.h 2026-02-26 11:36:18.000000000 +0000 @@ -29,7 +29,7 @@ FU_REMOTE_LIST_LOAD_FLAG_FIX_METADATA_URI = 1 << 3, /*< private >*/ FU_REMOTE_LIST_LOAD_FLAG_LAST -} FuRemoteListLoadFlags; +} G_GNUC_FLAG_ENUM FuRemoteListLoadFlags; FuRemoteList * fu_remote_list_new(void); @@ -48,7 +48,8 @@ GPtrArray * fu_remote_list_get_all(FuRemoteList *self) G_GNUC_NON_NULL(1); FwupdRemote * -fu_remote_list_get_by_id(FuRemoteList *self, const gchar *remote_id) G_GNUC_NON_NULL(1, 2); +fu_remote_list_get_by_id(FuRemoteList *self, const gchar *remote_id, GError **error) + G_GNUC_NON_NULL(1, 2); void fu_remote_list_set_lvfs_metadata_format(FuRemoteList *self, const gchar *lvfs_metadata_format); diff -Nru fwupd-2.0.8/src/fu-remote.c fwupd-2.0.20/src/fu-remote.c --- fwupd-2.0.8/src/fu-remote.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-remote.c 2026-02-26 11:36:18.000000000 +0000 @@ -91,6 +91,12 @@ else fwupd_remote_remove_flag(self, FWUPD_REMOTE_FLAG_APPROVAL_REQUIRED); } + if (g_key_file_has_key(kf, group, "NoPhasedUpdates", NULL)) { + if (g_key_file_get_boolean(kf, group, "NoPhasedUpdates", NULL)) + fwupd_remote_add_flag(self, FWUPD_REMOTE_FLAG_NO_PHASED_UPDATES); + else + fwupd_remote_remove_flag(self, FWUPD_REMOTE_FLAG_NO_PHASED_UPDATES); + } if (g_key_file_has_key(kf, group, "Title", NULL)) { g_autofree gchar *tmp = g_key_file_get_string(kf, group, "Title", NULL); fwupd_remote_set_title(self, tmp); @@ -227,6 +233,51 @@ return g_key_file_save_to_file(kf, filename, error); } +/** + * fu_remote_clean: + * @self: a #FwupdRemote + * @error: (nullable): optional return location for an error + * + * Cleans all files in the remote cachedir. + * + * Returns: %TRUE for success + **/ +gboolean +fu_remote_clean(FwupdRemote *self, GError **error) +{ + g_autofree gchar *dirname = NULL; + g_autoptr(GPtrArray) files = NULL; + g_autoptr(GError) error_local = NULL; + + g_return_val_if_fail(FWUPD_IS_REMOTE(self), FALSE); + g_return_val_if_fail(error == NULL || *error == NULL, FALSE); + + dirname = g_path_get_dirname(fwupd_remote_get_filename_cache(self)); + files = fu_path_get_files(dirname, &error_local); + if (files == NULL) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE)) + return TRUE; + g_propagate_error(error, g_steal_pointer(&error_local)); + return FALSE; + } + for (guint i = 0; i < files->len; i++) { + const gchar *fn = g_ptr_array_index(files, i); + g_autoptr(GFile) file = g_file_new_for_path(fn); + + /* cache */ + if (!g_file_delete(file, NULL, error)) { + fwupd_error_convert(error); + g_prefix_error(error, "failed to delete %s: ", fn); + return FALSE; + } + } + + /* success */ + if (!fwupd_remote_ensure_checksum_sig(self, error)) + return FALSE; + return fwupd_remote_ensure_mtime(self, error); +} + static void fu_remote_init(FuRemote *self) { diff -Nru fwupd-2.0.8/src/fu-remote.h fwupd-2.0.20/src/fu-remote.h --- fwupd-2.0.8/src/fu-remote.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-remote.h 2026-02-26 11:36:18.000000000 +0000 @@ -21,6 +21,8 @@ const gchar *filename, GCancellable *cancellable, GError **error) G_GNUC_NON_NULL(1, 2); +gboolean +fu_remote_clean(FwupdRemote *self, GError **error) G_GNUC_NON_NULL(1); FwupdRemote * fu_remote_new(void); diff -Nru fwupd-2.0.8/src/fu-security-attr-common.c fwupd-2.0.20/src/fu-security-attr-common.c --- fwupd-2.0.8/src/fu-security-attr-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-security-attr-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -12,7 +12,6 @@ #include "fwupd-security-attr-private.h" #include "fu-security-attr-common.h" -#include "fu-security-attrs-private.h" gchar * fu_security_attr_get_name(FwupdSecurityAttr *attr) diff -Nru fwupd-2.0.8/src/fu-security-attr-common.h fwupd-2.0.20/src/fu-security-attr-common.h --- fwupd-2.0.8/src/fu-security-attr-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-security-attr-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -10,8 +10,6 @@ #include -#include "fu-security-attrs-private.h" - gchar * fu_security_attr_get_name(FwupdSecurityAttr *attr) G_GNUC_NON_NULL(1); const gchar * diff -Nru fwupd-2.0.8/src/fu-self-test.c fwupd-2.0.20/src/fu-self-test.c --- fwupd-2.0.8/src/fu-self-test.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-self-test.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,11 +10,9 @@ #include #include -#include #include #include "fwupd-remote-private.h" -#include "fwupd-security-attr-private.h" #include "../plugins/test/fu-test-plugin.h" #include "fu-bios-settings-private.h" @@ -25,6 +23,8 @@ #include "fu-context-private.h" #include "fu-device-list.h" #include "fu-device-private.h" +#include "fu-drm-device-private.h" +#include "fu-efivars-private.h" #include "fu-engine-config.h" #include "fu-engine-helper.h" #include "fu-engine-requirements.h" @@ -36,19 +36,22 @@ #include "fu-release-common.h" #include "fu-remote-list.h" #include "fu-remote.h" -#include "fu-security-attr-common.h" -#include "fu-smbios-private.h" +#include "fu-security-attrs-private.h" #include "fu-usb-backend.h" +#include "fu-util-common.h" #ifdef HAVE_GIO_UNIX #include "fu-unix-seekable-input-stream.h" #endif +#pragma GCC diagnostic ignored "-Wanalyzer-null-argument" + typedef struct { FuPlugin *plugin; FuContext *ctx; } FuTest; +/* nocheck:static */ static GMainLoop *_test_loop = NULL; static guint _test_loop_timeout_id = 0; @@ -166,6 +169,45 @@ } static void +fu_util_func(void) +{ + const gchar *tmp; + g_autoptr(FwupdClient) client = fwupd_client_new(); + g_autoptr(FwupdDevice) device = fwupd_device_new(); + + for (FwupdDeviceProblem i = 1; i < G_MAXUINT64; i <<= 1) { + g_autofree gchar *str = NULL; + tmp = fwupd_device_problem_to_string(i); + if (tmp == NULL) + break; + str = fu_util_device_problem_to_string(client, device, i); + g_assert_nonnull(str); + } + for (FwupdReleaseFlags i = 1; i < G_MAXUINT64; i <<= 1) { + tmp = fwupd_release_flag_to_string(i); + if (tmp == NULL) + break; + g_assert_nonnull(fu_util_release_flag_to_string(i)); + } + for (FwupdRequestFlags i = 1; i < G_MAXUINT64; i <<= 1) { + tmp = fwupd_request_flag_to_string(i); + if (tmp == NULL) + break; + g_assert_nonnull(fu_util_request_flag_to_string(i)); + } + for (FwupdPluginFlags i = 1; i < G_MAXUINT64; i <<= 1) { + g_autofree gchar *str = NULL; + if (i == FWUPD_PLUGIN_FLAG_CLEAR_UPDATABLE || i == FWUPD_PLUGIN_FLAG_USER_WARNING) + continue; + tmp = fwupd_plugin_flag_to_string(i); + if (tmp == NULL) + break; + str = fu_util_plugin_flag_to_string(i); + g_assert_nonnull(str); + } +} + +static void fu_idle_func(void) { guint token; @@ -461,6 +503,92 @@ } static void +fu_engine_requirements_vercmp_glob_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(self->ctx); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(NULL); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + const gchar *xml = "" + " " + " id-requirement-glob" + " org.freedesktop.fwupd\n" + " " + " " + " " + " " + ""; + + /* hardcode to specific branch */ + fu_context_add_runtime_version(self->ctx, "org.freedesktop.fwupd", "1.9.8"); + + /* make the component require one thing */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this passes */ + fu_release_set_request(release, request); + ret = fu_release_load(release, NULL, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_requirements_check(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* reset back to reality */ + fu_context_add_runtime_version(self->ctx, "org.freedesktop.fwupd", VERSION); +} + +static void +fu_engine_requirements_vercmp_glob_fallback_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(self->ctx); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(NULL); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + const gchar *xml = "" + " " + " org.freedesktop.fwupd\n" + " id-requirement-glob" + " " + " " + " " + " " + ""; + + /* make the component require one thing */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this passes */ + fu_release_set_request(release, request); + ret = fu_release_load(release, NULL, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_requirements_check(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void fu_engine_requirements_not_hardware_func(gconstpointer user_data) { FuTest *self = (FuTest *)user_data; @@ -503,6 +631,119 @@ } static void +fu_engine_requirements_phased_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(self->ctx); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(NULL); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(FwupdRemote) remote = fwupd_remote_new(); + g_autoptr(GError) error = NULL; + const gchar *xml = "" + " " + " 10" + " org.freedesktop.fwupd\n" + " " + " " + " " + " " + ""; + + /* do not include into seed */ + g_assert_cmpstr(fu_engine_get_host_machine_id(engine), ==, NULL); + + /* make the component require one thing */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this passes */ + fu_release_set_request(release, request); + fu_release_set_remote(release, remote); + fwupd_remote_set_mtime(remote, 12340); + ret = fu_release_load(release, NULL, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_engine_requirements_check(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check this still passes as we're ignoring */ + fwupd_remote_set_mtime(remote, 12345); + ret = fu_engine_requirements_check(engine, + release, + FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check this now fails */ + ret = fu_engine_requirements_check(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_false(ret); + g_clear_error(&error); + + /* user disabled this */ + fwupd_remote_add_flag(remote, FWUPD_REMOTE_FLAG_NO_PHASED_UPDATES); + ret = fu_engine_requirements_check(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); +} + +static void +fu_engine_requirements_phased_old_fwupd_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + g_autoptr(FuEngine) engine = fu_engine_new(self->ctx); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(NULL); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(FwupdRemote) remote = fwupd_remote_new(); + g_autoptr(GError) error = NULL; + const gchar *xml = "" + " " + " 10" + " org.freedesktop.fwupd\n" + " " + " " + " " + " " + ""; + + /* do not include into seed */ + g_assert_cmpstr(fu_engine_get_host_machine_id(engine), ==, NULL); + + /* make the component require one thing */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this passes */ + fu_release_set_request(release, request); + fu_release_set_remote(release, remote); + fwupd_remote_set_mtime(remote, 12340); + ret = fu_release_load(release, NULL, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check this fails because the fwupd requirement is too low */ + ret = fu_engine_requirements_check(engine, release, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_false(ret); +} + +static void fu_engine_requirements_version_require_func(gconstpointer user_data) { FuTest *self = (FuTest *)user_data; @@ -1053,27 +1294,74 @@ } static void +fu_engine_requirements_only_upgrade_reinstall_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngineRequest) request = fu_engine_request_new(NULL); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + g_autoptr(XbNode) component = NULL; + g_autoptr(XbSilo) silo = NULL; + const gchar *xml = + "" + " " + " 12345678-1234-1234-1234-123456789012" + " " + " " + " " + " " + ""; + + /* set up a dummy device */ + fu_device_set_version(device, "1.2.3"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UPDATABLE); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_ONLY_VERSION_UPGRADE); + fu_device_add_instance_id(device, "12345678-1234-1234-1234-123456789012"); + + /* make the component require three things */ + silo = xb_silo_new_from_xml(xml, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + component = xb_silo_query_first(silo, "component", &error); + g_assert_no_error(error); + g_assert_nonnull(component); + + /* check this fails */ + fu_release_set_device(release, device); + fu_release_set_request(release, request); + ret = fu_release_load(release, NULL, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_release_check_version(release, component, FWUPD_INSTALL_FLAG_NONE, &error); + g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); + g_assert_nonnull(g_strstr_len(error->message, -1, "Device only supports version upgrades")); + g_assert_false(ret); +} + +static void fu_engine_plugin_device_gtype(FuTest *self, GType gtype) { + GType proxy_gtype; gboolean ret; g_autofree gchar *str = NULL; g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuFirmware) firmware = NULL; g_autoptr(FuProgress) progress_tmp = fu_progress_new(G_STRLOC); g_autoptr(FuSecurityAttrs) attrs = fu_security_attrs_new(); g_autoptr(GError) error = NULL; g_autoptr(GHashTable) metadata_post = NULL; g_autoptr(GHashTable) metadata_pre = NULL; - const gchar *nolocker[] = { - "FuPciPspDevice", - "FuSynapticsRmiPs2Device", - "FuUefiSbatDevice", - NULL, - }; + g_autoptr(GInputStream) stream = g_memory_input_stream_new(); g_debug("loading %s", g_type_name(gtype)); device = g_object_new(gtype, "context", self->ctx, "physical-id", "/sys", NULL); g_assert_nonnull(device); fu_device_set_plugin(device, "test"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_EMULATED); /* version convert */ if (fu_device_get_version_format(device) != FWUPD_VERSION_FORMAT_UNKNOWN) @@ -1106,11 +1394,49 @@ str = fu_device_to_string(device); g_assert_nonnull(str); + /* proxy required */ + proxy_gtype = fu_device_get_proxy_gtype(device); + if (proxy_gtype != G_TYPE_INVALID && G_TYPE_FUNDAMENTAL(proxy_gtype) == G_TYPE_OBJECT) { + g_autoptr(FuDevice) proxy = + g_object_new(proxy_gtype, "context", self->ctx, "physical-id", "/sys", NULL); + fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY); + fu_device_set_proxy(device, proxy); + } + /* ->probe() and ->setup */ - if (!g_strv_contains(nolocker, g_type_name(gtype))) { - g_autoptr(FuDeviceLocker) locker = fu_device_locker_new(device, NULL); - if (locker != NULL) - g_debug("did ->probe() and ->setup()!"); + locker = fu_device_locker_new(device, NULL); + if (locker != NULL) + g_debug("did ->probe() and ->setup()!"); + + /* ->prepare(), ->attach(), ->detach, ->cleanup */ + if (fu_device_prepare(device, progress_tmp, FWUPD_INSTALL_FLAG_FORCE, NULL)) + g_debug("did ->prepare()"); + if (fu_device_attach_full(device, progress_tmp, NULL)) + g_debug("did ->attach()"); + if (fu_device_detach_full(device, progress_tmp, NULL)) + g_debug("did ->detach()"); + if (fu_device_cleanup(device, progress_tmp, FWUPD_INSTALL_FLAG_FORCE, NULL)) + g_debug("did ->cleanup()"); + + /* ->prepare_firmware() */ + firmware = fu_device_prepare_firmware(device, + stream, + progress_tmp, + FU_FIRMWARE_PARSE_FLAG_NONE, + NULL); + if (firmware == NULL) { + GType firmware_gtype = fu_device_get_firmware_gtype(device); + if (firmware_gtype != G_TYPE_INVALID) + firmware = g_object_new(firmware_gtype, NULL); + } + if (firmware != NULL) { + if (fu_device_write_firmware(device, + firmware, + progress_tmp, + FWUPD_INSTALL_FLAG_FORCE, + NULL)) { + g_debug("did ->write_firmware()!"); + } } } @@ -1135,6 +1461,9 @@ firmware = g_object_new(gtype, NULL); g_assert_nonnull(firmware); + /* ensure we have data set even if parsing fails */ + fu_firmware_set_bytes(firmware, fw); + /* version convert */ if (fu_firmware_get_version_format(firmware) != FWUPD_VERSION_FORMAT_UNKNOWN) fu_firmware_set_version_raw(firmware, 0); @@ -1142,8 +1471,12 @@ /* parse nonsense */ if (gtype != FU_TYPE_FIRMWARE && !fu_firmware_has_flag(FU_FIRMWARE(firmware), FU_FIRMWARE_FLAG_NO_AUTO_DETECTION)) { - ret = - fu_firmware_parse_bytes(firmware, fw, 0x0, FWUPD_INSTALL_FLAG_NO_SEARCH, NULL); + ret = fu_firmware_parse_bytes(firmware, + fw, + 0x0, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH | + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, + NULL); g_assert_false(ret); } @@ -1167,11 +1500,79 @@ } static void +fu_engine_test_plugin_mutable_enumeration(gconstpointer user_data) +{ + const gchar *fake_localconf_fn = "/tmp/fwupd-self-test/var/etc/fwupd/fwupd.conf"; + FuTest *self = (FuTest *)user_data; + g_autoptr(FuEngine) engine = NULL; + g_autoptr(FuPlugin) plugin = fu_plugin_new(NULL); + g_autoptr(GError) error = NULL; + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + gboolean ret; + + /* ensure empty tree */ + fu_self_test_mkroot(); + + g_unsetenv("CONFIGURATION_DIRECTORY"); + (void)g_setenv("FWUPD_SYSCONFDIR", "/tmp/fwupd-self-test", TRUE); + + ret = fu_path_mkdir_parent(fake_localconf_fn, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = g_file_set_contents(fake_localconf_fn, + "# use `man 5 fwupd.conf` for documentation\n" + "[fwupd]\n" + "RequireImmutableEnumeration=true\n", + -1, + &error); + g_assert_no_error(error); + g_assert_true(ret); + + engine = fu_engine_new(self->ctx); + g_assert_nonnull(engine); + + ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* engine requires, plugin doesn't have */ + ret = fu_engine_plugin_allows_enumeration(engine, plugin); + g_assert_true(ret); + + /* engine requires, plugin does have */ + fu_plugin_add_flag(plugin, FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); + ret = fu_engine_plugin_allows_enumeration(engine, plugin); + g_assert_false(ret); + + /* clear config and reload engine */ + fu_self_test_mkroot(); + g_clear_object(&engine); + + engine = fu_engine_new(self->ctx); + g_assert_nonnull(engine); + + ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* engine requires, plugin does have */ + ret = fu_engine_plugin_allows_enumeration(engine, plugin); + g_assert_true(ret); + + /* drop flag, engine shouldn't care */ + fu_plugin_remove_flag(plugin, FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION); + ret = fu_engine_plugin_allows_enumeration(engine, plugin); + g_assert_true(ret); +} + +static void fu_engine_plugin_gtypes_func(gconstpointer user_data) { FuTest *self = (FuTest *)user_data; GPtrArray *plugins; gboolean ret; + g_autoptr(FuDrmDevice) drm_device = g_object_new(FU_TYPE_DRM_DEVICE, NULL); + g_autoptr(FuEdid) edid = fu_edid_new(); g_autoptr(FuEngine) engine = fu_engine_new(self->ctx); g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); g_autoptr(FuSecurityAttrs) attrs = fu_security_attrs_new(); @@ -1186,6 +1587,7 @@ }; const gchar *external_plugins[] = { "flashrom", + "modem-manager", }; /* no metadata in daemon */ @@ -1235,6 +1637,51 @@ fu_plugin_runner_add_security_attrs(plugin, attrs); } + /* run the post-reboot action */ + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(plugins, i); + g_autoptr(FuDevice) device_nop = fu_device_new(self->ctx); + g_autoptr(GError) error_local = NULL; + if (!fu_plugin_runner_reboot_cleanup(plugin, device_nop, &error_local)) + g_debug("ignoring: %s", error_local->message); + } + + /* run the device unlock action */ + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(plugins, i); + g_autoptr(FuDevice) device_nop = fu_device_new(self->ctx); + g_autoptr(GError) error_local = NULL; + if (!fu_plugin_runner_unlock(plugin, device_nop, &error_local)) + g_debug("ignoring: %s", error_local->message); + } + + /* run the backend-device-added action */ + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(plugins, i); + GArray *device_gtypes = fu_plugin_get_device_gtypes(plugin); + if (device_gtypes == NULL || device_gtypes->len == 0) { + g_autoptr(FuDevice) device_nop = fu_device_new(self->ctx); + g_autoptr(GError) error_local = NULL; + if (!fu_plugin_runner_backend_device_added(plugin, + device_nop, + progress, + &error_local)) + g_debug("ignoring: %s", error_local->message); + } + } + + /* register a DRM device as some plugins use these for quirks */ + fu_edid_set_pnp_id(edid, "PNP"); + fu_edid_set_eisa_id(edid, "IBM"); + fu_edid_set_product_name(edid, "Display"); + fu_edid_set_serial_number(edid, "123456"); + fu_edid_set_product_code(edid, 0x1234); + fu_drm_device_set_edid(drm_device, edid); + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(plugins, i); + fu_plugin_runner_device_register(plugin, FU_DEVICE(drm_device)); + } + /* create each custom device with a context only */ for (guint i = 0; i < plugins->len; i++) { FuPlugin *plugin = g_ptr_array_index(plugins, i); @@ -1716,8 +2163,8 @@ fu_engine_add_device(engine, device3); /* verify both children were adopted */ - g_assert_true(fu_device_get_parent(device3) == device2); - g_assert_true(fu_device_get_parent(device1) == device2); + g_assert_true(fu_device_get_parent_internal(device3) == device2); + g_assert_true(fu_device_get_parent_internal(device1) == device2); g_assert_cmpstr(fu_device_get_vendor(device3), ==, "oem"); /* verify order */ @@ -1801,10 +2248,10 @@ fu_engine_add_device(engine, device5); /* verify both children were adopted */ - g_assert_true(fu_device_get_parent(device3) == device2); - g_assert_true(fu_device_get_parent(device4) == device2); - g_assert_true(fu_device_get_parent(device5) == device2); - g_assert_true(fu_device_get_parent(device1) == device2); + g_assert_true(fu_device_get_parent_internal(device3) == device2); + g_assert_true(fu_device_get_parent_internal(device4) == device2); + g_assert_true(fu_device_get_parent_internal(device5) == device2); + g_assert_true(fu_device_get_parent_internal(device1) == device2); g_assert_cmpstr(fu_device_get_vendor(device3), ==, "oem"); } @@ -1835,6 +2282,7 @@ fu_device_add_protocol(device1, "com.acme"); fu_device_set_plugin(device1, "test"); fu_device_add_instance_id(device1, "12345678-1234-1234-1234-123456789012"); + fu_device_set_id(device1, "99249eb1bd9ef0b6e192b271a8cb6a3090cfec7a"); fu_engine_add_device(engine, device1); fu_device_set_id(device2, "device21"); fu_device_build_vendor_id_u16(device2, "USB", 0xFFFF); @@ -1842,6 +2290,7 @@ fu_device_set_plugin(device2, "test"); fu_device_set_equivalent_id(device2, "b92f5b7560b84ca005a79f5a15de3c003ce494cf"); fu_device_add_instance_id(device2, "87654321-1234-1234-1234-123456789012"); + fu_device_set_id(device2, "99244162a6daa0b033d649c8d464529cec41d3de"); fu_engine_add_device(engine, device2); /* match nothing */ @@ -1850,21 +2299,21 @@ g_assert_false(ret); /* match both */ - ret = fu_engine_unlock(engine, "9", &error_both); + ret = fu_engine_unlock(engine, "9924", &error_both); g_assert_error(error_both, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_false(ret); /* match one exactly */ fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_LOCKED); fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_LOCKED); - ret = fu_engine_unlock(engine, "934b4162a6daa0b033d649c8d464529cec41d3de", &error); + ret = fu_engine_unlock(engine, "99244162a6daa0b033d649c8d464529cec41d3de", &error); g_assert_no_error(error); g_assert_true(ret); /* match one partially */ fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_LOCKED); fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_LOCKED); - ret = fu_engine_unlock(engine, "934b", &error); + ret = fu_engine_unlock(engine, "99249", &error); g_assert_no_error(error); g_assert_true(ret); @@ -2599,6 +3048,12 @@ "type=\"md5\">deadbeefdeadbeefdeadbeefdeadbeef" " deadbeefdeadbeefdeadbeefdeadbeef" + " " + " " + " 1024" + " 2048" + " " + " " " " "
    " " " @@ -2635,6 +3090,7 @@ fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_MD_SET_SIGNED); fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_MD_SET_VERFMT); fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_MD_SET_FLAGS); + fu_device_add_private_flag(device, FU_DEVICE_PRIVATE_FLAG_MD_SET_REQUIRED_FREE); fu_device_set_id(device, "test_device"); fu_device_build_vendor_id_u16(device, "USB", 0xFFFF); fu_device_add_protocol(device, "com.acme"); @@ -2651,6 +3107,7 @@ g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_SIGNED_PAYLOAD)); g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)); g_assert_true(fu_device_has_private_flag(device, FU_DEVICE_PRIVATE_FLAG_HOST_CPU)); + g_assert_cmpint(fu_device_get_required_free(device), ==, 1024); /* ensure the device was added */ devices = fu_engine_get_devices(engine, &error); @@ -2859,6 +3316,89 @@ } static void +fu_engine_history_convert_version_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + FuDevice *device_tmp; + FwupdRelease *release_tmp; + gboolean ret; + g_autofree gchar *device_str = NULL; + g_autoptr(FuDevice) device = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(self->ctx); + g_autoptr(FuHistory) history = fu_history_new(self->ctx); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(GError) error = NULL; + g_autoptr(GPtrArray) devices = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new(); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(XbSilo) silo = NULL; + + /* add the fake metadata */ + ret = xb_builder_source_load_xml( + source, + "\n" + "\n" + "\n" + " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " abcd\n" + " \n" + " \n" + " \n" + " \n" + "\n" + "", + XB_BUILDER_SOURCE_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + xb_builder_import_source(builder, source); + silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + fu_engine_set_silo(engine, silo); + + fu_device_set_id(device, "abc"); + fu_device_set_version(device, "1.2.3"); + fu_device_add_flag(device, FWUPD_DEVICE_FLAG_AFFECTS_FDE); + fu_release_set_appstream_id(release, "com.acme.example.firmware"); + fu_release_add_checksum(release, "abcd"); + fu_release_set_version(release, "1.2.4"); + + ret = fu_history_remove_all(history, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_history_add_device(history, device, release, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* do not overwrite the history-saved 1.2.4 with the release-provided 0x01020004 */ + devices = fu_engine_get_history(engine, &error); + g_assert_no_error(error); + g_assert_nonnull(devices); + g_assert_cmpint(devices->len, ==, 1); + device_tmp = g_ptr_array_index(devices, 0); + + device_str = fu_device_to_string(device_tmp); + g_debug("%s", device_str); + + g_assert_cmpstr(fu_device_get_id(device_tmp), + ==, + "a9993e364706816aba3e25717850c26c9cd0d89d"); + g_assert_cmpstr(fu_device_get_version(device_tmp), ==, "1.2.3"); + g_assert_true(fu_device_has_flag(device_tmp, FWUPD_DEVICE_FLAG_SUPPORTED)); + g_assert_true(fu_device_has_flag(device_tmp, FWUPD_DEVICE_FLAG_HISTORICAL)); + release_tmp = fu_device_get_release_default(device_tmp); + g_assert_cmpstr(fwupd_release_get_version(release_tmp), ==, "1.2.4"); +} + +static void fu_engine_history_func(gconstpointer user_data) { FuTest *self = (FuTest *)user_data; @@ -2944,12 +3484,7 @@ ret = fu_release_load(release, cabinet, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); - ret = fu_engine_install_release(engine, - release, - stream, - progress, - FWUPD_INSTALL_FLAG_NONE, - &error); + ret = fu_engine_install_release(engine, release, progress, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -2978,8 +3513,8 @@ " Flags: updatable|historical|unsigned-payload\n" " Version: 1.2.2\n" " VersionFormat: triplet\n" - " Created: 2018-01-07\n" - " Modified: 2017-12-27\n" + " Created: 2018-01-07 15:13:20\n" + " Modified: 2017-12-27 01:26:40\n" " UpdateState: success\n" " FuRelease:\n" " AppstreamId: com.hughski.test.firmware\n" @@ -2989,6 +3524,7 @@ " InstanceId[vi]: 12345678-1234-1234-1234-123456789012\n" " AcquiesceDelay: 50\n", checksum); + g_debug("%s", device_str); ret = g_strcmp0(device_str, device_str_expected) == 0; g_assert_true(ret); @@ -3059,6 +3595,7 @@ g_autoptr(FuEngine) engine = fu_engine_new(self->ctx); g_autoptr(FuPlugin) plugin = fu_plugin_new_from_gtype(fu_test_plugin_get_type(), self->ctx); g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GError) error = NULL; g_autoptr(GInputStream) stream_fw = NULL; g_autoptr(XbSilo) silo_empty = xb_silo_new(); @@ -3099,11 +3636,13 @@ fu_device_set_metadata_integer(device, "nr-attach", 0); stream_fw = g_memory_input_stream_new_from_data((const guint8 *)"1.2.3", 5, NULL); + fu_release_set_stream(release, stream_fw); ret = fu_engine_install_blob(engine, device, - stream_fw, + release, progress, - FWUPD_INSTALL_FLAG_NO_HISTORY, + FWUPD_INSTALL_FLAG_NO_HISTORY | + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, FWUPD_FEATURE_FLAG_NONE, &error); g_assert_no_error(error); @@ -3250,7 +3789,6 @@ FuTest *self = (FuTest *)user_data; gboolean ret; g_autofree gchar *filename = NULL; - g_autofree gchar *localstatedir = NULL; g_autofree gchar *history_db = NULL; g_autoptr(FuCabinet) cabinet = NULL; g_autoptr(FuDevice) device = fu_device_new(self->ctx); @@ -3265,8 +3803,7 @@ g_autoptr(XbSilo) silo_empty = xb_silo_new(); /* delete history */ - localstatedir = fu_path_from_kind(FU_PATH_KIND_LOCALSTATEDIR_PKG); - history_db = g_build_filename(localstatedir, "pending.db", NULL); + history_db = fu_path_build(FU_PATH_KIND_LOCALSTATEDIR_PKG, "pending.db", NULL); (void)g_unlink(history_db); /* no metadata in daemon */ @@ -3322,12 +3859,7 @@ ret = fu_release_load(release, cabinet, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); - ret = fu_engine_install_release(engine, - release, - stream, - progress, - FWUPD_INSTALL_FLAG_NONE, - &error); + ret = fu_engine_install_release(engine, release, progress, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -3349,12 +3881,7 @@ fu_progress_reset(progress); fu_device_set_version_format(device, FWUPD_VERSION_FORMAT_TRIPLET); fu_device_set_version(device, "1.2.2"); - ret = fu_engine_install_release(engine, - release, - stream, - progress, - FWUPD_INSTALL_FLAG_NONE, - &error); + ret = fu_engine_install_release(engine, release, progress, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); g_object_unref(engine); @@ -3409,6 +3936,13 @@ g_autoptr(GPtrArray) devices = NULL; g_autoptr(XbNode) component = NULL; g_autoptr(XbSilo) silo_empty = xb_silo_new(); + g_autofree gchar *rundir = fu_path_from_kind(FU_PATH_KIND_RUNDIR); + g_autofree gchar *reboot_file = NULL; + + /* create rundir */ + ret = fu_path_mkdir(rundir, &error); + g_assert_no_error(error); + g_assert_true(ret); /* no metadata in daemon */ fu_engine_set_silo(engine, silo_empty); @@ -3463,19 +3997,16 @@ ret = fu_release_load(release, cabinet, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); - ret = fu_engine_install_release(engine, - release, - stream, - progress, - FWUPD_INSTALL_FLAG_NONE, - &error); + ret = fu_engine_install_release(engine, release, progress, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); /* check the device requires reboot */ g_assert_true(fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)); - g_assert_cmpint(fu_device_get_update_state(device), ==, FWUPD_UPDATE_STATE_NEEDS_REBOOT); g_assert_cmpstr(fu_device_get_version(device), ==, "1.2.2"); + + reboot_file = g_build_filename(rundir, "reboot-required", NULL); + g_assert_true(g_file_test(reboot_file, G_FILE_TEST_EXISTS)); } typedef struct { @@ -3576,6 +4107,8 @@ ret = fu_release_load(release, cabinet, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); + g_assert_cmpstr(fu_release_get_firmware_basename(release), ==, "firmware.bin"); + g_assert_cmpstr(fu_release_get_version(release), ==, "1.2.3"); g_signal_connect(FU_ENGINE(engine), "device-request", @@ -3586,12 +4119,7 @@ G_CALLBACK(fu_test_engine_status_changed_cb), &helper); - ret = fu_engine_install_release(engine, - release, - stream, - progress, - FWUPD_INSTALL_FLAG_NONE, - &error); + ret = fu_engine_install_release(engine, release, progress, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); g_assert_cmpint(helper.request_cnt, ==, 1); @@ -3672,12 +4200,7 @@ ret = fu_release_load(release, cabinet, component, NULL, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); - ret = fu_engine_install_release(engine, - release, - stream, - progress, - FWUPD_INSTALL_FLAG_NONE, - &error); + ret = fu_engine_install_release(engine, release, progress, FWUPD_INSTALL_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); g_assert_cmpstr(error->message, ==, @@ -3707,8 +4230,8 @@ " Flags: updatable|historical|unsigned-payload\n" " Version: 1.2.2\n" " VersionFormat: triplet\n" - " Created: 2018-01-07\n" - " Modified: 2017-12-27\n" + " Created: 2018-01-07 15:13:20\n" + " Modified: 2017-12-27 01:26:40\n" " UpdateState: failed\n" " UpdateError: failed to write-firmware: device was not in supported mode\n" " FuRelease:\n" @@ -3719,6 +4242,7 @@ " InstanceId[vi]: 12345678-1234-1234-1234-123456789012\n" " AcquiesceDelay: 50\n", checksum); + g_debug("%s", device_str); ret = g_strcmp0(device_str, device_str_expected) == 0; g_assert_true(ret); } @@ -3899,7 +4423,7 @@ g_assert_false(fu_device_has_flag(device1, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG)); /* check device2 now has parent too */ - g_assert_true(fu_device_get_parent(device2) == parent); + g_assert_true(fu_device_get_parent_internal(device2) == parent); /* waiting, failed */ fu_device_add_flag(device2, FWUPD_DEVICE_FLAG_WAIT_FOR_REPLUG); @@ -4163,6 +4687,50 @@ } static void +fu_device_list_install_parent_first_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + g_autoptr(FuDevice) device_child = fu_device_new(self->ctx); + g_autoptr(FuDevice) device_root = fu_device_new(self->ctx); + g_autoptr(FuDeviceList) device_list = fu_device_list_new(); + + /* add both */ + fu_device_set_id(device_root, "device"); + fu_device_add_instance_id(device_root, "foobar"); + fu_device_add_private_flag(device_root, FU_DEVICE_PRIVATE_FLAG_INSTALL_PARENT_FIRST); + fu_device_set_id(device_child, "device-child"); + fu_device_add_instance_id(device_child, "baz"); + fu_device_add_child(device_root, device_child); + fu_device_list_add(device_list, device_root); + fu_device_list_add(device_list, device_child); + + fu_device_list_depsolve_order(device_list, device_root); + g_assert_cmpint(fu_device_get_order(device_root), <, fu_device_get_order(device_child)); +} + +static void +fu_device_list_install_parent_first_child_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + g_autoptr(FuDevice) device_child = fu_device_new(self->ctx); + g_autoptr(FuDevice) device_root = fu_device_new(self->ctx); + g_autoptr(FuDeviceList) device_list = fu_device_list_new(); + + /* add both */ + fu_device_set_id(device_root, "device"); + fu_device_add_instance_id(device_root, "foobar"); + fu_device_set_id(device_child, "device-child"); + fu_device_add_instance_id(device_child, "baz"); + fu_device_add_private_flag(device_child, FU_DEVICE_PRIVATE_FLAG_INSTALL_PARENT_FIRST); + fu_device_add_child(device_root, device_child); + fu_device_list_add(device_list, device_root); + fu_device_list_add(device_list, device_child); + + fu_device_list_depsolve_order(device_list, device_root); + g_assert_cmpint(fu_device_get_order(device_root), <, fu_device_get_order(device_child)); +} + +static void fu_device_list_better_than_func(gconstpointer user_data) { FuTest *self = (FuTest *)user_data; @@ -4197,6 +4765,7 @@ fu_device_add_flag(device1, FWUPD_DEVICE_FLAG_UNSIGNED_PAYLOAD); fu_device_add_instance_id(device1, "12345678-1234-1234-1234-123456789012"); fu_device_add_protocol(device1, "com.acme"); + fu_device_set_remove_delay(device1, FU_DEVICE_REMOVE_DELAY_RE_ENUMERATE); fu_plugin_device_add(plugin1, device1); /* should be ignored */ @@ -4266,7 +4835,8 @@ FuTest *self = (FuTest *)user_data; g_autoptr(FuDevice) device1 = fu_device_new(self->ctx); g_autoptr(FuDevice) device2 = fu_device_new(self->ctx); - g_autoptr(FuDevice) device = NULL; + g_autoptr(FuDevice) device3 = NULL; + g_autoptr(FuDevice) device4 = NULL; g_autoptr(FuDeviceList) device_list = fu_device_list_new(); g_autoptr(GError) error = NULL; @@ -4278,16 +4848,16 @@ fu_device_set_priority(device2, 999); fu_device_list_add(device_list, device2); - device = fu_device_list_get_by_id(device_list, "8e9c", &error); + device3 = fu_device_list_get_by_id(device_list, "8e9c", &error); g_assert_no_error(error); - g_assert_nonnull(device); - g_assert_cmpstr(fu_device_get_id(device), ==, "1a8d0d9a96ad3e67ba76cf3033623625dc6d6882"); + g_assert_nonnull(device3); + g_assert_cmpstr(fu_device_get_id(device3), ==, "1a8d0d9a96ad3e67ba76cf3033623625dc6d6882"); /* two devices with the 'same' priority */ fu_device_set_priority(device2, 0); - device = fu_device_list_get_by_id(device_list, "8e9c", &error); + device4 = fu_device_list_get_by_id(device_list, "8e9c", &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED); - g_assert_null(device); + g_assert_null(device4); } static void @@ -4708,7 +5278,7 @@ "*invalid platform version 0x0000000a, expected >= 0x00010805*"); g_test_expect_message("FuUsbDevice", G_LOG_LEVEL_WARNING, - "failed to parse * BOS descriptor: *did not find magic*"); + "failed to parse * BOS descriptor: *invalid UUID*"); #endif /* load the JSON into the backend */ @@ -4951,6 +5521,7 @@ static GBytes * fu_test_build_cab(gboolean compressed, ...) { + gboolean ret; va_list args; g_autoptr(FuCabFirmware) cabinet = fu_cab_firmware_new(); g_autoptr(GError) error = NULL; @@ -4981,7 +5552,9 @@ blob = g_bytes_new_static(text, strlen(text)); fu_firmware_set_id(FU_FIRMWARE(img), fn); fu_firmware_set_bytes(FU_FIRMWARE(img), blob); - fu_firmware_add_image(FU_FIRMWARE(cabinet), FU_FIRMWARE(img)); + ret = fu_firmware_add_image(FU_FIRMWARE(cabinet), FU_FIRMWARE(img), &error); + g_assert_no_error(error); + g_assert_true(ret); } while (TRUE); va_end(args); @@ -5080,7 +5653,7 @@ ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet), blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, &error); g_assert_no_error(error); g_assert_true(ret); @@ -5122,11 +5695,11 @@ } else if (g_strcmp0(fu_device_get_id(device), "c0a0a4aa6480ac28eea1ce164fbb466ca934e1ff") == 0) { g_assert_cmpstr(fu_device_get_version(device), ==, "1"); - g_assert_nonnull(fu_device_get_parent(device)); + g_assert_nonnull(fu_device_get_parent_internal(device)); } else if (g_strcmp0(fu_device_get_id(device), "bf455e9f371d2608d1cb67660fd2b335d3f6ef73") == 0) { g_assert_cmpstr(fu_device_get_version(device), ==, "10"); - g_assert_nonnull(fu_device_get_parent(device)); + g_assert_nonnull(fu_device_get_parent_internal(device)); } } @@ -5317,6 +5890,106 @@ } static void +fu_plugin_engine_get_results_appstream_id_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + gboolean ret; + FwupdRelease *release_default; + g_autoptr(FuDevice) device_tmp = fu_device_new(self->ctx); + g_autoptr(FuEngine) engine = fu_engine_new(self->ctx); + g_autoptr(FuHistory) history = fu_history_new(self->ctx); + g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); + g_autoptr(FuRelease) release = fu_release_new(); + g_autoptr(FwupdDevice) device = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(XbBuilder) builder = xb_builder_new(); + g_autoptr(XbBuilderSource) source = xb_builder_source_new(); + g_autoptr(XbSilo) silo = NULL; + + /* load engine to get FuConfig set up */ + ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_NO_CACHE, progress, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* add the fake metadata */ + ret = xb_builder_source_load_xml( + source, + "\n" + "\n" + "\n" + " com.acme.WRONGDEVICE.firmware\n" + " \n" + " 00000000-0000-0000-0000-000000000000\n" + " \n" + " \n" + " triplet\n" + " com.acme.test\n" + " \n" + " \n" + " \n" + " aaa\n" + " \n" + " \n" + " 7c211433f02071597741e6ff5a8ea34789abbf43\n" + " \n" + " \n" + " \n" + " \n" + "\n" + "\n" + " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" + " \n" + " triplet\n" + " com.acme.test\n" + " \n" + " \n" + " \n" + " aaa\n" + " \n" + " \n" + " 7c211433f02071597741e6ff5a8ea34789abbf43\n" + " \n" + " \n" + " \n" + " \n" + "\n" + "", + XB_BUILDER_SOURCE_FLAG_NONE, + &error); + g_assert_no_error(error); + g_assert_true(ret); + xb_builder_import_source(builder, source); + silo = xb_builder_compile(builder, XB_BUILDER_COMPILE_FLAG_NONE, NULL, &error); + g_assert_no_error(error); + g_assert_nonnull(silo); + fu_engine_set_silo(engine, silo); + + /* add a dummy device */ + fu_device_set_id(device_tmp, "08d460be0f1f9f128413f816022a6439e0078018"); + fu_engine_add_device(engine, device_tmp); + fu_release_set_appstream_id(release, "com.acme.example.firmware"); + fu_release_add_checksum(release, "7c211433f02071597741e6ff5a8ea34789abbf43"); + fu_device_add_release(device_tmp, FWUPD_RELEASE(release)); + fu_device_set_update_state(device_tmp, FWUPD_UPDATE_STATE_SUCCESS); + ret = fu_history_add_device(history, device_tmp, release, &error); + g_assert_no_error(error); + g_assert_true(ret); + + /* check we got the correct component */ + device = fu_engine_get_results(engine, "08d460be0f1f9f128413f816022a6439e0078018", &error); + g_assert_no_error(error); + g_assert_nonnull(device); + release_default = fu_device_get_release_default(device); + g_assert_nonnull(release_default); + g_assert_cmpstr(fwupd_release_get_appstream_id(release_default), + ==, + "com.acme.example.firmware"); +} + +static void fu_security_attr_func(gconstpointer user_data) { gboolean ret; @@ -5330,8 +6003,10 @@ fwupd_security_attr_set_plugin(attr1, "foo"); fwupd_security_attr_set_created(attr1, 0); + fwupd_security_attr_set_level(attr1, 1); fwupd_security_attr_set_plugin(attr2, "bar"); fwupd_security_attr_set_created(attr2, 0); + fwupd_security_attr_set_level(attr2, 2); fu_security_attrs_append(attrs1, attr1); fu_security_attrs_append(attrs1, attr2); @@ -5343,14 +6018,14 @@ " \"SecurityAttributes\" : [\n" " {\n" " \"AppstreamId\" : \"org.fwupd.hsi.foo\",\n" - " \"HsiLevel\" : 0,\n" + " \"HsiLevel\" : 1,\n" " \"Plugin\" : \"foo\",\n" " \"Uri\" : " "\"https://fwupd.github.io/libfwupdplugin/hsi.html#org.fwupd.hsi.foo\"\n" " },\n" " {\n" " \"AppstreamId\" : \"org.fwupd.hsi.bar\",\n" - " \"HsiLevel\" : 0,\n" + " \"HsiLevel\" : 2,\n" " \"Plugin\" : \"bar\",\n" " \"Uri\" : " "\"https://fwupd.github.io/libfwupdplugin/hsi.html#org.fwupd.hsi.bar\"\n" @@ -5399,16 +6074,20 @@ ret = fu_firmware_parse_stream(FU_FIRMWARE(cabinet), stream, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, &error); g_assert_no_error(error); g_assert_true(ret); /* add */ - fu_cabinet_add_file(cabinet, "firmware.jcat", jcat_blob1); + ret = fu_cabinet_add_file(cabinet, "firmware.jcat", jcat_blob1, &error); + g_assert_no_error(error); + g_assert_true(ret); /* replace */ - fu_cabinet_add_file(cabinet, "firmware.jcat", jcat_blob2); + ret = fu_cabinet_add_file(cabinet, "firmware.jcat", jcat_blob2, &error); + g_assert_no_error(error); + g_assert_true(ret); /* get data */ img1 = fu_firmware_get_image_by_id(FU_FIRMWARE(cabinet), "firmware.jcat", &error); @@ -5430,7 +6109,7 @@ { const guint8 src[] = {'a', 'b', 'c', 'd', 'e'}; gboolean ret; - guint8 dst[4]; + guint8 dst[4] = {0}; g_autoptr(GError) error = NULL; /* copy entire buffer */ @@ -5846,7 +6525,7 @@ ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet), blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, &error); g_assert_no_error(error); g_assert_true(ret); @@ -5895,6 +6574,9 @@ "acme.metainfo.xml", "\n" " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" " \n" " \n" " \n" @@ -5916,38 +6598,42 @@ ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet1), blob1, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, &error); g_assert_no_error(error); g_assert_true(ret); /* create silo (sha1, using artifacts object; mixed case) */ - blob2 = fu_test_build_cab(FALSE, - "acme.metainfo.xml", - "\n" - " com.acme.example.firmware\n" - " \n" - " \n" - " \n" - " \n" - " firmware.dfu\n" - " 7c211433f02071597741e6ff5a8ea34789abbF43\n" - " \n" - " \n" - " \n" - " \n" - "", - "firmware.dfu", - "world", - "firmware.dfu.asc", - "signature", - NULL); + blob2 = fu_test_build_cab( + FALSE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " firmware.dfu\n" + " 7c211433f02071597741e6ff5a8ea34789abbF43\n" + " \n" + " \n" + " \n" + " \n" + "", + "firmware.dfu", + "world", + "firmware.dfu.asc", + "signature", + NULL); ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet2), blob2, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, &error); g_assert_no_error(error); g_assert_true(ret); @@ -5958,6 +6644,9 @@ "acme.metainfo.xml", "\n" " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" " \n" " \n" " \n" @@ -5981,36 +6670,39 @@ ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet3), blob3, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, &error); g_assert_no_error(error); g_assert_true(ret); /* create silo (legacy release object) */ - blob4 = - fu_test_build_cab(FALSE, - "acme.metainfo.xml", - "\n" - " com.acme.example.firmware\n" - " \n" - " \n" - " " - "486EA46224D1BB4FB680F34F7C9AD96A8F24EC88BE73EA8E5A6C65260E9CB8A7\n" - " \n" - " \n" - "", - "firmware.dfu", - "world", - "firmware.dfu.asc", - "signature", - NULL); + blob4 = fu_test_build_cab( + FALSE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" + " \n" + " \n" + " " + "486EA46224D1BB4FB680F34F7C9AD96A8F24EC88BE73EA8E5A6C65260E9CB8A7\n" + " \n" + " \n" + "", + "firmware.dfu", + "world", + "firmware.dfu.asc", + "signature", + NULL); ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet4), blob4, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, &error); g_assert_no_error(error); g_assert_true(ret); @@ -6030,21 +6722,25 @@ g_autoptr(XbQuery) query = NULL; /* create silo */ - blob = fu_test_build_cab(FALSE, - "acme.metainfo.xml", - "\n" - " com.acme.example.firmware\n" - " \n" - " \n" - " \n" - "", - "firmware.bin", - "world", - NULL); + blob = fu_test_build_cab( + FALSE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" + " \n" + " \n" + " \n" + "", + "firmware.bin", + "world", + NULL); ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet), blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, &error); g_assert_no_error(error); g_assert_true(ret); @@ -6083,6 +6779,9 @@ "acme.metainfo.xml", "\n" " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" " \n" " \n" " \n" - " com.acme.example.firmware\n" - " \n" - " \n" - " \n" - "", - "lvfs\\firmware.bin", - "world", - NULL); + blob = fu_test_build_cab( + FALSE, + "lvfs\\acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" + " \n" + " \n" + " \n" + "", + "lvfs\\firmware.bin", + "world", + NULL); ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet), blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, &error); g_assert_no_error(error); g_assert_true(ret); @@ -6165,7 +6868,7 @@ ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet), blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); g_assert_false(ret); @@ -6179,25 +6882,29 @@ g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; - blob = fu_test_build_cab(FALSE, - "acme.metainfo.xml", - "\n" - " com.acme.example.firmware\n" - " \n" - " \n" - " 7004701\n" - " deadbeef\n" - " \n" - " \n" - "", - "firmware.bin", - "world", - NULL); + blob = fu_test_build_cab( + FALSE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" + " \n" + " \n" + " 7004701\n" + " deadbeef\n" + " \n" + " \n" + "", + "firmware.bin", + "world", + NULL); ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet), blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); g_assert_false(ret); @@ -6211,23 +6918,27 @@ g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; - blob = fu_test_build_cab(FALSE, - "acme.metainfo.xml", - "\n" - " com.acme.example.firmware\n" - " \n" - " \n" - " \n" - " \n" - " \n" - "", - "firmware.bin", - "world", - NULL); + blob = fu_test_build_cab( + FALSE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" + " \n" + " \n" + " \n" + " \n" + " \n" + "", + "firmware.bin", + "world", + NULL); ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet), blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); g_assert_false(ret); @@ -6241,22 +6952,26 @@ g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; - blob = fu_test_build_cab(FALSE, - "acme.metainfo.xml", - "\n" - " com.acme.example.firmware\n" - " \n" - " \n" - " \n" - "", - "firmware.bin", - "world", - NULL); + blob = fu_test_build_cab( + FALSE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" + " \n" + " \n" + " \n" + "", + "firmware.bin", + "world", + NULL); fu_firmware_set_size_max(FU_FIRMWARE(cabinet), 123); ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet), blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); g_assert_false(ret); @@ -6270,24 +6985,28 @@ g_autoptr(GBytes) blob = NULL; g_autoptr(GError) error = NULL; - blob = fu_test_build_cab(FALSE, - "acme.metainfo.xml", - "\n" - " com.acme.example.firmware\n" - " \n" - " \n" - " deadbeef\n" - " \n" - " \n" - "", - "firmware.bin", - "world", - NULL); + blob = fu_test_build_cab( + FALSE, + "acme.metainfo.xml", + "\n" + " com.acme.example.firmware\n" + " \n" + " b585990a-003e-5270-89d5-3705a17f9a43\n" + " \n" + " \n" + " \n" + " deadbeef\n" + " \n" + " \n" + "", + "firmware.bin", + "world", + NULL); ret = fu_firmware_parse_bytes(FU_FIRMWARE(cabinet), blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_NONE, &error); g_assert_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_FILE); g_assert_false(ret); @@ -6316,7 +7035,7 @@ return; #endif - /* Load contrived attributes */ + /* load contrived attributes */ test_dir = g_test_build_filename(G_TEST_DIST, "tests", "bios-attrs", NULL); (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", test_dir, TRUE); @@ -6440,7 +7159,7 @@ g_assert_no_error(error); g_assert_true(ret); - /* Read Only */ + /* read only */ attr4 = fu_context_get_bios_setting(fu_engine_get_context(engine), "com.fwupd-internal.pending_reboot"); g_assert_nonnull(attr4); @@ -6826,9 +7545,11 @@ static void fu_remote_list_repair_func(void) { - FwupdRemote *remote; gboolean ret; g_autoptr(FuRemoteList) remote_list = fu_remote_list_new(); + g_autoptr(FwupdRemote) remote1 = NULL; + g_autoptr(FwupdRemote) remote2 = NULL; + g_autoptr(FwupdRemote) remote3 = NULL; g_autoptr(GError) error = NULL; fu_remote_list_set_lvfs_metadata_format(remote_list, "zst"); @@ -6837,23 +7558,26 @@ g_assert_true(ret); /* check .gz converted to .zst */ - remote = fu_remote_list_get_by_id(remote_list, "legacy-lvfs"); - g_assert_nonnull(remote); - g_assert_cmpstr(fwupd_remote_get_metadata_uri(remote), + remote1 = fu_remote_list_get_by_id(remote_list, "legacy-lvfs", &error); + g_assert_no_error(error); + g_assert_nonnull(remote1); + g_assert_cmpstr(fwupd_remote_get_metadata_uri(remote1), ==, "http://localhost/stable.xml.zst"); /* check .xz converted to .zst */ - remote = fu_remote_list_get_by_id(remote_list, "legacy-lvfs-xz"); - g_assert_nonnull(remote); - g_assert_cmpstr(fwupd_remote_get_metadata_uri(remote), + remote2 = fu_remote_list_get_by_id(remote_list, "legacy-lvfs-xz", &error); + g_assert_no_error(error); + g_assert_nonnull(remote2); + g_assert_cmpstr(fwupd_remote_get_metadata_uri(remote2), ==, "http://localhost/stable.xml.zst"); /* check non-LVFS remote NOT .gz converted to .xz */ - remote = fu_remote_list_get_by_id(remote_list, "legacy"); - g_assert_nonnull(remote); - g_assert_cmpstr(fwupd_remote_get_metadata_uri(remote), + remote3 = fu_remote_list_get_by_id(remote_list, "legacy", &error); + g_assert_no_error(error); + g_assert_nonnull(remote3); + g_assert_cmpstr(fwupd_remote_get_metadata_uri(remote3), ==, "http://localhost/stable.xml.gz"); } @@ -6870,7 +7594,7 @@ /* ensure empty tree */ fu_self_test_mkroot(); - (void)g_unsetenv("CONFIGURATION_DIRECTORY"); + g_unsetenv("CONFIGURATION_DIRECTORY"); (void)g_setenv("FWUPD_SYSCONFDIR", "/tmp/fwupd-self-test", TRUE); ret = fu_path_mkdir_parent(fake_sysconf_fn, &error); @@ -6887,7 +7611,7 @@ g_assert_no_error(error); g_assert_true(ret); - ret = fu_config_load(config, &error); + ret = fu_config_load(config, FU_CONFIG_LOAD_FLAG_NONE, &error); g_assert_no_error(error); g_assert_true(ret); @@ -6950,7 +7674,7 @@ /* working directory */ (void)g_setenv("FWUPD_SYSCONFDIR", sysconfdir, TRUE); - (void)g_unsetenv("CONFIGURATION_DIRECTORY"); + g_unsetenv("CONFIGURATION_DIRECTORY"); fn_mut = g_build_filename(sysconfdir, "fwupd", "fwupd.conf", NULL); g_assert_nonnull(fn_mut); @@ -6968,7 +7692,7 @@ /* we don't want to run all the plugins just to get the _init() defaults */ fu_config_set_plugin_defaults(config); - ret = fu_config_load(config, &error); + ret = fu_config_load(config, FU_CONFIG_LOAD_FLAG_MIGRATE_FILES, &error); g_assert_no_error(error); g_assert_true(ret); @@ -6990,6 +7714,92 @@ } static void +fu_engine_integrity_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + g_autofree gchar *str = NULL; + g_autoptr(GError) error = NULL; + g_autoptr(GHashTable) integrity = NULL; + + integrity = fu_engine_integrity_new(self->ctx, &error); + g_assert_no_error(error); + g_assert_nonnull(integrity); + str = fu_engine_integrity_to_string(integrity); + g_debug("%s", str); +} + +static void +fu_engine_report_metadata_func(gconstpointer user_data) +{ + FuTest *self = (FuTest *)user_data; + g_autoptr(FuEngine) engine = fu_engine_new(self->ctx); + g_autoptr(GError) error = NULL; + g_autoptr(GHashTable) metadata = NULL; + g_autoptr(GList) keys = NULL; + const gchar *keys_exist[] = { + "BatteryThreshold", + "CompileVersion(org.freedesktop.fwupd)", + "CpuArchitecture", + "DistroId", + "FwupdSupported", + "RuntimeVersion(org.freedesktop.fwupd)", + "SELinux", + }; + + /* check report metadata */ + metadata = fu_engine_get_report_metadata(engine, &error); + g_assert_no_error(error); + g_assert_nonnull(metadata); + + keys = g_list_sort(g_hash_table_get_keys(metadata), (GCompareFunc)g_strcmp0); + for (GList *l = keys; l != NULL; l = l->next) { + const gchar *key = l->data; + const gchar *value = g_hash_table_lookup(metadata, key); + g_debug("%s=%s", key, value); + } + for (guint i = 0; i < G_N_ELEMENTS(keys_exist); i++) { + const gchar *value = g_hash_table_lookup(metadata, keys_exist[i]); + if (value == NULL) + g_warning("no %s in metadata", keys_exist[i]); + } +} + +static void +fu_engine_error_array_func(void) +{ + g_autoptr(GPtrArray) arr1 = g_ptr_array_new_with_free_func((GDestroyNotify)g_error_free); + g_autoptr(GPtrArray) arr2 = g_ptr_array_new_with_free_func((GDestroyNotify)g_error_free); + g_autoptr(GPtrArray) arr3 = g_ptr_array_new_with_free_func((GDestroyNotify)g_error_free); + g_autoptr(GPtrArray) arr4 = g_ptr_array_new_with_free_func((GDestroyNotify)g_error_free); + g_autoptr(GError) error1 = NULL; + g_autoptr(GError) error2 = NULL; + g_autoptr(GError) error3 = NULL; + g_autoptr(GError) error4 = NULL; + + /* fallback */ + error1 = fu_engine_error_array_get_best(arr1); + g_assert_error(error1, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + + /* get the most important single error */ + g_ptr_array_add(arr2, g_error_new_literal(FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "")); + error2 = fu_engine_error_array_get_best(arr2); + g_assert_error(error2, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND); + + /* version same */ + g_ptr_array_add(arr3, g_error_new_literal(FWUPD_ERROR, FWUPD_ERROR_VERSION_SAME, "")); + g_ptr_array_add(arr3, g_error_new_literal(FWUPD_ERROR, FWUPD_ERROR_VERSION_SAME, "")); + g_ptr_array_add(arr3, g_error_new_literal(FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "")); + error3 = fu_engine_error_array_get_best(arr3); + g_assert_error(error3, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO); + + /* already have newer versions */ + g_ptr_array_add(arr4, g_error_new_literal(FWUPD_ERROR, FWUPD_ERROR_VERSION_NEWER, "")); + g_ptr_array_add(arr4, g_error_new_literal(FWUPD_ERROR, FWUPD_ERROR_VERSION_NEWER, "")); + error4 = fu_engine_error_array_get_best(arr4); + g_assert_error(error4, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO); +} + +static void fu_engine_machine_hash_func(void) { gsize sz = 0; @@ -7044,7 +7854,7 @@ fu_engine_add_plugin_filter(engine, "pixart_rf"); ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | - FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES | FU_ENGINE_LOAD_FLAG_READONLY, + FU_ENGINE_LOAD_FLAG_READONLY, progress, &error); g_assert_no_error(error); @@ -7108,7 +7918,7 @@ fu_engine_add_plugin_filter(engine, "hughski_colorhug"); ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | - FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES | FU_ENGINE_LOAD_FLAG_READONLY, + FU_ENGINE_LOAD_FLAG_READONLY, progress, &error); g_assert_no_error(error); @@ -7129,47 +7939,6 @@ } static void -fu_test_engine_fake_pci(gconstpointer user_data) -{ - FuTest *self = (FuTest *)user_data; - gboolean ret; - g_autoptr(FuDevice) device = NULL; - g_autoptr(FuEngine) engine = fu_engine_new(self->ctx); - g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); - g_autoptr(GError) error = NULL; - - /* non-linux */ - if (!fu_context_has_backend(self->ctx, "udev")) { - g_test_skip("no Udev backend"); - return; - } - - /* load engine and check the device was found */ - fu_engine_add_plugin_filter(engine, "optionrom"); - ret = fu_engine_load(engine, - FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | - FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES | FU_ENGINE_LOAD_FLAG_READONLY, - progress, - &error); - g_assert_no_error(error); - g_assert_true(ret); - - /* PCI -> optionrom */ - device = fu_engine_get_device(engine, "20c947afbdc42deee9a7333290008cb384b10f74", &error); - g_assert_no_error(error); - g_assert_nonnull(device); - g_assert_cmpstr(fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)), ==, "pci"); - g_assert_cmpstr(fu_udev_device_get_devtype(FU_UDEV_DEVICE(device)), ==, NULL); - g_assert_cmpstr(fu_udev_device_get_driver(FU_UDEV_DEVICE(device)), ==, NULL); - g_assert_cmpstr(fu_udev_device_get_device_file(FU_UDEV_DEVICE(device)), ==, NULL); - g_assert_cmpint(fu_device_get_vid(device), ==, 0x8086); - g_assert_cmpint(fu_device_get_pid(device), ==, 0x06ed); - g_assert_cmpstr(fu_device_get_plugin(device), ==, "optionrom"); - g_assert_cmpstr(fu_device_get_physical_id(device), ==, "PCI_SLOT_NAME=0000:00:14.0"); - g_assert_cmpstr(fu_device_get_logical_id(device), ==, "rom"); -} - -static void fu_test_engine_fake_v4l(gconstpointer user_data) { FuTest *self = (FuTest *)user_data; @@ -7189,7 +7958,7 @@ fu_engine_add_plugin_filter(engine, "logitech_tap"); ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | - FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES | FU_ENGINE_LOAD_FLAG_READONLY, + FU_ENGINE_LOAD_FLAG_READONLY, progress, &error); g_assert_no_error(error); @@ -7236,7 +8005,6 @@ fu_engine_add_plugin_filter(engine, "nvme"); ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | - FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES | FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_NO_CACHE, progress, &error); @@ -7287,7 +8055,7 @@ fu_engine_add_plugin_filter(engine, "synaptics_rmi"); ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | - FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES | FU_ENGINE_LOAD_FLAG_READONLY, + FU_ENGINE_LOAD_FLAG_READONLY, progress, &error); g_assert_no_error(error); @@ -7336,11 +8104,16 @@ return; } + if (g_getenv("TPM2TOOLS_TCTI") != NULL) { + g_test_skip("Using software TPM, skipping fake TPM test"); + return; + } + /* load engine and check the device was found */ fu_engine_add_plugin_filter(engine, "tpm"); ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | - FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES | FU_ENGINE_LOAD_FLAG_READONLY, + FU_ENGINE_LOAD_FLAG_READONLY, progress, &error); g_assert_no_error(error); @@ -7368,53 +8141,6 @@ } static void -fu_test_engine_fake_mei(gconstpointer user_data) -{ - FuTest *self = (FuTest *)user_data; - gboolean ret; - g_autoptr(FuDevice) device = NULL; - g_autoptr(FuEngine) engine = fu_engine_new(self->ctx); - g_autoptr(FuProgress) progress = fu_progress_new(G_STRLOC); - g_autoptr(GError) error = NULL; - - /* non-linux */ - if (!fu_context_has_backend(self->ctx, "udev")) { - g_test_skip("no Udev backend"); - return; - } - - /* load engine and check the device was found */ - fu_engine_add_plugin_filter(engine, "intel_me"); - ret = fu_engine_load(engine, - FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | - FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES | FU_ENGINE_LOAD_FLAG_READONLY, - progress, - &error); - g_assert_no_error(error); - g_assert_true(ret); - - /* not linux */ - if (fu_engine_get_plugin_by_name(engine, "intel_me", &error) == NULL) { - g_test_skip(error->message); - return; - } - - /* mei */ - device = fu_engine_get_device(engine, "8d5470e73fd9a31eaa460b2b6aea95483fe3f14c", &error); - g_assert_no_error(error); - g_assert_nonnull(device); - g_assert_cmpstr(fu_udev_device_get_subsystem(FU_UDEV_DEVICE(device)), ==, "mei"); - g_assert_cmpstr(fu_udev_device_get_devtype(FU_UDEV_DEVICE(device)), ==, NULL); - g_assert_cmpstr(fu_udev_device_get_driver(FU_UDEV_DEVICE(device)), ==, NULL); - g_assert_cmpstr(fu_udev_device_get_device_file(FU_UDEV_DEVICE(device)), ==, "/dev/mei0"); - g_assert_cmpint(fu_device_get_vid(device), ==, 0x8086); - g_assert_cmpint(fu_device_get_pid(device), ==, 0x06E0); - g_assert_cmpstr(fu_device_get_plugin(device), ==, "intel_me"); - g_assert_cmpstr(fu_device_get_physical_id(device), ==, "PCI_SLOT_NAME=0000:00:16.0"); - g_assert_cmpstr(fu_device_get_logical_id(device), ==, "AMT"); -} - -static void fu_test_engine_fake_block(gconstpointer user_data) { FuTest *self = (FuTest *)user_data; @@ -7434,7 +8160,7 @@ fu_engine_add_plugin_filter(engine, "scsi"); ret = fu_engine_load(engine, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | - FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES | FU_ENGINE_LOAD_FLAG_READONLY, + FU_ENGINE_LOAD_FLAG_READONLY, progress, &error); g_assert_no_error(error); @@ -7481,22 +8207,33 @@ (void)g_setenv("FWUPD_SYSFSFWDIR", testdatadir, TRUE); (void)g_setenv("CONFIGURATION_DIRECTORY", testdatadir, TRUE); (void)g_setenv("FWUPD_LOCALSTATEDIR", "/tmp/fwupd-self-test/var", TRUE); + (void)g_setenv("FWUPD_RUNDIR", "/tmp/fwupd-self-test/run", TRUE); (void)g_setenv("FWUPD_SYSFSFWATTRIBDIR", testdatadir, TRUE); (void)g_setenv("FWUPD_PROCFS", testdatadir, TRUE); sysfsdir = g_test_build_filename(G_TEST_DIST, "tests", "sys", NULL); (void)g_setenv("FWUPD_SYSFSDIR", sysfsdir, TRUE); (void)g_setenv("FWUPD_SELF_TEST", "1", TRUE); (void)g_setenv("FWUPD_MACHINE_ID", "test", TRUE); + (void)g_setenv("FWUPD_EFIVARS", "dummy", TRUE); /* ensure empty tree */ fu_self_test_mkroot(); /* do not save silo */ self->ctx = fu_context_new(); + fu_context_add_flag(self->ctx, FU_CONTEXT_FLAG_NO_IDLE_SOURCES); ret = fu_context_load_quirks(self->ctx, FU_QUIRKS_LOAD_FLAG_NO_CACHE, &error); g_assert_no_error(error); g_assert_true(ret); + /* build a plausible EFI system */ + ret = fu_efivars_set_secure_boot(fu_context_get_efivars(self->ctx), TRUE, &error); + g_assert_no_error(error); + g_assert_true(ret); + ret = fu_efivars_set_boot_current(fu_context_get_efivars(self->ctx), 0x0000, &error); + g_assert_no_error(error); + g_assert_true(ret); + /* do not allow mounting disks in the self tests */ fu_context_add_flag(self->ctx, FU_CONTEXT_FLAG_INHIBIT_VOLUME_MOUNT); @@ -7506,10 +8243,10 @@ g_assert_true(ret); /* tests go here */ - if (g_test_slow()) { + if (g_test_slow()) g_test_add_data_func("/fwupd/console", self, fu_console_func); - } g_test_add_func("/fwupd/idle", fu_idle_func); + g_test_add_func("/fwupd/util", fu_util_func); g_test_add_func("/fwupd/client-list", fu_client_list_func); g_test_add_func("/fwupd/remote{download}", fu_remote_download_func); g_test_add_func("/fwupd/remote{base-uri}", fu_remote_baseuri_func); @@ -7539,6 +8276,12 @@ g_test_add_data_func("/fwupd/device-list{explicit-order-post}", self, fu_device_list_explicit_order_post_func); + g_test_add_data_func("/fwupd/device-list{install-parent-first}", + self, + fu_device_list_install_parent_first_func); + g_test_add_data_func("/fwupd/device-list{install-parent-first-child}", + self, + fu_device_list_install_parent_first_child_func); g_test_add_data_func("/fwupd/device-list{no-auto-remove-children}", self, fu_device_list_no_auto_remove_children_func); @@ -7603,9 +8346,7 @@ g_test_add_data_func("/fwupd/engine{fake-serio}", self, fu_test_engine_fake_serio); g_test_add_data_func("/fwupd/engine{fake-nvme}", self, fu_test_engine_fake_nvme); g_test_add_data_func("/fwupd/engine{fake-block}", self, fu_test_engine_fake_block); - g_test_add_data_func("/fwupd/engine{fake-mei}", self, fu_test_engine_fake_mei); g_test_add_data_func("/fwupd/engine{fake-tpm}", self, fu_test_engine_fake_tpm); - g_test_add_data_func("/fwupd/engine{fake-pci}", self, fu_test_engine_fake_pci); g_test_add_data_func("/fwupd/engine{fake-v4l}", self, fu_test_engine_fake_v4l); if (g_test_slow()) { g_test_add_data_func("/fwupd/device-list{replug-auto}", @@ -7616,11 +8357,19 @@ self, fu_device_list_replug_user_func); g_test_add_func("/fwupd/engine{machine-hash}", fu_engine_machine_hash_func); + g_test_add_func("/fwupd/engine{error-array}", fu_engine_error_array_func); + g_test_add_data_func("/fwupd/engine{report-metadata}", + self, + fu_engine_report_metadata_func); + g_test_add_data_func("/fwupd/engine{integrity}", self, fu_engine_integrity_func); g_test_add_data_func("/fwupd/engine{require-hwid}", self, fu_engine_require_hwid_func); g_test_add_data_func("/fwupd/engine{requires-reboot}", self, fu_engine_install_needs_reboot); g_test_add_data_func("/fwupd/engine{history-inherit}", self, fu_engine_history_inherit); + g_test_add_data_func("/fwupd/engine{history-convert-version}", + self, + fu_engine_history_convert_version_func); g_test_add_data_func("/fwupd/engine{partial-hash}", self, fu_engine_partial_hash_func); g_test_add_data_func("/fwupd/engine{downgrade}", self, fu_engine_downgrade_func); g_test_add_data_func("/fwupd/engine{md-verfmt}", self, fu_engine_md_verfmt_func); @@ -7645,6 +8394,12 @@ g_test_add_data_func("/fwupd/engine{requirements-not-hardware}", self, fu_engine_requirements_not_hardware_func); + g_test_add_data_func("/fwupd/engine{requirements-phased}", + self, + fu_engine_requirements_phased_func); + g_test_add_data_func("/fwupd/engine{requirements-phased-old-fwpud}", + self, + fu_engine_requirements_phased_old_fwupd_func); g_test_add_data_func("/fwupd/engine{requirements-version-require}", self, fu_engine_requirements_version_require_func); @@ -7681,6 +8436,9 @@ g_test_add_data_func("/fwupd/engine{requirements-only-upgrade}", self, fu_engine_requirements_only_upgrade_func); + g_test_add_data_func("/fwupd/engine{requirements-only-upgrade-reinstall}", + self, + fu_engine_requirements_only_upgrade_reinstall_func); g_test_add_data_func("/fwupd/engine{device-auto-parent-id}", self, fu_engine_device_parent_id_func); @@ -7690,15 +8448,27 @@ g_test_add_data_func("/fwupd/engine{install-duration}", self, fu_engine_install_duration_func); + g_test_add_data_func("/fwupd/engine{get-results-appstream-id}", + self, + fu_plugin_engine_get_results_appstream_id_func); g_test_add_data_func("/fwupd/engine{release-dedupe}", self, fu_engine_release_dedupe_func); g_test_add_data_func("/fwupd/engine{generate-md}", self, fu_engine_generate_md_func); g_test_add_data_func("/fwupd/engine{requirements-other-device}", self, fu_engine_requirements_other_device_func); - g_test_add_data_func("/fwupd/engine{fu_engine_requirements_sibling_device_func}", + g_test_add_data_func("/fwupd/engine{requirements-sibling-device}", self, fu_engine_requirements_sibling_device_func); + g_test_add_data_func("/fwupd/engine{requirements-vercmp-glob}", + self, + fu_engine_requirements_vercmp_glob_func); + g_test_add_data_func("/fwupd/engine{requirements-vercmp-glob-fallback}", + self, + fu_engine_requirements_vercmp_glob_fallback_func); g_test_add_data_func("/fwupd/engine{plugin-gtypes}", self, fu_engine_plugin_gtypes_func); + g_test_add_data_func("/fwupd/plugin/mutable", + self, + fu_engine_test_plugin_mutable_enumeration); g_test_add_data_func("/fwupd/plugin{composite}", self, fu_plugin_composite_func); g_test_add_data_func("/fwupd/plugin{composite-multistep}", self, diff -Nru fwupd-2.0.8/src/fu-systemd.c fwupd-2.0.20/src/fu-systemd.c --- fwupd-2.0.8/src/fu-systemd.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-systemd.c 2026-02-26 11:36:18.000000000 +0000 @@ -26,7 +26,7 @@ connection = g_bus_get_sync(G_BUS_TYPE_SYSTEM, NULL, error); if (connection == NULL) { - g_prefix_error(error, "failed to get bus: "); + g_prefix_error_literal(error, "failed to get bus: "); return NULL; } proxy = g_dbus_proxy_new_sync(connection, diff -Nru fwupd-2.0.8/src/fu-tool.c fwupd-2.0.20/src/fu-tool.c --- fwupd-2.0.8/src/fu-tool.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-tool.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,9 +21,6 @@ #include #include -#include "fwupd-client-private.h" -#include "fwupd-common-private.h" -#include "fwupd-device-private.h" #include "fwupd-enums-private.h" #include "fwupd-remote-private.h" @@ -38,7 +35,6 @@ #include "fu-engine.h" #include "fu-history.h" #include "fu-plugin-private.h" -#include "fu-security-attr-common.h" #include "fu-security-attrs-private.h" #include "fu-smbios-private.h" #include "fu-util-bios-setting.h" @@ -58,12 +54,13 @@ FU_UTIL_OPERATION_LAST } FuUtilOperation; -struct FuUtilPrivate { +struct FuUtil { GCancellable *cancellable; GMainContext *main_ctx; GMainLoop *loop; GOptionContext *context; FuContext *ctx; + GSource *source_sigint; FuEngine *engine; FuEngineRequest *request; FuProgress *progress; @@ -73,11 +70,13 @@ gboolean no_reboot_check; gboolean no_safety_check; gboolean no_device_prompt; + gboolean assume_yes; gboolean prepare_blob; gboolean cleanup_blob; gboolean enable_json_state; gboolean interactive; FwupdInstallFlags flags; + FuFirmwareParseFlags parse_flags; gboolean show_all; gboolean disable_ssl_strict; gint lock_fd; @@ -93,26 +92,26 @@ }; static void -fu_util_client_notify_cb(GObject *object, GParamSpec *pspec, FuUtilPrivate *priv) +fu_util_client_notify_cb(GObject *object, GParamSpec *pspec, FuUtil *self) { - if (priv->as_json) + if (self->as_json) return; - fu_console_set_progress(priv->console, - fwupd_client_get_status(priv->client), - fwupd_client_get_percentage(priv->client)); + fu_console_set_progress(self->console, + fwupd_client_get_status(self->client), + fwupd_client_get_percentage(self->client)); } static void -fu_util_show_plugin_warnings(FuUtilPrivate *priv) +fu_util_show_plugin_warnings(FuUtil *self) { FwupdPluginFlags flags = FWUPD_PLUGIN_FLAG_NONE; GPtrArray *plugins; - if (priv->as_json) + if (self->as_json) return; /* get a superset so we do not show the same message more than once */ - plugins = fu_engine_get_plugins(priv->engine); + plugins = fu_engine_get_plugins(self->engine); for (guint i = 0; i < plugins->len; i++) { FwupdPlugin *plugin = g_ptr_array_index(plugins, i); if (fwupd_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)) @@ -139,16 +138,16 @@ tmp = fu_util_plugin_flag_to_string((guint64)1 << i); if (tmp == NULL) continue; - fu_console_print_full(priv->console, FU_CONSOLE_PRINT_FLAG_WARNING, "%s\n", tmp); + fu_console_print_full(self->console, FU_CONSOLE_PRINT_FLAG_WARNING, "%s\n", tmp); url = g_strdup_printf("https://github.com/fwupd/fwupd/wiki/PluginFlag:%s", fwupd_plugin_flag_to_string(flag)); /* TRANSLATORS: %s is a link to a website */ - fu_console_print(priv->console, _("See %s for more information."), url); + fu_console_print(self->console, _("See %s for more information."), url); } } static gboolean -fu_util_lock(FuUtilPrivate *priv, GError **error) +fu_util_lock(FuUtil *self, GError **error) { #ifdef HAVE_WRLCK struct flock lockp = { @@ -167,13 +166,12 @@ if (use_user) { lockfn = fu_util_get_user_cache_path("fwupdtool"); } else { - g_autofree gchar *lockdir = fu_path_from_kind(FU_PATH_KIND_LOCKDIR); - lockfn = g_build_filename(lockdir, "fwupdtool", NULL); + lockfn = fu_path_build(FU_PATH_KIND_LOCKDIR, "fwupdtool", NULL); } if (!fu_path_mkdir_parent(lockfn, error)) return FALSE; - priv->lock_fd = g_open(lockfn, O_RDWR | O_CREAT, S_IRWXU); - if (priv->lock_fd < 0) { + self->lock_fd = g_open(lockfn, O_RDWR | O_CREAT, S_IRWXU); + if (self->lock_fd < 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -184,7 +182,7 @@ /* write lock */ #ifdef HAVE_OFD - if (fcntl(priv->lock_fd, F_OFD_SETLK, &lockp) < 0) { + if (fcntl(self->lock_fd, F_OFD_SETLK, &lockp) < 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -193,7 +191,7 @@ return FALSE; } #else - if (fcntl(priv->lock_fd, F_SETLK, &lockp) < 0) { + if (fcntl(self->lock_fd, F_SETLK, &lockp) < 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -220,16 +218,13 @@ #endif static gboolean -fu_util_start_engine(FuUtilPrivate *priv, - FuEngineLoadFlags flags, - FuProgress *progress, - GError **error) +fu_util_start_engine(FuUtil *self, FuEngineLoadFlags flags, FuProgress *progress, GError **error) { /* already done */ - if (fu_engine_get_loaded(priv->engine)) + if (fu_engine_get_loaded(self->engine)) return TRUE; - if (!fu_util_lock(priv, error)) { + if (!fu_util_lock(self, error)) { /* TRANSLATORS: another fwupdtool instance is already running */ g_prefix_error(error, "%s: ", _("Failed to lock")); return FALSE; @@ -243,28 +238,27 @@ g_info("failed to stop daemon: %s", error_local->message); } #endif - flags |= FU_ENGINE_LOAD_FLAG_NO_IDLE_SOURCES; flags |= FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS; flags |= FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS; - if (!fu_engine_load(priv->engine, flags, progress, error)) + if (!fu_engine_load(self->engine, flags, progress, error)) return FALSE; - if (!priv->as_json) { - fu_util_show_plugin_warnings(priv); - fu_util_show_unsupported_warning(priv->console); + if (!self->as_json) { + fu_util_show_plugin_warnings(self); + fu_util_show_unsupported_warning(self->console); } /* copy properties from engine to client */ if (flags & FU_ENGINE_LOAD_FLAG_HWINFO) { - g_object_set(priv->client, + g_object_set(self->client, "host-vendor", - fu_engine_get_host_vendor(priv->engine), + fu_engine_get_host_vendor(self->engine), "host-product", - fu_engine_get_host_product(priv->engine), + fu_engine_get_host_product(self->engine), "battery-level", - fu_context_get_battery_level(fu_engine_get_context(priv->engine)), + fu_context_get_battery_level(fu_engine_get_context(self->engine)), "battery-threshold", - fu_context_get_battery_threshold(fu_engine_get_context(priv->engine)), + fu_context_get_battery_threshold(fu_engine_get_context(self->engine)), NULL); } @@ -273,10 +267,11 @@ } static void -fu_util_maybe_prefix_sandbox_error(const gchar *value, GError **error) +fu_util_maybe_prefix_sandbox_error(const gchar *value, GError **error) /* nocheck:error */ { g_autofree gchar *path = g_path_get_dirname(value); if (!g_file_test(path, G_FILE_TEST_EXISTS | G_FILE_TEST_IS_DIR)) { + /* nocheck:error */ g_prefix_error(error, "Unable to access %s. You may need to copy %s to %s: ", path, @@ -288,14 +283,14 @@ static void fu_util_cancelled_cb(GCancellable *cancellable, gpointer user_data) { - FuUtilPrivate *priv = (FuUtilPrivate *)user_data; + FuUtil *self = (FuUtil *)user_data; /* TRANSLATORS: this is when a device ctrl+c's a watch */ - fu_console_print_literal(priv->console, _("Cancelled")); - g_main_loop_quit(priv->loop); + fu_console_print_literal(self->console, _("Cancelled")); + g_main_loop_quit(self->loop); } static gboolean -fu_util_smbios_dump(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_smbios_dump(FuUtil *self, gchar **values, GError **error) { g_autofree gchar *tmp = NULL; g_autoptr(FuSmbios) smbios = NULL; @@ -310,7 +305,7 @@ if (!fu_smbios_setup_from_file(smbios, values[0], error)) return FALSE; tmp = fu_firmware_to_string(FU_FIRMWARE(smbios)); - fu_console_print_literal(priv->console, tmp); + fu_console_print_literal(self->console, tmp); return TRUE; } @@ -318,64 +313,87 @@ static gboolean fu_util_sigint_cb(gpointer user_data) { - FuUtilPrivate *priv = (FuUtilPrivate *)user_data; + FuUtil *self = (FuUtil *)user_data; g_info("handling SIGINT"); - g_cancellable_cancel(priv->cancellable); + g_cancellable_cancel(self->cancellable); return FALSE; } #endif static void -fu_util_setup_signal_handlers(FuUtilPrivate *priv) +fu_util_handle_sigint_start(FuUtil *self) { #ifdef HAVE_GIO_UNIX - g_autoptr(GSource) source = g_unix_signal_source_new(SIGINT); - g_source_set_callback(source, fu_util_sigint_cb, priv, NULL); - g_source_attach(g_steal_pointer(&source), priv->main_ctx); + if (self->source_sigint != NULL) + return; + self->source_sigint = g_unix_signal_source_new(SIGINT); + g_source_set_callback(self->source_sigint, fu_util_sigint_cb, self, NULL); + g_source_attach(self->source_sigint, self->main_ctx); #endif } static void -fu_util_private_free(FuUtilPrivate *priv) +fu_util_handle_sigint_stop(FuUtil *self) +{ + if (self->source_sigint == NULL) + return; + g_source_destroy(self->source_sigint); + self->source_sigint = NULL; +} + +static void +fu_util_context_flags_notify_cb(FuContext *ctx, GParamSpec *pspec, FuUtil *self) { - if (priv->current_device != NULL) - g_object_unref(priv->current_device); - if (priv->ctx != NULL) - g_object_unref(priv->ctx); - if (priv->engine != NULL) - g_object_unref(priv->engine); - if (priv->request != NULL) - g_object_unref(priv->request); - if (priv->client != NULL) - g_object_unref(priv->client); - if (priv->main_ctx != NULL) - g_main_context_unref(priv->main_ctx); - if (priv->loop != NULL) - g_main_loop_unref(priv->loop); - if (priv->cancellable != NULL) - g_object_unref(priv->cancellable); - if (priv->console != NULL) - g_object_unref(priv->console); - if (priv->progress != NULL) - g_object_unref(priv->progress); - if (priv->context != NULL) - g_option_context_free(priv->context); - if (priv->lock_fd >= 0) - g_close(priv->lock_fd, NULL); - g_ptr_array_unref(priv->post_requests); - g_free(priv); + if (fu_context_has_flag(ctx, FU_CONTEXT_FLAG_SYSTEM_INHIBIT)) { + fu_util_handle_sigint_start(self); + } else { + fu_util_handle_sigint_stop(self); + } +} + +static void +fu_util_private_free(FuUtil *self) +{ + if (self->current_device != NULL) + g_object_unref(self->current_device); + if (self->ctx != NULL) + g_object_unref(self->ctx); + if (self->engine != NULL) + g_object_unref(self->engine); + if (self->request != NULL) + g_object_unref(self->request); + if (self->client != NULL) + g_object_unref(self->client); + if (self->main_ctx != NULL) + g_main_context_unref(self->main_ctx); + if (self->loop != NULL) + g_main_loop_unref(self->loop); + if (self->cancellable != NULL) + g_object_unref(self->cancellable); + if (self->console != NULL) + g_object_unref(self->console); + if (self->progress != NULL) + g_object_unref(self->progress); + if (self->context != NULL) + g_option_context_free(self->context); + if (self->source_sigint != NULL) + g_source_destroy(self->source_sigint); + if (self->lock_fd >= 0) + g_close(self->lock_fd, NULL); + g_ptr_array_unref(self->post_requests); + g_free(self); } #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtil, fu_util_private_free) #pragma clang diagnostic pop static void -fu_util_update_device_request_cb(FwupdClient *client, FwupdRequest *request, FuUtilPrivate *priv) +fu_util_update_device_request_cb(FwupdClient *client, FwupdRequest *request, FuUtil *self) { /* action has not been assigned yet */ - if (priv->current_operation == FU_UTIL_OPERATION_UNKNOWN) + if (self->current_operation == FU_UTIL_OPERATION_UNKNOWN) return; /* nothing sensible to show */ @@ -390,66 +408,68 @@ /* TRANSLATORS: the user needs to do something, e.g. remove the device */ fmt = fu_console_color_format(_("Action Required:"), FU_CONSOLE_COLOR_RED); tmp = g_strdup_printf("%s %s", fmt, fwupd_request_get_message(request)); - fu_console_set_progress_title(priv->console, tmp); - fu_console_beep(priv->console, 5); + fu_console_set_progress_title(self->console, tmp); + fu_console_beep(self->console, 5); } /* save for later */ if (fwupd_request_get_kind(request) == FWUPD_REQUEST_KIND_POST) - g_ptr_array_add(priv->post_requests, g_object_ref(request)); + g_ptr_array_add(self->post_requests, g_object_ref(request)); } static void -fu_util_engine_device_added_cb(FuEngine *engine, FuDevice *device, FuUtilPrivate *priv) +fu_util_engine_device_added_cb(FuEngine *engine, FuDevice *device, FuUtil *self) { if (g_getenv("FWUPD_VERBOSE") != NULL) { g_autofree gchar *tmp = fu_device_to_string(device); + /* nocheck:print */ g_debug("ADDED:\n%s", tmp); } } static void -fu_util_engine_device_removed_cb(FuEngine *engine, FuDevice *device, FuUtilPrivate *priv) +fu_util_engine_device_removed_cb(FuEngine *engine, FuDevice *device, FuUtil *self) { if (g_getenv("FWUPD_VERBOSE") != NULL) { g_autofree gchar *tmp = fu_device_to_string(device); + /* nocheck:print */ g_debug("REMOVED:\n%s", tmp); } } static void -fu_util_engine_status_changed_cb(FuEngine *engine, FwupdStatus status, FuUtilPrivate *priv) +fu_util_engine_status_changed_cb(FuEngine *engine, FwupdStatus status, FuUtil *self) { - if (priv->as_json) + if (self->as_json) return; - fu_console_set_progress(priv->console, status, 0); + fu_console_set_progress(self->console, status, 0); } static void -fu_util_progress_percentage_changed_cb(FuProgress *progress, guint percentage, FuUtilPrivate *priv) +fu_util_progress_percentage_changed_cb(FuProgress *progress, guint percentage, FuUtil *self) { - if (priv->as_json) + if (self->as_json) return; - fu_console_set_progress(priv->console, fu_progress_get_status(progress), percentage); + fu_console_set_progress(self->console, fu_progress_get_status(progress), percentage); } static void -fu_util_progress_status_changed_cb(FuProgress *progress, FwupdStatus status, FuUtilPrivate *priv) +fu_util_progress_status_changed_cb(FuProgress *progress, FwupdStatus status, FuUtil *self) { - if (priv->as_json) + if (self->as_json) return; - fu_console_set_progress(priv->console, status, fu_progress_get_percentage(progress)); + fu_console_set_progress(self->console, status, fu_progress_get_percentage(progress)); } static gboolean -fu_util_watch(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_watch(FuUtil *self, gchar **values, GError **error) { - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG, - priv->progress, + self->progress, error)) return FALSE; - g_main_loop_run(priv->loop); + g_main_loop_run(self->loop); return TRUE; } @@ -460,7 +480,7 @@ } static gboolean -fu_util_get_verfmts(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_verfmts(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) verfmts = g_ptr_array_new_with_free_func((GDestroyNotify)g_free); @@ -473,7 +493,7 @@ g_ptr_array_sort(verfmts, (GCompareFunc)fu_util_verfmt_sort_cb); /* print */ - if (priv->as_json) { + if (self->as_json) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_array(builder); for (guint i = 0; i < verfmts->len; i++) { @@ -481,55 +501,55 @@ json_builder_add_string_value(builder, verfmt); } json_builder_end_array(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } /* print */ for (guint i = 0; i < verfmts->len; i++) { const gchar *verfmt = g_ptr_array_index(verfmts, i); - fu_console_print_literal(priv->console, verfmt); + fu_console_print_literal(self->console, verfmt); } return TRUE; } static gboolean -fu_util_get_plugins(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_plugins(FuUtil *self, gchar **values, GError **error) { GPtrArray *plugins; /* load engine */ if (!fu_util_start_engine( - priv, + self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; /* print */ - plugins = fu_engine_get_plugins(priv->engine); + plugins = fu_engine_get_plugins(self->engine); g_ptr_array_sort(plugins, (GCompareFunc)fu_util_plugin_name_sort_cb); - if (priv->as_json) { + if (self->as_json) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); fwupd_codec_array_to_json(plugins, "Plugins", builder, FWUPD_CODEC_FLAG_TRUSTED); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } /* print */ for (guint i = 0; i < plugins->len; i++) { FuPlugin *plugin = g_ptr_array_index(plugins, i); g_autofree gchar *str = fu_util_plugin_to_string(FWUPD_PLUGIN(plugin), 0); - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); } return TRUE; } static FuDevice * -fu_util_prompt_for_device(FuUtilPrivate *priv, GPtrArray *devices_opt, GError **error) +fu_util_prompt_for_device(FuUtil *self, GPtrArray *devices_opt, GError **error) { FuDevice *dev; guint idx; @@ -540,7 +560,7 @@ if (devices_opt != NULL) { devices = g_ptr_array_ref(devices_opt); } else { - devices = fu_engine_get_devices(priv->engine, error); + devices = fu_engine_get_devices(self->engine, error); if (devices == NULL) return NULL; } @@ -548,8 +568,8 @@ /* filter results */ devices_filtered = fwupd_device_array_filter_flags(devices, - priv->filter_device_include, - priv->filter_device_exclude, + self->filter_device_include, + self->filter_device_exclude, error); if (devices_filtered == NULL) return NULL; @@ -557,9 +577,9 @@ /* exactly one */ if (devices_filtered->len == 1) { dev = g_ptr_array_index(devices_filtered, 0); - if (!priv->as_json) { + if (!self->as_json) { fu_console_print( - priv->console, + self->console, "%s: %s", /* TRANSLATORS: device has been chosen by the daemon for the user */ _("Selected device"), @@ -569,7 +589,7 @@ } /* no questions */ - if (priv->no_device_prompt) { + if (self->no_device_prompt) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, @@ -578,18 +598,15 @@ } /* TRANSLATORS: this is to abort the interactive prompt */ - fu_console_print(priv->console, "0.\t%s", _("Cancel")); + fu_console_print(self->console, "0.\t%s", _("Cancel")); for (guint i = 0; i < devices_filtered->len; i++) { - dev = g_ptr_array_index(devices_filtered, i); - fu_console_print(priv->console, - "%u.\t%s (%s)", - i + 1, - fu_device_get_id(dev), - fu_device_get_name(dev)); + FuDevice *device_tmp = g_ptr_array_index(devices_filtered, i); + g_autofree gchar *id_display = fu_device_get_id_display(device_tmp); + fu_console_print(self->console, "%u.\t%s", i + 1, id_display); } /* TRANSLATORS: get interactive prompt */ - idx = fu_console_input_uint(priv->console, devices_filtered->len, "%s", _("Choose device")); + idx = fu_console_input_uint(self->console, devices_filtered->len, "%s", _("Choose device")); if (idx == 0) { g_set_error_literal(error, FWUPD_ERROR, @@ -602,14 +619,14 @@ } static FuDevice * -fu_util_get_device(FuUtilPrivate *priv, const gchar *id, GError **error) +fu_util_get_device(FuUtil *self, const gchar *id, GError **error) { if (fwupd_guid_is_valid(id)) { g_autoptr(GPtrArray) devices = NULL; - devices = fu_engine_get_devices_by_guid(priv->engine, id, error); + devices = fu_engine_get_devices_by_guid(self->engine, id, error); if (devices == NULL) return NULL; - return fu_util_prompt_for_device(priv, devices, error); + return fu_util_prompt_for_device(self, devices, error); } /* did this look like a GUID? */ @@ -622,11 +639,54 @@ return NULL; } } - return fu_engine_get_device(priv->engine, id, error); + return fu_engine_get_device(self->engine, id, error); +} + +static gboolean +fu_util_get_updates_as_json(FuUtil *self, GPtrArray *devices, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + json_builder_set_member_name(builder, "Devices"); + json_builder_begin_array(builder); + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *dev = g_ptr_array_index(devices, i); + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(GError) error_local = NULL; + + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED)) + continue; + + /* get the releases for this device and filter for validity */ + rels = fu_engine_get_upgrades(self->engine, + self->request, + fwupd_device_get_id(dev), + &error_local); + if (rels == NULL) { + g_debug("no upgrades: %s", error_local->message); + continue; + } + for (guint j = 0; j < rels->len; j++) { + FwupdRelease *rel = g_ptr_array_index(rels, j); + if (!fwupd_release_match_flags(rel, + self->filter_release_include, + self->filter_release_exclude)) + continue; + fwupd_device_add_release(dev, rel); + } + + /* add to builder */ + json_builder_begin_object(builder); + fwupd_codec_to_json(FWUPD_CODEC(dev), builder, FWUPD_CODEC_FLAG_TRUSTED); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + json_builder_end_object(builder); + return fu_util_print_builder(self->console, builder, error); } static gboolean -fu_util_get_updates(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_updates(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; g_autoptr(FuUtilNode) root = g_node_new(NULL); @@ -634,33 +694,38 @@ g_autoptr(GPtrArray) devices_no_upgrades = g_ptr_array_new(); /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; /* parse arguments */ if (g_strv_length(values) == 0) { - devices = fu_engine_get_devices(priv->engine, error); + devices = fu_engine_get_devices(self->engine, error); if (devices == NULL) return FALSE; - } else if (g_strv_length(values) == 1) { - FuDevice *device; - device = fu_util_get_device(priv, values[0], error); - if (device == NULL) - return FALSE; - devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); - g_ptr_array_add(devices, device); } else { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_ARGS, - "Invalid arguments"); - return FALSE; + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint idx = 0; idx < g_strv_length(values); idx++) { + FuDevice *device = fu_util_get_device(self, values[idx], error); + if (device == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "'%s' is not a valid GUID nor DEVICE-ID", + values[idx]); + return FALSE; + } + g_ptr_array_add(devices, device); + } } + /* not for human consumption */ + if (self->as_json) + return fu_util_get_updates_as_json(self, devices, error); + fwupd_device_array_ensure_parents(devices); g_ptr_array_sort(devices, fu_util_sort_devices_by_flags_cb); for (guint i = 0; i < devices->len; i++) { @@ -674,8 +739,8 @@ !fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)) continue; if (!fwupd_device_match_flags(dev, - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED)) { g_ptr_array_add(devices_no_support, dev); @@ -683,8 +748,8 @@ } /* get the releases for this device and filter for validity */ - rels = fu_engine_get_upgrades(priv->engine, - priv->request, + rels = fu_engine_get_upgrades(self->engine, + self->request, fwupd_device_get_id(dev), &error_local); if (rels == NULL) { @@ -698,8 +763,8 @@ for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel = g_ptr_array_index(rels, j); if (!fwupd_release_match_flags(rel, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; g_node_append_data(child, g_object_ref(rel)); } @@ -707,23 +772,23 @@ /* devices that have no updates available for whatever reason */ if (devices_no_support->len > 0) { - fu_console_print_literal(priv->console, + fu_console_print_literal(self->console, /* TRANSLATORS: message letting the user know no device * upgrade available due to missing on LVFS */ - _("Devices with no available firmware updates: ")); + _("Devices with no available firmware updates:")); for (guint i = 0; i < devices_no_support->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices_no_support, i); - fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev)); + fu_console_print(self->console, " • %s", fwupd_device_get_name(dev)); } } if (devices_no_upgrades->len > 0) { fu_console_print_literal( - priv->console, + self->console, /* TRANSLATORS: message letting the user know no device upgrade available */ _("Devices with the latest available firmware version:")); for (guint i = 0; i < devices_no_upgrades->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices_no_upgrades, i); - fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev)); + fu_console_print(self->console, " • %s", fwupd_device_get_name(dev)); } } @@ -736,22 +801,22 @@ return FALSE; } - fu_util_print_node(priv->console, priv->client, root); + fu_util_print_node(self->console, self->client, root); return TRUE; } static gboolean -fu_util_get_details(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_details(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) array = NULL; g_autoptr(FuUtilNode) root = g_node_new(NULL); g_autoptr(GInputStream) stream = NULL; /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; @@ -765,7 +830,7 @@ } /* implied, important for get-details on a device not in your system */ - priv->show_all = TRUE; + self->show_all = TRUE; /* open file */ stream = fu_input_stream_from_path(values[0], error); @@ -773,7 +838,7 @@ fu_util_maybe_prefix_sandbox_error(values[0], error); return FALSE; } - array = fu_engine_get_details(priv->engine, priv->request, stream, error); + array = fu_engine_get_details(self->engine, self->request, stream, error); if (array == NULL) return FALSE; for (guint i = 0; i < array->len; i++) { @@ -781,21 +846,21 @@ FwupdRelease *rel; FuUtilNode *child; if (!fwupd_device_match_flags(dev, - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; child = g_node_append_data(root, g_object_ref(dev)); rel = fwupd_device_get_release_default(dev); if (rel != NULL) g_node_append_data(child, g_object_ref(rel)); } - fu_util_print_node(priv->console, priv->client, root); + fu_util_print_node(self->console, self->client, root); return TRUE; } static gboolean -fu_util_get_device_flags(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_device_flags(FuUtil *self, gchar **values, GError **error) { g_autoptr(GString) str = g_string_new(NULL); @@ -810,31 +875,31 @@ g_string_append(str, " ~"); g_string_append(str, tmp); } - fu_console_print_literal(priv->console, str->str); + fu_console_print_literal(self->console, str->str); return TRUE; } static void -fu_util_build_device_tree(FuUtilPrivate *priv, FuUtilNode *root, GPtrArray *devs, FuDevice *dev) +fu_util_build_device_tree(FuUtil *self, FuUtilNode *root, GPtrArray *devs, FuDevice *dev) { for (guint i = 0; i < devs->len; i++) { FuDevice *dev_tmp = g_ptr_array_index(devs, i); if (!fwupd_device_match_flags(FWUPD_DEVICE(dev_tmp), - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; - if (!priv->show_all && !fu_util_is_interesting_device(FWUPD_DEVICE(dev_tmp))) + if (!self->show_all && !fu_util_is_interesting_device(devs, FWUPD_DEVICE(dev_tmp))) continue; - if (fu_device_get_parent(dev_tmp) == dev) { + if (fu_device_get_parent_internal(dev_tmp) == dev) { FuUtilNode *child = g_node_append_data(root, g_object_ref(dev_tmp)); - fu_util_build_device_tree(priv, child, devs, dev_tmp); + fu_util_build_device_tree(self, child, devs, dev_tmp); } } } static gboolean -fu_util_get_devices_as_json(FuUtilPrivate *priv, GPtrArray *devs, GError **error) +fu_util_get_devices_as_json(FuUtil *self, GPtrArray *devs, GError **error) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); @@ -846,8 +911,8 @@ g_autoptr(GError) error_local = NULL; /* add all releases that could be applied */ - rels = fu_engine_get_releases_for_device(priv->engine, - priv->request, + rels = fu_engine_get_releases_for_device(self->engine, + self->request, dev, &error_local); if (rels == NULL) { @@ -856,8 +921,8 @@ for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel = g_ptr_array_index(rels, j); if (!fwupd_release_match_flags(rel, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; fu_device_add_release(dev, rel); } @@ -870,45 +935,47 @@ } json_builder_end_array(builder); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } static gboolean -fu_util_get_devices(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_devices(FuUtil *self, gchar **values, GError **error) { + FuEngineLoadFlags load_flags = + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO; g_autoptr(FuUtilNode) root = g_node_new(NULL); g_autoptr(GPtrArray) devs = NULL; + /* show all devices, even those without assigned plugins */ + if (self->flags & FWUPD_INSTALL_FLAG_FORCE) + load_flags |= FU_ENGINE_LOAD_FLAG_COLDPLUG_FORCE; + /* load engine */ - if (!fu_util_start_engine(priv, - FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | - FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, - error)) + if (!fu_util_start_engine(self, load_flags, self->progress, error)) return FALSE; /* get devices and build tree */ if (g_strv_length(values) > 0) { devs = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); for (guint i = 0; values[i] != NULL; i++) { - FuDevice *device = fu_util_get_device(priv, values[i], error); + FuDevice *device = fu_util_get_device(self, values[i], error); if (device == NULL) return FALSE; g_ptr_array_add(devs, device); } } else { - devs = fu_engine_get_devices(priv->engine, error); + devs = fu_engine_get_devices(self->engine, error); if (devs == NULL) return FALSE; } /* not for human consumption */ - if (priv->as_json) - return fu_util_get_devices_as_json(priv, devs, error); + if (self->as_json) + return fu_util_get_devices_as_json(self, devs, error); if (devs->len > 0) { fwupd_device_array_ensure_parents(devs); - fu_util_build_device_tree(priv, root, devs, NULL); + fu_util_build_device_tree(self, root, devs, NULL); } /* print */ @@ -920,27 +987,27 @@ _("No hardware detected with firmware update capability")); return FALSE; } - fu_util_print_node(priv->console, priv->client, root); + fu_util_print_node(self->console, self->client, root); return TRUE; } static void -fu_util_update_device_changed_cb(FwupdClient *client, FwupdDevice *device, FuUtilPrivate *priv) +fu_util_update_device_changed_cb(FwupdClient *client, FwupdDevice *device, FuUtil *self) { g_autofree gchar *str = NULL; /* allowed to set whenever the device has changed */ if (fwupd_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) - priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; if (fwupd_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) - priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; /* same as last time, so ignore */ - if (priv->current_device == NULL || - g_strcmp0(fwupd_device_get_composite_id(priv->current_device), + if (self->current_device == NULL || + g_strcmp0(fwupd_device_get_composite_id(self->current_device), fwupd_device_get_composite_id(device)) == 0) { - g_set_object(&priv->current_device, device); + g_set_object(&self->current_device, device); return; } @@ -954,46 +1021,51 @@ } /* show message in console */ - if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) { + if (self->current_operation == FU_UTIL_OPERATION_UPDATE) { /* TRANSLATORS: %1 is a device name */ str = g_strdup_printf(_("Updating %s…"), fwupd_device_get_name(device)); - fu_console_set_progress_title(priv->console, str); - } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) { + fu_console_set_progress_title(self->console, str); + } else if (self->current_operation == FU_UTIL_OPERATION_INSTALL) { /* TRANSLATORS: %1 is a device name */ str = g_strdup_printf(_("Installing on %s…"), fwupd_device_get_name(device)); - fu_console_set_progress_title(priv->console, str); - } else if (priv->current_operation == FU_UTIL_OPERATION_READ) { + fu_console_set_progress_title(self->console, str); + } else if (self->current_operation == FU_UTIL_OPERATION_READ) { /* TRANSLATORS: %1 is a device name */ str = g_strdup_printf(_("Reading from %s…"), fwupd_device_get_name(device)); - fu_console_set_progress_title(priv->console, str); + fu_console_set_progress_title(self->console, str); } else { g_warning("no FuUtilOperation set"); } - g_set_object(&priv->current_device, device); + g_set_object(&self->current_device, device); } static void -fu_util_display_current_message(FuUtilPrivate *priv) +fu_util_display_current_message(FuUtil *self) { + if (self->as_json) + return; + /* print all POST requests */ - for (guint i = 0; i < priv->post_requests->len; i++) { - FwupdRequest *request = g_ptr_array_index(priv->post_requests, i); - fu_console_print_literal(priv->console, fu_util_request_get_message(request)); + for (guint i = 0; i < self->post_requests->len; i++) { + FwupdRequest *request = g_ptr_array_index(self->post_requests, i); + fu_console_print_literal(self->console, fu_util_request_get_message(request)); } } static gboolean -fu_util_install_blob(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_install_blob(FuUtil *self, gchar **values, GError **error) { + g_autofree gchar *firmware_basename = NULL; g_autoptr(FuDevice) device = NULL; + g_autoptr(FuRelease) release = fu_release_new(); g_autoptr(GInputStream) stream_fw = NULL; /* progress */ - fu_progress_set_id(priv->progress, G_STRLOC); - fu_progress_add_flag(priv->progress, FU_PROGRESS_FLAG_NO_PROFILE); - fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 2, "parse"); - fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 30, "start-engine"); - fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_WRITE, 68, NULL); + fu_progress_set_id(self->progress, G_STRLOC); + fu_progress_add_flag(self->progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(self->progress, FWUPD_STATUS_LOADING, 2, "parse"); + fu_progress_add_step(self->progress, FWUPD_STATUS_LOADING, 30, "start-engine"); + fu_progress_add_step(self->progress, FWUPD_STATUS_DEVICE_WRITE, 68, NULL); /* invalid args */ if (g_strv_length(values) == 0) { @@ -1010,85 +1082,95 @@ fu_util_maybe_prefix_sandbox_error(values[0], error); return FALSE; } - fu_progress_step_done(priv->progress); + fu_release_set_stream(release, stream_fw); + fu_progress_step_done(self->progress); + + /* some plugins need the firmware name */ + firmware_basename = g_path_get_basename(values[0]); + fu_release_set_firmware_basename(release, firmware_basename); /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* get device */ - priv->filter_device_include |= FWUPD_DEVICE_FLAG_UPDATABLE; + self->filter_device_include |= FWUPD_DEVICE_FLAG_UPDATABLE; if (g_strv_length(values) >= 2) { - device = fu_util_get_device(priv, values[1], error); + device = fu_util_get_device(self, values[1], error); if (device == NULL) return FALSE; } else { - device = fu_util_prompt_for_device(priv, NULL, error); + device = fu_util_prompt_for_device(self, NULL, error); if (device == NULL) return FALSE; } - priv->current_operation = FU_UTIL_OPERATION_INSTALL; - g_signal_connect(FU_ENGINE(priv->engine), + /* optional version */ + if (g_strv_length(values) >= 3) + fu_release_set_version(release, values[2]); + + self->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect(FU_ENGINE(self->engine), "device-changed", G_CALLBACK(fu_util_update_device_changed_cb), - priv); + self); /* write bare firmware */ - if (priv->prepare_blob) { + if (self->prepare_blob) { g_autoptr(GPtrArray) devices = NULL; devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); g_ptr_array_add(devices, g_object_ref(device)); - if (!fu_engine_composite_prepare(priv->engine, devices, error)) { - g_prefix_error(error, "failed to prepare composite action: "); + if (!fu_engine_composite_prepare(self->engine, devices, error)) { + g_prefix_error_literal(error, "failed to prepare composite action: "); return FALSE; } } - priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY; - if (!fu_engine_install_blob(priv->engine, + self->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY; + if (!fu_engine_install_blob(self->engine, device, - stream_fw, - fu_progress_get_child(priv->progress), - priv->flags, - fu_engine_request_get_feature_flags(priv->request), + release, + fu_progress_get_child(self->progress), + self->flags, + fu_engine_request_get_feature_flags(self->request), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* cleanup */ - if (priv->cleanup_blob) { + if (self->cleanup_blob) { g_autoptr(FuDevice) device_new = NULL; g_autoptr(GError) error_local = NULL; /* get the possibly new device from the old ID */ - device_new = fu_util_get_device(priv, fu_device_get_id(device), &error_local); + device_new = fu_util_get_device(self, fu_device_get_id(device), &error_local); if (device_new == NULL) { g_debug("failed to find new device: %s", error_local->message); } else { g_autoptr(GPtrArray) devices_new = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); g_ptr_array_add(devices_new, g_steal_pointer(&device_new)); - if (!fu_engine_composite_cleanup(priv->engine, devices_new, error)) { - g_prefix_error(error, "failed to cleanup composite action: "); + if (!fu_engine_composite_cleanup(self->engine, devices_new, error)) { + g_prefix_error_literal(error, + "failed to cleanup composite action: "); return FALSE; } } } - fu_util_display_current_message(priv); + fu_util_display_current_message(self); /* success */ - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean -fu_util_firmware_sign(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_firmware_sign(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuCabinet) cabinet = fu_cabinet_new(); g_autoptr(GBytes) archive_blob_new = NULL; @@ -1118,7 +1200,7 @@ archive_file_old = g_file_new_for_path(values[0]); if (!fu_firmware_parse_file(FU_FIRMWARE(cabinet), archive_file_old, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM, error)) return FALSE; if (!fu_cabinet_sign(cabinet, cert, privkey, FU_CABINET_SIGN_FLAG_NONE, error)) @@ -1130,17 +1212,17 @@ } static gboolean -fu_util_firmware_dump(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_firmware_dump(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuDevice) device = NULL; g_autoptr(GBytes) blob_empty = g_bytes_new(NULL, 0); g_autoptr(GBytes) blob_fw = NULL; /* progress */ - fu_progress_set_id(priv->progress, G_STRLOC); - fu_progress_add_flag(priv->progress, FU_PROGRESS_FLAG_NO_PROFILE); - fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 5, "start-engine"); - fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_READ, 95, NULL); + fu_progress_set_id(self->progress, G_STRLOC); + fu_progress_add_flag(self->progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(self->progress, FWUPD_STATUS_LOADING, 5, "start-engine"); + fu_progress_add_step(self->progress, FWUPD_STATUS_DEVICE_READ, 95, NULL); /* invalid args */ if (g_strv_length(values) == 0) { @@ -1152,7 +1234,7 @@ } /* file already exists */ - if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + if ((self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && g_file_test(values[0], G_FILE_TEST_EXISTS)) { g_set_error_literal(error, FWUPD_ERROR, @@ -1167,44 +1249,44 @@ return FALSE; /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO, - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* get device */ - priv->filter_device_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE; + self->filter_device_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE; if (g_strv_length(values) >= 2) { - device = fu_util_get_device(priv, values[1], error); + device = fu_util_get_device(self, values[1], error); if (device == NULL) return FALSE; } else { - device = fu_util_prompt_for_device(priv, NULL, error); + device = fu_util_prompt_for_device(self, NULL, error); if (device == NULL) return FALSE; } - priv->current_operation = FU_UTIL_OPERATION_READ; - g_signal_connect(FU_ENGINE(priv->engine), + self->current_operation = FU_UTIL_OPERATION_READ; + g_signal_connect(FU_ENGINE(self->engine), "device-changed", G_CALLBACK(fu_util_update_device_changed_cb), - priv); + self); /* dump firmware */ - blob_fw = fu_engine_firmware_dump(priv->engine, + blob_fw = fu_engine_firmware_dump(self->engine, device, - fu_progress_get_child(priv->progress), - priv->flags, + fu_progress_get_child(self->progress), + self->flags, error); if (blob_fw == NULL) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); return fu_bytes_set_contents(values[0], blob_fw, error); } static gboolean -fu_util_firmware_read(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_firmware_read(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuDevice) device = NULL; g_autoptr(FuFirmware) fw = NULL; @@ -1212,10 +1294,10 @@ g_autoptr(GBytes) blob_fw = NULL; /* progress */ - fu_progress_set_id(priv->progress, G_STRLOC); - fu_progress_add_flag(priv->progress, FU_PROGRESS_FLAG_NO_PROFILE); - fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 5, "start-engine"); - fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_READ, 95, NULL); + fu_progress_set_id(self->progress, G_STRLOC); + fu_progress_add_flag(self->progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(self->progress, FWUPD_STATUS_LOADING, 5, "start-engine"); + fu_progress_add_step(self->progress, FWUPD_STATUS_DEVICE_READ, 95, NULL); /* invalid args */ if (g_strv_length(values) == 0) { @@ -1227,7 +1309,7 @@ } /* file already exists */ - if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + if ((self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && g_file_test(values[0], G_FILE_TEST_EXISTS)) { g_set_error_literal(error, FWUPD_ERROR, @@ -1242,44 +1324,44 @@ return FALSE; /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_HWINFO, - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* get device */ - priv->filter_device_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE; + self->filter_device_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY_IMAGE; if (g_strv_length(values) >= 2) { - device = fu_util_get_device(priv, values[1], error); + device = fu_util_get_device(self, values[1], error); if (device == NULL) return FALSE; } else { - device = fu_util_prompt_for_device(priv, NULL, error); + device = fu_util_prompt_for_device(self, NULL, error); if (device == NULL) return FALSE; } - priv->current_operation = FU_UTIL_OPERATION_READ; - g_signal_connect(FU_ENGINE(priv->engine), + self->current_operation = FU_UTIL_OPERATION_READ; + g_signal_connect(FU_ENGINE(self->engine), "device-changed", G_CALLBACK(fu_util_update_device_changed_cb), - priv); + self); /* read firmware into the container format */ - fw = fu_engine_firmware_read(priv->engine, + fw = fu_engine_firmware_read(self->engine, device, - fu_progress_get_child(priv->progress), - priv->flags, + fu_progress_get_child(self->progress), + self->flags, error); if (fw == NULL) return FALSE; blob_fw = fu_firmware_write(fw, error); if (blob_fw == NULL) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); return fu_bytes_set_contents(values[0], blob_fw, error); } @@ -1292,7 +1374,7 @@ } static gchar * -fu_util_download_if_required(FuUtilPrivate *priv, const gchar *perhapsfn, GError **error) +fu_util_download_if_required(FuUtil *self, const gchar *perhapsfn, GError **error) { g_autofree gchar *filename = NULL; g_autoptr(GFile) file = NULL; @@ -1308,18 +1390,18 @@ if (!fu_path_mkdir_parent(filename, error)) return NULL; file = g_file_new_for_path(filename); - if (!fwupd_client_download_file(priv->client, + if (!fwupd_client_download_file(self->client, perhapsfn, file, FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, - priv->cancellable, + self->cancellable, error)) return NULL; return g_steal_pointer(&filename); } static gboolean -fu_util_install_stream(FuUtilPrivate *priv, +fu_util_install_stream(FuUtil *self, GInputStream *stream, GPtrArray *devices, FuProgress *progress, @@ -1330,7 +1412,7 @@ g_autoptr(GPtrArray) errors = NULL; g_autoptr(GPtrArray) releases = NULL; - cabinet = fu_engine_build_cabinet_from_stream(priv->engine, stream, error); + cabinet = fu_engine_build_cabinet_from_stream(self->engine, stream, error); if (cabinet == NULL) return FALSE; components = fu_cabinet_get_components(cabinet, error); @@ -1351,13 +1433,13 @@ /* is this component valid for the device */ fu_release_set_device(release, device); - fu_release_set_request(release, priv->request); - if (!fu_engine_load_release(priv->engine, + fu_release_set_request(release, self->request); + if (!fu_engine_load_release(self->engine, release, cabinet, component, NULL, - priv->flags, + self->flags, &error_local)) { g_debug("loading release failed on %s:%s failed: %s", fu_device_get_id(device), @@ -1366,9 +1448,9 @@ g_ptr_array_add(errors, g_steal_pointer(&error_local)); continue; } - if (!fu_engine_requirements_check(priv->engine, + if (!fu_engine_requirements_check(self->engine, release, - priv->flags, + self->flags, &error_local)) { g_debug("requirement on %s:%s failed: %s", fu_device_get_id(device), @@ -1397,61 +1479,61 @@ return FALSE; } - priv->current_operation = FU_UTIL_OPERATION_INSTALL; - g_signal_connect(FU_ENGINE(priv->engine), + self->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect(FU_ENGINE(self->engine), "device-changed", G_CALLBACK(fu_util_update_device_changed_cb), - priv); + self); /* install all the tasks */ - return fu_engine_install_releases(priv->engine, - priv->request, + return fu_engine_install_releases(self->engine, + self->request, releases, cabinet, - fu_progress_get_child(priv->progress), - priv->flags, + fu_progress_get_child(self->progress), + self->flags, error); } static gboolean -fu_util_install(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_install(FuUtil *self, gchar **values, GError **error) { g_autofree gchar *filename = NULL; g_autoptr(GInputStream) stream = NULL; g_autoptr(GPtrArray) devices_possible = NULL; /* progress */ - fu_progress_set_id(priv->progress, G_STRLOC); - fu_progress_add_flag(priv->progress, FU_PROGRESS_FLAG_NO_PROFILE); - fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 50, "start-engine"); - fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); + fu_progress_set_id(self->progress, G_STRLOC); + fu_progress_add_flag(self->progress, FU_PROGRESS_FLAG_NO_PROFILE); + fu_progress_add_step(self->progress, FWUPD_STATUS_LOADING, 50, "start-engine"); + fu_progress_add_step(self->progress, FWUPD_STATUS_DEVICE_WRITE, 50, NULL); /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* handle both forms */ if (g_strv_length(values) == 1) { - devices_possible = fu_engine_get_devices(priv->engine, error); + devices_possible = fu_engine_get_devices(self->engine, error); if (devices_possible == NULL) return FALSE; fwupd_device_array_ensure_parents(devices_possible); } else if (g_strv_length(values) == 2) { - FuDevice *device = fu_util_get_device(priv, values[1], error); + FuDevice *device = fu_util_get_device(self, values[1], error); if (device == NULL) return FALSE; - if (!priv->no_safety_check) { - if (!fu_util_prompt_warning_fde(priv->console, FWUPD_DEVICE(device), error)) + if (!self->no_safety_check) { + if (!fu_util_prompt_warning_fde(self->console, FWUPD_DEVICE(device), error)) return FALSE; } devices_possible = - fu_engine_get_devices_by_composite_id(priv->engine, + fu_engine_get_devices_by_composite_id(self->engine, fu_device_get_composite_id(device), error); if (devices_possible == NULL) @@ -1467,7 +1549,7 @@ } /* download if required */ - filename = fu_util_download_if_required(priv, values[0], error); + filename = fu_util_download_if_required(self, values[0], error); if (filename == NULL) return FALSE; stream = fu_input_stream_from_path(filename, error); @@ -1475,28 +1557,28 @@ fu_util_maybe_prefix_sandbox_error(filename, error); return FALSE; } - if (!fu_util_install_stream(priv, + if (!fu_util_install_stream(self, stream, devices_possible, - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); - fu_util_display_current_message(priv); + fu_util_display_current_message(self); /* we don't want to ask anything */ - if (priv->no_reboot_check) { + if (self->no_reboot_check) { g_debug("skipping reboot check"); return TRUE; } /* success */ - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return TRUE; } static gboolean -fu_util_install_release(FuUtilPrivate *priv, FwupdRelease *rel, GError **error) +fu_util_install_release(FuUtil *self, FwupdDevice *dev, FwupdRelease *rel, GError **error) { FwupdRemote *remote; GPtrArray *locations; @@ -1504,6 +1586,21 @@ const gchar *uri_tmp; g_auto(GStrv) argv = NULL; + if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)) { + const gchar *name = fwupd_device_get_name(dev); + g_autofree gchar *str = NULL; + + /* TRANSLATORS: the device has a reason it can't update, e.g. laptop lid closed */ + str = g_strdup_printf(_("%s is not currently updatable"), name); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "%s: %s", + str, + fwupd_device_get_update_error(dev)); + return FALSE; + } + /* get the default release only until other parts of fwupd can cope */ locations = fwupd_release_get_locations(rel); if (locations->len == 0) { @@ -1524,7 +1621,7 @@ return FALSE; } - remote = fu_engine_get_remote_by_id(priv->engine, remote_id, error); + remote = fu_engine_get_remote_by_id(self->engine, remote_id, error); if (remote == NULL) return FALSE; @@ -1542,20 +1639,20 @@ } /* reset progress before reusing it. */ - fu_progress_reset(priv->progress); + fu_progress_reset(self->progress); - return fu_util_install(priv, argv, error); + return fu_util_install(self, argv, error); } static gboolean -fu_util_update(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_update(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; g_autoptr(GPtrArray) devices_latest = g_ptr_array_new(); g_autoptr(GPtrArray) devices_pending = g_ptr_array_new(); g_autoptr(GPtrArray) devices_unsupported = g_ptr_array_new(); - if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) { + if (self->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS, @@ -1563,7 +1660,7 @@ return FALSE; } - if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { + if (self->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS, @@ -1571,11 +1668,11 @@ return FALSE; } - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; @@ -1591,9 +1688,9 @@ } } - priv->current_operation = FU_UTIL_OPERATION_UPDATE; + self->current_operation = FU_UTIL_OPERATION_UPDATE; - devices = fu_engine_get_devices(priv->engine, error); + devices = fu_engine_get_devices(self->engine, error); if (devices == NULL) return FALSE; fwupd_device_array_ensure_parents(devices); @@ -1616,7 +1713,7 @@ } if (g_strv_length(values) > 0 && dev_skip_byid) continue; - if (!fu_util_is_interesting_device(dev)) + if (!fu_util_is_interesting_device(devices, dev)) continue; /* only show stuff that has metadata available */ @@ -1628,11 +1725,11 @@ continue; } if (!fwupd_device_match_flags(dev, - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; - rels = fu_engine_get_upgrades(priv->engine, priv->request, device_id, &error_local); + rels = fu_engine_get_upgrades(self->engine, self->request, device_id, &error_local); if (rels == NULL) { g_ptr_array_add(devices_latest, dev); /* discard the actual reason from user, but leave for debugging */ @@ -1647,78 +1744,85 @@ } rel = g_ptr_array_index(rels, 0); - if (!priv->no_safety_check) { + if (!self->no_safety_check) { g_autofree gchar *title = g_strdup_printf("%s %s", - fu_engine_get_host_vendor(priv->engine), - fu_engine_get_host_product(priv->engine)); - if (!fu_util_prompt_warning(priv->console, dev, rel, title, error)) + fu_engine_get_host_vendor(self->engine), + fu_engine_get_host_product(self->engine)); + if (!fu_util_prompt_warning(self->console, dev, rel, title, &error_local)) { + if (g_error_matches(error_local, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO)) { + g_debug("%s", error_local->message); + continue; + } + g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; - if (!fu_util_prompt_warning_fde(priv->console, dev, error)) + } + if (!fu_util_prompt_warning_fde(self->console, dev, error)) return FALSE; } - if (!fu_util_install_release(priv, rel, &error_local)) { - fu_console_print_literal(priv->console, error_local->message); + if (!fu_util_install_release(self, dev, rel, &error_local)) { + fu_console_print_literal(self->console, error_local->message); continue; } - fu_util_display_current_message(priv); + fu_util_display_current_message(self); } /* show warnings */ - if (devices_latest->len > 0 && !priv->as_json) { - fu_console_print_literal(priv->console, + if (devices_latest->len > 0 && !self->as_json) { + fu_console_print_literal(self->console, /* TRANSLATORS: message letting the user know no device * upgrade available */ _("Devices with the latest available firmware version:")); for (guint i = 0; i < devices_latest->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices_latest, i); - fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev)); + fu_console_print(self->console, " • %s", fwupd_device_get_name(dev)); } } - if (devices_unsupported->len > 0 && !priv->as_json) { - fu_console_print_literal(priv->console, + if (devices_unsupported->len > 0 && !self->as_json) { + fu_console_print_literal(self->console, /* TRANSLATORS: message letting the user know no * device upgrade available due to missing on LVFS */ - _("Devices with no available firmware updates: ")); + _("Devices with no available firmware updates:")); for (guint i = 0; i < devices_unsupported->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices_unsupported, i); - fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev)); + fu_console_print(self->console, " • %s", fwupd_device_get_name(dev)); } } - if (devices_pending->len > 0 && !priv->as_json) { - fu_console_print_literal( - priv->console, - /* TRANSLATORS: message letting the user there is an update - * waiting, but there is a reason it cannot be deployed */ - _("Devices with firmware updates that need user action: ")); + if (devices_pending->len > 0 && !self->as_json) { + fu_console_print_literal(self->console, + /* TRANSLATORS: message letting the user there is an update + * waiting, but there is a reason it cannot be deployed */ + _("Devices with firmware updates that need user action:")); for (guint i = 0; i < devices_pending->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices_pending, i); - fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev)); + fu_console_print(self->console, " • %s", fwupd_device_get_name(dev)); for (guint j = 0; j < 64; j++) { FwupdDeviceProblem problem = (guint64)1 << j; g_autofree gchar *desc = NULL; if (!fwupd_device_has_problem(dev, problem)) continue; - desc = fu_util_device_problem_to_string(priv->client, dev, problem); + desc = fu_util_device_problem_to_string(self->client, dev, problem); if (desc == NULL) continue; - fu_console_print(priv->console, " ‣ %s", desc); + fu_console_print(self->console, " ‣ %s", desc); } } } /* we don't want to ask anything */ - if (priv->no_reboot_check || priv->as_json) { + if (self->no_reboot_check || self->as_json) { g_debug("skipping reboot check"); return TRUE; } - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean -fu_util_reinstall(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_reinstall(FuUtil *self, gchar **values, GError **error) { g_autoptr(FwupdRelease) rel = NULL; g_autoptr(GPtrArray) rels = NULL; @@ -1732,28 +1836,28 @@ return FALSE; } - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; - dev = fu_util_get_device(priv, values[0], error); + dev = fu_util_get_device(self, values[0], error); if (dev == NULL) return FALSE; /* try to lookup/match release from client */ - rels = fu_engine_get_releases_for_device(priv->engine, priv->request, dev, error); + rels = fu_engine_get_releases_for_device(self->engine, self->request, dev, error); if (rels == NULL) return FALSE; for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel_tmp = g_ptr_array_index(rels, j); if (!fwupd_release_match_flags(rel_tmp, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; if (fu_version_compare(fwupd_release_get_version(rel_tmp), fu_device_get_version(dev), @@ -1773,54 +1877,54 @@ } /* update the console if composite devices are also updated */ - priv->current_operation = FU_UTIL_OPERATION_INSTALL; - g_signal_connect(FU_ENGINE(priv->engine), + self->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect(FU_ENGINE(self->engine), "device-changed", G_CALLBACK(fu_util_update_device_changed_cb), - priv); - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; - if (!fu_util_install_release(priv, rel, error)) + self); + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + if (!fu_util_install_release(self, FWUPD_DEVICE(dev), rel, error)) return FALSE; - fu_util_display_current_message(priv); + fu_util_display_current_message(self); /* we don't want to ask anything */ - if (priv->no_reboot_check) { + if (self->no_reboot_check) { g_debug("skipping reboot check"); return TRUE; } - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean -fu_util_detach(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_detach(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuDevice) device = NULL; g_autoptr(FuDeviceLocker) locker = NULL; /* progress */ - fu_progress_set_id(priv->progress, G_STRLOC); - fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); - fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_BUSY, 5, NULL); + fu_progress_set_id(self->progress, G_STRLOC); + fu_progress_add_step(self->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); + fu_progress_add_step(self->progress, FWUPD_STATUS_DEVICE_BUSY, 5, NULL); /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* get device */ - priv->filter_device_exclude |= FWUPD_DEVICE_FLAG_IS_BOOTLOADER; + self->filter_device_exclude |= FWUPD_DEVICE_FLAG_IS_BOOTLOADER; if (g_strv_length(values) >= 1) { - device = fu_util_get_device(priv, values[0], error); + device = fu_util_get_device(self, values[0], error); if (device == NULL) return FALSE; } else { - device = fu_util_prompt_for_device(priv, NULL, error); + device = fu_util_prompt_for_device(self, NULL, error); if (device == NULL) return FALSE; } @@ -1829,32 +1933,32 @@ locker = fu_device_locker_new(device, error); if (locker == NULL) return FALSE; - if (!fu_device_detach_full(device, fu_progress_get_child(priv->progress), error)) + if (!fu_device_detach_full(device, fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); return TRUE; } static gboolean -fu_util_unbind_driver(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_unbind_driver(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuDevice) device = NULL; g_autoptr(FuDeviceLocker) locker = NULL; /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; /* get device */ if (g_strv_length(values) == 1) { - device = fu_util_get_device(priv, values[0], error); + device = fu_util_get_device(self, values[0], error); } else { - device = fu_util_prompt_for_device(priv, NULL, error); + device = fu_util_prompt_for_device(self, NULL, error); } if (device == NULL) return FALSE; @@ -1867,27 +1971,27 @@ } static gboolean -fu_util_bind_driver(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_bind_driver(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuDevice) device = NULL; g_autoptr(FuDeviceLocker) locker = NULL; /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; /* get device */ if (g_strv_length(values) == 3) { - device = fu_util_get_device(priv, values[2], error); + device = fu_util_get_device(self, values[2], error); if (device == NULL) return FALSE; } else if (g_strv_length(values) == 2) { - device = fu_util_prompt_for_device(priv, NULL, error); + device = fu_util_prompt_for_device(self, NULL, error); if (device == NULL) return FALSE; } else { @@ -1906,35 +2010,35 @@ } static gboolean -fu_util_attach(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_attach(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuDevice) device = NULL; g_autoptr(FuDeviceLocker) locker = NULL; /* progress */ - fu_progress_set_id(priv->progress, G_STRLOC); - fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); - fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_BUSY, 5, NULL); + fu_progress_set_id(self->progress, G_STRLOC); + fu_progress_add_step(self->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); + fu_progress_add_step(self->progress, FWUPD_STATUS_DEVICE_BUSY, 5, NULL); /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* get device */ - if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) - priv->filter_device_include |= FWUPD_DEVICE_FLAG_IS_BOOTLOADER; + if ((self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) + self->filter_device_include |= FWUPD_DEVICE_FLAG_IS_BOOTLOADER; if (g_strv_length(values) >= 1) { - device = fu_util_get_device(priv, values[0], error); + device = fu_util_get_device(self, values[0], error); if (device == NULL) return FALSE; } else { - device = fu_util_prompt_for_device(priv, NULL, error); + device = fu_util_prompt_for_device(self, NULL, error); if (device == NULL) return FALSE; } @@ -1943,9 +2047,9 @@ locker = fu_device_locker_new(device, error); if (locker == NULL) return FALSE; - if (!fu_device_attach_full(device, fu_progress_get_child(priv->progress), error)) + if (!fu_device_attach_full(device, fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* success */ return TRUE; @@ -1964,7 +2068,80 @@ } static gboolean -fu_util_get_report_metadata(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_report_metadata_as_json(FuUtil *self, JsonBuilder *builder, GError **error) +{ + GPtrArray *plugins; + g_autoptr(GHashTable) metadata = NULL; + g_autoptr(GPtrArray) devices = NULL; + + /* daemon metadata */ + metadata = fu_engine_get_report_metadata(self->engine, error); + if (metadata == NULL) + return FALSE; + fwupd_codec_json_append_map(builder, "daemon", metadata); + + /* device metadata */ + devices = fu_engine_get_devices(self->engine, error); + if (devices == NULL) + return FALSE; + json_builder_set_member_name(builder, "devices"); + json_builder_begin_array(builder); + for (guint i = 0; i < devices->len; i++) { + FuDevice *device = g_ptr_array_index(devices, i); + g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(GHashTable) metadata_post = NULL; + g_autoptr(GHashTable) metadata_pre = NULL; + + locker = fu_device_locker_new(device, error); + if (locker == NULL) + return FALSE; + metadata_pre = fu_device_report_metadata_pre(device); + metadata_post = fu_device_report_metadata_post(device); + if (metadata_pre == NULL && metadata_post == NULL) + continue; + + json_builder_begin_object(builder); + json_builder_set_member_name(builder, fu_device_get_id(device)); + json_builder_begin_array(builder); + if (metadata_pre != NULL) { + json_builder_begin_object(builder); + fwupd_codec_json_append_map(builder, "pre", metadata_pre); + json_builder_end_object(builder); + } + if (metadata_post != NULL) { + json_builder_begin_object(builder); + fwupd_codec_json_append_map(builder, "post", metadata_post); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + + /* plugin metadata */ + plugins = fu_engine_get_plugins(self->engine); + json_builder_set_member_name(builder, "plugins"); + json_builder_begin_array(builder); + for (guint i = 0; i < plugins->len; i++) { + FuPlugin *plugin = g_ptr_array_index(plugins, i); + if (fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)) + continue; + if (fu_plugin_get_report_metadata(plugin) == NULL) + continue; + json_builder_begin_object(builder); + fwupd_codec_json_append_map(builder, + fu_plugin_get_name(plugin), + fu_plugin_get_report_metadata(plugin)); + json_builder_end_object(builder); + } + json_builder_end_array(builder); + + /* success */ + return TRUE; +} + +static gboolean +fu_util_get_report_metadata(FuUtil *self, gchar **values, GError **error) { GPtrArray *plugins; g_autoptr(GHashTable) metadata = NULL; @@ -1972,26 +2149,36 @@ g_autoptr(GString) str = g_string_new(NULL); /* progress */ - fu_progress_set_id(priv->progress, G_STRLOC); - fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); - fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_BUSY, 5, NULL); + fu_progress_set_id(self->progress, G_STRLOC); + fu_progress_add_step(self->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); + fu_progress_add_step(self->progress, FWUPD_STATUS_DEVICE_BUSY, 5, NULL); /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO, - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); + + /* not for human consumption */ + if (self->as_json) { + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + if (!fu_util_get_report_metadata_as_json(self, builder, error)) + return FALSE; + json_builder_end_object(builder); + return fu_util_print_builder(self->console, builder, error); + } /* daemon metadata */ - metadata = fu_engine_get_report_metadata(priv->engine, error); + metadata = fu_engine_get_report_metadata(self->engine, error); if (metadata == NULL) return FALSE; fu_util_report_metadata_to_string(metadata, 0, str); /* device metadata */ - devices = fu_engine_get_devices(priv->engine, error); + devices = fu_engine_get_devices(self->engine, error); if (devices == NULL) return FALSE; for (guint i = 0; i < devices->len; i++) { @@ -2022,37 +2209,38 @@ } /* plugin metadata */ - plugins = fu_engine_get_plugins(priv->engine); + plugins = fu_engine_get_plugins(self->engine); for (guint i = 0; i < plugins->len; i++) { FuPlugin *plugin = g_ptr_array_index(plugins, i); if (fu_plugin_has_flag(plugin, FWUPD_PLUGIN_FLAG_DISABLED)) continue; if (fu_plugin_get_report_metadata(plugin) == NULL) continue; + fwupd_codec_string_append(str, 1, fu_plugin_get_name(plugin), ""); fu_util_report_metadata_to_string(fu_plugin_get_report_metadata(plugin), 3, str); } - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* display */ - fu_console_print_literal(priv->console, str->str); + fu_console_print_literal(self->console, str->str); /* success */ return TRUE; } static gboolean -fu_util_modify_config(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_modify_config(FuUtil *self, gchar **values, GError **error) { /* start engine */ - if (!fu_util_start_engine(priv, FU_ENGINE_LOAD_FLAG_HWINFO, priv->progress, error)) + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_HWINFO, self->progress, error)) return FALSE; /* check args */ if (g_strv_length(values) == 3) { - if (!fu_engine_modify_config(priv->engine, values[0], values[1], values[2], error)) + if (!fu_engine_modify_config(self->engine, values[0], values[1], values[2], error)) return FALSE; } else if (g_strv_length(values) == 2) { - if (!fu_engine_modify_config(priv->engine, "fwupd", values[0], values[1], error)) + if (!fu_engine_modify_config(self->engine, "fwupd", values[0], values[1], error)) return FALSE; } else { g_set_error_literal(error, @@ -2062,16 +2250,16 @@ return FALSE; } - if (priv->as_json) + if (self->as_json) return TRUE; /* TRANSLATORS: success message -- a per-system setting value */ - fu_console_print_literal(priv->console, _("Successfully modified configuration value")); + fu_console_print_literal(self->console, _("Successfully modified configuration value")); return TRUE; } static gboolean -fu_util_reset_config(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_reset_config(FuUtil *self, gchar **values, GError **error) { /* check args */ if (g_strv_length(values) != 1) { @@ -2083,22 +2271,22 @@ } /* start engine */ - if (!fu_util_start_engine(priv, FU_ENGINE_LOAD_FLAG_NONE, priv->progress, error)) + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_NONE, self->progress, error)) return FALSE; - if (!fu_engine_reset_config(priv->engine, values[0], error)) + if (!fu_engine_reset_config(self->engine, values[0], error)) return FALSE; - if (priv->as_json) + if (self->as_json) return TRUE; /* TRANSLATORS: success message -- a per-system setting value */ - fu_console_print_literal(priv->console, _("Successfully reset configuration section")); + fu_console_print_literal(self->console, _("Successfully reset configuration section")); return TRUE; } static gboolean -fu_util_remote_modify(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_remote_modify(FuUtil *self, gchar **values, GError **error) { FwupdRemote *remote = NULL; @@ -2110,29 +2298,65 @@ return FALSE; } - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; - remote = fu_engine_get_remote_by_id(priv->engine, values[0], error); + remote = fu_engine_get_remote_by_id(self->engine, values[0], error); if (remote == NULL) return FALSE; - if (!fu_engine_modify_remote(priv->engine, + if (!fu_engine_modify_remote(self->engine, fwupd_remote_get_id(remote), values[1], values[2], error)) return FALSE; - fu_console_print_literal(priv->console, _("Successfully modified remote")); + if (self->as_json) + return TRUE; + + fu_console_print_literal(self->console, _("Successfully modified remote")); + return TRUE; +} + +static gboolean +fu_util_remote_clean(FuUtil *self, gchar **values, GError **error) +{ + FwupdRemote *remote = NULL; + + 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(self, + FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, + self->progress, + error)) + return FALSE; + + remote = fu_engine_get_remote_by_id(self->engine, values[0], error); + if (remote == NULL) + return FALSE; + if (!fu_engine_clean_remote(self->engine, fwupd_remote_get_id(remote), error)) + return FALSE; + + if (self->as_json) + return TRUE; + + /* TRANSLATORS: success message */ + fu_console_print_literal(self->console, _("Successfully cleaned remote")); return TRUE; } static gboolean -fu_util_remote_disable(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_remote_disable(FuUtil *self, gchar **values, GError **error) { FwupdRemote *remote = NULL; @@ -2144,29 +2368,187 @@ return FALSE; } - if (!fu_util_start_engine(priv, FU_ENGINE_LOAD_FLAG_REMOTES, priv->progress, error)) + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_REMOTES, self->progress, error)) return FALSE; - remote = fu_engine_get_remote_by_id(priv->engine, values[0], error); + remote = fu_engine_get_remote_by_id(self->engine, values[0], error); if (remote == NULL) return FALSE; - if (!fu_engine_modify_remote(priv->engine, + if (!fu_engine_modify_remote(self->engine, fwupd_remote_get_id(remote), "Enabled", "false", error)) return FALSE; - if (priv->as_json) + if (self->as_json) return TRUE; - fu_console_print_literal(priv->console, _("Successfully disabled remote")); + /* TRANSLATORS: success message */ + fu_console_print_literal(self->console, _("Successfully disabled remote")); + + /* delete the now-unused cache files? */ + if (fwupd_remote_get_kind(remote) == FWUPD_REMOTE_KIND_DOWNLOAD && + fwupd_remote_get_age(remote) != G_MAXUINT64) { + if (self->assume_yes || + fu_console_input_bool(self->console, + FALSE, + "%s", + /* TRANSLATORS: this is now useless */ + _("Delete the now-unused remote cache files?"))) { + if (!fu_engine_clean_remote(self->engine, values[0], error)) + return FALSE; + } + fu_console_print_literal(self->console, + /* TRANSLATORS: success message */ + _("Successfully cleaned remote")); + } + + /* success */ return TRUE; } static gboolean -fu_util_vercmp(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_crc(FuUtil *self, gchar **values, GError **error) +{ + FuCrcKind kind; + + /* sanity check */ + if (g_strv_length(values) < 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments, expected KIND FILENAME [FILENAME]"); + return FALSE; + } + + /* get kind */ + kind = fu_crc_kind_from_string(values[0]); + if (kind == FU_CRC_KIND_UNKNOWN) { + g_autofree gchar *str = NULL; + g_autoptr(GPtrArray) crc_kinds = g_ptr_array_new(); + for (guint i = 1; i < FU_CRC_KIND_LAST; i++) + g_ptr_array_add(crc_kinds, (gpointer)fu_crc_kind_to_string(i)); + str = fu_strjoin("|", crc_kinds); + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid CRC kind, expected %s", + str); + return FALSE; + } + + /* get CRC of each file */ + for (guint i = 1; values[i] != NULL; i++) { + g_autoptr(GBytes) blob = NULL; + + blob = fu_bytes_get_contents(values[i], error); + if (blob == NULL) + return FALSE; + if (fu_crc_size(kind) == 8) { + guint8 crc = fu_crc8_bytes(kind, blob); + fu_console_print(self->console, "%s: 0x%02x", values[i], crc); + } else if (fu_crc_size(kind) == 16) { + guint16 crc = fu_crc16_bytes(kind, blob); + fu_console_print(self->console, "%s: 0x%04x", values[i], crc); + } else if (fu_crc_size(kind) == 32) { + guint32 crc = fu_crc32_bytes(kind, blob); + fu_console_print(self->console, "%s: 0x%08x", values[i], crc); + } + } + + /* success */ + return TRUE; +} + +static gboolean +fu_util_crc_find(FuUtil *self, gchar **values, GError **error) +{ + FuCrcKind kind; + guint64 crc_target = 0; + g_autoptr(GBytes) blob = NULL; + + /* sanity check */ + if (g_strv_length(values) < 2) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments, expected CRC FILENAME"); + return FALSE; + } + + /* parse CRC */ + if (!fu_strtoull(values[0], &crc_target, 0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) + return FALSE; + + /* find the first CRC that matches */ + blob = fu_bytes_get_contents(values[1], error); + if (blob == NULL) + return FALSE; + kind = fu_crc_find(g_bytes_get_data(blob, NULL), g_bytes_get_size(blob), crc_target); + if (kind == FU_CRC_KIND_UNKNOWN) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "did not find known CRC kind"); + return FALSE; + } + fu_console_print_literal(self->console, fu_crc_kind_to_string(kind)); + + /* success */ + return TRUE; +} + +static gboolean +fu_util_search(FuUtil *self, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) rels = NULL; + g_autoptr(FuUtilNode) root = g_node_new(NULL); + + /* sanity check */ + if (g_strv_length(values) < 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments, expected WORD"); + return FALSE; + } + + /* load engine */ + if (!fu_engine_load(self->engine, FU_ENGINE_LOAD_FLAG_REMOTES, self->progress, error)) + return FALSE; + + /* get search results */ + rels = fu_engine_search(self->engine, values[0], error); + if (rels == NULL) + return FALSE; + if (rels->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "No matching releases for search token"); + return FALSE; + } + if (self->as_json) { + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + fwupd_codec_array_to_json(rels, "Releases", builder, FWUPD_CODEC_FLAG_TRUSTED); + json_builder_end_object(builder); + return fu_util_print_builder(self->console, builder, error); + } + for (guint i = 0; i < rels->len; i++) { + FuRelease *rel = g_ptr_array_index(rels, i); + g_node_append_data(root, g_object_ref(rel)); + } + fu_util_print_node(self->console, self->client, root); + + /* success */ + return TRUE; +} + +static gboolean +fu_util_vercmp(FuUtil *self, gchar **values, GError **error) { FwupdVersionFormat verfmt = FWUPD_VERSION_FORMAT_UNKNOWN; gint rc; @@ -2196,17 +2578,17 @@ /* compare */ rc = fu_version_compare(values[0], values[1], verfmt); if (rc > 0) { - fu_console_print(priv->console, "%s > %s", values[0], values[1]); + fu_console_print(self->console, "%s > %s", values[0], values[1]); } else if (rc < 0) { - fu_console_print(priv->console, "%s < %s", values[0], values[1]); + fu_console_print(self->console, "%s < %s", values[0], values[1]); } else { - fu_console_print(priv->console, "%s == %s", values[0], values[1]); + fu_console_print(self->console, "%s == %s", values[0], values[1]); } return TRUE; } static gboolean -fu_util_remote_enable(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_remote_enable(FuUtil *self, gchar **values, GError **error) { FwupdRemote *remote = NULL; @@ -2218,34 +2600,34 @@ return FALSE; } - if (!fu_util_start_engine(priv, FU_ENGINE_LOAD_FLAG_REMOTES, priv->progress, error)) + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_REMOTES, self->progress, error)) return FALSE; - remote = fu_engine_get_remote_by_id(priv->engine, values[0], error); + remote = fu_engine_get_remote_by_id(self->engine, values[0], error); if (remote == NULL) return FALSE; - if (!fu_util_modify_remote_warning(priv->console, remote, FALSE, error)) + if (!fu_util_modify_remote_warning(self->console, remote, FALSE, error)) return FALSE; - if (!fu_engine_modify_remote(priv->engine, + if (!fu_engine_modify_remote(self->engine, fwupd_remote_get_id(remote), "Enabled", "true", error)) return FALSE; - if (priv->as_json) + if (self->as_json) return TRUE; - fu_console_print_literal(priv->console, _("Successfully enabled remote")); + fu_console_print_literal(self->console, _("Successfully enabled remote")); return TRUE; } static gboolean -fu_util_set_test_devices_enabled(FuUtilPrivate *priv, gboolean enable, GError **error) +fu_util_set_test_devices_enabled(FuUtil *self, gboolean enable, GError **error) { - return fu_engine_modify_config(priv->engine, + return fu_engine_modify_config(self->engine, "fwupd", "TestDevices", enable ? "true" : "false", @@ -2253,37 +2635,40 @@ } static gboolean -fu_util_disable_test_devices(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_disable_test_devices(FuUtil *self, gchar **values, GError **error) { - if (!fu_util_start_engine(priv, FU_ENGINE_LOAD_FLAG_HWINFO, priv->progress, error)) + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_HWINFO, self->progress, error)) return FALSE; - if (!fu_util_set_test_devices_enabled(priv, FALSE, error)) + if (!fu_util_set_test_devices_enabled(self, FALSE, error)) return FALSE; + if (self->as_json) + return TRUE; + /* TRANSLATORS: comment explaining result of command */ - fu_console_print_literal(priv->console, _("Successfully disabled test devices")); + fu_console_print_literal(self->console, _("Successfully disabled test devices")); return TRUE; } static gboolean -fu_util_enable_test_devices(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_enable_test_devices(FuUtil *self, gchar **values, GError **error) { gboolean found = FALSE; g_autoptr(GPtrArray) remotes = NULL; - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; - if (!fu_util_set_test_devices_enabled(priv, TRUE, error)) + if (!fu_util_set_test_devices_enabled(self, TRUE, error)) return FALSE; /* verify remote is present */ - remotes = fu_engine_get_remotes(priv->engine, error); + remotes = fu_engine_get_remotes(self->engine, error); if (remotes == NULL) return FALSE; for (guint i = 0; i < remotes->len; i++) { @@ -2296,29 +2681,29 @@ } } if (!found) { - if (!fu_util_set_test_devices_enabled(priv, FALSE, error)) + if (!fu_util_set_test_devices_enabled(self, FALSE, error)) return FALSE; - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_INTERNAL, - "failed to enable fwupd-tests remote"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INTERNAL, + "failed to enable fwupd-tests remote"); return FALSE; } - if (priv->as_json) + if (self->as_json) return TRUE; /* TRANSLATORS: comment explaining result of command */ - fu_console_print_literal(priv->console, _("Successfully enabled test devices")); + fu_console_print_literal(self->console, _("Successfully enabled test devices")); return TRUE; } static gboolean -fu_util_check_activation_needed(FuUtilPrivate *priv, GError **error) +fu_util_check_activation_needed(FuUtil *self, GError **error) { gboolean has_pending = FALSE; - g_autoptr(FuHistory) history = fu_history_new(priv->ctx); + g_autoptr(FuHistory) history = fu_history_new(self->ctx); g_autoptr(GPtrArray) devices = fu_history_get_devices(history, error); if (devices == NULL) return FALSE; @@ -2327,7 +2712,7 @@ for (guint i = 0; i < devices->len; i++) { FuDevice *dev = g_ptr_array_index(devices, i); if (fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { - fu_engine_add_plugin_filter(priv->engine, fu_device_get_plugin(dev)); + fu_engine_add_plugin_filter(self->engine, fu_device_get_plugin(dev)); has_pending = TRUE; } } @@ -2344,40 +2729,40 @@ } static gboolean -fu_util_activate(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_activate(FuUtil *self, gchar **values, GError **error) { gboolean has_pending = FALSE; g_autoptr(GPtrArray) devices = NULL; /* check the history database before starting the daemon */ - if (!fu_util_check_activation_needed(priv, error)) + if (!fu_util_check_activation_needed(self, error)) return FALSE; /* progress */ - fu_progress_set_id(priv->progress, G_STRLOC); - fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); - fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_BUSY, 5, NULL); + fu_progress_set_id(self->progress, G_STRLOC); + fu_progress_add_step(self->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); + fu_progress_add_step(self->progress, FWUPD_STATUS_DEVICE_BUSY, 5, NULL); /* load engine */ if (!fu_util_start_engine( - priv, + self, FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | FU_ENGINE_LOAD_FLAG_HWINFO, - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* parse arguments */ if (g_strv_length(values) == 0) { - devices = fu_engine_get_devices(priv->engine, error); + devices = fu_engine_get_devices(self->engine, error); if (devices == NULL) return FALSE; } else if (g_strv_length(values) == 1) { FuDevice *device; - device = fu_util_get_device(priv, values[0], error); + device = fu_util_get_device(self, values[0], error); if (device == NULL) return FALSE; devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); @@ -2394,25 +2779,27 @@ for (guint i = 0; i < devices->len; i++) { FuDevice *device = g_ptr_array_index(devices, i); if (!fwupd_device_match_flags(FWUPD_DEVICE(device), - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; if (!fu_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) continue; has_pending = TRUE; - fu_console_print( - priv->console, - "%s %s…", - /* TRANSLATORS: shown when shutting down to switch to the new version */ - _("Activating firmware update"), - fu_device_get_name(device)); - if (!fu_engine_activate(priv->engine, + if (!self->as_json) { + fu_console_print( + self->console, + "%s %s…", + /* TRANSLATORS: shown when shutting down to switch to the new version */ + _("Activating firmware update"), + fu_device_get_name(device)); + } + if (!fu_engine_activate(self->engine, fu_device_get_id(device), - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; } - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); if (!has_pending) { g_set_error_literal(error, @@ -2426,9 +2813,9 @@ } static gboolean -fu_util_export_hwids(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_export_hwids(FuUtil *self, gchar **values, GError **error) { - FuContext *ctx = fu_engine_get_context(priv->engine); + FuContext *ctx = fu_engine_get_context(self->engine); FuHwids *hwids = fu_context_get_hwids(ctx); g_autoptr(GKeyFile) kf = g_key_file_new(); g_autoptr(GPtrArray) hwid_keys = NULL; @@ -2443,7 +2830,7 @@ } /* setup default hwids */ - if (!fu_context_load_hwinfo(ctx, priv->progress, FU_CONTEXT_HWID_FLAG_LOAD_ALL, error)) + if (!fu_context_load_hwinfo(ctx, self->progress, FU_CONTEXT_HWID_FLAG_LOAD_ALL, error)) return FALSE; /* save all keys */ @@ -2461,9 +2848,9 @@ } static gboolean -fu_util_hwids(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_hwids(FuUtil *self, gchar **values, GError **error) { - FuContext *ctx = fu_engine_get_context(priv->engine); + FuContext *ctx = fu_engine_get_context(self->engine); FuHwids *hwids = fu_context_get_hwids(ctx); g_autoptr(GPtrArray) chid_keys = fu_hwids_get_chid_keys(hwids); g_autoptr(GPtrArray) hwid_keys = fu_hwids_get_keys(hwids); @@ -2480,12 +2867,12 @@ fu_hwids_add_value(hwids, hwid_key, tmp); } } - if (!fu_context_load_hwinfo(ctx, priv->progress, FU_CONTEXT_HWID_FLAG_LOAD_ALL, error)) + if (!fu_context_load_hwinfo(ctx, self->progress, FU_CONTEXT_HWID_FLAG_LOAD_ALL, error)) return FALSE; /* show debug output */ - fu_console_print_literal(priv->console, "Computer Information"); - fu_console_print_literal(priv->console, "--------------------"); + fu_console_print_literal(self->console, "Computer Information"); + fu_console_print_literal(self->console, "--------------------"); for (guint i = 0; i < hwid_keys->len; i++) { const gchar *hwid_key = g_ptr_array_index(hwid_keys, i); const gchar *value = fu_hwids_get_value(hwids, hwid_key); @@ -2496,44 +2883,15 @@ guint64 val = 0; if (!fu_strtoull(value, &val, 0, G_MAXUINT64, FU_INTEGER_BASE_16, error)) return FALSE; - fu_console_print(priv->console, "%s: %" G_GUINT64_FORMAT, hwid_key, val); + fu_console_print(self->console, "%s: %" G_GUINT64_FORMAT, hwid_key, val); } else { - fu_console_print(priv->console, "%s: %s", hwid_key, value); + fu_console_print(self->console, "%s: %s", hwid_key, value); } } /* show GUIDs */ - fu_console_print_literal(priv->console, "Hardware IDs"); - fu_console_print_literal(priv->console, "------------"); - for (guint i = 0; i < chid_keys->len; i++) { - const gchar *key = g_ptr_array_index(chid_keys, i); - const gchar *keys = NULL; - g_autofree gchar *guid = NULL; - g_autofree gchar *keys_str = NULL; - g_auto(GStrv) keysv = NULL; - g_autoptr(GError) error_local = NULL; - - /* filter */ - if (!g_str_has_prefix(key, "HardwareID")) - continue; - - /* get the GUID */ - keys = fu_hwids_get_replace_keys(hwids, key); - guid = fu_hwids_get_guid(hwids, key, &error_local); - if (guid == NULL) { - fu_console_print_literal(priv->console, error_local->message); - continue; - } - - /* show what makes up the GUID */ - keysv = g_strsplit(keys, "&", -1); - keys_str = g_strjoinv(" + ", keysv); - fu_console_print(priv->console, "{%s} <- %s", guid, keys_str); - } - - /* show extra GUIDs */ - fu_console_print_literal(priv->console, "Extra Hardware IDs"); - fu_console_print_literal(priv->console, "------------------"); + fu_console_print_literal(self->console, "Hardware IDs"); + fu_console_print_literal(self->console, "------------"); for (guint i = 0; i < chid_keys->len; i++) { const gchar *key = g_ptr_array_index(chid_keys, i); const gchar *keys = NULL; @@ -2542,29 +2900,26 @@ g_auto(GStrv) keysv = NULL; g_autoptr(GError) error_local = NULL; - /* filter */ - if (g_str_has_prefix(key, "HardwareID")) - continue; - /* get the GUID */ keys = fu_hwids_get_replace_keys(hwids, key); guid = fu_hwids_get_guid(hwids, key, &error_local); if (guid == NULL) { - fu_console_print_literal(priv->console, error_local->message); + fu_console_print_literal(self->console, error_local->message); continue; } /* show what makes up the GUID */ keysv = g_strsplit(keys, "&", -1); keys_str = g_strjoinv(" + ", keysv); - fu_console_print(priv->console, "{%s} <- %s", guid, keys_str); + fu_console_print(self->console, "{%s} <- %s", guid, keys_str); } + /* success */ return TRUE; } static gboolean -fu_util_self_sign(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_self_sign(FuUtil *self, gchar **values, GError **error) { g_autofree gchar *sig = NULL; @@ -2578,22 +2933,22 @@ } /* start engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_ENSURE_CLIENT_CERT, - priv->progress, + self->progress, error)) return FALSE; - sig = fu_engine_self_sign(priv->engine, + sig = fu_engine_self_sign(self->engine, values[0], JCAT_SIGN_FLAG_ADD_TIMESTAMP | JCAT_SIGN_FLAG_ADD_CERT, error); if (sig == NULL) return FALSE; - if (priv->as_json) - fu_console_print(priv->console, "{\"signature\": \"%s\"}", sig); + if (self->as_json) + fu_console_print(self->console, "{\"signature\": \"%s\"}", sig); else - fu_console_print(priv->console, "%s", sig); + fu_console_print(self->console, "%s", sig); return TRUE; } @@ -2601,109 +2956,109 @@ static void fu_util_device_added_cb(FwupdClient *client, FwupdDevice *device, gpointer user_data) { - FuUtilPrivate *priv = (FuUtilPrivate *)user_data; + FuUtil *self = (FuUtil *)user_data; g_autofree gchar *tmp = NULL; - if (priv->as_json) + if (self->as_json) return; - tmp = fu_util_device_to_string(priv->client, device, 0); + tmp = fu_util_device_to_string(self->client, device, 0); /* TRANSLATORS: this is when a device is hotplugged */ - fu_console_print(priv->console, "%s\n%s", _("Device added:"), tmp); + fu_console_print(self->console, "%s\n%s", _("Device added:"), tmp); } static void fu_util_device_removed_cb(FwupdClient *client, FwupdDevice *device, gpointer user_data) { - FuUtilPrivate *priv = (FuUtilPrivate *)user_data; + FuUtil *self = (FuUtil *)user_data; g_autofree gchar *tmp = NULL; - if (priv->as_json) + if (self->as_json) return; - tmp = fu_util_device_to_string(priv->client, device, 0); + tmp = fu_util_device_to_string(self->client, device, 0); /* TRANSLATORS: this is when a device is hotplugged */ - fu_console_print(priv->console, "%s\n%s", _("Device removed:"), tmp); + fu_console_print(self->console, "%s\n%s", _("Device removed:"), tmp); } static void fu_util_device_changed_cb(FwupdClient *client, FwupdDevice *device, gpointer user_data) { - FuUtilPrivate *priv = (FuUtilPrivate *)user_data; + FuUtil *self = (FuUtil *)user_data; g_autofree gchar *tmp = NULL; - if (priv->as_json) + if (self->as_json) return; - tmp = fu_util_device_to_string(priv->client, device, 0); + tmp = fu_util_device_to_string(self->client, device, 0); /* TRANSLATORS: this is when a device has been updated */ - fu_console_print(priv->console, "%s\n%s", _("Device changed:"), tmp); + fu_console_print(self->console, "%s\n%s", _("Device changed:"), tmp); } static void fu_util_changed_cb(FwupdClient *client, gpointer user_data) { - FuUtilPrivate *priv = (FuUtilPrivate *)user_data; + FuUtil *self = (FuUtil *)user_data; - if (priv->as_json) + if (self->as_json) return; /* TRANSLATORS: this is when the daemon state changes */ - fu_console_print_literal(priv->console, _("Changed")); + fu_console_print_literal(self->console, _("Changed")); } static gboolean -fu_util_monitor(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_monitor(FuUtil *self, gchar **values, GError **error) { /* get all the devices */ - if (!fwupd_client_connect(priv->client, priv->cancellable, error)) + if (!fwupd_client_connect(self->client, self->cancellable, error)) return FALSE; /* watch for any hotplugged device */ - g_signal_connect(FWUPD_CLIENT(priv->client), + g_signal_connect(FWUPD_CLIENT(self->client), "changed", G_CALLBACK(fu_util_changed_cb), - priv); - g_signal_connect(FWUPD_CLIENT(priv->client), + self); + g_signal_connect(FWUPD_CLIENT(self->client), "device-added", G_CALLBACK(fu_util_device_added_cb), - priv); - g_signal_connect(FWUPD_CLIENT(priv->client), + self); + g_signal_connect(FWUPD_CLIENT(self->client), "device-removed", G_CALLBACK(fu_util_device_removed_cb), - priv); - g_signal_connect(FWUPD_CLIENT(priv->client), + self); + g_signal_connect(FWUPD_CLIENT(self->client), "device-changed", G_CALLBACK(fu_util_device_changed_cb), - priv); - g_signal_connect(G_CANCELLABLE(priv->cancellable), + self); + g_signal_connect(G_CANCELLABLE(self->cancellable), "cancelled", G_CALLBACK(fu_util_cancelled_cb), - priv); - g_main_loop_run(priv->loop); + self); + g_main_loop_run(self->loop); return TRUE; } static gboolean -fu_util_get_firmware_types(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_firmware_types(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) firmware_types = NULL; /* load engine */ - if (!fu_engine_load(priv->engine, + if (!fu_engine_load(self->engine, FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, - priv->progress, + self->progress, error)) return FALSE; - firmware_types = fu_context_get_firmware_gtype_ids(fu_engine_get_context(priv->engine)); + firmware_types = fu_context_get_firmware_gtype_ids(fu_engine_get_context(self->engine)); for (guint i = 0; i < firmware_types->len; i++) { const gchar *id = g_ptr_array_index(firmware_types, i); - fu_console_print_literal(priv->console, id); + fu_console_print_literal(self->console, id); } if (firmware_types->len == 0) { g_set_error_literal(error, @@ -2718,22 +3073,22 @@ } static gboolean -fu_util_get_firmware_gtypes(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_firmware_gtypes(FuUtil *self, gchar **values, GError **error) { g_autoptr(GArray) firmware_types = NULL; /* load engine */ - if (!fu_engine_load(priv->engine, + if (!fu_engine_load(self->engine, FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, - priv->progress, + self->progress, error)) return FALSE; - firmware_types = fu_context_get_firmware_gtypes(fu_engine_get_context(priv->engine)); + firmware_types = fu_context_get_firmware_gtypes(fu_engine_get_context(self->engine)); for (guint i = 0; i < firmware_types->len; i++) { GType gtype = g_array_index(firmware_types, GType, i); - fu_console_print_literal(priv->console, g_type_name(gtype)); + fu_console_print_literal(self->console, g_type_name(gtype)); } if (firmware_types->len == 0) { g_set_error_literal(error, @@ -2748,7 +3103,7 @@ } static gchar * -fu_util_prompt_for_firmware_type(FuUtilPrivate *priv, GPtrArray *firmware_types, GError **error) +fu_util_prompt_for_firmware_type(FuUtil *self, GPtrArray *firmware_types, GError **error) { guint idx; @@ -2768,13 +3123,13 @@ } /* TRANSLATORS: this is to abort the interactive prompt */ - fu_console_print(priv->console, "0.\t%s", _("Cancel")); + fu_console_print(self->console, "0.\t%s", _("Cancel")); for (guint i = 0; i < firmware_types->len; i++) { const gchar *id = g_ptr_array_index(firmware_types, i); - fu_console_print(priv->console, "%u.\t%s", i + 1, id); + fu_console_print(self->console, "%u.\t%s", i + 1, id); } /* TRANSLATORS: get interactive prompt */ - idx = fu_console_input_uint(priv->console, firmware_types->len, "%s", _("Choose firmware")); + idx = fu_console_input_uint(self->console, firmware_types->len, "%s", _("Choose firmware")); if (idx == 0) { g_set_error_literal(error, FWUPD_ERROR, @@ -2787,9 +3142,9 @@ } static gboolean -fu_util_firmware_parse(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_firmware_parse(FuUtil *self, gchar **values, GError **error) { - FuContext *ctx = fu_engine_get_context(priv->engine); + FuContext *ctx = fu_engine_get_context(self->engine); GType gtype; g_autoptr(FuFirmware) firmware = NULL; g_autoptr(GInputStream) stream = NULL; @@ -2811,17 +3166,17 @@ return FALSE; /* load engine */ - if (!fu_engine_load(priv->engine, + if (!fu_engine_load(self->engine, FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, - priv->progress, + self->progress, error)) return FALSE; /* find the GType to use */ if (g_strv_length(values) == 1) { g_autoptr(GPtrArray) firmware_types = fu_context_get_firmware_gtype_ids(ctx); - firmware_type = fu_util_prompt_for_firmware_type(priv, firmware_types, error); + firmware_type = fu_util_prompt_for_firmware_type(self, firmware_types, error); if (firmware_type == NULL) return FALSE; } else if (g_strcmp0(values[1], "auto") == 0) { @@ -2852,7 +3207,7 @@ if (!fu_firmware_parse_stream(firmware_tmp, stream, 0x0, - FWUPD_INSTALL_FLAG_NO_SEARCH, + FU_FIRMWARE_PARSE_FLAG_NO_SEARCH, &error_local)) { g_debug("failed to parse as %s: %s", gtype_id, @@ -2863,7 +3218,7 @@ g_debug("parsed as %s: %s", gtype_id, firmware_str); g_ptr_array_add(firmware_auto_types, g_strdup(gtype_id)); } - firmware_type = fu_util_prompt_for_firmware_type(priv, firmware_auto_types, error); + firmware_type = fu_util_prompt_for_firmware_type(self, firmware_auto_types, error); if (firmware_type == NULL) return FALSE; } else { @@ -2879,12 +3234,19 @@ return FALSE; } + /* match the behavior of the daemon as we're printing the children */ + self->parse_flags |= FU_FIRMWARE_PARSE_FLAG_CACHE_STREAM; + /* does firmware specify an internal size */ firmware = g_object_new(gtype, NULL); - if (fu_firmware_has_flag(firmware, FU_FIRMWARE_FLAG_HAS_STORED_SIZE)) { + if (fu_firmware_has_flag(firmware, FU_FIRMWARE_FLAG_ALLOW_LINEAR)) { g_autoptr(FuFirmware) firmware_linear = fu_linear_firmware_new(gtype); g_autoptr(GPtrArray) imgs = NULL; - if (!fu_firmware_parse_stream(firmware_linear, stream, 0x0, priv->flags, error)) + if (!fu_firmware_parse_stream(firmware_linear, + stream, + 0x0, + self->parse_flags, + error)) return FALSE; imgs = fu_firmware_get_images(firmware_linear); if (imgs->len == 1) { @@ -2893,19 +3255,19 @@ g_set_object(&firmware, firmware_linear); } } else { - if (!fu_firmware_parse_stream(firmware, stream, 0x0, priv->flags, error)) + if (!fu_firmware_parse_stream(firmware, stream, 0x0, self->parse_flags, error)) return FALSE; } str = fu_firmware_to_string(firmware); - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); return TRUE; } static gboolean -fu_util_firmware_export(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_firmware_export(FuUtil *self, gchar **values, GError **error) { - FuContext *ctx = fu_engine_get_context(priv->engine); + FuContext *ctx = fu_engine_get_context(self->engine); FuFirmwareExportFlags flags = FU_FIRMWARE_EXPORT_FLAG_NONE; GType gtype; g_autoptr(FuFirmware) firmware = NULL; @@ -2926,17 +3288,17 @@ firmware_type = g_strdup(values[1]); /* load engine */ - if (!fu_engine_load(priv->engine, + if (!fu_engine_load(self->engine, FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, - priv->progress, + self->progress, error)) return FALSE; /* find the GType to use */ if (firmware_type == NULL) { g_autoptr(GPtrArray) firmware_types = fu_context_get_firmware_gtype_ids(ctx); - firmware_type = fu_util_prompt_for_firmware_type(priv, firmware_types, error); + firmware_type = fu_util_prompt_for_firmware_type(self, firmware_types, error); } if (firmware_type == NULL) return FALSE; @@ -2951,27 +3313,86 @@ } firmware = g_object_new(gtype, NULL); file = g_file_new_for_path(values[0]); - if (!fu_firmware_parse_file(firmware, file, priv->flags, error)) + if (!fu_firmware_parse_file(firmware, file, self->parse_flags, error)) return FALSE; - if (priv->show_all) + if (self->show_all) flags |= FU_FIRMWARE_EXPORT_FLAG_INCLUDE_DEBUG; - str = fu_firmware_export_to_xml(firmware, flags, error); + str = fu_firmware_export_to_xml(firmware, flags | FU_FIRMWARE_EXPORT_FLAG_SORTED, error); if (str == NULL) return FALSE; - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); return TRUE; } static gboolean -fu_util_firmware_extract(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_firmware_extract_image(FuUtil *self, + FuFirmware *firmware, + const gchar *idxstr, + GError **error) { - FuContext *ctx = fu_engine_get_context(priv->engine); + g_autofree gchar *fn = NULL; + g_autoptr(GBytes) blob = NULL; + + /* get raw image without generated header, footer or crc */ + blob = fu_firmware_get_bytes(firmware, error); + if (blob == NULL) { + g_prefix_error(error, + "failed to get bytes for image %s: ", + fu_firmware_get_id(firmware)); + return FALSE; + } + if (g_bytes_get_size(blob) == 0) + return TRUE; + + /* use suitable filename */ + if (fu_firmware_get_filename(firmware) != NULL) { + fn = g_strdup(fu_firmware_get_filename(firmware)); + } else if (fu_firmware_get_id(firmware) != NULL) { + fn = g_strdup_printf("id-%s.fw", fu_firmware_get_id(firmware)); + } else if (fu_firmware_get_idx(firmware) != 0x0) { + fn = g_strdup_printf("idx-0x%x.fw", (guint)fu_firmware_get_idx(firmware)); + } else { + fn = g_strdup_printf("img-%s.fw", idxstr); + } + + /* TRANSLATORS: decompressing images from a container firmware */ + fu_console_print(self->console, "%s : %s", _("Writing file:"), fn); + return fu_bytes_set_contents(fn, blob, error); +} + +static gboolean +fu_util_firmware_extract_images(FuUtil *self, + FuFirmware *firmware, + const gchar *idxstr, + GError **error) +{ + g_autoptr(GPtrArray) images = NULL; + + images = fu_firmware_get_images(firmware); + if (images->len == 0) + return fu_util_firmware_extract_image(self, firmware, idxstr, error); + for (guint i = 0; i < images->len; i++) { + FuFirmware *img = g_ptr_array_index(images, i); + g_autofree gchar *idxstr_new = idxstr != NULL + ? g_strdup_printf("%s:0x%x", idxstr, i) + : g_strdup_printf("0x%x", i); + if (!fu_util_firmware_extract_images(self, img, idxstr_new, error)) + return FALSE; + } + + /* success */ + return TRUE; +} + +static gboolean +fu_util_firmware_extract(FuUtil *self, gchar **values, GError **error) +{ + FuContext *ctx = fu_engine_get_context(self->engine); GType gtype; g_autofree gchar *firmware_type = NULL; g_autofree gchar *str = NULL; g_autoptr(FuFirmware) firmware = NULL; g_autoptr(GFile) file = NULL; - g_autoptr(GPtrArray) images = NULL; /* check args */ if (g_strv_length(values) == 0 || g_strv_length(values) > 2) { @@ -2985,17 +3406,17 @@ firmware_type = g_strdup(values[1]); /* load engine */ - if (!fu_engine_load(priv->engine, + if (!fu_engine_load(self->engine, FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, - priv->progress, + self->progress, error)) return FALSE; /* find the GType to use */ if (firmware_type == NULL) { g_autoptr(GPtrArray) firmware_types = fu_context_get_firmware_gtype_ids(ctx); - firmware_type = fu_util_prompt_for_firmware_type(priv, firmware_types, error); + firmware_type = fu_util_prompt_for_firmware_type(self, firmware_types, error); } if (firmware_type == NULL) return FALSE; @@ -3010,45 +3431,15 @@ } firmware = g_object_new(gtype, NULL); file = g_file_new_for_path(values[0]); - if (!fu_firmware_parse_file(firmware, file, priv->flags, error)) + if (!fu_firmware_parse_file(firmware, file, self->parse_flags, error)) return FALSE; str = fu_firmware_to_string(firmware); - fu_console_print_literal(priv->console, str); - images = fu_firmware_get_images(firmware); - for (guint i = 0; i < images->len; i++) { - FuFirmware *img = g_ptr_array_index(images, i); - g_autofree gchar *fn = NULL; - g_autoptr(GBytes) blob_img = NULL; - - /* get raw image without generated header, footer or crc */ - blob_img = fu_firmware_get_bytes(img, error); - if (blob_img == NULL) - return FALSE; - if (g_bytes_get_size(blob_img) == 0) - continue; - - /* use suitable filename */ - if (fu_firmware_get_filename(img) != NULL) { - fn = g_strdup(fu_firmware_get_filename(img)); - } else if (fu_firmware_get_id(img) != NULL) { - fn = g_strdup_printf("id-%s.fw", fu_firmware_get_id(img)); - } else if (fu_firmware_get_idx(img) != 0x0) { - fn = g_strdup_printf("idx-0x%x.fw", (guint)fu_firmware_get_idx(img)); - } else { - fn = g_strdup_printf("img-0x%x.fw", i); - } - /* TRANSLATORS: decompressing images from a container firmware */ - fu_console_print(priv->console, "%s : %s", _("Writing file:"), fn); - if (!fu_bytes_set_contents(fn, blob_img, error)) - return FALSE; - } - - /* success */ - return TRUE; + fu_console_print_literal(self->console, str); + return fu_util_firmware_extract_images(self, firmware, NULL, error); } static gboolean -fu_util_firmware_build(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_firmware_build(FuUtil *self, gchar **values, GError **error) { GType gtype = FU_TYPE_FIRMWARE; const gchar *tmp; @@ -3077,16 +3468,16 @@ return FALSE; /* load engine */ - if (!fu_engine_load(priv->engine, + if (!fu_engine_load(self->engine, FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, - priv->progress, + self->progress, error)) return FALSE; /* parse XML */ if (!xb_builder_source_load_bytes(source, blob_src, XB_BUILDER_SOURCE_FLAG_NONE, error)) { - g_prefix_error(error, "could not parse XML: "); + g_prefix_error_literal(error, "could not parse XML: "); fwupd_error_convert(error); return FALSE; } @@ -3118,7 +3509,7 @@ tmp = xb_node_get_attr(n, "id"); if (tmp != NULL) { gtype = - fu_context_get_firmware_gtype_by_id(fu_engine_get_context(priv->engine), tmp); + fu_context_get_firmware_gtype_by_id(fu_engine_get_context(self->engine), tmp); if (gtype == G_TYPE_INVALID) { g_set_error(error, FWUPD_ERROR, @@ -3141,19 +3532,19 @@ /* show what we wrote */ firmware_dst = g_object_new(gtype, NULL); - if (!fu_firmware_parse_bytes(firmware_dst, blob_dst, 0x0, priv->flags, error)) + if (!fu_firmware_parse_bytes(firmware_dst, blob_dst, 0x0, self->parse_flags, error)) return FALSE; str = fu_firmware_to_string(firmware_dst); - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); /* success */ return TRUE; } static gboolean -fu_util_firmware_convert(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_firmware_convert(FuUtil *self, gchar **values, GError **error) { - FuContext *ctx = fu_engine_get_context(priv->engine); + FuContext *ctx = fu_engine_get_context(self->engine); GType gtype_dst; GType gtype_src; g_autofree gchar *firmware_type_dst = NULL; @@ -3181,23 +3572,23 @@ firmware_type_dst = g_strdup(values[3]); /* load engine */ - if (!fu_engine_load(priv->engine, + if (!fu_engine_load(self->engine, FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, - priv->progress, + self->progress, error)) return FALSE; /* find the GType to use */ if (firmware_type_src == NULL) { g_autoptr(GPtrArray) firmware_types = fu_context_get_firmware_gtype_ids(ctx); - firmware_type_src = fu_util_prompt_for_firmware_type(priv, firmware_types, error); + firmware_type_src = fu_util_prompt_for_firmware_type(self, firmware_types, error); } if (firmware_type_src == NULL) return FALSE; if (firmware_type_dst == NULL) { g_autoptr(GPtrArray) firmware_types = fu_context_get_firmware_gtype_ids(ctx); - firmware_type_dst = fu_util_prompt_for_firmware_type(priv, firmware_types, error); + firmware_type_dst = fu_util_prompt_for_firmware_type(self, firmware_types, error); } if (firmware_type_dst == NULL) return FALSE; @@ -3212,7 +3603,7 @@ } firmware_src = g_object_new(gtype_src, NULL); file_src = g_file_new_for_path(values[0]); - if (!fu_firmware_parse_file(firmware_src, file_src, priv->flags, error)) + if (!fu_firmware_parse_file(firmware_src, file_src, self->parse_flags, error)) return FALSE; gtype_dst = fu_context_get_firmware_gtype_by_id(ctx, firmware_type_dst); if (gtype_dst == G_TYPE_INVALID) { @@ -3224,14 +3615,15 @@ return FALSE; } str_src = fu_firmware_to_string(firmware_src); - fu_console_print_literal(priv->console, str_src); + fu_console_print_literal(self->console, str_src); /* copy images */ firmware_dst = g_object_new(gtype_dst, NULL); images = fu_firmware_get_images(firmware_src); for (guint i = 0; i < images->len; i++) { FuFirmware *img = g_ptr_array_index(images, i); - fu_firmware_add_image(firmware_dst, img); + if (!fu_firmware_add_image(firmware_dst, img, error)) + return FALSE; } /* copy data as fallback, preferring a binary blob to the export */ @@ -3245,7 +3637,8 @@ return FALSE; } img = fu_firmware_new_from_bytes(fw); - fu_firmware_add_image(firmware_dst, img); + if (!fu_firmware_add_image(firmware_dst, img, error)) + return FALSE; } /* write new file */ @@ -3255,7 +3648,7 @@ if (!fu_bytes_set_contents(values[1], blob_dst, error)) return FALSE; str_dst = fu_firmware_to_string(firmware_dst); - fu_console_print_literal(priv->console, str_dst); + fu_console_print_literal(self->console, str_dst); /* success */ return TRUE; @@ -3285,9 +3678,9 @@ } static gboolean -fu_util_firmware_patch(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_firmware_patch(FuUtil *self, gchar **values, GError **error) { - FuContext *ctx = fu_engine_get_context(priv->engine); + FuContext *ctx = fu_engine_get_context(self->engine); GType gtype; g_autofree gchar *firmware_type = NULL; g_autofree gchar *str = NULL; @@ -3313,7 +3706,7 @@ /* parse offset */ if (!fu_strtoull(values[1], &offset, 0x0, G_MAXUINT32, FU_INTEGER_BASE_AUTO, error)) { - g_prefix_error(error, "failed to parse offset: "); + g_prefix_error_literal(error, "failed to parse offset: "); return FALSE; } @@ -3322,22 +3715,25 @@ if (patch == NULL) return FALSE; if (g_bytes_get_size(patch) == 0) { - g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS, "no data provided"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "no data provided"); return FALSE; } /* load engine */ - if (!fu_engine_load(priv->engine, + if (!fu_engine_load(self->engine, FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS, - priv->progress, + self->progress, error)) return FALSE; /* find the GType to use */ if (firmware_type == NULL) { g_autoptr(GPtrArray) firmware_types = fu_context_get_firmware_gtype_ids(ctx); - firmware_type = fu_util_prompt_for_firmware_type(priv, firmware_types, error); + firmware_type = fu_util_prompt_for_firmware_type(self, firmware_types, error); } if (firmware_type == NULL) return FALSE; @@ -3352,7 +3748,7 @@ } firmware = g_object_new(gtype, NULL); file_src = g_file_new_for_path(values[0]); - if (!fu_firmware_parse_file(firmware, file_src, priv->flags, error)) + if (!fu_firmware_parse_file(firmware, file_src, self->parse_flags, error)) return FALSE; /* add patch */ @@ -3365,85 +3761,85 @@ if (!fu_bytes_set_contents(values[0], blob_dst, error)) return FALSE; str = fu_firmware_to_string(firmware); - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); /* success */ return TRUE; } static gboolean -fu_util_verify_update(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_verify_update(FuUtil *self, gchar **values, GError **error) { g_autofree gchar *str = NULL; g_autoptr(FuDevice) dev = NULL; /* progress */ - fu_progress_set_id(priv->progress, G_STRLOC); - fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 50, "start-engine"); - fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_VERIFY, 50, "verify-update"); + fu_progress_set_id(self->progress, G_STRLOC); + fu_progress_add_step(self->progress, FWUPD_STATUS_LOADING, 50, "start-engine"); + fu_progress_add_step(self->progress, FWUPD_STATUS_DEVICE_VERIFY, 50, "verify-update"); /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* get device */ - priv->filter_device_include |= FWUPD_DEVICE_FLAG_UPDATABLE; + self->filter_device_include |= FWUPD_DEVICE_FLAG_UPDATABLE; if (g_strv_length(values) == 1) { - dev = fu_util_get_device(priv, values[0], error); + dev = fu_util_get_device(self, values[0], error); if (dev == NULL) return FALSE; } else { - dev = fu_util_prompt_for_device(priv, NULL, error); + dev = fu_util_prompt_for_device(self, NULL, error); if (dev == NULL) return FALSE; } /* add checksums */ - if (!fu_engine_verify_update(priv->engine, + if (!fu_engine_verify_update(self->engine, fu_device_get_id(dev), - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* show checksums */ str = fu_device_to_string(dev); - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); return TRUE; } static gboolean -fu_util_get_history(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_history(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; g_autoptr(FuUtilNode) root = g_node_new(NULL); /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; /* get all devices from the history database */ - devices = fu_engine_get_history(priv->engine, error); + devices = fu_engine_get_history(self->engine, error); if (devices == NULL) return FALSE; /* not for human consumption */ - if (priv->as_json) { + if (self->as_json) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); fwupd_codec_array_to_json(devices, "Devices", builder, FWUPD_CODEC_FLAG_TRUSTED); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } /* show each device */ @@ -3456,8 +3852,8 @@ g_autoptr(GError) error_local = NULL; if (!fwupd_device_match_flags(dev, - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; child = g_node_append_data(root, g_object_ref(dev)); @@ -3473,8 +3869,8 @@ } /* try to lookup releases from client, falling back to the history release */ - rels = fu_engine_get_releases(priv->engine, - priv->request, + rels = fu_engine_get_releases(self->engine, + self->request, fwupd_device_get_id(dev), &error_local); if (rels == NULL) { @@ -3491,8 +3887,8 @@ for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel2 = g_ptr_array_index(rels, j); if (!fwupd_release_match_flags(rel2, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; if (g_strcmp0(remote, fwupd_release_get_remote_id(rel2)) != 0) continue; @@ -3510,13 +3906,13 @@ continue; } } - fu_util_print_node(priv->console, priv->client, root); + fu_util_print_node(self->console, self->client, root); return TRUE; } static gboolean -fu_util_refresh_remote(FuUtilPrivate *priv, FwupdRemote *remote, GError **error) +fu_util_refresh_remote(FuUtil *self, FwupdRemote *remote, GError **error) { g_autofree gchar *uri_raw = NULL; g_autofree gchar *uri_sig = NULL; @@ -3535,10 +3931,10 @@ uri_sig = fwupd_remote_build_metadata_sig_uri(remote, error); if (uri_sig == NULL) return FALSE; - bytes_sig = fwupd_client_download_bytes(priv->client, + bytes_sig = fwupd_client_download_bytes(self->client, uri_sig, FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, - priv->cancellable, + self->cancellable, error); if (bytes_sig == NULL) return FALSE; @@ -3557,17 +3953,17 @@ uri_raw = fwupd_remote_build_metadata_uri(remote, error); if (uri_raw == NULL) return FALSE; - bytes_raw = fwupd_client_download_bytes(priv->client, + bytes_raw = fwupd_client_download_bytes(self->client, uri_raw, FWUPD_CLIENT_DOWNLOAD_FLAG_NONE, - priv->cancellable, + self->cancellable, error); if (bytes_raw == NULL) return FALSE; /* send to daemon */ g_info("updating %s", fwupd_remote_get_id(remote)); - return fu_engine_update_metadata_bytes(priv->engine, + return fu_engine_update_metadata_bytes(self->engine, fwupd_remote_get_id(remote), bytes_raw, bytes_sig, @@ -3575,53 +3971,126 @@ } static gboolean -fu_util_refresh(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_download_metadata(FuUtil *self, GError **error) { + guint refresh_cnt = 0; g_autoptr(GPtrArray) remotes = NULL; /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; /* download new metadata */ - remotes = fu_engine_get_remotes(priv->engine, error); + remotes = fu_engine_get_remotes(self->engine, error); if (remotes == NULL) return FALSE; for (guint i = 0; i < remotes->len; i++) { FwupdRemote *remote = g_ptr_array_index(remotes, i); + g_autoptr(GError) error_local = NULL; if (!fwupd_remote_has_flag(remote, FWUPD_REMOTE_FLAG_ENABLED)) continue; if (fwupd_remote_get_kind(remote) != FWUPD_REMOTE_KIND_DOWNLOAD) continue; - if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + if ((self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && !fwupd_remote_needs_refresh(remote)) { g_debug("skipping as remote %s age is %us", fwupd_remote_get_id(remote), (guint)fwupd_remote_get_age(remote)); continue; } - if (!fu_util_refresh_remote(priv, remote, error)) + if (!fu_util_refresh_remote(self, remote, &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + g_debug("ignoring: %s", error_local->message); + continue; + } + g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; + } + refresh_cnt++; } + + /* metadata refreshed recently */ + if (refresh_cnt == 0) { + if (self->flags & FWUPD_INSTALL_FLAG_FORCE) { + g_set_error_literal( + error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + /* TRANSLATORS: error message for a user who ran fwupdmgr */ + _("Metadata is already up to date")); + return FALSE; + } + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + /* TRANSLATORS: error message for a user who ran fwupdmgr + * refresh recently -- %1 is '--force' */ + _("Metadata is up to date; use %s to refresh again."), + "--force"); + return FALSE; + } + + /* success */ return TRUE; } static gboolean -fu_util_get_remotes(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_refresh(FuUtil *self, gchar **values, GError **error) +{ + g_autoptr(GBytes) bytes_raw = NULL; + g_autoptr(GBytes) bytes_sig = NULL; + + /* just do everything */ + if (g_strv_length(values) == 0) + return fu_util_download_metadata(self, error); + + /* sanity check */ + if (g_strv_length(values) != 3) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + + /* load engine */ + if (!fu_util_start_engine(self, + FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | + FU_ENGINE_LOAD_FLAG_HWINFO, + self->progress, + error)) + return FALSE; + + /* open files */ + bytes_raw = fu_bytes_get_contents(values[0], error); + if (bytes_raw == NULL) + return FALSE; + bytes_sig = fu_bytes_get_contents(values[1], error); + if (bytes_sig == NULL) + return FALSE; + return fu_engine_update_metadata_bytes(self->engine, + values[2], + bytes_raw, + bytes_sig, + error); +} + +static gboolean +fu_util_get_remotes(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuUtilNode) root = g_node_new(NULL); g_autoptr(GPtrArray) remotes = NULL; /* load engine */ - if (!fu_util_start_engine(priv, FU_ENGINE_LOAD_FLAG_REMOTES, priv->progress, error)) + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_REMOTES, self->progress, error)) return FALSE; /* list remotes */ - remotes = fu_engine_get_remotes(priv->engine, error); + remotes = fu_engine_get_remotes(self->engine, error); if (remotes == NULL) return FALSE; if (remotes->len == 0) { @@ -3631,24 +4100,24 @@ "no remotes available"); return FALSE; } - if (priv->as_json) { + if (self->as_json) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); fwupd_codec_array_to_json(remotes, "Remotes", builder, FWUPD_CODEC_FLAG_TRUSTED); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } for (guint i = 0; i < remotes->len; i++) { FwupdRemote *remote_tmp = g_ptr_array_index(remotes, i); g_node_append_data(root, g_object_ref(remote_tmp)); } - fu_util_print_node(priv->console, priv->client, root); + fu_util_print_node(self->console, self->client, root); return TRUE; } static gboolean -fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_security(FuUtil *self, gchar **values, GError **error) { FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE; const gchar *fwupd_version = NULL; @@ -3661,11 +4130,11 @@ g_autofree gchar *host_security_id = NULL; #ifndef HAVE_HSI - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - /* TRANSLATORS: error message for unsupported feature */ - _("Host Security ID (HSI) is not supported")); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + /* TRANSLATORS: error message for unsupported feature */ + _("Host Security ID (HSI) is not supported")); return FALSE; #endif /* HAVE_HSI */ @@ -3673,60 +4142,60 @@ if (g_strv_length(values) > 0) fwupd_version = values[0]; - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; /* show or hide different elements */ - if (priv->show_all) { + if (self->show_all) { flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES; flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS; } - attrs = fu_engine_get_host_security_attrs(priv->engine); + attrs = fu_engine_get_host_security_attrs(self->engine); items = fu_security_attrs_get_all(attrs, fwupd_version); /* print the "why" */ - if (priv->as_json) { + if (self->as_json) { str = fwupd_codec_to_json_string(FWUPD_CODEC(attrs), FWUPD_CODEC_FLAG_NONE, error); if (str == NULL) return FALSE; - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); return TRUE; } - host_security_id = fu_engine_get_host_security_id(priv->engine, fwupd_version); - fu_console_print(priv->console, + host_security_id = fu_engine_get_host_security_id(self->engine, fwupd_version); + fu_console_print(self->console, "%s \033[1m%s\033[0m", /* TRANSLATORS: this is a string like 'HSI:2-U' */ _("Host Security ID:"), host_security_id); str = fu_util_security_attrs_to_string(items, flags); - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); /* print the "when" */ - events = fu_engine_get_host_security_events(priv->engine, 10, error); + events = fu_engine_get_host_security_events(self->engine, 10, error); if (events == NULL) return FALSE; events_array = fu_security_attrs_get_all(events, fwupd_version); if (events_array->len > 0) { g_autofree gchar *estr = fu_util_security_events_to_string(events_array, flags); if (estr != NULL) - fu_console_print_literal(priv->console, estr); + fu_console_print_literal(self->console, estr); } /* print the "also" */ - devices = fu_engine_get_devices(priv->engine, error); + devices = fu_engine_get_devices(self->engine, error); if (devices == NULL) return FALSE; if (devices->len > 0) { g_autofree gchar *estr = fu_util_security_issues_to_string(devices); if (estr != NULL) - fu_console_print_literal(priv->console, estr); + fu_console_print_literal(self->console, estr); } /* success */ @@ -3734,9 +4203,9 @@ } static FuVolume * -fu_util_prompt_for_volume(FuUtilPrivate *priv, GError **error) +fu_util_prompt_for_volume(FuUtil *self, GError **error) { - FuContext *ctx = fu_engine_get_context(priv->engine); + FuContext *ctx = fu_engine_get_context(self->engine); FuVolume *volume; guint idx; g_autoptr(GPtrArray) volumes = NULL; @@ -3748,7 +4217,7 @@ if (volumes->len == 1) { volume = g_ptr_array_index(volumes, 0); if (fu_volume_get_id(volume) != NULL) { - fu_console_print(priv->console, + fu_console_print(self->console, "%s: %s", /* TRANSLATORS: Volume has been chosen by the user */ _("Selected volume"), @@ -3758,13 +4227,13 @@ } /* TRANSLATORS: this is to abort the interactive prompt */ - fu_console_print(priv->console, "0.\t%s", _("Cancel")); + fu_console_print(self->console, "0.\t%s", _("Cancel")); for (guint i = 0; i < volumes->len; i++) { volume = g_ptr_array_index(volumes, i); - fu_console_print(priv->console, "%u.\t%s", i + 1, fu_volume_get_id(volume)); + fu_console_print(self->console, "%u.\t%s", i + 1, fu_volume_get_id(volume)); } /* TRANSLATORS: get interactive prompt */ - idx = fu_console_input_uint(priv->console, volumes->len, "%s", _("Choose volume")); + idx = fu_console_input_uint(self->console, volumes->len, "%s", _("Choose volume")); if (idx == 0) { g_set_error_literal(error, FWUPD_ERROR, @@ -3777,58 +4246,58 @@ } static gboolean -fu_util_esp_mount(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_esp_mount(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuVolume) volume = NULL; - volume = fu_util_prompt_for_volume(priv, error); + volume = fu_util_prompt_for_volume(self, error); if (volume == NULL) return FALSE; return fu_volume_mount(volume, error); } static gboolean -fu_util_esp_unmount(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_esp_unmount(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuVolume) volume = NULL; - volume = fu_util_prompt_for_volume(priv, error); + volume = fu_util_prompt_for_volume(self, error); if (volume == NULL) return FALSE; return fu_volume_unmount(volume, error); } static gboolean -fu_util_esp_list_as_json(FuUtilPrivate *priv, GError **error) +fu_util_esp_list_as_json(FuUtil *self, GError **error) { g_autoptr(JsonBuilder) builder = json_builder_new(); g_autoptr(GPtrArray) volumes = NULL; - volumes = fu_context_get_esp_volumes(fu_engine_get_context(priv->engine), error); + volumes = fu_context_get_esp_volumes(fu_engine_get_context(self->engine), error); if (volumes == NULL) return FALSE; json_builder_begin_object(builder); fwupd_codec_array_to_json(volumes, "Volumes", builder, FWUPD_CODEC_FLAG_TRUSTED); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } static gboolean -fu_util_esp_list(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_esp_list(FuUtil *self, gchar **values, GError **error) { g_autofree gchar *mount_point = NULL; - g_autoptr(FuDeviceLocker) locker = NULL; + g_autoptr(FuVolumeLocker) locker = NULL; g_autoptr(FuVolume) volume = NULL; g_autoptr(GPtrArray) files = NULL; - if (!fu_util_start_engine(priv, FU_ENGINE_LOAD_FLAG_HWINFO, priv->progress, error)) + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_HWINFO, self->progress, error)) return FALSE; - if (priv->as_json) - return fu_util_esp_list_as_json(priv, error); + if (self->as_json) + return fu_util_esp_list_as_json(self, error); - volume = fu_util_prompt_for_volume(priv, error); + volume = fu_util_prompt_for_volume(self, error); if (volume == NULL) return FALSE; - locker = fu_volume_locker(volume, error); + locker = fu_volume_locker_new(volume, error); if (locker == NULL) return FALSE; mount_point = fu_volume_get_mount_point(volume); @@ -3844,52 +4313,52 @@ return FALSE; for (guint i = 0; i < files->len; i++) { const gchar *fn = g_ptr_array_index(files, i); - fu_console_print_literal(priv->console, fn); + fu_console_print_literal(self->console, fn); } return TRUE; } static gboolean -fu_util_modify_tag(FuUtilPrivate *priv, gchar **values, gboolean enable, GError **error) +fu_util_modify_tag(FuUtil *self, gchar **values, gboolean enable, GError **error) { g_autoptr(FuDevice) dev = NULL; const gchar *tag = enable ? "emulation-tag" : "~emulation-tag"; - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; /* set the flag */ - priv->filter_device_include |= FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG; + self->filter_device_include |= FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG; if (g_strv_length(values) >= 1) { - dev = fu_util_get_device(priv, values[0], error); + dev = fu_util_get_device(self, values[0], error); if (dev == NULL) return FALSE; } else { - dev = fu_util_prompt_for_device(priv, NULL, error); + dev = fu_util_prompt_for_device(self, NULL, error); if (dev == NULL) return FALSE; } - return fu_engine_modify_device(priv->engine, fu_device_get_id(dev), "Flags", tag, error); + return fu_engine_modify_device(self->engine, fu_device_get_id(dev), "Flags", tag, error); } static gboolean -fu_util_emulation_tag(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_emulation_tag(FuUtil *self, gchar **values, GError **error) { - return fu_util_modify_tag(priv, values, TRUE, error); + return fu_util_modify_tag(self, values, TRUE, error); } static gboolean -fu_util_emulation_untag(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_emulation_untag(FuUtil *self, gchar **values, GError **error) { - return fu_util_modify_tag(priv, values, FALSE, error); + return fu_util_modify_tag(self, values, FALSE, error); } static gboolean -fu_util_emulation_load(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_emulation_load(FuUtil *self, gchar **values, GError **error) { g_autoptr(GInputStream) stream = NULL; @@ -3903,26 +4372,26 @@ } /* progress */ - fu_progress_set_id(priv->progress, G_STRLOC); - fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); - fu_progress_add_step(priv->progress, FWUPD_STATUS_LOADING, 5, "load-emulation"); - fu_progress_add_step(priv->progress, FWUPD_STATUS_DEVICE_WRITE, 5, "write"); + fu_progress_set_id(self->progress, G_STRLOC); + fu_progress_add_step(self->progress, FWUPD_STATUS_LOADING, 95, "start-engine"); + fu_progress_add_step(self->progress, FWUPD_STATUS_LOADING, 5, "load-emulation"); + fu_progress_add_step(self->progress, FWUPD_STATUS_DEVICE_WRITE, 5, "write"); /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO, - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* load emulation */ stream = fu_input_stream_from_path(values[0], error); if (stream == NULL) return FALSE; - if (!fu_engine_emulation_load(priv->engine, stream, error)) + if (!fu_engine_emulation_load(self->engine, stream, error)) return FALSE; - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* "install" archive */ if (values[1] != NULL) { @@ -3932,17 +4401,17 @@ stream_cab = fu_input_stream_from_path(values[1], error); if (stream_cab == NULL) return FALSE; - devices_possible = fu_engine_get_devices(priv->engine, error); + devices_possible = fu_engine_get_devices(self->engine, error); if (devices_possible == NULL) return FALSE; - if (!fu_util_install_stream(priv, + if (!fu_util_install_stream(self, stream_cab, devices_possible, - fu_progress_get_child(priv->progress), + fu_progress_get_child(self->progress), error)) return FALSE; } - fu_progress_step_done(priv->progress); + fu_progress_step_done(self->progress); /* success */ return TRUE; @@ -3955,7 +4424,7 @@ } static gboolean -fu_util_switch_branch(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_switch_branch(FuUtil *self, gchar **values, GError **error) { const gchar *branch; g_autoptr(FwupdRelease) rel = NULL; @@ -3964,21 +4433,21 @@ g_autoptr(FuDevice) dev = NULL; /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; /* find the device and check it has multiple branches */ - priv->filter_device_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES; - priv->filter_device_include |= FWUPD_DEVICE_FLAG_UPDATABLE; + self->filter_device_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES; + self->filter_device_include |= FWUPD_DEVICE_FLAG_UPDATABLE; if (g_strv_length(values) == 1) - dev = fu_util_get_device(priv, values[1], error); + dev = fu_util_get_device(self, values[0], error); else - dev = fu_util_prompt_for_device(priv, NULL, error); + dev = fu_util_prompt_for_device(self, NULL, error); if (dev == NULL) return FALSE; if (!fu_device_has_flag(dev, FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES)) { @@ -3990,7 +4459,7 @@ } /* get all releases, including the alternate branch versions */ - rels = fu_engine_get_releases(priv->engine, priv->request, fu_device_get_id(dev), error); + rels = fu_engine_get_releases(self->engine, self->request, fu_device_get_id(dev), error); if (rels == NULL) return FALSE; @@ -3999,8 +4468,8 @@ FwupdRelease *rel_tmp = g_ptr_array_index(rels, i); const gchar *branch_tmp = fwupd_release_get_branch(rel_tmp); if (!fwupd_release_match_flags(rel_tmp, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; if (g_ptr_array_find_with_equal_func(branches, branch_tmp, _g_str_equal0, NULL)) continue; @@ -4016,17 +4485,17 @@ guint idx; /* TRANSLATORS: this is to abort the interactive prompt */ - fu_console_print(priv->console, "0.\t%s", _("Cancel")); + fu_console_print(self->console, "0.\t%s", _("Cancel")); for (guint i = 0; i < branches->len; i++) { const gchar *branch_tmp = g_ptr_array_index(branches, i); - fu_console_print(priv->console, + fu_console_print(self->console, "%u.\t%s", i + 1, fu_util_branch_for_display(branch_tmp)); } /* TRANSLATORS: get interactive prompt, where branch is the * supplier of the firmware, e.g. "non-free" or "free" */ - idx = fu_console_input_uint(priv->console, branches->len, "%s", _("Choose branch")); + idx = fu_console_input_uint(self->console, branches->len, "%s", _("Choose branch")); if (idx == 0) { g_set_error_literal(error, FWUPD_ERROR, @@ -4066,51 +4535,89 @@ } /* we're switching branch */ - if (!fu_util_switch_branch_warning(priv->console, FWUPD_DEVICE(dev), rel, FALSE, error)) + if (!fu_util_switch_branch_warning(self->console, FWUPD_DEVICE(dev), rel, FALSE, error)) return FALSE; /* update the console if composite devices are also updated */ - priv->current_operation = FU_UTIL_OPERATION_INSTALL; - g_signal_connect(FU_ENGINE(priv->engine), + self->current_operation = FU_UTIL_OPERATION_INSTALL; + g_signal_connect(FU_ENGINE(self->engine), "device-changed", G_CALLBACK(fu_util_update_device_changed_cb), - priv); - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; - if (!fu_util_install_release(priv, rel, error)) + self); + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (!fu_util_install_release(self, FWUPD_DEVICE(dev), rel, error)) return FALSE; - fu_util_display_current_message(priv); + fu_util_display_current_message(self); /* we don't want to ask anything */ - if (priv->no_reboot_check) { + if (self->no_reboot_check) { g_debug("skipping reboot check"); return TRUE; } - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); +} + +static gboolean +fu_util_get_results(FuUtil *self, gchar **values, GError **error) +{ + g_autofree gchar *str = NULL; + g_autoptr(FwupdDevice) device = NULL; + + /* check args */ + if (g_strv_length(values) < 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments, expected DEVICE-ID"); + return FALSE; + } + + /* load engine */ + if (!fu_util_start_engine(self, + FU_ENGINE_LOAD_FLAG_COLDPLUG | + FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | + FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_HWINFO, + self->progress, + error)) + return FALSE; + + /* print device */ + device = fu_engine_get_results(self->engine, values[0], error); + if (device == NULL) + return FALSE; + if (self->as_json) { + str = fwupd_codec_to_json_string(FWUPD_CODEC(device), FWUPD_CODEC_FLAG_NONE, error); + if (str == NULL) + return FALSE; + } else { + str = fwupd_codec_to_string(FWUPD_CODEC(device)); + } + fu_console_print_literal(self->console, str); + return TRUE; } static gboolean -fu_util_set_bios_setting(FuUtilPrivate *priv, gchar **input, GError **error) +fu_util_set_bios_setting(FuUtil *self, gchar **input, GError **error) { g_autoptr(GHashTable) settings = fu_util_bios_settings_parse_argv(input, error); if (settings == NULL) return FALSE; - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; - if (!fu_engine_modify_bios_settings(priv->engine, settings, FALSE, error)) { - if (!g_error_matches(*error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) - g_prefix_error(error, "failed to set BIOS setting: "); + if (!fu_engine_modify_bios_settings(self->engine, settings, FALSE, error)) { + g_prefix_error_literal(error, "failed to set BIOS setting: "); return FALSE; } - if (!priv->as_json) { + if (!self->as_json) { gpointer key, value; GHashTableIter iter; @@ -4121,28 +4628,28 @@ g_strdup_printf(_("Set BIOS setting '%s' using '%s'."), (const gchar *)key, (const gchar *)value); - fu_console_print_literal(priv->console, msg); + fu_console_print_literal(self->console, msg); } } - priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; - if (priv->no_reboot_check) { + if (self->no_reboot_check) { g_debug("skipping reboot check"); return TRUE; } - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean -fu_util_security_fix(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_security_fix(FuUtil *self, gchar **values, GError **error) { #ifndef HAVE_HSI - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - /* TRANSLATORS: error message for unsupported feature */ - _("Host Security ID (HSI) is not supported")); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + /* TRANSLATORS: error message for unsupported feature */ + _("Host Security ID (HSI) is not supported")); return FALSE; #endif /* HAVE_HSI */ @@ -4156,30 +4663,30 @@ return FALSE; } - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; - if (!fu_engine_fix_host_security_attr(priv->engine, values[0], error)) + if (!fu_engine_fix_host_security_attr(self->engine, values[0], error)) return FALSE; /* TRANSLATORS: we've fixed a security problem on the machine */ - fu_console_print_literal(priv->console, _("Fixed successfully")); + fu_console_print_literal(self->console, _("Fixed successfully")); return TRUE; } static gboolean -fu_util_security_undo(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_security_undo(FuUtil *self, gchar **values, GError **error) { #ifndef HAVE_HSI - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - /* TRANSLATORS: error message for unsupported feature */ - _("Host Security ID (HSI) is not supported")); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + /* TRANSLATORS: error message for unsupported feature */ + _("Host Security ID (HSI) is not supported")); return FALSE; #endif /* HAVE_HSI */ @@ -4193,46 +4700,46 @@ return FALSE; } - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_REMOTES | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; - if (!fu_engine_undo_host_security_attr(priv->engine, values[0], error)) + if (!fu_engine_undo_host_security_attr(self->engine, values[0], error)) return FALSE; /* TRANSLATORS: we've fixed a security problem on the machine */ - fu_console_print_literal(priv->console, _("Fix reverted successfully")); + fu_console_print_literal(self->console, _("Fix reverted successfully")); return TRUE; } static gboolean -fu_util_get_bios_setting(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_bios_setting(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuBiosSettings) attrs = NULL; g_autoptr(GPtrArray) items = NULL; - FuContext *ctx = fu_engine_get_context(priv->engine); + FuContext *ctx = fu_engine_get_context(self->engine); gboolean found = FALSE; /* load engine */ - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; attrs = fu_context_get_bios_settings(ctx); items = fu_bios_settings_get_all(attrs); - if (priv->as_json) - return fu_util_bios_setting_console_print(priv->console, values, items, error); + if (self->as_json) + return fu_util_bios_setting_console_print(self->console, values, items, error); for (guint i = 0; i < items->len; i++) { FwupdBiosSetting *attr = g_ptr_array_index(items, i); if (fu_util_bios_setting_matches_args(attr, values)) { g_autofree gchar *tmp = fu_util_bios_setting_to_string(attr, 0); - fu_console_print_literal(priv->console, tmp); + fu_console_print_literal(self->console, tmp); found = TRUE; } } @@ -4258,39 +4765,39 @@ } static gboolean -fu_util_reboot_cleanup(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_reboot_cleanup(FuUtil *self, gchar **values, GError **error) { FuPlugin *plugin; g_autoptr(FuDevice) device = NULL; - if (!fu_util_start_engine(priv, + if (!fu_util_start_engine(self, FU_ENGINE_LOAD_FLAG_COLDPLUG | FU_ENGINE_LOAD_FLAG_DEVICE_HOTPLUG | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; /* both arguments are optional */ if (g_strv_length(values) >= 1) { - device = fu_engine_get_device(priv->engine, values[1], error); + device = fu_engine_get_device(self->engine, values[1], error); if (device == NULL) return FALSE; } else { - device = fu_util_prompt_for_device(priv, NULL, error); + device = fu_util_prompt_for_device(self, NULL, error); if (device == NULL) return FALSE; } - plugin = fu_engine_get_plugin_by_name(priv->engine, fu_device_get_plugin(device), error); + plugin = fu_engine_get_plugin_by_name(self->engine, fu_device_get_plugin(device), error); if (plugin == NULL) return FALSE; return fu_plugin_runner_reboot_cleanup(plugin, device, error); } static gboolean -fu_util_efiboot_info_as_json(FuUtilPrivate *priv, GPtrArray *entries, GError **error) +fu_util_efiboot_info_as_json(FuUtil *self, GPtrArray *entries, GError **error) { - FuEfivars *efivars = fu_context_get_efivars(priv->ctx); + FuEfivars *efivars = fu_context_get_efivars(self->ctx); guint16 idx = 0; g_autoptr(JsonBuilder) builder = json_builder_new(); @@ -4316,13 +4823,13 @@ json_builder_end_object(builder); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } static gboolean -fu_util_efiboot_next(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_efiboot_next(FuUtil *self, gchar **values, GError **error) { - FuEfivars *efivars = fu_context_get_efivars(priv->ctx); + FuEfivars *efivars = fu_context_get_efivars(self->ctx); guint64 value = 0; /* just show */ @@ -4330,7 +4837,7 @@ guint16 idx = 0; if (!fu_efivars_get_boot_next(efivars, &idx, error)) return FALSE; - fu_console_print(priv->console, "Boot%04X", idx); + fu_console_print(self->console, "Boot%04X", idx); return TRUE; } @@ -4341,9 +4848,9 @@ } static gboolean -fu_util_efiboot_order(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_efiboot_order(FuUtil *self, gchar **values, GError **error) { - FuEfivars *efivars = fu_context_get_efivars(priv->ctx); + FuEfivars *efivars = fu_context_get_efivars(self->ctx); g_auto(GStrv) split = NULL; g_autoptr(GArray) order = NULL; @@ -4354,7 +4861,7 @@ return FALSE; for (guint i = 0; i < order->len; i++) { guint16 idx = g_array_index(order, guint16, i); - fu_console_print(priv->console, "Boot%04X", idx); + fu_console_print(self->console, "Boot%04X", idx); } return TRUE; } @@ -4374,9 +4881,9 @@ } static gboolean -fu_util_efiboot_create(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_efiboot_create(FuUtil *self, gchar **values, GError **error) { - FuEfivars *efivars = fu_context_get_efivars(priv->ctx); + FuEfivars *efivars = fu_context_get_efivars(self->ctx); g_autoptr(FuVolume) volume = NULL; guint64 idx = 0; @@ -4394,7 +4901,7 @@ /* check the index does not already exist */ if (!fu_strtoull(values[0], &idx, 0x0, G_MAXUINT16, FU_INTEGER_BASE_16, error)) return FALSE; - if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if ((self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { g_autoptr(GBytes) blob = fu_efivars_get_boot_data(efivars, (guint16)idx, NULL); if (blob != NULL) { g_set_error_literal(error, @@ -4408,12 +4915,12 @@ /* get volume */ if (values[3] == NULL) { - volume = fu_util_prompt_for_volume(priv, error); + volume = fu_util_prompt_for_volume(self, error); if (volume == NULL) return FALSE; } else { g_autoptr(GPtrArray) volumes = NULL; - volumes = fu_context_get_esp_volumes(priv->ctx, error); + volumes = fu_context_get_esp_volumes(self->ctx, error); if (volumes == NULL) return FALSE; for (guint i = 0; i < volumes->len; i++) { @@ -4443,9 +4950,9 @@ } static gboolean -fu_util_efiboot_delete(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_efiboot_delete(FuUtil *self, gchar **values, GError **error) { - FuEfivars *efivars = fu_context_get_efivars(priv->ctx); + FuEfivars *efivars = fu_context_get_efivars(self->ctx); guint64 value = 0; if (values[0] == NULL) { @@ -4502,9 +5009,9 @@ } static gboolean -fu_util_efiboot_hive(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_efiboot_hive(FuUtil *self, gchar **values, GError **error) { - FuEfivars *efivars = fu_context_get_efivars(priv->ctx); + FuEfivars *efivars = fu_context_get_efivars(self->ctx); g_autoptr(FuEfiLoadOption) loadopt = NULL; guint64 idx = 0; @@ -4528,7 +5035,7 @@ /* get value */ if (values[2] == NULL) { const gchar *value; - fu_console_print_full(priv->console, + fu_console_print_full(self->console, FU_CONSOLE_PRINT_FLAG_WARNING, "%s\n", /* TRANSLATORS: try to treat the legacy format as a hive */ @@ -4536,7 +5043,7 @@ value = fu_efi_load_option_get_metadata(loadopt, values[1], error); if (value == NULL) return FALSE; - fu_console_print_literal(priv->console, value); + fu_console_print_literal(self->console, value); return TRUE; } @@ -4546,14 +5053,14 @@ /* change the format if required */ if (fu_efi_load_option_get_kind(loadopt) != FU_EFI_LOAD_OPTION_KIND_HIVE) { - fu_console_print_full(priv->console, + fu_console_print_full(self->console, FU_CONSOLE_PRINT_FLAG_WARNING, "%s\n", /* TRANSLATORS: the boot entry was in a legacy format */ _("The EFI boot entry is not in hive format, " "and shim may not be new enough to read it.")); - if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && - !fu_console_input_bool(priv->console, + if ((self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + !fu_console_input_bool(self->console, FALSE, "%s", /* TRANSLATORS: ask the user if it's okay to convert, @@ -4574,9 +5081,9 @@ } static gboolean -fu_util_efiboot_info(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_efiboot_info(FuUtil *self, gchar **values, GError **error) { - FuEfivars *efivars = fu_context_get_efivars(priv->ctx); + FuEfivars *efivars = fu_context_get_efivars(self->ctx); g_autoptr(GPtrArray) entries = NULL; g_autoptr(GString) str = g_string_new(NULL); guint16 idx = 0; @@ -4586,8 +5093,8 @@ return FALSE; /* dump to the screen in the most appropriate format */ - if (priv->as_json) - return fu_util_efiboot_info_as_json(priv, entries, error); + if (self->as_json) + return fu_util_efiboot_info_as_json(self, entries, error); if (fu_efivars_get_boot_current(efivars, &idx, NULL)) fwupd_codec_string_append_hex(str, 0, "BootCurrent", idx); @@ -4603,12 +5110,12 @@ } /* success */ - fu_console_print_literal(priv->console, str->str); + fu_console_print_literal(self->console, str->str); return TRUE; } static gboolean -fu_util_efivar_files_as_json(FuUtilPrivate *priv, GPtrArray *files, GError **error) +fu_util_efivar_files_as_json(FuUtil *self, GPtrArray *files, GError **error) { g_autoptr(JsonBuilder) builder = json_builder_new(); g_autoptr(GHashTable) hash = g_hash_table_new_full(g_str_hash, @@ -4650,28 +5157,28 @@ json_builder_end_array(builder); } json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } static gboolean -fu_util_efivar_files(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_efivar_files(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) files = NULL; - files = fu_context_get_esp_files(priv->ctx, + files = fu_context_get_esp_files(self->ctx, FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_FIRST_STAGE | FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_SECOND_STAGE | FU_CONTEXT_ESP_FILE_FLAG_INCLUDE_REVOCATIONS, error); if (files == NULL) return FALSE; - if (priv->as_json) - return fu_util_efivar_files_as_json(priv, files, error); + if (self->as_json) + return fu_util_efivar_files_as_json(self, files, error); for (guint i = 0; i < files->len; i++) { FuFirmware *firmware = g_ptr_array_index(files, i); g_autofree gchar *name = g_strdup_printf("Boot%04X", (guint)fu_firmware_get_idx(firmware)); - fu_console_print(priv->console, + fu_console_print(self->console, "%s → %s", name, fu_firmware_get_filename(firmware)); @@ -4682,9 +5189,9 @@ } static gboolean -fu_util_efivar_list(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_efivar_list(FuUtil *self, gchar **values, GError **error) { - FuEfivars *efivars = fu_context_get_efivars(priv->ctx); + FuEfivars *efivars = fu_context_get_efivars(self->ctx); g_autoptr(GPtrArray) names = NULL; /* sanity check */ @@ -4701,7 +5208,7 @@ return FALSE; for (guint i = 0; i < names->len; i++) { const gchar *name = g_ptr_array_index(names, i); - fu_console_print(priv->console, "name: %s", name); + fu_console_print(self->console, "name: %s", name); } /* success */ @@ -4709,7 +5216,7 @@ } static gboolean -fu_util_build_cabinet(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_build_cabinet(FuUtil *self, gchar **values, GError **error) { g_autoptr(GBytes) cab_blob = NULL; g_autoptr(FuCabinet) cab_file = fu_cabinet_new(); @@ -4726,7 +5233,7 @@ } /* file already exists */ - if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + if ((self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && g_file_test(values[0], G_FILE_TEST_EXISTS)) { g_set_error_literal(error, FWUPD_ERROR, @@ -4750,7 +5257,8 @@ values[i]); return FALSE; } - fu_cabinet_add_file(cab_file, basename, blob); + if (!fu_cabinet_add_file(cab_file, basename, blob, error)) + return FALSE; } /* export */ @@ -4762,7 +5270,7 @@ if (!fu_firmware_parse_bytes(FU_FIRMWARE(cab_file), cab_blob, 0x0, - FWUPD_INSTALL_FLAG_NONE, + FU_FIRMWARE_PARSE_FLAG_CACHE_BLOB, error)) return FALSE; @@ -4770,58 +5278,58 @@ } static gboolean -fu_util_version(FuUtilPrivate *priv, GError **error) +fu_util_version(FuUtil *self, GError **error) { g_autoptr(GHashTable) metadata = NULL; g_autofree gchar *str = NULL; /* load engine */ if (!fu_util_start_engine( - priv, + self, FU_ENGINE_LOAD_FLAG_READONLY | FU_ENGINE_LOAD_FLAG_EXTERNAL_PLUGINS | FU_ENGINE_LOAD_FLAG_BUILTIN_PLUGINS | FU_ENGINE_LOAD_FLAG_HWINFO, - priv->progress, + self->progress, error)) return FALSE; /* get metadata */ - metadata = fu_engine_get_report_metadata(priv->engine, error); + metadata = fu_engine_get_report_metadata(self->engine, error); if (metadata == NULL) return FALSE; /* dump to the screen in the most appropriate format */ - if (priv->as_json) - return fu_util_project_versions_as_json(priv->console, metadata, error); + if (self->as_json) + return fu_util_project_versions_as_json(self->console, metadata, error); str = fu_util_project_versions_to_string(metadata); - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); return TRUE; } static gboolean -fu_util_clear_history(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_clear_history(FuUtil *self, gchar **values, GError **error) { - g_autoptr(FuHistory) history = fu_history_new(priv->ctx); + g_autoptr(FuHistory) history = fu_history_new(self->ctx); return fu_history_remove_all(history, error); } static gboolean -fu_util_setup_interactive(FuUtilPrivate *priv, GError **error) +fu_util_setup_interactive(FuUtil *self, GError **error) { - if (priv->as_json) { + if (self->as_json) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "using --json"); return FALSE; } - return fu_console_setup(priv->console, error); + return fu_console_setup(self->console, error); } static void -fu_util_print_error(FuUtilPrivate *priv, const GError *error) +fu_util_print_error(FuUtil *self, const GError *error) { - if (priv->as_json) { - fu_util_print_error_as_json(priv->console, error); + if (self->as_json) { + fu_util_print_error_as_json(self->console, error); return; } - fu_console_print_full(priv->console, FU_CONSOLE_PRINT_FLAG_STDERR, "%s\n", error->message); + fu_console_print_full(self->console, FU_CONSOLE_PRINT_FLAG_STDERR, "%s\n", error->message); } int @@ -4838,7 +5346,7 @@ gboolean ignore_requirements = FALSE; gboolean ignore_vid_pid = FALSE; g_auto(GStrv) plugin_glob = NULL; - g_autoptr(FuUtilPrivate) priv = g_new0(FuUtilPrivate, 1); + g_autoptr(FuUtil) self = g_new0(FuUtil, 1); g_autoptr(GError) error_console = NULL; g_autoptr(GError) error = NULL; g_autoptr(GPtrArray) cmd_array = fu_util_cmd_array_new(); @@ -4914,7 +5422,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->no_reboot_check, + &self->no_reboot_check, /* TRANSLATORS: command line option */ N_("Do not check or prompt for reboot after update"), NULL}, @@ -4930,7 +5438,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->no_safety_check, + &self->no_safety_check, /* TRANSLATORS: command line option */ N_("Do not perform device safety checks"), NULL}, @@ -4938,7 +5446,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->no_device_prompt, + &self->no_device_prompt, /* TRANSLATORS: command line option */ N_("Do not prompt for devices"), NULL}, @@ -4946,7 +5454,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->show_all, + &self->show_all, /* TRANSLATORS: command line option */ N_("Show all results"), NULL}, @@ -4954,7 +5462,7 @@ '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, - &priv->show_all, + &self->show_all, /* TRANSLATORS: command line option */ N_("Show devices that are not updatable"), NULL}, @@ -4978,7 +5486,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->prepare_blob, + &self->prepare_blob, /* TRANSLATORS: command line option */ N_("Run the plugin composite prepare routine when using install-blob"), NULL}, @@ -4986,7 +5494,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->cleanup_blob, + &self->cleanup_blob, /* TRANSLATORS: command line option */ N_("Run the plugin composite cleanup routine when using install-blob"), NULL}, @@ -4994,7 +5502,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->disable_ssl_strict, + &self->disable_ssl_strict, /* TRANSLATORS: command line option */ N_("Ignore SSL strict checks when downloading files"), NULL}, @@ -5016,13 +5524,21 @@ N_("Filter with a set of release flags using a ~ prefix to " "exclude, e.g. 'trusted-release,~trusted-metadata'"), NULL}, + {"assume-yes", + 'y', + 0, + G_OPTION_ARG_NONE, + &self->assume_yes, + /* TRANSLATORS: command line option */ + N_("Answer yes to all questions"), + NULL}, {"json", '\0', 0, G_OPTION_ARG_NONE, - &priv->as_json, + &self->as_json, /* TRANSLATORS: command line option */ - N_("Output in JSON format"), + N_("Output in JSON format (disables all interactive prompts)"), NULL}, {NULL}}; @@ -5039,38 +5555,38 @@ g_set_prgname(fu_util_get_prgname(argv[0])); /* create helper object */ - priv->lock_fd = -1; - priv->main_ctx = g_main_context_new(); - priv->loop = g_main_loop_new(priv->main_ctx, FALSE); - priv->console = fu_console_new(); - priv->post_requests = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); - fu_console_set_main_context(priv->console, priv->main_ctx); - priv->request = fu_engine_request_new(NULL); + self->lock_fd = -1; + self->main_ctx = g_main_context_new(); + self->loop = g_main_loop_new(self->main_ctx, FALSE); + self->console = fu_console_new(); + self->post_requests = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + fu_console_set_main_context(self->console, self->main_ctx); + self->request = fu_engine_request_new(NULL); /* used for monitoring and downloading */ - priv->client = fwupd_client_new(); - fwupd_client_set_main_context(priv->client, priv->main_ctx); - fwupd_client_set_daemon_version(priv->client, PACKAGE_VERSION); - fwupd_client_set_user_agent_for_package(priv->client, "fwupdtool", PACKAGE_VERSION); - g_signal_connect(FWUPD_CLIENT(priv->client), + self->client = fwupd_client_new(); + fwupd_client_set_main_context(self->client, self->main_ctx); + fwupd_client_set_daemon_version(self->client, PACKAGE_VERSION); + fwupd_client_set_user_agent_for_package(self->client, "fwupdtool", PACKAGE_VERSION); + g_signal_connect(FWUPD_CLIENT(self->client), "notify::percentage", G_CALLBACK(fu_util_client_notify_cb), - priv); - g_signal_connect(FWUPD_CLIENT(priv->client), + self); + g_signal_connect(FWUPD_CLIENT(self->client), "notify::status", G_CALLBACK(fu_util_client_notify_cb), - priv); + self); /* when not using the engine */ - priv->progress = fu_progress_new(G_STRLOC); - g_signal_connect(priv->progress, + self->progress = fu_progress_new(G_STRLOC); + g_signal_connect(self->progress, "percentage-changed", G_CALLBACK(fu_util_progress_percentage_changed_cb), - priv); - g_signal_connect(priv->progress, + self); + g_signal_connect(self->progress, "status-changed", G_CALLBACK(fu_util_progress_status_changed_cb), - priv); + self); /* add commands */ fu_util_cmd_array_add(cmd_array, @@ -5104,7 +5620,8 @@ /* TRANSLATORS: command argument: uppercase, spaces->dashes */ _("[DEVICE-ID|GUID]"), /* TRANSLATORS: command description */ - _("Gets the list of updates for connected hardware"), + _("Gets the list of updates for all specified devices, or all " + "devices if unspecified"), fu_util_get_updates); fu_util_cmd_array_add(cmd_array, "get-devices,get-topology", @@ -5127,7 +5644,7 @@ fu_util_cmd_array_add(cmd_array, "install-blob", /* TRANSLATORS: command argument: uppercase, spaces->dashes */ - _("FILENAME DEVICE-ID"), + _("FILENAME DEVICE-ID [VERSION]"), /* TRANSLATORS: command description */ _("Install a raw firmware blob on a device"), fu_util_install_blob); @@ -5313,7 +5830,8 @@ fu_util_get_remotes); fu_util_cmd_array_add(cmd_array, "refresh", - NULL, + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("[FILE FILE_SIG REMOTE-ID]"), /* TRANSLATORS: command description */ _("Refresh metadata from remote server"), fu_util_refresh); @@ -5371,6 +5889,13 @@ _("Switch the firmware branch on the device"), fu_util_switch_branch); fu_util_cmd_array_add(cmd_array, + "get-results", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("DEVICE-ID"), + /* TRANSLATORS: command description */ + _("Gets the results from the last update"), + fu_util_get_results); + fu_util_cmd_array_add(cmd_array, "clear-history", NULL, /* TRANSLATORS: command description */ @@ -5498,6 +6023,13 @@ _("Modifies a given remote"), fu_util_remote_modify); fu_util_cmd_array_add(cmd_array, + "clean-remote", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("REMOTE-ID"), + /* TRANSLATORS: command description */ + _("Cleans a given remote"), + fu_util_remote_clean); + fu_util_cmd_array_add(cmd_array, "enable-remote", /* TRANSLATORS: command argument: uppercase, spaces->dashes */ _("REMOTE-ID"), @@ -5536,64 +6068,84 @@ /* TRANSLATORS: command description */ _("Compares two versions for equality"), fu_util_vercmp); + fu_util_cmd_array_add(cmd_array, + "search", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("WORD"), + /* TRANSLATORS: command description */ + _("Finds firmware releases from the metadata"), + fu_util_search); + fu_util_cmd_array_add(cmd_array, + "crc", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("KIND FILENAME"), + /* TRANSLATORS: command description */ + _("Calculates a CRC of a file"), + fu_util_crc); + fu_util_cmd_array_add(cmd_array, + "crc-find", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("CRC FILENAME"), + /* TRANSLATORS: command description */ + _("Finds a algorithm that matches the file CRC"), + fu_util_crc_find); /* do stuff on ctrl+c */ - priv->cancellable = g_cancellable_new(); - fu_util_setup_signal_handlers(priv); - g_signal_connect(G_CANCELLABLE(priv->cancellable), + self->cancellable = g_cancellable_new(); + g_signal_connect(G_CANCELLABLE(self->cancellable), "cancelled", G_CALLBACK(fu_util_cancelled_cb), - priv); + self); /* sort by command name */ fu_util_cmd_array_sort(cmd_array); /* non-TTY consoles cannot answer questions */ - if (!fu_util_setup_interactive(priv, &error_console)) { + if (!fu_util_setup_interactive(self, &error_console)) { g_info("failed to initialize interactive console: %s", error_console->message); - priv->no_reboot_check = TRUE; - priv->no_safety_check = TRUE; - priv->no_device_prompt = TRUE; + self->no_reboot_check = TRUE; + self->no_safety_check = TRUE; + self->no_device_prompt = TRUE; } else { - priv->interactive = TRUE; + self->interactive = TRUE; /* set our implemented feature set */ fu_engine_request_set_feature_flags( - priv->request, + self->request, FWUPD_FEATURE_FLAG_DETACH_ACTION | FWUPD_FEATURE_FLAG_SWITCH_BRANCH | FWUPD_FEATURE_FLAG_FDE_WARNING | FWUPD_FEATURE_FLAG_UPDATE_ACTION | FWUPD_FEATURE_FLAG_COMMUNITY_TEXT | FWUPD_FEATURE_FLAG_SHOW_PROBLEMS | FWUPD_FEATURE_FLAG_REQUESTS | FWUPD_FEATURE_FLAG_REQUESTS_NON_GENERIC); } - fu_console_set_interactive(priv->console, priv->interactive); + fu_console_set_interactive(self->console, self->interactive); /* get a list of the commands */ - priv->context = g_option_context_new(NULL); + self->context = g_option_context_new(NULL); cmd_descriptions = fu_util_cmd_array_to_string(cmd_array); - g_option_context_set_summary(priv->context, cmd_descriptions); + g_option_context_set_summary(self->context, cmd_descriptions); g_option_context_set_description( - priv->context, + self->context, /* TRANSLATORS: CLI description */ _("This tool allows an administrator to use the fwupd plugins " "without being installed on the host system.")); /* TRANSLATORS: program name */ g_set_application_name(_("Firmware Utility")); - g_option_context_add_main_entries(priv->context, options, NULL); - g_option_context_add_group(priv->context, fu_debug_get_option_group()); - ret = g_option_context_parse(priv->context, &argc, &argv, &error); + g_option_context_add_main_entries(self->context, options, NULL); + g_option_context_add_group(self->context, fu_debug_get_option_group()); + ret = g_option_context_parse(self->context, &argc, &argv, &error); if (!ret) { - fu_console_print(priv->console, + fu_console_print(self->console, "%s: %s", /* TRANSLATORS: the user didn't read the man page */ _("Failed to parse arguments"), error->message); return EXIT_FAILURE; } - fu_progress_set_profile(priv->progress, g_getenv("FWUPD_VERBOSE") != NULL); + fu_progress_set_profile(self->progress, g_getenv("FWUPD_VERBOSE") != NULL); /* allow disabling SSL strict mode for broken corporate proxies */ - if (priv->disable_ssl_strict) { - fu_console_print_full(priv->console, + if (self->disable_ssl_strict) { + fu_console_print_full(self->console, FU_CONSOLE_PRINT_FLAG_WARNING, "%s\n", /* TRANSLATORS: try to help */ @@ -5606,74 +6158,79 @@ /* parse filter flags */ if (filter_device != NULL) { if (!fu_util_parse_filter_device_flags(filter_device, - &priv->filter_device_include, - &priv->filter_device_exclude, + &self->filter_device_include, + &self->filter_device_exclude, &error)) { g_autofree gchar *str = /* TRANSLATORS: the user didn't read the man page, %1 is '--filter' */ g_strdup_printf(_("Failed to parse flags for %s"), "--filter"); g_prefix_error(&error, "%s: ", str); - fu_util_print_error(priv, error); + fu_util_print_error(self, error); return EXIT_FAILURE; } } if (filter_release != NULL) { if (!fu_util_parse_filter_release_flags(filter_release, - &priv->filter_release_include, - &priv->filter_release_exclude, + &self->filter_release_include, + &self->filter_release_exclude, &error)) { g_autofree gchar *str = /* TRANSLATORS: the user didn't read the man page, * %1 is '--filter-release' */ g_strdup_printf(_("Failed to parse flags for %s"), "--filter-release"); g_prefix_error(&error, "%s: ", str); - fu_util_print_error(priv, error); + fu_util_print_error(self, error); return EXIT_FAILURE; } } /* set flags */ if (allow_reinstall) - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; if (allow_older) - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; if (allow_branch_switch) - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; if (force) - priv->flags |= FWUPD_INSTALL_FLAG_FORCE; + self->flags |= FWUPD_INSTALL_FLAG_FORCE; if (no_search) - priv->flags |= FWUPD_INSTALL_FLAG_NO_SEARCH; + self->parse_flags |= FU_FIRMWARE_PARSE_FLAG_NO_SEARCH; if (ignore_checksum) - priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_CHECKSUM; + self->parse_flags |= FU_FIRMWARE_PARSE_FLAG_IGNORE_CHECKSUM; if (ignore_vid_pid) - priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_VID_PID; + self->parse_flags |= FU_FIRMWARE_PARSE_FLAG_IGNORE_VID_PID; if (ignore_requirements) - priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS; + self->flags |= FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS; /* load engine */ - priv->ctx = fu_context_new(); - priv->engine = fu_engine_new(priv->ctx); - g_signal_connect(FU_ENGINE(priv->engine), + self->ctx = fu_context_new(); + g_signal_connect(FU_CONTEXT(self->ctx), + "notify::flags", + G_CALLBACK(fu_util_context_flags_notify_cb), + self); + fu_context_add_flag(self->ctx, FU_CONTEXT_FLAG_NO_IDLE_SOURCES); + self->engine = fu_engine_new(self->ctx); + g_signal_connect(FU_ENGINE(self->engine), "device-request", G_CALLBACK(fu_util_update_device_request_cb), - priv); - g_signal_connect(FU_ENGINE(priv->engine), + self); + g_signal_connect(FU_ENGINE(self->engine), "device-added", G_CALLBACK(fu_util_engine_device_added_cb), - priv); - g_signal_connect(FU_ENGINE(priv->engine), + self); + g_signal_connect(FU_ENGINE(self->engine), "device-removed", G_CALLBACK(fu_util_engine_device_removed_cb), - priv); - g_signal_connect(FU_ENGINE(priv->engine), + self); + g_signal_connect(FU_ENGINE(self->engine), "status-changed", G_CALLBACK(fu_util_engine_status_changed_cb), - priv); + self); /* just show versions and exit */ if (version) { - if (!fu_util_version(priv, &error)) { - fu_util_print_error(priv, error); + if (!fu_util_version(self, &error)) { + fu_util_print_error(self, error); return EXIT_FAILURE; } return EXIT_SUCCESS; @@ -5681,10 +6238,10 @@ /* any plugin allowlist specified */ for (guint i = 0; plugin_glob != NULL && plugin_glob[i] != NULL; i++) - fu_engine_add_plugin_filter(priv->engine, plugin_glob[i]); + fu_engine_add_plugin_filter(self->engine, plugin_glob[i]); /* run the specified command */ - ret = fu_util_cmd_array_run(cmd_array, priv, argv[1], (gchar **)&argv[2], &error); + ret = fu_util_cmd_array_run(cmd_array, self, argv[1], (gchar **)&argv[2], &error); if (!ret) { #ifdef SUPPORTED_BUILD /* sanity check */ @@ -5693,28 +6250,31 @@ return EXIT_FAILURE; } #endif - fu_util_print_error(priv, error); - if (!priv->as_json && + fu_util_print_error(self, error); + if (!self->as_json && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) { - fu_console_print(priv->console, + fu_console_print(self->console, /* TRANSLATORS: explain how to get help, %1 is * 'fwupdtool --help' */ _("Use %s for help"), "fwupdtool --help"); } else if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { + /* nocheck:print */ g_info("%s\n", error->message); return EXIT_NOTHING_TO_DO; } else if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_REACHABLE)) { + /* nocheck:print */ g_info("%s\n", error->message); return EXIT_NOT_REACHABLE; } else if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { + /* nocheck:print */ g_info("%s\n", error->message); return EXIT_NOT_FOUND; } #ifdef HAVE_GETUID /* if not root, then notify users on the error path */ - if (priv->interactive && (getuid() != 0 || geteuid() != 0)) { - fu_console_print_full(priv->console, + if (self->interactive && (getuid() != 0 || geteuid() != 0)) { + fu_console_print_full(self->console, FU_CONSOLE_PRINT_FLAG_STDERR | FU_CONSOLE_PRINT_FLAG_WARNING, "%s\n", @@ -5726,10 +6286,10 @@ } /* a good place to do the traceback */ - if (fu_progress_get_profile(priv->progress)) { - g_autofree gchar *str = fu_progress_traceback(priv->progress); + if (fu_progress_get_profile(self->progress)) { + g_autofree gchar *str = fu_progress_traceback(self->progress); if (str != NULL) - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); } /* success */ diff -Nru fwupd-2.0.8/src/fu-udev-backend.c fwupd-2.0.20/src/fu-udev-backend.c --- fwupd-2.0.8/src/fu-udev-backend.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-udev-backend.c 2026-02-26 11:36:18.000000000 +0000 @@ -16,7 +16,6 @@ #include #include "fu-context-private.h" -#include "fu-device-private.h" #include "fu-engine-struct.h" #include "fu-udev-backend.h" #include "fu-udev-device-private.h" @@ -24,17 +23,55 @@ struct _FuUdevBackend { FuBackend parent_instance; gint netlink_fd; - GHashTable *map_paths; /* of str:None */ - GPtrArray *dpaux_devices; /* of FuDpauxDevice */ + GHashTable *map_paths; /* of str:None */ + GHashTable *coldplug_cache; /* of str:FuUdevBackendColdplugCacheItem */ + GPtrArray *dpaux_devices; /* of FuDpauxDevice */ guint dpaux_devices_rescan_id; gboolean done_coldplug; }; +typedef struct { + FuUdevDevice *udev_device; + GError *error; +} FuUdevBackendColdplugCacheItem; + G_DEFINE_TYPE(FuUdevBackend, fu_udev_backend, FU_TYPE_BACKEND) #define FU_UDEV_BACKEND_DPAUX_RESCAN_DELAY 5 /* s */ static void +fu_udev_backend_coldplug_cache_item_free(FuUdevBackendColdplugCacheItem *item) +{ + if (item->udev_device != NULL) + g_object_unref(item->udev_device); + if (item->error) + g_error_free(item->error); + g_free(item); +} + +static void +fu_udev_backend_coldplug_cache_add_device(FuUdevBackend *self, + const gchar *fn, + FuUdevDevice *udev_device) +{ + if (!self->done_coldplug) { + FuUdevBackendColdplugCacheItem *item = g_new0(FuUdevBackendColdplugCacheItem, 1); + item->udev_device = g_object_ref(udev_device); + g_hash_table_insert(self->coldplug_cache, g_strdup(fn), item); + } +} + +static void +fu_udev_backend_coldplug_cache_add_error(FuUdevBackend *self, const gchar *fn, GError *error) +{ + if (!self->done_coldplug) { + FuUdevBackendColdplugCacheItem *item = g_new0(FuUdevBackendColdplugCacheItem, 1); + item->error = g_error_copy(error); + g_hash_table_insert(self->coldplug_cache, g_strdup(fn), item); + } +} + +static void fu_udev_backend_to_string(FuBackend *backend, guint idt, GString *str) { FuUdevBackend *self = FU_UDEV_BACKEND(backend); @@ -124,6 +161,7 @@ return; } fu_device_add_private_flag(FU_DEVICE(udev_device), FU_DEVICE_PRIVATE_FLAG_REFCOUNTED_PROXY); + fu_device_set_proxy_gtype(FU_DEVICE(udev_device), FU_TYPE_I2C_DEVICE); fu_device_set_proxy(FU_DEVICE(udev_device), FU_DEVICE(proxy)); } @@ -189,7 +227,7 @@ device = g_object_new(gtype, "backend", FU_BACKEND(self), NULL); fu_device_incorporate(device, donor, FU_DEVICE_INCORPORATE_FLAG_ALL); if (!fu_device_probe(device, error)) { - g_prefix_error(error, "failed to probe: "); + g_prefix_error_literal(error, "failed to probe: "); return NULL; } } @@ -228,16 +266,43 @@ fu_udev_backend_create_device(FuUdevBackend *self, const gchar *fn, GError **error) { FuContext *ctx = fu_backend_get_context(FU_BACKEND(self)); - g_autoptr(FuUdevDevice) device_donor = fu_udev_device_new(ctx, fn); + g_autoptr(FuDevice) device = NULL; + g_autoptr(FuUdevDevice) device_donor = NULL; + g_autoptr(GError) error_local = NULL; + + /* query the cache to avoid scanning parent devices multiple times */ + if (!self->done_coldplug) { + FuUdevBackendColdplugCacheItem *item = + g_hash_table_lookup(self->coldplug_cache, fn); + if (item != NULL) { + if (item->udev_device == NULL) { + if (error != NULL) + *error = g_error_copy(item->error); + return NULL; + } + return g_object_ref(item->udev_device); + } + } /* use a donor device to probe for the subsystem and devtype */ - if (!fu_device_probe(FU_DEVICE(device_donor), error)) { - g_prefix_error(error, "failed to probe donor: "); + device_donor = fu_udev_device_new(ctx, fn); + if (!fu_device_probe(FU_DEVICE(device_donor), &error_local)) { + fu_udev_backend_coldplug_cache_add_error(self, fn, error_local); + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to probe donor: "); return NULL; } - return FU_UDEV_DEVICE(fu_udev_backend_create_device_for_donor(FU_BACKEND(self), - FU_DEVICE(device_donor), - error)); + device = fu_udev_backend_create_device_for_donor(FU_BACKEND(self), + FU_DEVICE(device_donor), + &error_local); + if (device == NULL) { + fu_udev_backend_coldplug_cache_add_error(self, fn, error_local); + g_propagate_error(error, g_steal_pointer(&error_local)); + return NULL; + } + fu_udev_backend_coldplug_cache_add_device(self, fn, FU_UDEV_DEVICE(device)); + return FU_UDEV_DEVICE(g_steal_pointer(&device)); } static void @@ -255,7 +320,7 @@ g_autoptr(FuDeviceLocker) locker = NULL; g_autoptr(GError) error_local = NULL; - locker = fu_device_locker_new(device, &error_local); + locker = fu_device_locker_new(FU_DEVICE(device), &error_local); if (locker == NULL) { g_debug("failed to open device %s: %s", fu_device_get_backend_id(FU_DEVICE(device)), @@ -358,14 +423,17 @@ } } +/* if enabled, systemd takes the kernel event, runs the udev rules (which might rename devices) + * and then re-broadcasts on the udev netlink socket */ static gboolean fu_udev_backend_netlink_parse_blob(FuUdevBackend *self, GBytes *blob, GError **error) { - FuContext *ctx = fu_backend_get_context(FU_BACKEND(self)); FuUdevAction action = FU_UDEV_ACTION_UNKNOWN; + g_autofree gchar *sysfsdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR); +#ifdef HAVE_UDEV_HOTPLUG + FuContext *ctx = fu_backend_get_context(FU_BACKEND(self)); const guint8 *buf; gsize bufsz = 0; - g_autofree gchar *sysfsdir = fu_path_from_kind(FU_PATH_KIND_SYSFSDIR); g_autoptr(FuStructUdevMonitorNetlinkHeader) st_hdr = NULL; g_autoptr(FuUdevDevice) device_donor = NULL; g_autoptr(GBytes) blob_payload = NULL; @@ -393,7 +461,7 @@ g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INTERNAL, - "invalid ACSII buffer"); + "invalid ASCII buffer"); return FALSE; } kv = g_strsplit(kvstr, "=", 2); @@ -461,7 +529,7 @@ if (device_donor == NULL) { g_set_error_literal(error, FWUPD_ERROR, - FWUPD_ERROR_INVALID_DATA, + FWUPD_ERROR_NOT_FOUND, "no device to change"); return FALSE; } @@ -488,6 +556,33 @@ /* success */ fu_udev_backend_device_add_from_device(self, device_actual); } +#else + g_auto(GStrv) split = fu_strsplit_bytes(blob, "@", 2); + + action = fu_udev_action_from_string(split[0]); + if (action == FU_UDEV_ACTION_ADD) { + g_autofree gchar *sysfspath = g_build_filename(sysfsdir, split[1], NULL); + g_autoptr(FuUdevDevice) device = + fu_udev_backend_create_device(self, sysfspath, error); + if (device == NULL) + return FALSE; + fu_udev_backend_device_add_from_device(self, device); + } else if (action == FU_UDEV_ACTION_REMOVE) { + g_autofree gchar *sysfspath = g_build_filename(sysfsdir, split[1], NULL); + fu_udev_backend_device_remove(self, sysfspath); + } else if (action == FU_UDEV_ACTION_CHANGE) { + g_autofree gchar *sysfspath = g_build_filename(sysfsdir, split[1], NULL); + FuDevice *device_tmp = fu_backend_lookup_by_id(FU_BACKEND(self), sysfspath); + if (device_tmp == NULL) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "no device to change"); + return FALSE; + } + fu_backend_device_changed(FU_BACKEND(self), device_tmp); + } +#endif /* success */ return TRUE; @@ -507,7 +602,8 @@ return TRUE; blob = g_bytes_new(buf, len); if (!fu_udev_backend_netlink_parse_blob(self, blob, &error_local)) { - if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) || + g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { g_debug("ignoring netlink message: %s", error_local->message); return TRUE; } @@ -523,7 +619,11 @@ struct sockaddr_nl nls = { .nl_family = AF_NETLINK, .nl_pid = getpid(), +#ifdef HAVE_UDEV_HOTPLUG .nl_groups = FU_UDEV_MONITOR_NETLINK_GROUP_UDEV, +#else + .nl_groups = FU_UDEV_MONITOR_NETLINK_GROUP_KERNEL, +#endif }; g_autoptr(GSource) source = NULL; @@ -542,7 +642,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "failed to connect to netlink: %s", - g_strerror(errno)); + fwupd_strerror(errno)); return FALSE; } if (bind(self->netlink_fd, (void *)&nls, sizeof(nls))) { @@ -550,7 +650,7 @@ FWUPD_ERROR, FWUPD_ERROR_INTERNAL, "bind to udev socket failed: %s", - g_strerror(errno)); + fwupd_strerror(errno)); return FALSE; } source = g_unix_fd_source_new(self->netlink_fd, G_IO_IN); @@ -601,6 +701,7 @@ } /* success */ + g_hash_table_remove_all(self->coldplug_cache); self->done_coldplug = TRUE; return TRUE; } @@ -616,7 +717,7 @@ /* set up hotplug events */ if (flags & FU_BACKEND_SETUP_FLAG_USE_HOTPLUG) { if (!fu_udev_backend_netlink_setup(self, error)) { - g_prefix_error(error, "failed to set up netlink: "); + g_prefix_error_literal(error, "failed to set up netlink: "); return FALSE; } } @@ -632,7 +733,6 @@ GError **error) { FuUdevBackend *self = FU_UDEV_BACKEND(backend); - g_autofree gchar *devtype_new = NULL; g_autofree gchar *sysfs_path = NULL; g_autoptr(FuUdevDevice) device_new = NULL; @@ -737,6 +837,7 @@ if (self->netlink_fd > 0) g_close(self->netlink_fd, NULL); g_hash_table_unref(self->map_paths); + g_hash_table_unref(self->coldplug_cache); g_ptr_array_unref(self->dpaux_devices); G_OBJECT_CLASS(fu_udev_backend_parent_class)->finalize(object); } @@ -745,6 +846,11 @@ fu_udev_backend_init(FuUdevBackend *self) { self->map_paths = g_hash_table_new_full(g_str_hash, g_str_equal, g_free, NULL); + self->coldplug_cache = + g_hash_table_new_full(g_str_hash, + g_str_equal, + g_free, + (GDestroyNotify)fu_udev_backend_coldplug_cache_item_free); self->dpaux_devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); } diff -Nru fwupd-2.0.8/src/fu-unix-seekable-input-stream.c fwupd-2.0.20/src/fu-unix-seekable-input-stream.c --- fwupd-2.0.8/src/fu-unix-seekable-input-stream.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-unix-seekable-input-stream.c 2026-02-26 11:36:18.000000000 +0000 @@ -8,6 +8,8 @@ #include "config.h" +#include "fwupd-error.h" + #include "fu-unix-seekable-input-stream.h" struct _FuUnixSeekableInputStream { @@ -29,7 +31,7 @@ { goffset rc = lseek(g_unix_input_stream_get_fd(G_UNIX_INPUT_STREAM(seekable)), 0, SEEK_CUR); if (rc < 0) - g_critical("cannot tell FuUnixSeekableInputStream: %s", g_strerror(errno)); + g_critical("cannot tell FuUnixSeekableInputStream: %s", fwupd_strerror(errno)); return rc; } @@ -75,7 +77,7 @@ G_IO_ERROR, /* nocheck:error */ g_io_error_from_errno(errno), "Error seeking file descriptor: %s", - g_strerror(errno)); + fwupd_strerror(errno)); return FALSE; } return TRUE; diff -Nru fwupd-2.0.8/src/fu-usb-backend.c fwupd-2.0.20/src/fu-usb-backend.c --- fwupd-2.0.8/src/fu-usb-backend.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-usb-backend.c 2026-02-26 11:36:18.000000000 +0000 @@ -9,9 +9,15 @@ #include "config.h" +#include + +#include + #include "fu-context-private.h" #include "fu-usb-backend.h" +#ifndef HAVE_UDEV #include "fu-usb-device-private.h" +#endif struct _FuUsbBackend { FuBackend parent_instance; @@ -56,6 +62,55 @@ NULL); } +#ifndef HAVE_UDEV +static FuDevice * +fu_usb_backend_create_device_impl(FuBackend *backend, const gchar *backend_id, GError **error) +{ + FuUsbBackend *self = FU_USB_BACKEND(backend); + FuContext *ctx = fu_backend_get_context(backend); + guint64 usb_addr = 0; + guint64 usb_bus = 0; + libusb_device **dev_list = NULL; + g_auto(GStrv) bus_addr = NULL; + g_autoptr(FuUsbDevice) usb_device = NULL; + + /* back from bus:addr */ + bus_addr = g_strsplit(backend_id, ":", 2); + if (!fu_strtoull(bus_addr[0], &usb_bus, 0x0, G_MAXUINT8, FU_INTEGER_BASE_16, error)) { + g_prefix_error(error, "failed to parse bus from %s: ", backend_id); + return NULL; + } + if (!fu_strtoull(bus_addr[1], &usb_addr, 0x0, G_MAXUINT8, FU_INTEGER_BASE_16, error)) { + g_prefix_error(error, "failed to parse addr from %s: ", backend_id); + return NULL; + } + + /* find in the current device list */ + libusb_get_device_list(self->ctx, &dev_list); + for (guint i = 0; dev_list != NULL && dev_list[i] != NULL; i++) { + if (libusb_get_bus_number(dev_list[i]) == usb_bus && + libusb_get_device_address(dev_list[i]) == usb_addr) { + usb_device = fu_usb_device_new(ctx, dev_list[i]); + break; + } + } + libusb_free_device_list(dev_list, 1); + + /* did not find */ + if (usb_device == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + "%s was not found", + backend_id); + return NULL; + } + + /* success */ + return FU_DEVICE(g_steal_pointer(&usb_device)); +} +#endif + static void fu_usb_backend_add_device(FuUsbBackend *self, libusb_device *usb_device) { @@ -275,15 +330,11 @@ gint log_level = g_getenv("FWUPD_VERBOSE") != NULL ? 3 : 0; gint rc; -#ifdef HAVE_LIBUSB_INIT_CONTEXT -#ifdef HAVE_UDEV +#if defined(HAVE_LIBUSB_INIT_CONTEXT) && defined(HAVE_UDEV) const struct libusb_init_option options[] = {{.option = LIBUSB_OPTION_NO_DEVICE_DISCOVERY, .value = { .ival = 1, }}}; -#else - const struct libusb_init_option options[] = {}; -#endif rc = libusb_init_context(&self->ctx, options, G_N_ELEMENTS(options)); #else rc = libusb_init(&self->ctx); @@ -303,7 +354,6 @@ libusb_set_debug(self->ctx, log_level); #endif fu_context_set_data(ctx, "libusb_context", self->ctx); - fu_context_add_udev_subsystem(ctx, "usb", NULL); /* no hotplug required, probably in tests */ if ((flags & FU_BACKEND_SETUP_FLAG_USE_HOTPLUG) == 0) @@ -369,6 +419,15 @@ #endif } +/* not defined in FreeBSD */ +#ifndef HAVE_LIBUSB_GET_PARENT +static libusb_device * +libusb_get_parent(libusb_device *dev) /* nocheck:name */ +{ + return NULL; +} +#endif + static FuDevice * fu_usb_backend_get_device_parent(FuBackend *backend, FuDevice *device, @@ -449,6 +508,7 @@ backend_class->coldplug = fu_usb_backend_coldplug; backend_class->registered = fu_usb_backend_registered; backend_class->get_device_parent = fu_usb_backend_get_device_parent; + backend_class->create_device = fu_usb_backend_create_device_impl; #endif } diff -Nru fwupd-2.0.8/src/fu-util-common.c fwupd-2.0.20/src/fu-util-common.c --- fwupd-2.0.8/src/fu-util-common.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-util-common.c 2026-02-26 11:36:18.000000000 +0000 @@ -10,12 +10,10 @@ #include #include -#include #include #include #include "fu-console.h" -#include "fu-device-private.h" #include "fu-util-common.h" static gchar * @@ -139,19 +137,20 @@ } static gboolean -fu_util_is_interesting_child(FwupdDevice *dev) +fu_util_is_interesting_child(GPtrArray *devs, FwupdDevice *dev) { - GPtrArray *children = fwupd_device_get_children(dev); - for (guint i = 0; i < children->len; i++) { - FwupdDevice *child = g_ptr_array_index(children, i); - if (fu_util_is_interesting_device(child)) + for (guint i = 0; i < devs->len; i++) { + FwupdDevice *child = g_ptr_array_index(devs, i); + if (fwupd_device_get_parent(child) != dev) + continue; + if (fu_util_is_interesting_device(devs, child)) return TRUE; } return FALSE; } gboolean -fu_util_is_interesting_device(FwupdDevice *dev) +fu_util_is_interesting_device(GPtrArray *devs, FwupdDevice *dev) { if (fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE)) return TRUE; @@ -162,7 +161,7 @@ /* device not plugged in, get-details */ if (fwupd_device_get_flags(dev) == 0) return TRUE; - if (fu_util_is_interesting_child(dev)) + if (fu_util_is_interesting_child(devs, dev)) return TRUE; return FALSE; } @@ -474,6 +473,7 @@ } else { /* TRANSLATORS: this is a command alias, e.g. 'get-devices' */ item->description = g_strdup_printf(_("Alias to %s"), names[0]); + item->flags |= FU_UTIL_CMD_FLAG_IS_ALIAS; } item->arguments = g_strdup(arguments); item->callback = callback; @@ -483,7 +483,7 @@ gboolean fu_util_cmd_array_run(GPtrArray *array, - FuUtilPrivate *priv, + FuUtil *self, const gchar *command, gchar **values, GError **error) @@ -497,11 +497,22 @@ values_copy[i] = g_strdup(values[i]); } + /* return all possible actions */ + if (g_strcmp0(command, "get-actions") == 0) { + for (guint i = 0; i < array->len; i++) { + FuUtilCmd *item = g_ptr_array_index(array, i); + if (item->flags & FU_UTIL_CMD_FLAG_IS_ALIAS) + continue; + g_print("%s\n", item->name); /* nocheck:print */ + } + return TRUE; + } + /* find command */ for (guint i = 0; i < array->len; i++) { FuUtilCmd *item = g_ptr_array_index(array, i); if (g_strcmp0(item->name, command) == 0) - return item->callback(priv, values_copy, error); + return item->callback(self, values_copy, error); } /* not found */ @@ -620,7 +631,7 @@ } if (g_strcmp0(cat, "X-CpuMicrocode") == 0) { /* TRANSLATORS: the CPU microcode is firmware loaded onto the CPU - * at system bootup */ + * at system boot-up */ return g_strdup_printf(_("%s CPU Microcode Update"), name); } if (g_strcmp0(cat, "X-Battery") == 0) { @@ -1002,9 +1013,8 @@ static gchar * fu_util_device_flag_to_string(guint64 device_flag) { - if (device_flag == FWUPD_DEVICE_FLAG_NONE) { + if (device_flag == FWUPD_DEVICE_FLAG_NONE) return NULL; - } if (device_flag == FWUPD_DEVICE_FLAG_INTERNAL) { /* TRANSLATORS: Device cannot be removed easily*/ return _("Internal device"); @@ -1154,14 +1164,13 @@ /* TRANSLATORS: we can save all device enumeration events for emulation */ return _("Can tag for emulation"); } - if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) { + if (device_flag == FWUPD_DEVICE_FLAG_UNKNOWN) return NULL; - } return NULL; } -static gchar * -fu_util_request_flag_to_string(guint64 request_flag) +const gchar * +fu_util_request_flag_to_string(FwupdRequestFlags request_flag) { if (request_flag == FWUPD_REQUEST_FLAG_NONE) return NULL; @@ -1261,7 +1270,7 @@ return g_strdup(_("Device is emulated")); } if (problem == FWUPD_DEVICE_PROBLEM_MISSING_LICENSE) { - /* TRANSLATORS: The device cannot be updated due to missing vendor's license." */ + /* TRANSLATORS: The device cannot be updated due to missing vendor's license. */ return g_strdup(_("Device requires a software license to update")); } if (problem == FWUPD_DEVICE_PROBLEM_SYSTEM_INHIBIT) { @@ -1284,6 +1293,14 @@ /* TRANSLATORS: we have two ways of communicating with the device, so we hide one */ return g_strdup(_("Device is lower priority than an equivalent device")); } + if (problem == FWUPD_DEVICE_PROBLEM_INSECURE_PLATFORM) { + /* TRANSLATORS: firmware is signed with insecure key */ + return g_strdup(_("System has been signed with an insecure key")); + } + if (problem == FWUPD_DEVICE_PROBLEM_FIRMWARE_LOCKED) { + /* TRANSLATORS: firmware is locked from the BIOS */ + return g_strdup(_("Device firmware has been locked ")); + } return NULL; } @@ -1624,6 +1641,10 @@ /* TRANSLATORS: The plugin is only for testing */ return g_strdup(_("Plugin is only for testing")); } + if (plugin_flag == FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION) { + /* TRANSLATORS: The plugin enumeration might change the device current mode */ + return g_strdup(_("Plugin enumeration may change device state")); + } /* fall back for unknown types */ return g_strdup(fwupd_plugin_flag_to_string(plugin_flag)); @@ -1648,7 +1669,8 @@ case FWUPD_PLUGIN_FLAG_DISABLED: case FWUPD_PLUGIN_FLAG_NO_HARDWARE: case FWUPD_PLUGIN_FLAG_TEST_ONLY: - return fu_console_color_format(plugin_flag_str, FU_CONSOLE_COLOR_BLACK); + case FWUPD_PLUGIN_FLAG_MUTABLE_ENUMERATION: + return fu_console_color_format(plugin_flag_str, FU_CONSOLE_COLOR_YELLOW); case FWUPD_PLUGIN_FLAG_LEGACY_BIOS: case FWUPD_PLUGIN_FLAG_CAPSULES_UNSUPPORTED: case FWUPD_PLUGIN_FLAG_UNLOCK_REQUIRED: @@ -1753,7 +1775,7 @@ return _("Unknown"); } -static const gchar * +const gchar * fu_util_release_flag_to_string(FwupdReleaseFlags release_flag) { if (release_flag == FWUPD_RELEASE_FLAG_NONE) @@ -2045,8 +2067,7 @@ fwupd_codec_string_append(str, idt + 1, _("Checksum"), fwupd_remote_get_checksum(remote)); /* optional parameters */ - if (kind == FWUPD_REMOTE_KIND_DOWNLOAD && fwupd_remote_get_age(remote) > 0 && - fwupd_remote_get_age(remote) != G_MAXUINT64) { + if (kind == FWUPD_REMOTE_KIND_DOWNLOAD && fwupd_remote_get_age(remote) != G_MAXUINT64) { g_autofree gchar *age_str = fu_util_time_to_str(fwupd_remote_get_age(remote)); /* TRANSLATORS: the age of the metadata */ fwupd_codec_string_append(str, idt + 1, _("Age"), age_str); diff -Nru fwupd-2.0.8/src/fu-util-common.h fwupd-2.0.20/src/fu-util-common.h --- fwupd-2.0.8/src/fu-util-common.h 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-util-common.h 2026-02-26 11:36:18.000000000 +0000 @@ -10,8 +10,6 @@ #include -#include "fwupd-security-attr-private.h" - #include "fu-console.h" /* custom return codes */ @@ -22,13 +20,18 @@ /* this is only valid for tools */ #define FWUPD_ERROR_INVALID_ARGS (FWUPD_ERROR_LAST + 1) -typedef struct FuUtilPrivate FuUtilPrivate; -typedef gboolean (*FuUtilCmdFunc)(FuUtilPrivate *util, gchar **values, GError **error) - G_GNUC_NON_NULL(1); +typedef enum { + FU_UTIL_CMD_FLAG_NONE = 0, + FU_UTIL_CMD_FLAG_IS_ALIAS = 1 << 0, +} G_GNUC_FLAG_ENUM FuUtilCmdFlags; + +typedef struct FuUtil FuUtil; +typedef gboolean (*FuUtilCmdFunc)(FuUtil *util, gchar **values, GError **error) G_GNUC_NON_NULL(1); typedef struct { gchar *name; gchar *arguments; gchar *description; + FuUtilCmdFlags flags; FuUtilCmdFunc callback; } FuUtilCmd; @@ -38,7 +41,7 @@ FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS = 1 << 1, /*< private >*/ FU_SECURITY_ATTR_TO_STRING_FLAG_LAST -} FuSecurityAttrToStringFlags; +} G_GNUC_FLAG_ENUM FuSecurityAttrToStringFlags; /* node with refcounted data */ typedef GNode FuUtilNode; @@ -49,7 +52,7 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilNode, fu_util_free_node) gboolean -fu_util_is_interesting_device(FwupdDevice *dev) G_GNUC_NON_NULL(1); +fu_util_is_interesting_device(GPtrArray *devs, FwupdDevice *dev) G_GNUC_NON_NULL(1, 2); gchar * fu_util_get_user_cache_path(const gchar *fn) G_GNUC_NON_NULL(1); @@ -86,7 +89,7 @@ fu_util_cmd_array_sort(GPtrArray *array) G_GNUC_NON_NULL(1); gboolean fu_util_cmd_array_run(GPtrArray *array, - FuUtilPrivate *priv, + FuUtil *self, const gchar *command, gchar **values, GError **error) G_GNUC_NON_NULL(1, 2); @@ -111,6 +114,10 @@ fu_util_plugin_to_string(FwupdPlugin *plugin, guint idt) G_GNUC_NON_NULL(1); gchar * fu_util_plugin_flag_to_string(FwupdPluginFlags plugin_flag); +const gchar * +fu_util_release_flag_to_string(FwupdReleaseFlags release_flag); +const gchar * +fu_util_request_flag_to_string(FwupdRequestFlags request_flag); gint fu_util_plugin_name_sort_cb(FwupdPlugin **item1, FwupdPlugin **item2); gchar * diff -Nru fwupd-2.0.8/src/fu-util.c fwupd-2.0.20/src/fu-util.c --- fwupd-2.0.8/src/fu-util.c 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fu-util.c 2026-02-26 11:36:18.000000000 +0000 @@ -21,12 +21,7 @@ #include #include -#include "fwupd-common-private.h" -#include "fwupd-device-private.h" -#include "fwupd-remote-private.h" - #include "fu-console.h" -#include "fu-plugin-private.h" #include "fu-polkit-agent.h" #include "fu-util-bios-setting.h" #include "fu-util-common.h" @@ -43,7 +38,7 @@ FU_UTIL_OPERATION_LAST } FuUtilOperation; -struct FuUtilPrivate { +struct FuUtil { GCancellable *cancellable; GMainContext *main_ctx; GMainLoop *loop; @@ -77,22 +72,22 @@ }; static gboolean -fu_util_report_history(FuUtilPrivate *priv, gchar **values, GError **error); +fu_util_report_history(FuUtil *self, gchar **values, GError **error); static FwupdDevice * -fu_util_get_device_by_id(FuUtilPrivate *priv, const gchar *id, GError **error); +fu_util_get_device_by_id(FuUtil *self, const gchar *id, GError **error); static void -fu_util_client_notify_cb(GObject *object, GParamSpec *pspec, FuUtilPrivate *priv) +fu_util_client_notify_cb(GObject *object, GParamSpec *pspec, FuUtil *self) { - if (priv->as_json) + if (self->as_json) return; - fu_console_set_progress(priv->console, - fwupd_client_get_status(priv->client), - fwupd_client_get_percentage(priv->client)); + fu_console_set_progress(self->console, + fwupd_client_get_status(self->client), + fwupd_client_get_percentage(self->client)); } static void -fu_util_update_device_request_cb(FwupdClient *client, FwupdRequest *request, FuUtilPrivate *priv) +fu_util_update_device_request_cb(FwupdClient *client, FwupdRequest *request, FuUtil *self) { /* nothing sensible to show */ if (fwupd_request_get_message(request) == NULL) @@ -106,35 +101,35 @@ /* TRANSLATORS: the user needs to do something, e.g. remove the device */ fmt = fu_console_color_format(_("Action Required:"), FU_CONSOLE_COLOR_RED); tmp = g_strdup_printf("%s %s", fmt, fwupd_request_get_message(request)); - fu_console_set_progress_title(priv->console, tmp); - fu_console_beep(priv->console, 5); + fu_console_set_progress_title(self->console, tmp); + fu_console_beep(self->console, 5); } /* save for later */ if (fwupd_request_get_kind(request) == FWUPD_REQUEST_KIND_POST) - g_ptr_array_add(priv->post_requests, g_object_ref(request)); + g_ptr_array_add(self->post_requests, g_object_ref(request)); } static void -fu_util_update_device_changed_cb(FwupdClient *client, FwupdDevice *device, FuUtilPrivate *priv) +fu_util_update_device_changed_cb(FwupdClient *client, FwupdDevice *device, FuUtil *self) { g_autofree gchar *str = NULL; /* action has not been assigned yet */ - if (priv->current_operation == FU_UTIL_OPERATION_UNKNOWN) + if (self->current_operation == FU_UTIL_OPERATION_UNKNOWN) return; /* allowed to set whenever the device has changed */ if (fwupd_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) - priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; if (fwupd_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) - priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; /* same as last time, so ignore */ - if (priv->current_device == NULL || - g_strcmp0(fwupd_device_get_composite_id(priv->current_device), + if (self->current_device == NULL || + g_strcmp0(fwupd_device_get_composite_id(self->current_device), fwupd_device_get_composite_id(device)) == 0) { - g_set_object(&priv->current_device, device); + g_set_object(&self->current_device, device); return; } @@ -148,26 +143,26 @@ } /* show message in console */ - if (priv->current_operation == FU_UTIL_OPERATION_UPDATE) { + if (self->current_operation == FU_UTIL_OPERATION_UPDATE) { /* TRANSLATORS: %1 is a device name */ str = g_strdup_printf(_("Updating %s…"), fwupd_device_get_name(device)); - fu_console_set_progress_title(priv->console, str); - } else if (priv->current_operation == FU_UTIL_OPERATION_DOWNGRADE) { + fu_console_set_progress_title(self->console, str); + } else if (self->current_operation == FU_UTIL_OPERATION_DOWNGRADE) { /* TRANSLATORS: %1 is a device name */ str = g_strdup_printf(_("Downgrading %s…"), fwupd_device_get_name(device)); - fu_console_set_progress_title(priv->console, str); - } else if (priv->current_operation == FU_UTIL_OPERATION_INSTALL) { + fu_console_set_progress_title(self->console, str); + } else if (self->current_operation == FU_UTIL_OPERATION_INSTALL) { /* TRANSLATORS: %1 is a device name */ str = g_strdup_printf(_("Installing on %s…"), fwupd_device_get_name(device)); - fu_console_set_progress_title(priv->console, str); + fu_console_set_progress_title(self->console, str); } else { g_warning("no FuUtilOperation set"); } - g_set_object(&priv->current_device, device); + g_set_object(&self->current_device, device); } static FwupdDevice * -fu_util_prompt_for_device(FuUtilPrivate *priv, GPtrArray *devices, GError **error) +fu_util_prompt_for_device(FuUtil *self, GPtrArray *devices, GError **error) { FwupdDevice *dev; guint idx; @@ -175,8 +170,8 @@ /* filter results */ devices_filtered = fwupd_device_array_filter_flags(devices, - priv->filter_device_include, - priv->filter_device_exclude, + self->filter_device_include, + self->filter_device_exclude, error); if (devices_filtered == NULL) return NULL; @@ -184,9 +179,9 @@ /* exactly one */ if (devices_filtered->len == 1) { dev = g_ptr_array_index(devices_filtered, 0); - if (!priv->as_json) { + if (!self->as_json) { fu_console_print( - priv->console, + self->console, "%s: %s", /* TRANSLATORS: device has been chosen by the daemon for the user */ _("Selected device"), @@ -196,7 +191,7 @@ } /* no questions */ - if (priv->no_device_prompt) { + if (self->no_device_prompt) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, @@ -205,17 +200,17 @@ } /* TRANSLATORS: this is to abort the interactive prompt */ - fu_console_print(priv->console, "0.\t%s", _("Cancel")); + fu_console_print(self->console, "0.\t%s", _("Cancel")); for (guint i = 0; i < devices_filtered->len; i++) { dev = g_ptr_array_index(devices_filtered, i); - fu_console_print(priv->console, + fu_console_print(self->console, "%u.\t%s (%s)", i + 1, fwupd_device_get_id(dev), fwupd_device_get_name(dev)); } /* TRANSLATORS: get interactive prompt */ - idx = fu_console_input_uint(priv->console, devices_filtered->len, "%s", _("Choose device")); + idx = fu_console_input_uint(self->console, devices_filtered->len, "%s", _("Choose device")); if (idx == 0) { g_set_error_literal(error, FWUPD_ERROR, @@ -228,7 +223,7 @@ } static gboolean -fu_util_perhaps_show_unreported(FuUtilPrivate *priv, GError **error) +fu_util_perhaps_show_unreported(FuUtil *self, GError **error) { g_autoptr(GError) error_local = NULL; g_autoptr(GPtrArray) devices = NULL; @@ -239,13 +234,13 @@ gboolean all_automatic = FALSE; /* we don't want to ask anything */ - if (priv->no_unreported_check || priv->as_json) { + if (self->no_unreported_check || self->as_json) { g_debug("skipping unreported check"); return TRUE; } /* get all devices from the history database */ - devices = fwupd_client_get_history(priv->client, priv->cancellable, &error_local); + devices = fwupd_client_get_history(self->client, self->cancellable, &error_local); if (devices == NULL) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) return TRUE; @@ -254,7 +249,7 @@ } /* create a map of RemoteID to RemoteURI */ - remotes = fwupd_client_get_remotes(priv->client, priv->cancellable, error); + remotes = fwupd_client_get_remotes(self->client, self->cancellable, error); if (remotes == NULL) return FALSE; remote_id_uri_map = g_hash_table_new(g_str_hash, g_str_equal); @@ -290,8 +285,8 @@ const gchar *remote_uri; if (!fwupd_device_match_flags(dev, - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; if (fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_REPORTED)) continue; @@ -328,21 +323,21 @@ return TRUE; } - g_debug("All automatic: %d", all_automatic); + g_debug("all automatic: %d", all_automatic); /* show the success and failures */ - if (!priv->assume_yes && !all_automatic) { + if (!self->assume_yes && !all_automatic) { /* delimit */ - fu_console_line(priv->console, 48); + fu_console_line(self->console, 48); /* failures */ if (devices_failed->len > 0) { - fu_console_print_literal(priv->console, + fu_console_print_literal(self->console, /* TRANSLATORS: a list of failed updates */ _("Devices that were not updated correctly:")); for (guint i = 0; i < devices_failed->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices_failed, i); FwupdRelease *rel = fwupd_device_get_release_default(dev); - fu_console_print(priv->console, + fu_console_print(self->console, " • %s (%s → %s)", fwupd_device_get_name(dev), fwupd_device_get_version(dev), @@ -352,13 +347,13 @@ /* success */ if (devices_success->len > 0) { - fu_console_print_literal(priv->console, + fu_console_print_literal(self->console, /* TRANSLATORS: a list of successful updates */ _("Devices that have been updated successfully:")); for (guint i = 0; i < devices_success->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices_success, i); FwupdRelease *rel = fwupd_device_get_release_default(dev); - fu_console_print(priv->console, + fu_console_print(self->console, " • %s (%s → %s)", fwupd_device_get_name(dev), fwupd_device_get_version(dev), @@ -367,19 +362,19 @@ } /* ask for permission */ - fu_console_print_literal(priv->console, + fu_console_print_literal(self->console, /* TRANSLATORS: explain why we want to upload */ _("Uploading firmware reports helps hardware vendors " "to quickly identify failing and successful updates " "on real devices.")); - if (!fu_console_input_bool(priv->console, + if (!fu_console_input_bool(self->console, TRUE, "%s (%s)", /* TRANSLATORS: ask the user to upload */ _("Review and upload report now?"), /* TRANSLATORS: metadata is downloaded */ _("Requires internet connection"))) { - if (fu_console_input_bool(priv->console, + if (fu_console_input_bool(self->console, FALSE, "%s", /* TRANSLATORS: offer to disable this nag */ @@ -390,11 +385,11 @@ const gchar *remote_id = fwupd_remote_get_id(remote); if (fwupd_remote_get_report_uri(remote) == NULL) continue; - if (!fwupd_client_modify_remote(priv->client, + if (!fwupd_client_modify_remote(self->client, remote_id, "ReportURI", "", - priv->cancellable, + self->cancellable, error)) return FALSE; } @@ -408,12 +403,12 @@ } /* upload */ - if (!fu_util_report_history(priv, NULL, error)) + if (!fu_util_report_history(self, NULL, error)) return FALSE; /* offer to make automatic */ - if (!priv->assume_yes && !all_automatic) { - if (fu_console_input_bool(priv->console, + if (!self->assume_yes && !all_automatic) { + if (fu_console_input_bool(self->console, FALSE, "%s", /* TRANSLATORS: offer to stop asking the question */ @@ -427,11 +422,11 @@ if (fwupd_remote_has_flag(remote, FWUPD_REMOTE_FLAG_AUTOMATIC_REPORTS)) continue; - if (!fwupd_client_modify_remote(priv->client, + if (!fwupd_client_modify_remote(self->client, remote_id, "AutomaticReports", "true", - priv->cancellable, + self->cancellable, error)) return FALSE; } @@ -443,78 +438,25 @@ } static void -fu_util_build_device_tree_node(FuUtilPrivate *priv, FuUtilNode *root, FwupdDevice *dev) -{ - FuUtilNode *root_child = g_node_append_data(root, g_object_ref(dev)); - if (fwupd_device_get_release_default(dev) != NULL) - g_node_append_data(root_child, g_object_ref(fwupd_device_get_release_default(dev))); -} - -static gboolean -fu_util_build_device_tree_cb(FuUtilNode *n, gpointer user_data) -{ - FuUtilPrivate *priv = (FuUtilPrivate *)user_data; - FwupdDevice *dev = n->data; - - /* root node */ - if (dev == NULL) - return FALSE; - - /* release */ - if (FWUPD_IS_RELEASE(n->data)) - return FALSE; - - /* an interesting child, so include the parent */ - for (FuUtilNode *c = n->children; c != NULL; c = c->next) { - if (c->data != NULL) - return FALSE; - } - - /* not interesting, clear the node data */ - if (!fwupd_device_match_flags(dev, - priv->filter_device_include, - priv->filter_device_exclude)) - g_clear_object(&n->data); - else if (!priv->show_all && !fu_util_is_interesting_device(dev)) - g_clear_object(&n->data); - - /* continue */ - return FALSE; -} - -static void -fu_util_build_device_tree(FuUtilPrivate *priv, FuUtilNode *root, GPtrArray *devs) +fu_util_build_device_tree(FuUtil *self, FuUtilNode *root, GPtrArray *devs, FwupdDevice *dev) { - /* add the top-level parents */ for (guint i = 0; i < devs->len; i++) { FwupdDevice *dev_tmp = g_ptr_array_index(devs, i); - if (fwupd_device_get_parent(dev_tmp) != NULL) + if (!fwupd_device_match_flags(dev_tmp, + self->filter_device_include, + self->filter_device_exclude)) continue; - fu_util_build_device_tree_node(priv, root, dev_tmp); - } - - /* children */ - for (guint i = 0; i < devs->len; i++) { - FwupdDevice *dev_tmp = g_ptr_array_index(devs, i); - FuUtilNode *root_parent; - - if (fwupd_device_get_parent(dev_tmp) == NULL) + if (!self->show_all && !fu_util_is_interesting_device(devs, dev_tmp)) continue; - root_parent = g_node_find(root, - G_PRE_ORDER, - G_TRAVERSE_ALL, - fwupd_device_get_parent(dev_tmp)); - if (root_parent == NULL) - continue; - fu_util_build_device_tree_node(priv, root_parent, dev_tmp); + if (fwupd_device_get_parent(dev_tmp) == dev) { + FuUtilNode *child = g_node_append_data(root, g_object_ref(dev_tmp)); + fu_util_build_device_tree(self, child, devs, dev_tmp); + } } - - /* prune children that are not updatable */ - g_node_traverse(root, G_POST_ORDER, G_TRAVERSE_ALL, -1, fu_util_build_device_tree_cb, priv); } static gboolean -fu_util_get_releases_as_json(FuUtilPrivate *priv, GPtrArray *rels, GError **error) +fu_util_get_releases_as_json(FuUtil *self, GPtrArray *rels, GError **error) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); @@ -523,8 +465,8 @@ for (guint i = 0; i < rels->len; i++) { FwupdRelease *rel = g_ptr_array_index(rels, i); if (!fwupd_release_match_flags(rel, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; json_builder_begin_object(builder); fwupd_codec_to_json(FWUPD_CODEC(rel), builder, FWUPD_CODEC_FLAG_NONE); @@ -532,11 +474,11 @@ } json_builder_end_array(builder); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } static gboolean -fu_util_get_devices_as_json(FuUtilPrivate *priv, GPtrArray *devs, GError **error) +fu_util_get_devices_as_json(FuUtil *self, GPtrArray *devs, GError **error) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); @@ -549,14 +491,14 @@ /* filter */ if (!fwupd_device_match_flags(dev, - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; /* add all releases that could be applied */ - rels = fwupd_client_get_releases(priv->client, + rels = fwupd_client_get_releases(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, &error_local); if (rels == NULL) { g_debug("not adding releases to device: %s", error_local->message); @@ -564,8 +506,8 @@ for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel = g_ptr_array_index(rels, j); if (!fwupd_release_match_flags(rel, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; fwupd_device_add_release(dev, rel); } @@ -578,11 +520,57 @@ } json_builder_end_array(builder); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } static gboolean -fu_util_get_devices(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_check_reboot_needed(FuUtil *self, gchar **values, GError **error) +{ + /* handle both forms */ + if (g_strv_length(values) == 0) { + g_autoptr(GPtrArray) devices = + fwupd_client_get_devices(self->client, self->cancellable, error); + if (devices == NULL) + return FALSE; + for (guint i = 0; i < devices->len; i++) { + FwupdDevice *device = g_ptr_array_index(devices, i); + + if (fwupd_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + if (fwupd_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; + } + } else { + for (guint i = 0; values[i] != NULL; i++) { + g_autoptr(FwupdDevice) device = NULL; + device = fu_util_get_device_by_id(self, values[i], error); + if (device == NULL) + return FALSE; + if (fwupd_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + if (fwupd_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; + } + } + + if (!(self->completion_flags & + (FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN | FWUPD_DEVICE_FLAG_NEEDS_REBOOT))) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + /* TRANSLATORS: no rebooting needed */ + _("No reboot is necessary")); + return FALSE; + } + + if (self->as_json) + return TRUE; + + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); +} + +static gboolean +fu_util_get_devices(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuUtilNode) root = g_node_new(NULL); g_autoptr(GPtrArray) devs = NULL; @@ -591,24 +579,24 @@ if (g_strv_length(values) > 0) { devs = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); for (guint i = 0; values[i] != NULL; i++) { - FwupdDevice *device = fu_util_get_device_by_id(priv, values[i], error); + FwupdDevice *device = fu_util_get_device_by_id(self, values[i], error); if (device == NULL) return FALSE; g_ptr_array_add(devs, device); } } else { - devs = fwupd_client_get_devices(priv->client, priv->cancellable, error); + devs = fwupd_client_get_devices(self->client, self->cancellable, error); if (devs == NULL) return FALSE; } /* not for human consumption */ - if (priv->as_json) - return fu_util_get_devices_as_json(priv, devs, error); + if (self->as_json) + return fu_util_get_devices_as_json(self, devs, error); /* print */ if (devs->len > 0) - fu_util_build_device_tree(priv, root, devs); + fu_util_build_device_tree(self, root, devs, NULL); if (g_node_n_children(root) == 0) { g_set_error_literal(error, FWUPD_ERROR, @@ -617,38 +605,38 @@ _("No hardware detected with firmware update capability")); return FALSE; } - fu_util_print_node(priv->console, priv->client, root); + fu_util_print_node(self->console, self->client, root); /* nag? */ - if (!fu_util_perhaps_show_unreported(priv, error)) + if (!fu_util_perhaps_show_unreported(self, error)) return FALSE; return TRUE; } static gboolean -fu_util_get_plugins(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_plugins(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) plugins = NULL; /* get results from daemon */ - plugins = fwupd_client_get_plugins(priv->client, priv->cancellable, error); + plugins = fwupd_client_get_plugins(self->client, self->cancellable, error); g_ptr_array_sort(plugins, (GCompareFunc)fu_util_plugin_name_sort_cb); if (plugins == NULL) return FALSE; - if (priv->as_json) { + if (self->as_json) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); fwupd_codec_array_to_json(plugins, "Plugins", builder, FWUPD_CODEC_FLAG_TRUSTED); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } /* print */ for (guint i = 0; i < plugins->len; i++) { FuPlugin *plugin = g_ptr_array_index(plugins, i); g_autofree gchar *str = fu_util_plugin_to_string(FWUPD_PLUGIN(plugin), 0); - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); } /* success */ @@ -656,7 +644,7 @@ } static gchar * -fu_util_download_if_required(FuUtilPrivate *priv, const gchar *perhapsfn, GError **error) +fu_util_download_if_required(FuUtil *self, const gchar *perhapsfn, GError **error) { g_autofree gchar *filename = NULL; g_autoptr(GBytes) blob = NULL; @@ -673,10 +661,10 @@ return g_steal_pointer(&filename); if (!fu_path_mkdir_parent(filename, error)) return NULL; - blob = fwupd_client_download_bytes(priv->client, + blob = fwupd_client_download_bytes(self->client, perhapsfn, - priv->download_flags, - priv->cancellable, + self->download_flags, + self->cancellable, error); if (blob == NULL) return NULL; @@ -688,15 +676,18 @@ } static void -fu_util_display_current_message(FuUtilPrivate *priv) +fu_util_display_current_message(FuUtil *self) { + if (self->as_json) + return; + /* TRANSLATORS: success message */ - fu_console_print_literal(priv->console, _("Successfully installed firmware")); + fu_console_print_literal(self->console, _("Successfully installed firmware")); /* print all POST requests */ - for (guint i = 0; i < priv->post_requests->len; i++) { - FwupdRequest *request = g_ptr_array_index(priv->post_requests, i); - fu_console_print_literal(priv->console, fu_util_request_get_message(request)); + for (guint i = 0; i < self->post_requests->len; i++) { + FwupdRequest *request = g_ptr_array_index(self->post_requests, i); + fu_console_print_literal(self->console, fu_util_request_get_message(request)); } } @@ -731,15 +722,15 @@ G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilDeviceTestHelper, fu_util_device_test_helper_free) static GPtrArray * -fu_util_filter_devices(FuUtilPrivate *priv, GPtrArray *devices, GError **error) +fu_util_filter_devices(FuUtil *self, GPtrArray *devices, GError **error) { g_autoptr(GPtrArray) devices_filtered = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices, i); if (!fwupd_device_match_flags(dev, - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; g_ptr_array_add(devices_filtered, g_object_ref(dev)); } @@ -754,7 +745,7 @@ } static gboolean -fu_util_device_test_component(FuUtilPrivate *priv, +fu_util_device_test_component(FuUtil *self, FuUtilDeviceTestHelper *helper, JsonObject *json_obj, GError **error) @@ -796,10 +787,10 @@ g_debug("looking for guid %s", guid); devices = - fwupd_client_get_devices_by_guid(priv->client, guid, priv->cancellable, NULL); + fwupd_client_get_devices_by_guid(self->client, guid, self->cancellable, NULL); if (devices == NULL) continue; - devices_filtered = fu_util_filter_devices(priv, devices, NULL); + devices_filtered = fu_util_filter_devices(self, devices, NULL); if (devices_filtered == NULL) continue; if (devices_filtered->len > 1) { @@ -819,13 +810,13 @@ } json_builder_end_array(helper->builder); if (device == NULL) { - if (!priv->as_json) { + if (!self->as_json) { g_autofree gchar *msg = NULL; msg = fu_console_color_format( /* TRANSLATORS: this is for the device tests */ _("Did not find any devices with matching GUIDs"), FU_CONSOLE_COLOR_RED); - fu_console_print(priv->console, "%s: %s", name, msg); + fu_console_print(self->console, "%s: %s", name, msg); } g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND, "no devices found"); return FALSE; @@ -875,14 +866,14 @@ } /* success */ - if (!priv->as_json) { + if (!self->as_json) { g_autofree gchar *msg = NULL; /* TRANSLATORS: this is for the device tests */ msg = fu_console_color_format(_("OK!"), FU_CONSOLE_COLOR_GREEN); if (g_strcmp0(name, "component") != 0) { - fu_console_print(priv->console, "%s [%s]: %s", helper->name, name, msg); + fu_console_print(self->console, "%s [%s]: %s", helper->name, name, msg); } else { - fu_console_print(priv->console, "%s: %s", helper->name, msg); + fu_console_print(self->console, "%s: %s", helper->name, msg); } } helper->nr_success++; @@ -890,11 +881,11 @@ } static gboolean -fu_util_device_test_remove_emulated_devices(FuUtilPrivate *priv, GError **error) +fu_util_device_test_remove_emulated_devices(FuUtil *self, GError **error) { g_autoptr(GPtrArray) devices = NULL; - devices = fwupd_client_get_devices(priv->client, priv->cancellable, error); + devices = fwupd_client_get_devices(self->client, self->cancellable, error); if (devices == NULL) return FALSE; for (guint i = 0; i < devices->len; i++) { @@ -902,11 +893,11 @@ g_autoptr(GError) error_local = NULL; if (!fwupd_device_has_flag(device, FWUPD_DEVICE_FLAG_EMULATED)) continue; - if (!fwupd_client_modify_device(priv->client, + if (!fwupd_client_modify_device(self->client, fwupd_device_get_id(device), "Flags", "~emulated", - priv->cancellable, + self->cancellable, &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { g_debug("ignoring: %s", error_local->message); @@ -923,25 +914,51 @@ return TRUE; } +static gchar * +fu_util_maybe_expand_basename(FuUtil *self, const gchar *maybe_basename, GError **error) +{ + g_autoptr(FwupdRemote) remote = NULL; + + if (g_str_has_prefix(maybe_basename, "https://")) + return g_strdup(maybe_basename); + if (g_str_has_prefix(maybe_basename, "/")) + return g_strdup(maybe_basename); + + /* find LVFS remote */ + remote = fwupd_client_get_remote_by_id(self->client, "lvfs", self->cancellable, error); + if (remote == NULL) + return NULL; + if (fwupd_remote_get_firmware_base_uri(remote)) { + g_debug("no FirmwareBaseURI set in lvfs.conf, using default"); + return g_strdup_printf("https://fwupd.org/downloads/%s", maybe_basename); + } + return g_strdup_printf("%s/%s", fwupd_remote_get_firmware_base_uri(remote), maybe_basename); +} + static gboolean -fu_util_device_test_step(FuUtilPrivate *priv, +fu_util_device_test_step(FuUtil *self, FuUtilDeviceTestHelper *helper, JsonObject *json_obj, GError **error) { JsonArray *json_array; - const gchar *emulation_url = NULL; /* send this data to the daemon */ if (helper->use_emulation) { g_autofree gchar *emulation_filename = NULL; + g_autofree gchar *emulation_url = NULL; + g_autoptr(GError) error_local = NULL; /* just ignore anything without emulation data */ if (json_object_has_member(json_obj, "emulation-url")) { - emulation_url = json_object_get_string_member(json_obj, "emulation-url"); + const gchar *url_tmp = + json_object_get_string_member(json_obj, "emulation-url"); + emulation_url = fu_util_maybe_expand_basename(self, url_tmp, error); + if (emulation_url == NULL) + return FALSE; emulation_filename = - fu_util_download_if_required(priv, emulation_url, error); + fu_util_download_if_required(self, emulation_url, error); if (emulation_filename == NULL) { g_prefix_error(error, "failed to download %s: ", emulation_url); return FALSE; @@ -960,22 +977,34 @@ } json_builder_set_member_name(helper->builder, "emulation-file"); json_builder_add_string_value(helper->builder, emulation_filename); - if (!fwupd_client_emulation_load(priv->client, + if (!fwupd_client_emulation_load(self->client, emulation_filename, - priv->cancellable, - error)) { - g_prefix_error(error, "failed to load %s: ", emulation_filename); + self->cancellable, + &error_local)) { + if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { + g_debug("ignoring: %s", error_local->message); + helper->nr_skipped++; + return TRUE; + } + g_propagate_prefixed_error(error, + g_steal_pointer(&error_local), + "failed to load %s: ", + emulation_filename); return FALSE; } } /* download file if required */ if (json_object_has_member(json_obj, "url")) { - const gchar *url = json_object_get_string_member(json_obj, "url"); + const gchar *url_tmp = json_object_get_string_member(json_obj, "url"); g_autofree gchar *filename = NULL; + g_autofree gchar *url = NULL; g_autoptr(GError) error_local = NULL; - filename = fu_util_download_if_required(priv, url, error); + url = fu_util_maybe_expand_basename(self, url_tmp, error); + if (url == NULL) + return FALSE; + filename = fu_util_download_if_required(self, url, error); if (filename == NULL) { g_prefix_error(error, "failed to download %s: ", url); return FALSE; @@ -986,16 +1015,16 @@ json_builder_add_string_value(helper->builder, url); /* install file */ - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; - if (!fwupd_client_install(priv->client, + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + if (!fwupd_client_install(self->client, FWUPD_DEVICE_ID_ANY, filename, - priv->flags, - priv->cancellable, + self->flags, + self->cancellable, &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_FOUND)) { - if (priv->as_json) { + if (self->as_json) { json_builder_set_member_name(helper->builder, "info"); json_builder_add_string_value(helper->builder, error_local->message); @@ -1003,7 +1032,7 @@ g_autofree gchar *msg = NULL; msg = fu_console_color_format(error_local->message, FU_CONSOLE_COLOR_YELLOW); - fu_console_print(priv->console, + fu_console_print(self->console, "%s: %s", helper->name, msg); @@ -1028,14 +1057,14 @@ for (guint i = 0; i < json_array_get_length(json_array); i++) { JsonNode *json_node = json_array_get_element(json_array, i); JsonObject *json_obj_tmp = json_node_get_object(json_node); - if (!fu_util_device_test_component(priv, helper, json_obj_tmp, error)) + if (!fu_util_device_test_component(self, helper, json_obj_tmp, error)) return FALSE; } /* remove emulated devices */ if (helper->use_emulation) { - if (!fu_util_device_test_remove_emulated_devices(priv, error)) { - g_prefix_error(error, "failed to remove emulated devices: "); + if (!fu_util_device_test_remove_emulated_devices(self, error)) { + g_prefix_error_literal(error, "failed to remove emulated devices: "); return FALSE; } } @@ -1047,7 +1076,7 @@ } static gboolean -fu_util_device_test_filename(FuUtilPrivate *priv, +fu_util_device_test_filename(FuUtil *self, FuUtilDeviceTestHelper *helper, const gchar *filename, GError **error) @@ -1064,7 +1093,7 @@ /* parse JSON */ if (!json_parser_load_from_file(parser, filename, error)) { - g_prefix_error(error, "test not in JSON format: "); + g_prefix_error_literal(error, "test not in JSON format: "); return FALSE; } json_root = json_parser_get_root(parser); @@ -1119,6 +1148,24 @@ return TRUE; } } + if (json_object_has_member(json_obj, "platform-architectures")) { + JsonArray *json_array = + json_object_get_array_member(json_obj, "platform-architectures"); + gboolean matched = FALSE; + const gchar *arch = + g_hash_table_lookup(helper->report_metadata, "PlatformArchitecture"); + for (guint i = 0; i < json_array_get_length(json_array); i++) { + const gchar *arch_tmp = json_array_get_string_element(json_array, i); + if (g_strcmp0(arch, arch_tmp) == 0) { + matched = TRUE; + break; + } + } + if (!matched) { + helper->nr_skipped++; + return TRUE; + } + } /* process each step */ if (json_object_has_member(json_obj, "repeat")) { @@ -1134,7 +1181,7 @@ JsonNode *json_node = json_array_get_element(json_array, i); json_obj = json_node_get_object(json_node); json_builder_begin_object(helper->builder); - if (!fu_util_device_test_step(priv, helper, json_obj, error)) + if (!fu_util_device_test_step(self, helper, json_obj, error)) return FALSE; json_builder_end_object(helper->builder); } @@ -1146,7 +1193,7 @@ } typedef struct { - FuUtilPrivate *priv; + FuUtil *self; gchar *inhibit_id; } FuUtilInhibitHelper; @@ -1162,21 +1209,21 @@ static gboolean fu_util_inhibit_timeout_cb(FuUtilInhibitHelper *helper) { - FuUtilPrivate *priv = helper->priv; + FuUtil *self = helper->self; g_autoptr(GError) error_local = NULL; - if (!fwupd_client_uninhibit(priv->client, + if (!fwupd_client_uninhibit(self->client, helper->inhibit_id, - priv->cancellable, + self->cancellable, &error_local)) { g_warning("failed to auto-uninhibit: %s", error_local->message); } - g_main_loop_quit(priv->loop); + g_main_loop_quit(self->loop); return G_SOURCE_REMOVE; } static gboolean -fu_util_inhibit(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_inhibit(FuUtil *self, gchar **values, GError **error) { const gchar *reason = "not set"; guint64 timeout_ms = 0; @@ -1196,8 +1243,8 @@ } /* inhibit then wait */ - helper->priv = priv; - helper->inhibit_id = fwupd_client_inhibit(priv->client, reason, priv->cancellable, error); + helper->self = self; + helper->inhibit_id = fwupd_client_inhibit(self->client, reason, self->cancellable, error); if (helper->inhibit_id == NULL) return FALSE; if (timeout_ms > 0) { @@ -1206,7 +1253,7 @@ (GSourceFunc)fu_util_inhibit_timeout_cb, helper, NULL); - g_source_attach(source, priv->main_ctx); + g_source_attach(source, self->main_ctx); } /* TRANSLATORS: the inhibit ID is a short string like dbus-123456 */ @@ -1222,13 +1269,13 @@ /* TRANSLATORS: CTRL^C [holding control, and then pressing C] will exit the program */ g_string_append(str, _("Use CTRL^C to cancel.")); /* TRANSLATORS: this CLI tool is now preventing system updates */ - fu_console_box(priv->console, _("System Update Inhibited"), str->str, 80); - g_main_loop_run(priv->loop); + fu_console_box(self->console, _("System Update Inhibited"), str->str, 80); + g_main_loop_run(self->loop); return TRUE; } static gboolean -fu_util_uninhibit(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_uninhibit(FuUtil *self, gchar **values, GError **error) { /* one argument required */ if (g_strv_length(values) != 1) { @@ -1240,11 +1287,11 @@ } /* just uninhibit with the token */ - return fwupd_client_uninhibit(priv->client, values[0], priv->cancellable, error); + return fwupd_client_uninhibit(self->client, values[0], self->cancellable, error); } typedef struct { - FuUtilPrivate *priv; + FuUtil *self; const gchar *value; FwupdDevice *device; /* no-ref */ } FuUtilWaitHelper; @@ -1252,11 +1299,11 @@ static void fu_util_device_wait_added_cb(FwupdClient *client, FwupdDevice *device, FuUtilWaitHelper *helper) { - FuUtilPrivate *priv = helper->priv; + FuUtil *self = helper->self; if (g_strcmp0(fwupd_device_get_id(device), helper->value) == 0 || fwupd_device_has_guid(device, helper->value)) { helper->device = device; - g_main_loop_quit(priv->loop); + g_main_loop_quit(self->loop); return; } } @@ -1264,19 +1311,19 @@ static gboolean fu_util_device_wait_timeout_cb(gpointer user_data) { - FuUtilPrivate *priv = (FuUtilPrivate *)user_data; - g_main_loop_quit(priv->loop); + FuUtil *self = (FuUtil *)user_data; + g_main_loop_quit(self->loop); return G_SOURCE_REMOVE; } static gboolean -fu_util_device_wait(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_device_wait(FuUtil *self, gchar **values, GError **error) { g_autoptr(FwupdDevice) device = NULL; g_autoptr(GPtrArray) devices = NULL; g_autoptr(GSource) source = g_timeout_source_new_seconds(30); g_autoptr(GTimer) timer = g_timer_new(); - FuUtilWaitHelper helper = {.priv = priv, .value = values[0]}; + FuUtilWaitHelper helper = {.self = self, .value = values[0]}; /* one argument required */ if (g_strv_length(values) != 1) { @@ -1288,28 +1335,28 @@ } /* check if the device already exists */ - device = fwupd_client_get_device_by_id(priv->client, helper.value, NULL, NULL); + device = fwupd_client_get_device_by_id(self->client, helper.value, NULL, NULL); if (device != NULL) { /* TRANSLATORS: the device is already connected */ - fu_console_print_literal(priv->console, _("Device already exists")); + fu_console_print_literal(self->console, _("Device already exists")); return TRUE; } - devices = fwupd_client_get_devices_by_guid(priv->client, helper.value, NULL, NULL); + devices = fwupd_client_get_devices_by_guid(self->client, helper.value, NULL, NULL); if (devices != NULL) { /* TRANSLATORS: the device is already connected */ - fu_console_print_literal(priv->console, _("Device already exists")); + fu_console_print_literal(self->console, _("Device already exists")); return TRUE; } /* wait for device to show up */ - fu_console_set_progress(priv->console, FWUPD_STATUS_IDLE, 0); - g_signal_connect(FWUPD_CLIENT(priv->client), + fu_console_set_progress(self->console, FWUPD_STATUS_IDLE, 0); + g_signal_connect(FWUPD_CLIENT(self->client), "device-added", G_CALLBACK(fu_util_device_wait_added_cb), &helper); - g_source_set_callback(source, fu_util_device_wait_timeout_cb, priv, NULL); - g_source_attach(source, priv->main_ctx); - g_main_loop_run(priv->loop); + g_source_set_callback(source, fu_util_device_wait_timeout_cb, self, NULL); + g_source_attach(source, self->main_ctx); + g_main_loop_run(self->loop); /* timed out */ if (helper.device == NULL) { @@ -1323,7 +1370,7 @@ } /* success */ - fu_console_print(priv->console, + fu_console_print(self->console, /* TRANSLATORS: the device showed up in time */ _("Successfully waited %.0fms for device"), g_timer_elapsed(timer, NULL) * 1000.f); @@ -1331,20 +1378,20 @@ } static gboolean -fu_util_quit(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_quit(FuUtil *self, gchar **values, GError **error) { /* success */ - return fwupd_client_quit(priv->client, priv->cancellable, error); + return fwupd_client_quit(self->client, self->cancellable, error); } static gboolean -fu_util_device_test_full(FuUtilPrivate *priv, +fu_util_device_test_full(FuUtil *self, gchar **values, FuUtilDeviceTestHelper *helper, GError **error) { /* required for interactive devices */ - priv->current_operation = FU_UTIL_OPERATION_UPDATE; + self->current_operation = FU_UTIL_OPERATION_UPDATE; /* at least one argument required */ if (g_strv_length(values) == 0) { @@ -1357,7 +1404,7 @@ /* get the report metadata */ helper->report_metadata = - fwupd_client_get_report_metadata(priv->client, priv->cancellable, error); + fwupd_client_get_report_metadata(self->client, self->cancellable, error); if (helper->report_metadata == NULL) return FALSE; @@ -1369,16 +1416,18 @@ json_builder_begin_array(helper->builder); for (guint i = 0; values[i] != NULL; i++) { json_builder_begin_object(helper->builder); - if (!fu_util_device_test_filename(priv, helper, values[i], error)) + if (!fu_util_device_test_filename(self, helper, values[i], error)) { + g_prefix_error(error, "%s failed: ", values[i]); return FALSE; + } json_builder_end_object(helper->builder); } json_builder_end_array(helper->builder); /* dump to screen as JSON format */ json_builder_end_object(helper->builder); - if (priv->as_json) { - if (!fu_util_print_builder(priv->console, helper->builder, error)) + if (self->as_json) { + if (!fu_util_print_builder(self->console, helper->builder, error)) return FALSE; } @@ -1390,7 +1439,7 @@ /* TRANSLATORS: device tests can be specific to a CPU type */ ngettext("%u test was skipped", "%u tests were skipped", helper->nr_skipped), helper->nr_skipped); - fu_console_print_full(priv->console, + fu_console_print_full(self->console, FU_CONSOLE_PRINT_FLAG_WARNING, "%s\n", str->str); @@ -1406,7 +1455,7 @@ g_strv_length(values)); return FALSE; } - if (helper->nr_success == 0) { + if (helper->nr_success == 0 && helper->nr_skipped == 0) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, @@ -1415,7 +1464,7 @@ } /* nag? */ - if (!fu_util_perhaps_show_unreported(priv, error)) + if (!fu_util_perhaps_show_unreported(self, error)) return FALSE; /* success */ @@ -1423,24 +1472,25 @@ } static gboolean -fu_util_device_emulate(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_device_emulate(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuUtilDeviceTestHelper) helper = fu_util_device_test_helper_new(); helper->use_emulation = TRUE; - priv->filter_device_include |= FWUPD_DEVICE_FLAG_EMULATED; - return fu_util_device_test_full(priv, values, helper, error); + self->flags |= FWUPD_INSTALL_FLAG_ONLY_EMULATED; + self->filter_device_include |= FWUPD_DEVICE_FLAG_EMULATED; + return fu_util_device_test_full(self, values, helper, error); } static gboolean -fu_util_device_test(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_device_test(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuUtilDeviceTestHelper) helper = fu_util_device_test_helper_new(); - priv->filter_device_exclude |= FWUPD_DEVICE_FLAG_EMULATED; - return fu_util_device_test_full(priv, values, helper, error); + self->filter_device_exclude |= FWUPD_DEVICE_FLAG_EMULATED; + return fu_util_device_test_full(self, values, helper, error); } static gboolean -fu_util_download(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_download(FuUtil *self, gchar **values, GError **error) { g_autofree gchar *basename = NULL; g_autoptr(GBytes) blob = NULL; @@ -1456,7 +1506,7 @@ /* file already exists */ basename = g_path_get_basename(values[0]); - if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + if ((self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && g_file_test(basename, G_FILE_TEST_EXISTS)) { g_set_error(error, FWUPD_ERROR, @@ -1465,10 +1515,10 @@ basename); return FALSE; } - blob = fwupd_client_download_bytes(priv->client, + blob = fwupd_client_download_bytes(self->client, values[0], - priv->download_flags, - priv->cancellable, + self->download_flags, + self->cancellable, error); if (blob == NULL) return FALSE; @@ -1479,7 +1529,7 @@ } static gboolean -fu_util_local_install(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_local_install(FuUtil *self, gchar **values, GError **error) { const gchar *id; g_autofree gchar *filename = NULL; @@ -1489,7 +1539,7 @@ if (g_strv_length(values) == 1) { id = FWUPD_DEVICE_ID_ANY; } else if (g_strv_length(values) == 2) { - dev = fu_util_get_device_by_id(priv, values[1], error); + dev = fu_util_get_device_by_id(self, values[1], error); if (dev == NULL) return FALSE; id = fwupd_device_get_id(dev); @@ -1501,41 +1551,41 @@ return FALSE; } - priv->current_operation = FU_UTIL_OPERATION_INSTALL; + self->current_operation = FU_UTIL_OPERATION_INSTALL; /* install with flags chosen by the user */ - filename = fu_util_download_if_required(priv, values[0], error); + filename = fu_util_download_if_required(self, values[0], error); if (filename == NULL) return FALSE; /* detect bitlocker */ - if (dev != NULL && !priv->no_safety_check && !priv->assume_yes) { - if (!fu_util_prompt_warning_fde(priv->console, dev, error)) + if (dev != NULL && !self->no_safety_check && !self->assume_yes) { + if (!fu_util_prompt_warning_fde(self->console, dev, error)) return FALSE; } - if (!fwupd_client_install(priv->client, + if (!fwupd_client_install(self->client, id, filename, - priv->flags, - priv->cancellable, + self->flags, + self->cancellable, error)) return FALSE; - fu_util_display_current_message(priv); + fu_util_display_current_message(self); /* we don't want to ask anything */ - if (priv->no_reboot_check) { + if (self->no_reboot_check) { g_debug("skipping reboot check"); return TRUE; } /* show reboot if needed */ - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean -fu_util_get_details(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_details(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) array = NULL; g_autoptr(FuUtilNode) root = g_node_new(NULL); @@ -1550,27 +1600,27 @@ } /* implied, important for get-details on a device not in your system */ - priv->show_all = TRUE; + self->show_all = TRUE; - array = fwupd_client_get_details(priv->client, values[0], priv->cancellable, error); + array = fwupd_client_get_details(self->client, values[0], self->cancellable, error); if (array == NULL) return FALSE; - if (priv->as_json) { + if (self->as_json) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); fwupd_codec_array_to_json(array, "Devices", builder, FWUPD_CODEC_FLAG_TRUSTED); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } - fu_util_build_device_tree(priv, root, array); - fu_util_print_node(priv->console, priv->client, root); + fu_util_build_device_tree(self, root, array, NULL); + fu_util_print_node(self->console, self->client, root); return TRUE; } static gboolean -fu_util_report_history_for_remote(FuUtilPrivate *priv, +fu_util_report_history_for_remote(FuUtil *self, GPtrArray *devices, FwupdRemote *remote_filter, FwupdRemote *remote_upload, @@ -1583,10 +1633,10 @@ g_autoptr(GHashTable) metadata = NULL; /* convert to JSON */ - metadata = fwupd_client_get_report_metadata(priv->client, priv->cancellable, error); + metadata = fwupd_client_get_report_metadata(self->client, self->cancellable, error); if (metadata == NULL) return FALSE; - data = fwupd_client_build_report_history(priv->client, + data = fwupd_client_build_report_history(self->client, devices, remote_filter, metadata, @@ -1595,11 +1645,11 @@ return FALSE; /* self sign data */ - if (priv->sign) { - sig = fwupd_client_self_sign(priv->client, + if (self->sign) { + sig = fwupd_client_self_sign(self->client, data, FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP, - priv->cancellable, + self->cancellable, error); if (sig == NULL) return FALSE; @@ -1609,13 +1659,13 @@ report_uri = fwupd_remote_build_report_uri(remote_upload, error); if (report_uri == NULL) return FALSE; - if (!priv->assume_yes && + if (!self->assume_yes && !fwupd_remote_has_flag(remote_upload, FWUPD_REMOTE_FLAG_AUTOMATIC_REPORTS)) { - fu_console_print_kv(priv->console, _("Target"), report_uri); - fu_console_print_kv(priv->console, _("Payload"), data); + fu_console_print_kv(self->console, _("Target"), report_uri); + fu_console_print_kv(self->console, _("Payload"), data); if (sig != NULL) - fu_console_print_kv(priv->console, _("Signature"), sig); - if (!fu_console_input_bool(priv->console, TRUE, "%s", _("Proceed with upload?"))) { + fu_console_print_kv(self->console, _("Signature"), sig); + if (!fu_console_input_bool(self->console, TRUE, "%s", _("Proceed with upload?"))) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_PERMISSION_DENIED, @@ -1625,12 +1675,12 @@ } /* POST request and parse reply */ - uri = fwupd_client_upload_report(priv->client, + uri = fwupd_client_upload_report(self->client, report_uri, data, sig, FWUPD_CLIENT_UPLOAD_FLAG_NONE, - priv->cancellable, + self->cancellable, error); if (uri == NULL) return FALSE; @@ -1638,7 +1688,7 @@ /* server wanted us to see a message */ if (g_strcmp0(uri, "") != 0) { fu_console_print( - priv->console, + self->console, "%s %s", /* TRANSLATORS: the server sent the user a small message */ _("Update failure is a known issue, visit this URL for more information:"), @@ -1650,23 +1700,17 @@ } static gboolean -fu_util_report_history_force(FuUtilPrivate *priv, GError **error) +fu_util_report_history_force(FuUtil *self, GPtrArray *devices, GError **error) { g_autoptr(FwupdRemote) remote_upload = NULL; - g_autoptr(GPtrArray) devices = NULL; g_autoptr(GString) str = g_string_new(NULL); - /* get all devices */ - devices = fwupd_client_get_history(priv->client, priv->cancellable, error); - if (devices == NULL) - return FALSE; - /* just assume every report goes to this remote */ remote_upload = - fwupd_client_get_remote_by_id(priv->client, "lvfs", priv->cancellable, error); + fwupd_client_get_remote_by_id(self->client, "lvfs", self->cancellable, error); if (remote_upload == NULL) return FALSE; - if (!fu_util_report_history_for_remote(priv, + if (!fu_util_report_history_for_remote(self, devices, NULL, /* no filter */ remote_upload, @@ -1677,11 +1721,11 @@ for (guint i = 0; i < devices->len; i++) { FwupdDevice *device = g_ptr_array_index(devices, i); g_debug("setting flag on %s", fwupd_device_get_id(device)); - if (!fwupd_client_modify_device(priv->client, + if (!fwupd_client_modify_device(self->client, fwupd_device_get_id(device), "Flags", "reported", - priv->cancellable, + self->cancellable, error)) return FALSE; } @@ -1694,12 +1738,12 @@ "Successfully uploaded %u reports", devices->len), devices->len); - fu_console_print_literal(priv->console, str->str); + fu_console_print_literal(self->console, str->str); return TRUE; } static gboolean -fu_util_report_export(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_report_export(FuUtil *self, gchar **values, GError **error) { g_autoptr(GHashTable) metadata = NULL; g_autoptr(GPtrArray) devices_filtered = @@ -1707,7 +1751,7 @@ g_autoptr(GPtrArray) devices = NULL; /* get all devices from the history database, then filter them and export to JSON */ - devices = fwupd_client_get_history(priv->client, priv->cancellable, error); + devices = fwupd_client_get_history(self->client, self->cancellable, error); if (devices == NULL) return FALSE; g_debug("%u devices with history", devices->len); @@ -1730,10 +1774,10 @@ /* filter, if not forcing */ if (!fwupd_device_match_flags(dev, - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; - if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if ((self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { if (fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_REPORTED)) { g_debug("%s has already been reported", fwupd_device_get_id(dev)); continue; @@ -1752,7 +1796,7 @@ } /* nothing to report, but try harder with --force */ - if (devices_filtered->len == 0 && (priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { + if (devices_filtered->len == 0 && (self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, @@ -1761,7 +1805,7 @@ } /* get metadata */ - metadata = fwupd_client_get_report_metadata(priv->client, priv->cancellable, error); + metadata = fwupd_client_get_report_metadata(self->client, self->cancellable, error); if (metadata == NULL) return FALSE; @@ -1778,7 +1822,7 @@ /* convert single device to JSON */ g_ptr_array_add(devices_tmp, dev); - data = fwupd_client_build_report_history(priv->client, + data = fwupd_client_build_report_history(self->client, devices, NULL, /* remote */ metadata, @@ -1788,25 +1832,27 @@ payload_blob = g_bytes_new(data, strlen(data)); payload_img = fu_firmware_new_from_bytes(payload_blob); fu_firmware_set_id(payload_img, "report.json"); - fu_firmware_add_image(archive, payload_img); + if (!fu_firmware_add_image(archive, payload_img, error)) + return FALSE; /* self sign data */ - if (priv->sign) { + if (self->sign) { g_autofree gchar *sig = NULL; g_autoptr(FuFirmware) sig_img = NULL; g_autoptr(GBytes) sig_blob = NULL; - sig = fwupd_client_self_sign(priv->client, + sig = fwupd_client_self_sign(self->client, data, FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP, - priv->cancellable, + self->cancellable, error); if (sig == NULL) return FALSE; sig_blob = g_bytes_new(sig, strlen(sig)); sig_img = fu_firmware_new_from_bytes(sig_blob); fu_firmware_set_id(sig_img, "report.json.p7c"); - fu_firmware_add_image(archive, sig_img); + if (!fu_firmware_add_image(archive, sig_img, error)) + return FALSE; } /* save to local file */ @@ -1819,7 +1865,7 @@ return FALSE; /* TRANSLATORS: key for a offline report filename */ - fu_console_print_kv(priv->console, _("Saved report"), filename); + fu_console_print_kv(self->console, _("Saved report"), filename); } /* success */ @@ -1827,7 +1873,7 @@ } static gboolean -fu_util_report_history_full(FuUtilPrivate *priv, gboolean only_automatic_reports, GError **error) +fu_util_report_history_full(FuUtil *self, gboolean only_automatic_reports, GError **error) { guint cnt = 0; g_autoptr(GPtrArray) devices = NULL; @@ -1835,13 +1881,13 @@ /* get all devices from the history database, then filter them, * adding to a hash map of report-ids */ - devices = fwupd_client_get_history(priv->client, priv->cancellable, error); + devices = fwupd_client_get_history(self->client, self->cancellable, error); if (devices == NULL) return FALSE; g_debug("%u devices with history", devices->len); /* ignore the previous reported flag */ - if (priv->flags & FWUPD_INSTALL_FLAG_FORCE) { + if (self->flags & FWUPD_INSTALL_FLAG_FORCE) { for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices, i); fwupd_device_remove_flag(dev, FWUPD_DEVICE_FLAG_REPORTED); @@ -1854,7 +1900,7 @@ if (fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) { g_autofree gchar *cmd = g_strdup_printf("%s activate", g_get_prgname()); fu_console_print( - priv->console, + self->console, /* TRANSLATORS: %1 is a device name, e.g. "ThinkPad Universal * ThunderBolt 4 Dock" and %2 is "fwupdmgr activate" */ _("%s is pending activation; use %s to complete the update."), @@ -1864,7 +1910,7 @@ } /* get all remotes */ - remotes = fwupd_client_get_remotes(priv->client, priv->cancellable, error); + remotes = fwupd_client_get_remotes(self->client, self->cancellable, error); if (remotes == NULL) return FALSE; for (guint i = 0; i < remotes->len; i++) { @@ -1879,7 +1925,7 @@ } /* try to upload */ - if (!fu_util_report_history_for_remote(priv, + if (!fu_util_report_history_for_remote(self, devices, remote, /* filter */ remote, /* upload */ @@ -1896,8 +1942,8 @@ /* nothing to report, but try harder with --force */ if (cnt == 0) { - if (!only_automatic_reports && priv->flags & FWUPD_INSTALL_FLAG_FORCE) - return fu_util_report_history_force(priv, error); + if (!only_automatic_reports && self->flags & FWUPD_INSTALL_FLAG_FORCE) + return fu_util_report_history_force(self, devices, error); g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, @@ -1909,22 +1955,22 @@ for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices, i); g_debug("setting flag on %s", fwupd_device_get_id(dev)); - if (!fwupd_client_modify_device(priv->client, + if (!fwupd_client_modify_device(self->client, fwupd_device_get_id(dev), "Flags", "reported", - priv->cancellable, + self->cancellable, error)) return FALSE; } /* TRANSLATORS: where the user has uploaded success and/or failure report to the server */ - fu_console_print_literal(priv->console, "Successfully uploaded report"); + fu_console_print_literal(self->console, "Successfully uploaded report"); return TRUE; } static gboolean -fu_util_report_history(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_report_history(FuUtil *self, gchar **values, GError **error) { if (values != NULL && g_strv_length(values) != 0) { g_set_error_literal(error, @@ -1933,27 +1979,27 @@ "Invalid arguments"); return FALSE; } - return fu_util_report_history_full(priv, FALSE, error); + return fu_util_report_history_full(self, FALSE, error); } static gboolean -fu_util_get_history(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_history(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; g_autoptr(FuUtilNode) root = g_node_new(NULL); /* get all devices from the history database */ - devices = fwupd_client_get_history(priv->client, priv->cancellable, error); + devices = fwupd_client_get_history(self->client, self->cancellable, error); if (devices == NULL) return FALSE; /* not for human consumption */ - if (priv->as_json) { + if (self->as_json) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); fwupd_codec_array_to_json(devices, "Devices", builder, FWUPD_CODEC_FLAG_TRUSTED); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } /* show each device */ @@ -1963,8 +2009,8 @@ FuUtilNode *child; if (!fwupd_device_match_flags(dev, - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; child = g_node_append_data(root, g_object_ref(dev)); @@ -1974,21 +2020,21 @@ g_node_append_data(child, g_object_ref(rel)); } - fu_util_print_node(priv->console, priv->client, root); + fu_util_print_node(self->console, self->client, root); return TRUE; } static FwupdDevice * -fu_util_get_device_by_id(FuUtilPrivate *priv, const gchar *id, GError **error) +fu_util_get_device_by_id(FuUtil *self, const gchar *id, GError **error) { if (fwupd_guid_is_valid(id)) { g_autoptr(GPtrArray) devices = NULL; devices = - fwupd_client_get_devices_by_guid(priv->client, id, priv->cancellable, error); + fwupd_client_get_devices_by_guid(self->client, id, self->cancellable, error); if (devices == NULL) return NULL; - return fu_util_prompt_for_device(priv, devices, error); + return fu_util_prompt_for_device(self, devices, error); } /* did this look like a GUID? */ for (guint i = 0; id[i] != '\0'; i++) { @@ -2000,11 +2046,11 @@ return NULL; } } - return fwupd_client_get_device_by_id(priv->client, id, priv->cancellable, error); + return fwupd_client_get_device_by_id(self->client, id, self->cancellable, error); } static FwupdDevice * -fu_util_get_device_or_prompt(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_device_or_prompt(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; @@ -2014,10 +2060,10 @@ for (guint i = 1; i < g_strv_length(values); i++) g_debug("ignoring extra input %s", values[i]); } - return fu_util_get_device_by_id(priv, values[0], error); + return fu_util_get_device_by_id(self, values[0], error); } - if (priv->as_json) { + if (self->as_json) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS, @@ -2026,14 +2072,14 @@ } /* get all devices from daemon */ - devices = fwupd_client_get_devices(priv->client, priv->cancellable, error); + devices = fwupd_client_get_devices(self->client, self->cancellable, error); if (devices == NULL) return NULL; - return fu_util_prompt_for_device(priv, devices, error); + return fu_util_prompt_for_device(self, devices, error); } static FwupdRelease * -fu_util_get_release_for_device_version(FuUtilPrivate *priv, +fu_util_get_release_for_device_version(FuUtil *self, FwupdDevice *device, const gchar *version, GError **error) @@ -2041,9 +2087,9 @@ g_autoptr(GPtrArray) releases = NULL; /* get all releases */ - releases = fwupd_client_get_releases(priv->client, + releases = fwupd_client_get_releases(self->client, fwupd_device_get_id(device), - priv->cancellable, + self->cancellable, error); if (releases == NULL) return NULL; @@ -2069,91 +2115,91 @@ } static gboolean -fu_util_clear_results(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_clear_results(FuUtil *self, gchar **values, GError **error) { g_autoptr(FwupdDevice) dev = NULL; - dev = fu_util_get_device_or_prompt(priv, values, error); + dev = fu_util_get_device_or_prompt(self, values, error); if (dev == NULL) return FALSE; - return fwupd_client_clear_results(priv->client, + return fwupd_client_clear_results(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, error); } static gboolean -fu_util_verify_update(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_verify_update(FuUtil *self, gchar **values, GError **error) { g_autoptr(FwupdDevice) dev = NULL; - priv->filter_device_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY; - dev = fu_util_get_device_or_prompt(priv, values, error); + self->filter_device_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY; + dev = fu_util_get_device_or_prompt(self, values, error); if (dev == NULL) return FALSE; - if (!fwupd_client_verify_update(priv->client, + if (!fwupd_client_verify_update(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, error)) { g_prefix_error(error, "failed to verify update %s: ", fwupd_device_get_name(dev)); return FALSE; } /* TRANSLATORS: success message when user refreshes device checksums */ - fu_console_print_literal(priv->console, _("Successfully updated device checksums")); + fu_console_print_literal(self->console, _("Successfully updated device checksums")); return TRUE; } static gboolean -fu_util_download_metadata_enable_lvfs(FuUtilPrivate *priv, GError **error) +fu_util_download_metadata_enable_lvfs(FuUtil *self, GError **error) { g_autoptr(FwupdRemote) remote = NULL; /* is the LVFS available but disabled? */ - remote = fwupd_client_get_remote_by_id(priv->client, "lvfs", priv->cancellable, error); + remote = fwupd_client_get_remote_by_id(self->client, "lvfs", self->cancellable, error); if (remote == NULL) return TRUE; fu_console_print_literal( - priv->console, + self->console, /* TRANSLATORS: explain why no metadata available */ _("No remotes are currently enabled so no metadata is available.")); fu_console_print_literal( - priv->console, + self->console, /* TRANSLATORS: explain why no metadata available */ _("Metadata can be obtained from the Linux Vendor Firmware Service.")); /* TRANSLATORS: Turn on the remote */ - if (!fu_console_input_bool(priv->console, TRUE, "%s", _("Enable this remote?"))) + if (!fu_console_input_bool(self->console, TRUE, "%s", _("Enable this remote?"))) return TRUE; - if (!fwupd_client_modify_remote(priv->client, + if (!fwupd_client_modify_remote(self->client, fwupd_remote_get_id(remote), "Enabled", "true", - priv->cancellable, + self->cancellable, error)) return FALSE; - if (!fu_util_modify_remote_warning(priv->console, remote, priv->assume_yes, error)) + if (!fu_util_modify_remote_warning(self->console, remote, self->assume_yes, error)) return FALSE; /* refresh the newly-enabled remote */ - return fwupd_client_refresh_remote(priv->client, + return fwupd_client_refresh_remote(self->client, remote, - priv->download_flags, - priv->cancellable, + self->download_flags, + self->cancellable, error); } static gboolean -fu_util_check_oldest_remote(FuUtilPrivate *priv, guint64 *age_oldest, GError **error) +fu_util_check_oldest_remote(FuUtil *self, guint64 *age_oldest, GError **error) { g_autoptr(GPtrArray) remotes = NULL; gboolean checked = FALSE; /* get the age of the oldest enabled remotes */ - remotes = fwupd_client_get_remotes(priv->client, priv->cancellable, error); + remotes = fwupd_client_get_remotes(self->client, self->cancellable, error); if (remotes == NULL) return FALSE; for (guint i = 0; i < remotes->len; i++) { @@ -2184,7 +2230,7 @@ } static gboolean -fu_util_download_metadata(FuUtilPrivate *priv, GError **error) +fu_util_download_metadata(FuUtil *self, GError **error) { gboolean download_remote_enabled = FALSE; guint devices_supported_cnt = 0; @@ -2192,10 +2238,9 @@ guint refresh_cnt = 0; g_autoptr(GPtrArray) devs = NULL; g_autoptr(GPtrArray) remotes = NULL; - g_autoptr(GString) str = g_string_new(NULL); g_autoptr(GError) error_local = NULL; - remotes = fwupd_client_get_remotes(priv->client, priv->cancellable, error); + remotes = fwupd_client_get_remotes(self->client, self->cancellable, error); if (remotes == NULL) return FALSE; for (guint i = 0; i < remotes->len; i++) { @@ -2205,21 +2250,22 @@ if (fwupd_remote_get_kind(remote) != FWUPD_REMOTE_KIND_DOWNLOAD) continue; download_remote_enabled = TRUE; - if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + if ((self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && !fwupd_remote_needs_refresh(remote)) { g_debug("skipping as remote %s age is %us", fwupd_remote_get_id(remote), (guint)fwupd_remote_get_age(remote)); continue; } - fu_console_print(priv->console, - "%s %s", - _("Updating"), - fwupd_remote_get_id(remote)); - if (!fwupd_client_refresh_remote(priv->client, + if (!self->as_json) + fu_console_print(self->console, + "%s %s", + _("Updating"), + fwupd_remote_get_id(remote)); + if (!fwupd_client_refresh_remote(self->client, remote, - priv->download_flags, - priv->cancellable, + self->download_flags, + self->cancellable, error)) return FALSE; refresh_cnt++; @@ -2228,17 +2274,17 @@ /* no web remote is declared; try to enable LVFS */ if (!download_remote_enabled) { /* we don't want to ask anything */ - if (priv->no_remote_check) { + if (self->no_remote_check) { g_debug("skipping remote check"); return TRUE; } - if (!fu_util_download_metadata_enable_lvfs(priv, error)) + if (!fu_util_download_metadata_enable_lvfs(self, error)) return FALSE; } /* metadata refreshed recently */ - if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && refresh_cnt == 0) { + if ((self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && refresh_cnt == 0) { g_set_error(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, @@ -2249,8 +2295,11 @@ return FALSE; } + if (self->as_json) + return TRUE; + /* get devices from daemon */ - devs = fwupd_client_get_devices(priv->client, priv->cancellable, error); + devs = fwupd_client_get_devices(self->client, self->cancellable, error); if (devs == NULL) return FALSE; @@ -2265,19 +2314,40 @@ /* TRANSLATORS: success message -- where 'metadata' is information * about available firmware on the remote server */ - g_string_append(str, _("Successfully downloaded new metadata: ")); + fu_console_print_literal(self->console, _("Successfully downloaded new metadata:")); - g_string_append_printf(str, - /* TRANSLATORS: how many local devices can expect updates now */ - ngettext("Updates have been published for %u local device", - "Updates have been published for %u of %u local devices", - devices_supported_cnt), - devices_supported_cnt, - devices_updatable_cnt); - fu_console_print_literal(priv->console, str->str); + if (devices_updatable_cnt == 0) { + fu_console_print_full( + self->console, + FU_CONSOLE_PRINT_FLAG_LIST_ITEM | FU_CONSOLE_PRINT_FLAG_NEWLINE, + "%s", + /* TRANSLATORS: no devices that can be upgraded with new firmware */ + _("No devices are updatable")); + } else { + fu_console_print_full(self->console, + FU_CONSOLE_PRINT_FLAG_LIST_ITEM | + FU_CONSOLE_PRINT_FLAG_NEWLINE, + /* TRANSLATORS: how many devices could be updated in theory if + we had the firmware locally */ + ngettext("%u device is updatable", + "%u devices are updatable", + devices_updatable_cnt), + devices_updatable_cnt); + fu_console_print_full(self->console, + FU_CONSOLE_PRINT_FLAG_LIST_ITEM | + FU_CONSOLE_PRINT_FLAG_NEWLINE, + /* TRANSLATORS: how many devices have published updates on + something like the LVFS */ + ngettext("%u device is supported in the enabled remotes (an " + "update has been published)", + "%u devices are supported in the enabled remotes " + "(an update has been published)", + devices_supported_cnt), + devices_supported_cnt); + } /* auto-upload any reports */ - if (!fu_util_report_history_full(priv, TRUE, &error_local)) { + if (!fu_util_report_history_full(self, TRUE, &error_local)) { if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; @@ -2290,10 +2360,10 @@ } static gboolean -fu_util_refresh(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_refresh(FuUtil *self, gchar **values, GError **error) { if (g_strv_length(values) == 0) - return fu_util_download_metadata(priv, error); + return fu_util_download_metadata(self, error); if (g_strv_length(values) != 3) { g_set_error_literal(error, FWUPD_ERROR, @@ -2303,74 +2373,77 @@ } /* open file */ - if (!fwupd_client_update_metadata(priv->client, + if (!fwupd_client_update_metadata(self->client, values[2], values[0], values[1], - priv->cancellable, + self->cancellable, error)) return FALSE; + if (self->as_json) + return TRUE; + /* TRANSLATORS: success message -- the user can do this by-hand too */ - fu_console_print_literal(priv->console, _("Successfully refreshed metadata manually")); + fu_console_print_literal(self->console, _("Successfully refreshed metadata manually")); return TRUE; } static gboolean -fu_util_get_results(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_results(FuUtil *self, gchar **values, GError **error) { g_autofree gchar *tmp = NULL; g_autoptr(FwupdDevice) dev = NULL; g_autoptr(FwupdDevice) rel = NULL; - dev = fu_util_get_device_or_prompt(priv, values, error); + dev = fu_util_get_device_or_prompt(self, values, error); if (dev == NULL) return FALSE; - rel = fwupd_client_get_results(priv->client, + rel = fwupd_client_get_results(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, error); if (rel == NULL) return FALSE; - if (priv->as_json) { + if (self->as_json) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); fwupd_codec_to_json(FWUPD_CODEC(rel), builder, FWUPD_CODEC_FLAG_TRUSTED); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } - tmp = fu_util_device_to_string(priv->client, rel, 0); - fu_console_print_literal(priv->console, tmp); + tmp = fu_util_device_to_string(self->client, rel, 0); + fu_console_print_literal(self->console, tmp); return TRUE; } static gboolean -fu_util_get_releases(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_releases(FuUtil *self, gchar **values, GError **error) { g_autoptr(FwupdDevice) dev = NULL; g_autoptr(GPtrArray) rels = NULL; - priv->filter_device_include |= FWUPD_DEVICE_FLAG_SUPPORTED; - dev = fu_util_get_device_or_prompt(priv, values, error); + self->filter_device_include |= FWUPD_DEVICE_FLAG_SUPPORTED; + dev = fu_util_get_device_or_prompt(self, values, error); if (dev == NULL) return FALSE; /* get the releases for this device */ - rels = fwupd_client_get_releases(priv->client, + rels = fwupd_client_get_releases(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, error); if (rels == NULL) return FALSE; /* not for human consumption */ - if (priv->as_json) - return fu_util_get_releases_as_json(priv, rels, error); + if (self->as_json) + return fu_util_get_releases_as_json(self, rels, error); if (rels->len == 0) { /* TRANSLATORS: no repositories to download from */ - fu_console_print_literal(priv->console, _("No releases available")); + fu_console_print_literal(self->console, _("No releases available")); return TRUE; } if (g_getenv("FWUPD_VERBOSE") != NULL) { @@ -2378,30 +2451,88 @@ FwupdRelease *rel = g_ptr_array_index(rels, i); g_autofree gchar *tmp = NULL; if (!fwupd_release_match_flags(rel, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) + continue; + tmp = fwupd_codec_to_string(FWUPD_CODEC(rel)); + fu_console_print_literal(self->console, tmp); + } + } else { + g_autoptr(FuUtilNode) root = g_node_new(NULL); + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel = g_ptr_array_index(rels, i); + if (!fwupd_release_match_flags(rel, + self->filter_release_include, + self->filter_release_exclude)) + continue; + g_node_append_data(root, g_object_ref(rel)); + } + fu_util_print_node(self->console, self->client, root); + } + + return TRUE; +} + +static gboolean +fu_util_search(FuUtil *self, gchar **values, GError **error) +{ + g_autoptr(GPtrArray) rels = NULL; + + /* sanity check */ + if (g_strv_length(values) < 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments, expected WORD"); + return FALSE; + } + + /* get the releases for this device */ + rels = fwupd_client_search(self->client, values[0], self->cancellable, error); + if (rels == NULL) + return FALSE; + + /* not for human consumption */ + if (self->as_json) + return fu_util_get_releases_as_json(self, rels, error); + + if (rels->len == 0) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_FOUND, + /* TRANSLATORS: no repositories to download from */ + _("No matching releases for search token")); + return FALSE; + } + if (g_getenv("FWUPD_VERBOSE") != NULL) { + for (guint i = 0; i < rels->len; i++) { + FwupdRelease *rel = g_ptr_array_index(rels, i); + g_autofree gchar *tmp = NULL; + if (!fwupd_release_match_flags(rel, + self->filter_release_include, + self->filter_release_exclude)) continue; tmp = fwupd_codec_to_string(FWUPD_CODEC(rel)); - fu_console_print_literal(priv->console, tmp); + fu_console_print_literal(self->console, tmp); } } else { g_autoptr(FuUtilNode) root = g_node_new(NULL); for (guint i = 0; i < rels->len; i++) { FwupdRelease *rel = g_ptr_array_index(rels, i); if (!fwupd_release_match_flags(rel, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; g_node_append_data(root, g_object_ref(rel)); } - fu_util_print_node(priv->console, priv->client, root); + fu_util_print_node(self->console, self->client, root); } return TRUE; } static FwupdRelease * -fu_util_prompt_for_release(FuUtilPrivate *priv, GPtrArray *rels_unfiltered, GError **error) +fu_util_prompt_for_release(FuUtil *self, GPtrArray *rels_unfiltered, GError **error) { FwupdRelease *rel; guint idx; @@ -2409,8 +2540,8 @@ /* filter */ rels = fwupd_release_array_filter_flags(rels_unfiltered, - priv->filter_release_include, - priv->filter_release_exclude, + self->filter_release_include, + self->filter_release_exclude, error); if (rels == NULL) return NULL; @@ -2422,16 +2553,16 @@ } /* TRANSLATORS: this is to abort the interactive prompt */ - fu_console_print(priv->console, "0.\t%s", _("Cancel")); + fu_console_print(self->console, "0.\t%s", _("Cancel")); for (guint i = 0; i < rels->len; i++) { FwupdRelease *rel_tmp = g_ptr_array_index(rels, i); - fu_console_print(priv->console, + fu_console_print(self->console, "%u.\t%s", i + 1, fwupd_release_get_version(rel_tmp)); } /* TRANSLATORS: get interactive prompt */ - idx = fu_console_input_uint(priv->console, rels->len, "%s", _("Choose release")); + idx = fu_console_input_uint(self->console, rels->len, "%s", _("Choose release")); if (idx == 0) { g_set_error_literal(error, FWUPD_ERROR, @@ -2444,64 +2575,64 @@ } static gboolean -fu_util_verify(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_verify(FuUtil *self, gchar **values, GError **error) { g_autoptr(FwupdDevice) dev = NULL; - priv->filter_device_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY; - dev = fu_util_get_device_or_prompt(priv, values, error); + self->filter_device_include |= FWUPD_DEVICE_FLAG_CAN_VERIFY; + dev = fu_util_get_device_or_prompt(self, values, error); if (dev == NULL) return FALSE; - if (!fwupd_client_verify(priv->client, + if (!fwupd_client_verify(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, error)) { g_prefix_error(error, "failed to verify %s: ", fwupd_device_get_name(dev)); return FALSE; } /* TRANSLATORS: success message when user verified device checksums */ - fu_console_print_literal(priv->console, _("Successfully verified device checksums")); + fu_console_print_literal(self->console, _("Successfully verified device checksums")); return TRUE; } static gboolean -fu_util_unlock(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_unlock(FuUtil *self, gchar **values, GError **error) { g_autoptr(FwupdDevice) dev = NULL; - priv->filter_device_include |= FWUPD_DEVICE_FLAG_LOCKED; - dev = fu_util_get_device_or_prompt(priv, values, error); + self->filter_device_include |= FWUPD_DEVICE_FLAG_LOCKED; + dev = fu_util_get_device_or_prompt(self, values, error); if (dev == NULL) return FALSE; - if (!fwupd_client_unlock(priv->client, fwupd_device_get_id(dev), priv->cancellable, error)) + if (!fwupd_client_unlock(self->client, fwupd_device_get_id(dev), self->cancellable, error)) return FALSE; /* check flags after unlocking in case the operation changes them */ if (fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN)) - priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_SHUTDOWN; if (fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_NEEDS_REBOOT)) - priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean -fu_util_perhaps_refresh_remotes(FuUtilPrivate *priv, GError **error) +fu_util_perhaps_refresh_remotes(FuUtil *self, GError **error) { guint64 age_oldest = 0; const guint64 age_limit_days = 30; /* we don't want to ask anything */ - if (priv->no_metadata_check || priv->as_json) { + if (self->no_metadata_check || self->as_json) { g_debug("skipping metadata check"); return TRUE; } - if (!fu_util_check_oldest_remote(priv, &age_oldest, NULL)) + if (!fu_util_check_oldest_remote(self, &age_oldest, NULL)) return TRUE; /* metadata is new enough */ @@ -2509,9 +2640,9 @@ return TRUE; /* ask for permission */ - if (!priv->assume_yes) { + if (!self->assume_yes) { fu_console_print( - priv->console, + self->console, /* TRANSLATORS: the metadata is very out of date; %u is a number > 1 */ ngettext("Firmware metadata has not been updated for %u" " day and may not be up to date.", @@ -2519,7 +2650,7 @@ " days and may not be up to date.", (gint)age_limit_days), (guint)age_limit_days); - if (!fu_console_input_bool(priv->console, + if (!fu_console_input_bool(self->console, FALSE, "%s (%s)", /* TRANSLATORS: ask if we can update metadata */ @@ -2530,11 +2661,11 @@ } /* downloads new metadata */ - return fu_util_download_metadata(priv, error); + return fu_util_download_metadata(self, error); } static gboolean -fu_util_get_updates_as_json(FuUtilPrivate *priv, GPtrArray *devices, GError **error) +fu_util_get_updates_as_json(FuUtil *self, GPtrArray *devices, GError **error) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); @@ -2550,9 +2681,9 @@ continue; /* get the releases for this device and filter for validity */ - rels = fwupd_client_get_upgrades(priv->client, + rels = fwupd_client_get_upgrades(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, &error_local); if (rels == NULL) { g_debug("no upgrades: %s", error_local->message); @@ -2561,8 +2692,8 @@ for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel = g_ptr_array_index(rels, j); if (!fwupd_release_match_flags(rel, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; fwupd_device_add_release(dev, rel); } @@ -2574,11 +2705,11 @@ } json_builder_end_array(builder); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } static gboolean -fu_util_get_updates(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_updates(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; gboolean supported = FALSE; @@ -2587,32 +2718,34 @@ g_autoptr(GPtrArray) devices_no_upgrades = g_ptr_array_new(); /* are the remotes very old */ - if (!fu_util_perhaps_refresh_remotes(priv, error)) + if (!fu_util_perhaps_refresh_remotes(self, error)) return FALSE; /* handle both forms */ if (g_strv_length(values) == 0) { - devices = fwupd_client_get_devices(priv->client, priv->cancellable, error); + devices = fwupd_client_get_devices(self->client, self->cancellable, error); if (devices == NULL) return FALSE; - } else if (g_strv_length(values) == 1) { - FwupdDevice *device = fu_util_get_device_by_id(priv, values[0], error); - if (device == NULL) - return FALSE; - devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); - g_ptr_array_add(devices, device); } else { - g_set_error_literal(error, - FWUPD_ERROR, - FWUPD_ERROR_INVALID_ARGS, - "Invalid arguments"); - return FALSE; + devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + for (guint idx = 0; idx < g_strv_length(values); idx++) { + FwupdDevice *device = fu_util_get_device_by_id(self, values[idx], error); + if (device == NULL) { + g_set_error(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "'%s' is not a valid GUID nor DEVICE-ID", + values[idx]); + return FALSE; + } + g_ptr_array_add(devices, device); + } } g_ptr_array_sort(devices, fu_util_sort_devices_by_flags_cb); /* not for human consumption */ - if (priv->as_json) - return fu_util_get_updates_as_json(priv, devices, error); + if (self->as_json) + return fu_util_get_updates_as_json(self, devices, error); for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices, i); @@ -2625,8 +2758,8 @@ !fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_UPDATABLE_HIDDEN)) continue; if (!fwupd_device_match_flags(dev, - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; if (!fwupd_device_has_flag(dev, FWUPD_DEVICE_FLAG_SUPPORTED)) { g_ptr_array_add(devices_no_support, dev); @@ -2635,9 +2768,9 @@ supported = TRUE; /* get the releases for this device and filter for validity */ - rels = fwupd_client_get_upgrades(priv->client, + rels = fwupd_client_get_upgrades(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, &error_local); if (rels == NULL) { g_ptr_array_add(devices_no_upgrades, dev); @@ -2651,8 +2784,8 @@ for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel = g_ptr_array_index(rels, j); if (!fwupd_release_match_flags(rel, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; g_node_append_data(child, g_object_ref(rel)); } @@ -2660,28 +2793,28 @@ /* devices that have no updates available for whatever reason */ if (devices_no_support->len > 0) { - fu_console_print_literal(priv->console, + fu_console_print_literal(self->console, /* TRANSLATORS: message letting the user know no device * upgrade available due to missing on LVFS */ - _("Devices with no available firmware updates: ")); + _("Devices with no available firmware updates:")); for (guint i = 0; i < devices_no_support->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices_no_support, i); - fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev)); + fu_console_print(self->console, " • %s", fwupd_device_get_name(dev)); } } if (devices_no_upgrades->len > 0) { fu_console_print_literal( - priv->console, + self->console, /* TRANSLATORS: message letting the user know no device upgrade available */ _("Devices with the latest available firmware version:")); for (guint i = 0; i < devices_no_upgrades->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices_no_upgrades, i); - fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev)); + fu_console_print(self->console, " • %s", fwupd_device_get_name(dev)); } } /* nag? */ - if (!fu_util_perhaps_show_unreported(priv, error)) + if (!fu_util_perhaps_show_unreported(self, error)) return FALSE; /* no devices supported by LVFS or all are filtered */ @@ -2703,32 +2836,32 @@ return FALSE; } - fu_util_print_node(priv->console, priv->client, root); + fu_util_print_node(self->console, self->client, root); /* success */ return TRUE; } static gboolean -fu_util_get_remotes(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_remotes(FuUtil *self, gchar **values, GError **error) { g_autoptr(FuUtilNode) root = g_node_new(NULL); g_autoptr(GPtrArray) remotes = NULL; - remotes = fwupd_client_get_remotes(priv->client, priv->cancellable, error); + remotes = fwupd_client_get_remotes(self->client, self->cancellable, error); if (remotes == NULL) return FALSE; - if (priv->as_json) { + if (self->as_json) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); fwupd_codec_array_to_json(remotes, "Remotes", builder, FWUPD_CODEC_FLAG_TRUSTED); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } if (remotes->len == 0) { /* TRANSLATORS: no repositories to download from */ - fu_console_print_literal(priv->console, _("No remotes available")); + fu_console_print_literal(self->console, _("No remotes available")); return TRUE; } @@ -2736,32 +2869,29 @@ FwupdRemote *remote_tmp = g_ptr_array_index(remotes, i); g_node_append_data(root, g_object_ref(remote_tmp)); } - fu_util_print_node(priv->console, priv->client, root); + fu_util_print_node(self->console, self->client, root); return TRUE; } static FwupdRelease * -fu_util_get_release_with_tag(FuUtilPrivate *priv, - FwupdDevice *dev, - const gchar *host_bkc, - GError **error) +fu_util_get_release_with_tag(FuUtil *self, FwupdDevice *dev, const gchar *host_bkc, GError **error) { g_autoptr(GPtrArray) rels = NULL; g_auto(GStrv) host_bkcs = g_strsplit(host_bkc, ",", -1); /* find the newest release that matches */ - rels = fwupd_client_get_releases(priv->client, + rels = fwupd_client_get_releases(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, error); if (rels == NULL) return NULL; for (guint i = 0; i < rels->len; i++) { FwupdRelease *rel = g_ptr_array_index(rels, i); if (!fwupd_release_match_flags(rel, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; for (guint j = 0; host_bkcs[j] != NULL; j++) { if (fwupd_release_has_tag(rel, host_bkcs[j])) @@ -2778,25 +2908,22 @@ } static FwupdRelease * -fu_util_get_release_with_branch(FuUtilPrivate *priv, - FwupdDevice *dev, - const gchar *branch, - GError **error) +fu_util_get_release_with_branch(FuUtil *self, FwupdDevice *dev, const gchar *branch, GError **error) { g_autoptr(GPtrArray) rels = NULL; /* find the newest release that matches */ - rels = fwupd_client_get_releases(priv->client, + rels = fwupd_client_get_releases(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, error); if (rels == NULL) return NULL; for (guint i = 0; i < rels->len; i++) { FwupdRelease *rel = g_ptr_array_index(rels, i); if (!fwupd_release_match_flags(rel, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; if (g_strcmp0(branch, fwupd_release_get_branch(rel)) == 0) return g_object_ref(rel); @@ -2811,9 +2938,9 @@ } static gboolean -fu_util_prompt_warning_bkc(FuUtilPrivate *priv, FwupdDevice *dev, FwupdRelease *rel, GError **error) +fu_util_prompt_warning_bkc(FuUtil *self, FwupdDevice *dev, FwupdRelease *rel, GError **error) { - const gchar *host_bkc = fwupd_client_get_host_bkc(priv->client); + const gchar *host_bkc = fwupd_client_get_host_bkc(self->client); g_autofree gchar *cmd = g_strdup_printf("%s sync", g_get_prgname()); g_autoptr(FwupdRelease) rel_bkc = NULL; g_autoptr(GError) error_local = NULL; @@ -2824,7 +2951,7 @@ return TRUE; /* get the release that corresponds with the host BKC */ - rel_bkc = fu_util_get_release_with_tag(priv, dev, host_bkc, &error_local); + rel_bkc = fu_util_get_release_with_tag(self, dev, host_bkc, &error_local); if (rel_bkc == NULL) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) || g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { @@ -2852,7 +2979,7 @@ cmd); fu_console_box( - priv->console, + self->console, /* TRANSLATORS: the best known configuration is a set of software that we know works * well together. In the OEM and ODM industries it is often called a BKC */ _("Deviate from the best known configuration?"), @@ -2860,7 +2987,7 @@ 80); /* TRANSLATORS: prompt to apply the update */ - if (!fu_console_input_bool(priv->console, TRUE, "%s", _("Perform operation?"))) { + if (!fu_console_input_bool(self->console, TRUE, "%s", _("Perform operation?"))) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO, @@ -2873,10 +3000,7 @@ } static gboolean -fu_util_prompt_warning_composite(FuUtilPrivate *priv, - FwupdDevice *dev, - FwupdRelease *rel, - GError **error) +fu_util_prompt_warning_composite(FuUtil *self, FwupdDevice *dev, FwupdRelease *rel, GError **error) { const gchar *rel_csum; g_autoptr(GPtrArray) devices = NULL; @@ -2889,7 +3013,7 @@ } /* find other devices matching the composite ID and the release checksum */ - devices = fwupd_client_get_devices(priv->client, priv->cancellable, error); + devices = fwupd_client_get_devices(self->client, self->cancellable, error); if (devices == NULL) return FALSE; for (guint i = 0; i < devices->len; i++) { @@ -2909,9 +3033,9 @@ /* get releases */ if (!fwupd_device_has_flag(dev_tmp, FWUPD_DEVICE_FLAG_UPDATABLE)) continue; - rels = fwupd_client_get_releases(priv->client, + rels = fwupd_client_get_releases(self->client, fwupd_device_get_id(dev_tmp), - priv->cancellable, + self->cancellable, &error_local); if (rels == NULL) { g_debug("ignoring: %s", error_local->message); @@ -2921,19 +3045,22 @@ /* do any releases match this checksum */ for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel_tmp = g_ptr_array_index(rels, j); - if (fwupd_release_has_checksum(rel_tmp, rel_csum)) { - g_autofree gchar *title = - g_strdup_printf("%s %s", - fwupd_client_get_host_product(priv->client), - fwupd_client_get_host_product(priv->client)); - if (!fu_util_prompt_warning(priv->console, - dev_tmp, - rel_tmp, - title, - error)) - return FALSE; - break; - } + g_autofree gchar *title = NULL; + gint vercmp; + + if (!fwupd_release_has_checksum(rel_tmp, rel_csum)) + continue; + vercmp = fu_version_compare(fwupd_release_get_version(rel_tmp), + fu_device_get_version(dev_tmp), + fwupd_device_get_version_format(dev_tmp)); + if ((self->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) == 0 && vercmp == 0) + continue; + title = g_strdup_printf("%s %s", + fwupd_client_get_host_product(self->client), + fwupd_client_get_host_product(self->client)); + if (!fu_util_prompt_warning(self->console, dev_tmp, rel_tmp, title, error)) + return FALSE; + break; } } @@ -2942,7 +3069,7 @@ } static gboolean -fu_util_update_device_with_release(FuUtilPrivate *priv, +fu_util_update_device_with_release(FuUtil *self, FwupdDevice *dev, FwupdRelease *rel, GError **error) @@ -2961,28 +3088,28 @@ fwupd_device_get_update_error(dev)); return FALSE; } - if (!priv->no_safety_check && !priv->assume_yes) { - const gchar *title = fwupd_client_get_host_product(priv->client); - if (!fu_util_prompt_warning(priv->console, dev, rel, title, error)) + if (!self->as_json && !self->no_safety_check && !self->assume_yes) { + const gchar *title = fwupd_client_get_host_product(self->client); + if (!fu_util_prompt_warning(self->console, dev, rel, title, error)) return FALSE; - if (!fu_util_prompt_warning_fde(priv->console, dev, error)) + if (!fu_util_prompt_warning_fde(self->console, dev, error)) return FALSE; - if (!fu_util_prompt_warning_composite(priv, dev, rel, error)) + if (!fu_util_prompt_warning_composite(self, dev, rel, error)) return FALSE; - if (!fu_util_prompt_warning_bkc(priv, dev, rel, error)) + if (!fu_util_prompt_warning_bkc(self, dev, rel, error)) return FALSE; } - return fwupd_client_install_release(priv->client, + return fwupd_client_install_release(self->client, dev, rel, - priv->flags, - priv->download_flags, - priv->cancellable, + self->flags, + self->download_flags, + self->cancellable, error); } static gboolean -fu_util_maybe_send_reports(FuUtilPrivate *priv, FwupdRelease *rel, GError **error) +fu_util_maybe_send_reports(FuUtil *self, FwupdRelease *rel, GError **error) { g_autoptr(FwupdRemote) remote = NULL; g_autoptr(GError) error_local = NULL; @@ -2990,14 +3117,14 @@ g_debug("not sending reports, no remote"); return TRUE; } - remote = fwupd_client_get_remote_by_id(priv->client, + remote = fwupd_client_get_remote_by_id(self->client, fwupd_release_get_remote_id(rel), - priv->cancellable, + self->cancellable, error); if (remote == NULL) return FALSE; if (fwupd_remote_has_flag(remote, FWUPD_REMOTE_FLAG_AUTOMATIC_REPORTS)) { - if (!fu_util_report_history(priv, NULL, &error_local)) + if (!fu_util_report_history(self, NULL, &error_local)) if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) g_warning("%s", error_local->message); } @@ -3006,7 +3133,7 @@ } static gboolean -fu_util_update(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_update(FuUtil *self, gchar **values, GError **error) { gboolean supported = FALSE; g_autoptr(GPtrArray) devices = NULL; @@ -3014,7 +3141,7 @@ g_autoptr(GPtrArray) devices_pending = g_ptr_array_new(); g_autoptr(GPtrArray) devices_unsupported = g_ptr_array_new(); - if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) { + if (self->flags & FWUPD_INSTALL_FLAG_ALLOW_OLDER) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS, @@ -3022,7 +3149,7 @@ return FALSE; } - if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { + if (self->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS, @@ -3043,10 +3170,10 @@ } /* get devices from daemon */ - devices = fwupd_client_get_devices(priv->client, priv->cancellable, error); + devices = fwupd_client_get_devices(self->client, self->cancellable, error); if (devices == NULL) return FALSE; - priv->current_operation = FU_UTIL_OPERATION_UPDATE; + self->current_operation = FU_UTIL_OPERATION_UPDATE; g_ptr_array_sort(devices, fu_util_sort_devices_by_flags_cb); for (guint i = 0; i < devices->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices, i); @@ -3078,15 +3205,15 @@ if (g_strv_length(values) > 0 && dev_skip_byid) continue; if (!fwupd_device_match_flags(dev, - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; supported = TRUE; /* get the releases for this device and filter for validity */ - rels = fwupd_client_get_upgrades(priv->client, + rels = fwupd_client_get_upgrades(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, &error_install); if (rels == NULL) { g_ptr_array_add(devices_latest, dev); @@ -3097,8 +3224,8 @@ for (guint j = 0; j < rels->len; j++) { FwupdRelease *rel_tmp = g_ptr_array_index(rels, j); if (!fwupd_release_match_flags(rel_tmp, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; rel = g_object_ref(rel_tmp); break; @@ -3112,7 +3239,7 @@ continue; } - ret = fu_util_update_device_with_release(priv, dev, rel, &error_install); + ret = fu_util_update_device_with_release(self, dev, rel, &error_install); if (!ret && g_error_matches(error_install, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { g_debug("ignoring %s: %s", @@ -3121,10 +3248,10 @@ continue; } if (ret) - fu_util_display_current_message(priv); + fu_util_display_current_message(self); /* send report if we're supposed to */ - if (!fu_util_maybe_send_reports(priv, rel, &error_report)) { + if (!fu_util_maybe_send_reports(self, rel, &error_report)) { /* install failed, report failed */ if (!ret) { g_warning("%s", error_report->message); @@ -3142,44 +3269,43 @@ } /* show warnings */ - if (devices_latest->len > 0 && !priv->as_json) { - fu_console_print_literal(priv->console, + if (devices_latest->len > 0 && !self->as_json) { + fu_console_print_literal(self->console, /* TRANSLATORS: message letting the user know no device * upgrade available */ _("Devices with the latest available firmware version:")); for (guint i = 0; i < devices_latest->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices_latest, i); - fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev)); + fu_console_print(self->console, " • %s", fwupd_device_get_name(dev)); } } - if (devices_unsupported->len > 0 && !priv->as_json) { - fu_console_print_literal(priv->console, + if (devices_unsupported->len > 0 && !self->as_json) { + fu_console_print_literal(self->console, /* TRANSLATORS: message letting the user know no * device upgrade available due to missing on LVFS */ - _("Devices with no available firmware updates: ")); + _("Devices with no available firmware updates:")); for (guint i = 0; i < devices_unsupported->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices_unsupported, i); - fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev)); + fu_console_print(self->console, " • %s", fwupd_device_get_name(dev)); } } - if (devices_pending->len > 0 && !priv->as_json) { - fu_console_print_literal( - priv->console, - /* TRANSLATORS: message letting the user there is an update - * waiting, but there is a reason it cannot be deployed */ - _("Devices with firmware updates that need user action: ")); + if (devices_pending->len > 0 && !self->as_json) { + fu_console_print_literal(self->console, + /* TRANSLATORS: message letting the user there is an update + * waiting, but there is a reason it cannot be deployed */ + _("Devices with firmware updates that need user action:")); for (guint i = 0; i < devices_pending->len; i++) { FwupdDevice *dev = g_ptr_array_index(devices_pending, i); - fu_console_print(priv->console, " • %s", fwupd_device_get_name(dev)); + fu_console_print(self->console, " • %s", fwupd_device_get_name(dev)); for (guint j = 0; j < 64; j++) { FwupdDeviceProblem problem = (guint64)1 << j; g_autofree gchar *desc = NULL; if (!fwupd_device_has_problem(dev, problem)) continue; - desc = fu_util_device_problem_to_string(priv->client, dev, problem); + desc = fu_util_device_problem_to_string(self->client, dev, problem); if (desc == NULL) continue; - fu_console_print(priv->console, " ‣ %s", desc); + fu_console_print(self->console, " ‣ %s", desc); } } } @@ -3194,16 +3320,16 @@ } /* we don't want to ask anything */ - if (priv->no_reboot_check || priv->as_json) { + if (self->no_reboot_check || self->as_json) { g_debug("skipping reboot check"); return TRUE; } - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean -fu_util_remote_modify(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_remote_modify(FuUtil *self, gchar **values, GError **error) { g_autoptr(FwupdRemote) remote = NULL; if (g_strv_length(values) < 3) { @@ -3215,24 +3341,27 @@ } /* ensure the remote exists */ - remote = fwupd_client_get_remote_by_id(priv->client, values[0], priv->cancellable, error); + remote = fwupd_client_get_remote_by_id(self->client, values[0], self->cancellable, error); if (remote == NULL) return FALSE; - if (!fwupd_client_modify_remote(priv->client, + if (!fwupd_client_modify_remote(self->client, fwupd_remote_get_id(remote), values[1], values[2], - priv->cancellable, + self->cancellable, error)) return FALSE; + if (self->as_json) + return TRUE; + /* TRANSLATORS: success message for a per-remote setting change */ - fu_console_print_literal(priv->console, _("Successfully modified remote")); + fu_console_print_literal(self->console, _("Successfully modified remote")); return TRUE; } static gboolean -fu_util_remote_enable(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_remote_enable(FuUtil *self, gchar **values, GError **error) { g_autoptr(FwupdRemote) remote = NULL; if (g_strv_length(values) != 1) { @@ -3242,30 +3371,30 @@ "Invalid arguments"); return FALSE; } - remote = fwupd_client_get_remote_by_id(priv->client, values[0], priv->cancellable, error); + remote = fwupd_client_get_remote_by_id(self->client, values[0], self->cancellable, error); if (remote == NULL) return FALSE; - if (!fu_util_modify_remote_warning(priv->console, remote, priv->assume_yes, error)) + if (!fu_util_modify_remote_warning(self->console, remote, self->assume_yes, error)) return FALSE; - if (!fwupd_client_modify_remote(priv->client, + if (!fwupd_client_modify_remote(self->client, fwupd_remote_get_id(remote), "Enabled", "true", - priv->cancellable, + self->cancellable, error)) return FALSE; - if (priv->as_json) + if (self->as_json) return TRUE; /* ask for permission to refresh */ - if (priv->no_remote_check || fwupd_remote_get_kind(remote) != FWUPD_REMOTE_KIND_DOWNLOAD) { + if (self->no_remote_check || fwupd_remote_get_kind(remote) != FWUPD_REMOTE_KIND_DOWNLOAD) { /* TRANSLATORS: success message */ - fu_console_print_literal(priv->console, _("Successfully enabled remote")); + fu_console_print_literal(self->console, _("Successfully enabled remote")); return TRUE; } - if (!priv->assume_yes) { - if (!fu_console_input_bool(priv->console, + if (!self->assume_yes) { + if (!fu_console_input_bool(self->console, TRUE, "%s (%s)", /* TRANSLATORS: ask if we can update the metadata */ @@ -3273,24 +3402,52 @@ /* TRANSLATORS: metadata is downloaded */ _("Requires internet connection"))) { /* TRANSLATORS: success message */ - fu_console_print_literal(priv->console, _("Successfully enabled remote")); + fu_console_print_literal(self->console, _("Successfully enabled remote")); return TRUE; } } - if (!fwupd_client_refresh_remote(priv->client, + if (!fwupd_client_refresh_remote(self->client, remote, - priv->download_flags, - priv->cancellable, + self->download_flags, + self->cancellable, error)) return FALSE; /* TRANSLATORS: success message */ - fu_console_print_literal(priv->console, _("Successfully enabled and refreshed remote")); + fu_console_print_literal(self->console, _("Successfully enabled and refreshed remote")); return TRUE; } static gboolean -fu_util_remote_disable(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_remote_clean(FuUtil *self, gchar **values, GError **error) +{ + g_autoptr(FwupdRemote) remote = NULL; + if (g_strv_length(values) != 1) { + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_INVALID_ARGS, + "Invalid arguments"); + return FALSE; + } + remote = fwupd_client_get_remote_by_id(self->client, values[0], self->cancellable, error); + if (remote == NULL) + return FALSE; + if (!fwupd_client_clean_remote(self->client, + fwupd_remote_get_id(remote), + self->cancellable, + error)) + return FALSE; + + if (self->as_json) + return TRUE; + + /* TRANSLATORS: success message */ + fu_console_print_literal(self->console, _("Successfully cleaned remote")); + return TRUE; +} + +static gboolean +fu_util_remote_disable(FuUtil *self, gchar **values, GError **error) { g_autoptr(FwupdRemote) remote = NULL; @@ -3303,27 +3460,49 @@ } /* ensure the remote exists */ - remote = fwupd_client_get_remote_by_id(priv->client, values[0], priv->cancellable, error); + remote = fwupd_client_get_remote_by_id(self->client, values[0], self->cancellable, error); if (remote == NULL) return FALSE; - if (!fwupd_client_modify_remote(priv->client, + if (!fwupd_client_modify_remote(self->client, values[0], "Enabled", "false", - priv->cancellable, + self->cancellable, error)) return FALSE; - if (priv->as_json) + if (self->as_json) return TRUE; /* TRANSLATORS: success message */ - fu_console_print_literal(priv->console, _("Successfully disabled remote")); + fu_console_print_literal(self->console, _("Successfully disabled remote")); + + /* delete the now-unused cache files? */ + if (fwupd_remote_get_kind(remote) == FWUPD_REMOTE_KIND_DOWNLOAD && + fwupd_remote_get_age(remote) != G_MAXUINT64) { + if (self->assume_yes || + fu_console_input_bool(self->console, + FALSE, + "%s", + /* TRANSLATORS: this is now useless */ + _("Delete the now-unused remote cache files?"))) { + if (!fwupd_client_clean_remote(self->client, + values[0], + self->cancellable, + error)) + return FALSE; + } + fu_console_print_literal(self->console, + /* TRANSLATORS: success message */ + _("Successfully cleaned remote")); + } + + /* success */ return TRUE; } static gboolean -fu_util_downgrade(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_downgrade(FuUtil *self, gchar **values, GError **error) { gboolean ret; g_autoptr(FwupdDevice) dev = NULL; @@ -3331,7 +3510,7 @@ g_autoptr(GPtrArray) rels = NULL; g_autoptr(GError) error_report = NULL; - if (priv->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { + if (self->flags & FWUPD_INSTALL_FLAG_ALLOW_REINSTALL) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS, @@ -3339,39 +3518,39 @@ return FALSE; } - priv->filter_device_include |= FWUPD_DEVICE_FLAG_SUPPORTED; - dev = fu_util_get_device_or_prompt(priv, values, error); + self->filter_device_include |= FWUPD_DEVICE_FLAG_SUPPORTED; + dev = fu_util_get_device_or_prompt(self, values, error); if (dev == NULL) return FALSE; /* get the releases for this device and filter for validity */ - rels = fwupd_client_get_downgrades(priv->client, + rels = fwupd_client_get_downgrades(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, error); if (rels == NULL) { g_autofree gchar *downgrade_str = /* TRANSLATORS: message letting the user know no device downgrade available * %1 is the device name */ g_strdup_printf(_("No downgrades for %s"), fwupd_device_get_name(dev)); - g_prefix_error(error, "%s: ", downgrade_str); + g_prefix_error(error, "%s: ", downgrade_str); /* nocheck:error */ return FALSE; } /* get the chosen release */ - rel = fu_util_prompt_for_release(priv, rels, error); + rel = fu_util_prompt_for_release(self, rels, error); if (rel == NULL) return FALSE; /* update the console if composite devices are also updated */ - priv->current_operation = FU_UTIL_OPERATION_DOWNGRADE; - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; - ret = fu_util_update_device_with_release(priv, dev, rel, error); + self->current_operation = FU_UTIL_OPERATION_DOWNGRADE; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + ret = fu_util_update_device_with_release(self, dev, rel, error); if (ret) - fu_util_display_current_message(priv); + fu_util_display_current_message(self); /* send report if we're supposed to */ - if (!fu_util_maybe_send_reports(priv, rel, &error_report)) { + if (!fu_util_maybe_send_reports(self, rel, &error_report)) { /* install failed, report failed */ if (!ret) { g_warning("%s", error_report->message); @@ -3387,42 +3566,42 @@ return FALSE; /* we don't want to ask anything */ - if (priv->no_reboot_check) { + if (self->no_reboot_check) { g_debug("skipping reboot check"); return TRUE; } - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean -fu_util_reinstall(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_reinstall(FuUtil *self, gchar **values, GError **error) { gboolean ret; g_autoptr(FwupdRelease) rel = NULL; g_autoptr(FwupdDevice) dev = NULL; g_autoptr(GError) error_report = NULL; - priv->filter_device_include |= FWUPD_DEVICE_FLAG_SUPPORTED; - dev = fu_util_get_device_or_prompt(priv, values, error); + self->filter_device_include |= FWUPD_DEVICE_FLAG_SUPPORTED; + dev = fu_util_get_device_or_prompt(self, values, error); if (dev == NULL) return FALSE; /* try to lookup/match release from client */ rel = - fu_util_get_release_for_device_version(priv, dev, fwupd_device_get_version(dev), error); + fu_util_get_release_for_device_version(self, dev, fwupd_device_get_version(dev), error); if (rel == NULL) return FALSE; /* update the console if composite devices are also updated */ - priv->current_operation = FU_UTIL_OPERATION_INSTALL; - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; - ret = fu_util_update_device_with_release(priv, dev, rel, error); + self->current_operation = FU_UTIL_OPERATION_INSTALL; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + ret = fu_util_update_device_with_release(self, dev, rel, error); if (ret) - fu_util_display_current_message(priv); + fu_util_display_current_message(self); /* send report if we're supposed to */ - if (!fu_util_maybe_send_reports(priv, rel, &error_report)) { + if (!fu_util_maybe_send_reports(self, rel, &error_report)) { /* install failed, report failed */ if (!ret) { g_warning("%s", error_report->message); @@ -3438,16 +3617,16 @@ return FALSE; /* we don't want to ask anything */ - if (priv->no_reboot_check) { + if (self->no_reboot_check) { g_debug("skipping reboot check"); return TRUE; } - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean -fu_util_install(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_install(FuUtil *self, gchar **values, GError **error) { gboolean ret; g_autoptr(FwupdDevice) dev = NULL; @@ -3457,41 +3636,41 @@ /* fall back for CLI compatibility */ if (g_strv_length(values) >= 1) { if (g_file_test(values[0], G_FILE_TEST_EXISTS) || fu_util_is_url(values[0])) - return fu_util_local_install(priv, values, error); + return fu_util_local_install(self, values, error); } /* find device */ - priv->filter_device_include |= FWUPD_DEVICE_FLAG_SUPPORTED; - dev = fu_util_get_device_or_prompt(priv, values, error); + self->filter_device_include |= FWUPD_DEVICE_FLAG_SUPPORTED; + dev = fu_util_get_device_or_prompt(self, values, error); if (dev == NULL) return FALSE; /* find release */ if (g_strv_length(values) >= 2) { - rel = fu_util_get_release_for_device_version(priv, dev, values[1], error); + rel = fu_util_get_release_for_device_version(self, dev, values[1], error); if (rel == NULL) return FALSE; } else { g_autoptr(GPtrArray) rels = NULL; - rels = fwupd_client_get_releases(priv->client, + rels = fwupd_client_get_releases(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, error); if (rels == NULL) return FALSE; - rel = fu_util_prompt_for_release(priv, rels, error); + rel = fu_util_prompt_for_release(self, rels, error); if (rel == NULL) return FALSE; } /* allow all actions */ - priv->current_operation = FU_UTIL_OPERATION_INSTALL; - ret = fu_util_update_device_with_release(priv, dev, rel, error); + self->current_operation = FU_UTIL_OPERATION_INSTALL; + ret = fu_util_update_device_with_release(self, dev, rel, error); if (ret) - fu_util_display_current_message(priv); + fu_util_display_current_message(self); /* send report if we're supposed to */ - if (!fu_util_maybe_send_reports(priv, rel, &error_report)) { + if (!fu_util_maybe_send_reports(self, rel, &error_report)) { /* install failed, report failed */ if (!ret) { g_warning("%s", error_report->message); @@ -3507,12 +3686,12 @@ return FALSE; /* we don't want to ask anything */ - if (priv->no_reboot_check) { + if (self->no_reboot_check) { g_debug("skipping reboot check"); return TRUE; } - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean @@ -3522,7 +3701,7 @@ } static gboolean -fu_util_switch_branch(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_switch_branch(FuUtil *self, gchar **values, GError **error) { const gchar *branch; gboolean ret; @@ -3533,16 +3712,16 @@ g_autoptr(GError) error_report = NULL; /* find the device and check it has multiple branches */ - priv->filter_device_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES; - priv->filter_device_include |= FWUPD_DEVICE_FLAG_UPDATABLE; - dev = fu_util_get_device_or_prompt(priv, values, error); + self->filter_device_include |= FWUPD_DEVICE_FLAG_HAS_MULTIPLE_BRANCHES; + self->filter_device_include |= FWUPD_DEVICE_FLAG_UPDATABLE; + dev = fu_util_get_device_or_prompt(self, values, error); if (dev == NULL) return FALSE; /* get all releases, including the alternate branch versions */ - rels = fwupd_client_get_releases(priv->client, + rels = fwupd_client_get_releases(self->client, fwupd_device_get_id(dev), - priv->cancellable, + self->cancellable, error); if (rels == NULL) return FALSE; @@ -3552,8 +3731,8 @@ FwupdRelease *rel_tmp = g_ptr_array_index(rels, i); const gchar *branch_tmp = fwupd_release_get_branch(rel_tmp); if (!fwupd_release_match_flags(rel_tmp, - priv->filter_release_include, - priv->filter_release_exclude)) + self->filter_release_include, + self->filter_release_exclude)) continue; if (g_ptr_array_find_with_equal_func(branches, branch_tmp, _g_str_equal0, NULL)) continue; @@ -3569,17 +3748,17 @@ guint idx; /* TRANSLATORS: this is to abort the interactive prompt */ - fu_console_print(priv->console, "0.\t%s", _("Cancel")); + fu_console_print(self->console, "0.\t%s", _("Cancel")); for (guint i = 0; i < branches->len; i++) { const gchar *branch_tmp = g_ptr_array_index(branches, i); - fu_console_print(priv->console, + fu_console_print(self->console, "%u.\t%s", i + 1, fu_util_branch_for_display(branch_tmp)); } /* TRANSLATORS: get interactive prompt, where branch is the * supplier of the firmware, e.g. "non-free" or "free" */ - idx = fu_console_input_uint(priv->console, branches->len, "%s", _("Choose branch")); + idx = fu_console_input_uint(self->console, branches->len, "%s", _("Choose branch")); if (idx == 0) { g_set_error_literal(error, FWUPD_ERROR, @@ -3619,19 +3798,19 @@ } /* we're switching branch */ - if (!fu_util_switch_branch_warning(priv->console, dev, rel, priv->assume_yes, error)) + if (!fu_util_switch_branch_warning(self->console, dev, rel, self->assume_yes, error)) return FALSE; /* update the console if composite devices are also updated */ - priv->current_operation = FU_UTIL_OPERATION_INSTALL; - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; - ret = fu_util_update_device_with_release(priv, dev, rel, error); + self->current_operation = FU_UTIL_OPERATION_INSTALL; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + ret = fu_util_update_device_with_release(self, dev, rel, error); if (ret) - fu_util_display_current_message(priv); + fu_util_display_current_message(self); /* send report if we're supposed to */ - if (!fu_util_maybe_send_reports(priv, rel, &error_report)) { + if (!fu_util_maybe_send_reports(self, rel, &error_report)) { /* install failed, report failed */ if (!ret) { g_warning("%s", error_report->message); @@ -3647,16 +3826,16 @@ return FALSE; /* we don't want to ask anything */ - if (priv->no_reboot_check) { + if (self->no_reboot_check) { g_debug("skipping reboot check"); return TRUE; } - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean -fu_util_activate(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_activate(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) devices = NULL; gboolean has_pending = FALSE; @@ -3664,7 +3843,7 @@ /* handle both forms */ if (g_strv_length(values) == 0) { /* activate anything with _NEEDS_ACTIVATION */ - devices = fwupd_client_get_devices(priv->client, priv->cancellable, error); + devices = fwupd_client_get_devices(self->client, self->cancellable, error); if (devices == NULL) return FALSE; for (guint i = 0; i < devices->len; i++) { @@ -3675,7 +3854,7 @@ } } } else if (g_strv_length(values) == 1) { - FwupdDevice *device = fu_util_get_device_by_id(priv, values[0], error); + FwupdDevice *device = fu_util_get_device_by_id(self, values[0], error); if (device == NULL) return FALSE; devices = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); @@ -3703,32 +3882,35 @@ for (guint i = 0; i < devices->len; i++) { FwupdDevice *device = g_ptr_array_index(devices, i); if (!fwupd_device_match_flags(device, - priv->filter_device_include, - priv->filter_device_exclude)) + self->filter_device_include, + self->filter_device_exclude)) continue; if (!fwupd_device_has_flag(device, FWUPD_DEVICE_FLAG_NEEDS_ACTIVATION)) continue; fu_console_print( - priv->console, + self->console, "%s %s…", /* TRANSLATORS: shown when shutting down to switch to the new version */ _("Activating firmware update for"), fwupd_device_get_name(device)); - if (!fwupd_client_activate(priv->client, - priv->cancellable, + if (!fwupd_client_activate(self->client, + self->cancellable, fwupd_device_get_id(device), error)) return FALSE; } + if (self->as_json) + return TRUE; + /* TRANSLATORS: success message -- where activation is making the new * firmware take effect, usually after updating offline */ - fu_console_print_literal(priv->console, _("Successfully activated all devices")); + fu_console_print_literal(self->console, _("Successfully activated all devices")); return TRUE; } static gboolean -fu_util_set_approved_firmware(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_set_approved_firmware(FuUtil *self, gchar **values, GError **error) { g_auto(GStrv) checksums = NULL; @@ -3752,14 +3934,14 @@ } /* call into daemon */ - return fwupd_client_set_approved_firmware(priv->client, + return fwupd_client_set_approved_firmware(self->client, checksums, - priv->cancellable, + self->cancellable, error); } static gboolean -fu_util_get_checksums_as_json(FuUtilPrivate *priv, gchar **csums, GError **error) +fu_util_get_checksums_as_json(FuUtil *self, gchar **csums, GError **error) { g_autoptr(JsonBuilder) builder = json_builder_new(); json_builder_begin_object(builder); @@ -3769,11 +3951,11 @@ json_builder_add_string_value(builder, csums[i]); json_builder_end_array(builder); json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } static gboolean -fu_util_get_approved_firmware(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_approved_firmware(FuUtil *self, gchar **values, GError **error) { g_auto(GStrv) checksums = NULL; @@ -3787,45 +3969,45 @@ } /* call into daemon */ - checksums = fwupd_client_get_approved_firmware(priv->client, priv->cancellable, error); + checksums = fwupd_client_get_approved_firmware(self->client, self->cancellable, error); if (checksums == NULL) return FALSE; - if (priv->as_json) - return fu_util_get_checksums_as_json(priv, checksums, error); + if (self->as_json) + return fu_util_get_checksums_as_json(self, checksums, error); if (g_strv_length(checksums) == 0) { /* TRANSLATORS: approved firmware has been checked by * the domain administrator */ - fu_console_print_literal(priv->console, _("There is no approved firmware.")); + fu_console_print_literal(self->console, _("There is no approved firmware.")); } else { fu_console_print_literal( - priv->console, + self->console, /* TRANSLATORS: approved firmware has been checked by * the domain administrator */ ngettext("Approved firmware:", "Approved firmware:", g_strv_length(checksums))); for (guint i = 0; checksums[i] != NULL; i++) - fu_console_print(priv->console, " * %s", checksums[i]); + fu_console_print(self->console, " * %s", checksums[i]); } return TRUE; } static gboolean -fu_util_modify_config(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_modify_config(FuUtil *self, gchar **values, GError **error) { /* check args */ if (g_strv_length(values) == 3) { - if (!fwupd_client_modify_config(priv->client, + if (!fwupd_client_modify_config(self->client, values[0], values[1], values[2], - priv->cancellable, + self->cancellable, error)) return FALSE; } else if (g_strv_length(values) == 2) { - if (!fwupd_client_modify_config(priv->client, + if (!fwupd_client_modify_config(self->client, "fwupd", values[0], values[1], - priv->cancellable, + self->cancellable, error)) return FALSE; } else { @@ -3836,11 +4018,11 @@ return FALSE; } - if (priv->as_json) + if (self->as_json) return TRUE; - if (!priv->assume_yes) { - if (!fu_console_input_bool(priv->console, + if (!self->assume_yes) { + if (!fu_console_input_bool(self->console, FALSE, "%s", /* TRANSLATORS: changes only take effect on restart */ @@ -3848,18 +4030,18 @@ return TRUE; } - if (!fu_util_quit(priv, NULL, error)) + if (!fu_util_quit(self, NULL, error)) return FALSE; - if (!fwupd_client_connect(priv->client, priv->cancellable, error)) + if (!fwupd_client_connect(self->client, self->cancellable, error)) return FALSE; /* TRANSLATORS: success message -- a per-system setting value */ - fu_console_print_literal(priv->console, _("Successfully modified configuration value")); + fu_console_print_literal(self->console, _("Successfully modified configuration value")); return TRUE; } static gboolean -fu_util_reset_config(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_reset_config(FuUtil *self, gchar **values, GError **error) { /* check args */ if (g_strv_length(values) != 1) { @@ -3869,37 +4051,37 @@ "Invalid arguments: SECTION expected"); return FALSE; } - if (!fwupd_client_reset_config(priv->client, values[0], priv->cancellable, error)) + if (!fwupd_client_reset_config(self->client, values[0], self->cancellable, error)) return FALSE; - if (priv->as_json) + if (self->as_json) return TRUE; - if (!priv->assume_yes) { - if (!fu_console_input_bool(priv->console, + if (!self->assume_yes) { + if (!fu_console_input_bool(self->console, FALSE, "%s", /* TRANSLATORS: changes only take effect on restart */ _("Restart the daemon to make the change effective?"))) return TRUE; } - if (!fu_util_quit(priv, NULL, error)) + if (!fu_util_quit(self, NULL, error)) return FALSE; - if (!fwupd_client_connect(priv->client, priv->cancellable, error)) + if (!fwupd_client_connect(self->client, self->cancellable, error)) return FALSE; /* TRANSLATORS: success message -- a per-system setting value */ - fu_console_print_literal(priv->console, _("Successfully reset configuration values")); + fu_console_print_literal(self->console, _("Successfully reset configuration values")); return TRUE; } static FwupdRemote * -fu_util_get_remote_with_report_uri(FuUtilPrivate *priv, GError **error) +fu_util_get_remote_with_report_uri(FuUtil *self, GError **error) { g_autoptr(GPtrArray) remotes = NULL; /* get all remotes */ - remotes = fwupd_client_get_remotes(priv->client, priv->cancellable, error); + remotes = fwupd_client_get_remotes(self->client, self->cancellable, error); if (remotes == NULL) return NULL; @@ -3920,7 +4102,7 @@ } static gboolean -fu_util_upload_security(FuUtilPrivate *priv, GPtrArray *attrs, GError **error) +fu_util_upload_security(FuUtil *self, GPtrArray *attrs, GError **error) { g_autofree gchar *data = NULL; g_autofree gchar *report_uri = NULL; @@ -3931,24 +4113,24 @@ g_autoptr(GHashTable) metadata = NULL; /* can we find a remote with a security attr */ - remote = fu_util_get_remote_with_report_uri(priv, &error_local); + remote = fu_util_get_remote_with_report_uri(self, &error_local); if (remote == NULL) { g_debug("failed to find suitable remote: %s", error_local->message); return TRUE; } /* export as a string */ - metadata = fwupd_client_get_report_metadata(priv->client, priv->cancellable, error); + metadata = fwupd_client_get_report_metadata(self->client, self->cancellable, error); if (metadata == NULL) return FALSE; - data = fwupd_client_build_report_security(priv->client, attrs, metadata, error); + data = fwupd_client_build_report_security(self->client, attrs, metadata, error); if (data == NULL) return FALSE; /* ask for permission */ - if (!priv->assume_yes && + if (!self->assume_yes && !fwupd_remote_has_flag(remote, FWUPD_REMOTE_FLAG_AUTOMATIC_SECURITY_REPORTS)) { - if (!fu_console_input_bool(priv->console, + if (!fu_console_input_bool(self->console, FALSE, /* TRANSLATORS: ask the user to share, %s is something * like: "Linux Vendor Firmware Service" */ @@ -3960,26 +4142,26 @@ } /* self sign data */ - if (priv->sign) { - sig = fwupd_client_self_sign(priv->client, + if (self->sign) { + sig = fwupd_client_self_sign(self->client, data, FWUPD_SELF_SIGN_FLAG_ADD_TIMESTAMP, - priv->cancellable, + self->cancellable, error); if (sig == NULL) return FALSE; } /* ask for permission */ - if (!priv->assume_yes && + if (!self->assume_yes && !fwupd_remote_has_flag(remote, FWUPD_REMOTE_FLAG_AUTOMATIC_SECURITY_REPORTS)) { - fu_console_print_kv(priv->console, + fu_console_print_kv(self->console, _("Target"), fwupd_remote_get_report_uri(remote)); - fu_console_print_kv(priv->console, _("Payload"), data); + fu_console_print_kv(self->console, _("Payload"), data); if (sig != NULL) - fu_console_print_kv(priv->console, _("Signature"), sig); - if (!fu_console_input_bool(priv->console, TRUE, "%s", _("Proceed with upload?"))) { + fu_console_print_kv(self->console, _("Signature"), sig); + if (!fu_console_input_bool(self->console, TRUE, "%s", _("Proceed with upload?"))) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_PERMISSION_DENIED, @@ -3992,31 +4174,31 @@ report_uri = fwupd_remote_build_report_uri(remote, error); if (report_uri == NULL) return FALSE; - uri = fwupd_client_upload_report(priv->client, + uri = fwupd_client_upload_report(self->client, report_uri, data, sig, FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART, - priv->cancellable, + self->cancellable, error); if (uri == NULL) return FALSE; - fu_console_print_literal(priv->console, + fu_console_print_literal(self->console, /* TRANSLATORS: success, so say thank you to the user */ _("Host Security ID attributes uploaded successfully, thanks!")); /* as this worked, ask if the user want to do this every time */ if (!fwupd_remote_has_flag(remote, FWUPD_REMOTE_FLAG_AUTOMATIC_SECURITY_REPORTS)) { - if (fu_console_input_bool(priv->console, + if (fu_console_input_bool(self->console, FALSE, "%s", /* TRANSLATORS: can we JFDI? */ _("Automatically upload every time?"))) { - if (!fwupd_client_modify_remote(priv->client, + if (!fwupd_client_modify_remote(self->client, fwupd_remote_get_id(remote), "AutomaticSecurityReports", "true", - priv->cancellable, + self->cancellable, error)) return FALSE; } @@ -4026,7 +4208,7 @@ } static gboolean -fu_util_security_as_json(FuUtilPrivate *priv, +fu_util_security_as_json(FuUtil *self, GPtrArray *attrs, GPtrArray *events, GPtrArray *devices, @@ -4065,21 +4247,21 @@ } json_builder_end_object(builder); - return fu_util_print_builder(priv->console, builder, error); + return fu_util_print_builder(self->console, builder, error); } static gboolean -fu_util_sync(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_sync(FuUtil *self, gchar **values, GError **error) { - const gchar *host_bkc = fwupd_client_get_host_bkc(priv->client); + const gchar *host_bkc = fwupd_client_get_host_bkc(self->client); guint cnt = 0; g_autoptr(GPtrArray) devices = NULL; /* update the console if composite devices are also updated */ - priv->current_operation = FU_UTIL_OPERATION_INSTALL; - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + self->current_operation = FU_UTIL_OPERATION_INSTALL; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; - devices = fwupd_client_get_devices(priv->client, NULL, error); + devices = fwupd_client_get_devices(self->client, NULL, error); if (devices == NULL) return FALSE; for (guint i = 0; i < devices->len; i++) { @@ -4089,9 +4271,9 @@ /* find the release that matches the tag */ if (host_bkc != NULL) { - rel = fu_util_get_release_with_tag(priv, dev, host_bkc, &error_local); + rel = fu_util_get_release_with_tag(self, dev, host_bkc, &error_local); } else if (fu_device_get_branch(dev) != NULL) { - rel = fu_util_get_release_with_branch(priv, + rel = fu_util_get_release_with_branch(self, dev, fu_device_get_branch(dev), &error_local); @@ -4100,6 +4282,7 @@ FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "No device branch or system HostBkc set"); + /* nocheck:error-false-return */ } if (rel == NULL) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED) || @@ -4122,7 +4305,7 @@ fwupd_device_get_id(dev), fwupd_device_get_version(dev), fwupd_release_get_version(rel)); - if (!fu_util_update_device_with_release(priv, dev, rel, &error_local)) { + if (!fu_util_update_device_with_release(self, dev, rel, &error_local)) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { g_debug("ignoring %s: %s", fwupd_device_get_id(dev), @@ -4132,31 +4315,31 @@ g_propagate_error(error, g_steal_pointer(&error_local)); return FALSE; } - fu_util_display_current_message(priv); + fu_util_display_current_message(self); cnt++; } /* nothing was done */ if (cnt == 0) { - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOTHING_TO_DO, - "No devices required modification"); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOTHING_TO_DO, + "No devices required modification"); return FALSE; } /* we don't want to ask anything */ - if (priv->no_reboot_check) { + if (self->no_reboot_check) { g_debug("skipping reboot check"); return TRUE; } /* show reboot if needed */ - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean -fu_util_security_fix_attr(FuUtilPrivate *priv, FwupdSecurityAttr *attr, GError **error) +fu_util_security_fix_attr(FuUtil *self, FwupdSecurityAttr *attr, GError **error) { g_autoptr(GString) body = g_string_new(NULL); g_autoptr(GString) title = g_string_new(NULL); @@ -4216,24 +4399,24 @@ "instability.")); } - fu_console_box(priv->console, title->str, body->str, 80); + fu_console_box(self->console, title->str, body->str, 80); /* TRANSLATORS: prompt to apply the update */ - if (!fu_console_input_bool(priv->console, FALSE, "%s", _("Perform operation?"))) + if (!fu_console_input_bool(self->console, FALSE, "%s", _("Perform operation?"))) return TRUE; - if (!fwupd_client_fix_host_security_attr(priv->client, + if (!fwupd_client_fix_host_security_attr(self->client, fwupd_security_attr_get_appstream_id(attr), - priv->cancellable, + self->cancellable, error)) return FALSE; /* do not offer to upload the report */ - priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; return TRUE; } static gboolean -fu_util_security(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_security(FuUtil *self, gchar **values, GError **error) { FuSecurityAttrToStringFlags flags = FU_SECURITY_ATTR_TO_STRING_FLAG_NONE; g_autoptr(GPtrArray) attrs = NULL; @@ -4243,23 +4426,23 @@ g_autofree gchar *str = NULL; #ifndef HAVE_HSI - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - /* TRANSLATORS: error message for unsupported feature */ - _("Host Security ID (HSI) is not supported")); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + /* TRANSLATORS: error message for unsupported feature */ + _("Host Security ID (HSI) is not supported")); return FALSE; #endif /* HAVE_HSI */ /* the "why" */ - attrs = fwupd_client_get_host_security_attrs(priv->client, priv->cancellable, error); + attrs = fwupd_client_get_host_security_attrs(self->client, self->cancellable, error); if (attrs == NULL) return FALSE; /* the "when" */ - events = fwupd_client_get_host_security_events(priv->client, + events = fwupd_client_get_host_security_events(self->client, 10, - priv->cancellable, + self->cancellable, &error_local); if (events == NULL) { if (g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED)) { @@ -4271,7 +4454,7 @@ } /* the "also" */ - devices = fwupd_client_get_devices(priv->client, priv->cancellable, &error_local); + devices = fwupd_client_get_devices(self->client, self->cancellable, &error_local); if (devices == NULL) { if (!g_error_matches(error_local, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) { g_propagate_error(error, g_steal_pointer(&error_local)); @@ -4280,35 +4463,35 @@ } /* not for human consumption */ - if (priv->as_json) - return fu_util_security_as_json(priv, attrs, events, devices, error); + if (self->as_json) + return fu_util_security_as_json(self, attrs, events, devices, error); - fu_console_print(priv->console, + fu_console_print(self->console, "%s \033[1m%s\033[0m", /* TRANSLATORS: this is a string like 'HSI:2-U' */ _("Host Security ID:"), - fwupd_client_get_host_security_id(priv->client)); + fwupd_client_get_host_security_id(self->client)); /* show or hide different elements */ - if (priv->show_all) { + if (self->show_all) { flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_OBSOLETES; flags |= FU_SECURITY_ATTR_TO_STRING_FLAG_SHOW_URLS; } str = fu_util_security_attrs_to_string(attrs, flags); - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); /* events */ if (events != NULL && events->len > 0) { g_autofree gchar *estr = fu_util_security_events_to_string(events, flags); if (estr != NULL) - fu_console_print_literal(priv->console, estr); + fu_console_print_literal(self->console, estr); } /* known CVEs */ if (devices != NULL && devices->len > 0) { g_autofree gchar *estr = fu_util_security_issues_to_string(devices); if (estr != NULL) - fu_console_print_literal(priv->console, estr); + fu_console_print_literal(self->console, estr); } /* host emulation */ @@ -4316,33 +4499,33 @@ FwupdSecurityAttr *attr = g_ptr_array_index(attrs, j); if (g_strcmp0(fwupd_security_attr_get_appstream_id(attr), FWUPD_SECURITY_ATTR_ID_HOST_EMULATION) == 0) { - priv->no_unreported_check = TRUE; + self->no_unreported_check = TRUE; break; } } /* any things we can fix? */ - if (!priv->no_security_fix) { + if (!self->no_security_fix) { for (guint j = 0; j < attrs->len; j++) { FwupdSecurityAttr *attr = g_ptr_array_index(attrs, j); if (fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_CAN_FIX) && !fwupd_security_attr_has_flag(attr, FWUPD_SECURITY_ATTR_FLAG_SUCCESS)) { - if (!fu_util_security_fix_attr(priv, attr, error)) + if (!fu_util_security_fix_attr(self, attr, error)) return FALSE; } } } /* upload, with confirmation */ - if (!priv->no_unreported_check) { - if (!fu_util_upload_security(priv, attrs, error)) + if (!self->no_unreported_check) { + if (!fu_util_upload_security(self, attrs, error)) return FALSE; } /* reboot is required? */ - if (!priv->no_reboot_check && - (priv->completion_flags & FWUPD_DEVICE_FLAG_NEEDS_REBOOT) > 0) { - if (!fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error)) + if (!self->no_reboot_check && + (self->completion_flags & FWUPD_DEVICE_FLAG_NEEDS_REBOOT) > 0) { + if (!fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error)) return FALSE; } @@ -4362,51 +4545,51 @@ static gboolean fu_util_sigint_cb(gpointer user_data) { - FuUtilPrivate *priv = (FuUtilPrivate *)user_data; - g_debug("Handling SIGINT"); - g_cancellable_cancel(priv->cancellable); + FuUtil *self = (FuUtil *)user_data; + g_debug("handling SIGINT"); + g_cancellable_cancel(self->cancellable); return FALSE; } #endif static void -fu_util_setup_signal_handlers(FuUtilPrivate *priv) +fu_util_setup_signal_handlers(FuUtil *self) { #ifdef HAVE_GIO_UNIX g_autoptr(GSource) source = g_unix_signal_source_new(SIGINT); - g_source_set_callback(source, fu_util_sigint_cb, priv, NULL); - g_source_attach(g_steal_pointer(&source), priv->main_ctx); + g_source_set_callback(source, fu_util_sigint_cb, self, NULL); + g_source_attach(g_steal_pointer(&source), self->main_ctx); #endif } static void -fu_util_private_free(FuUtilPrivate *priv) +fu_util_private_free(FuUtil *self) { - if (priv->client != NULL) { + if (self->client != NULL) { /* when destroying GDBusProxy in a custom GMainContext, the context must be * iterated enough after finalization of the proxies that any pending D-Bus traffic * can be freed */ - fwupd_client_disconnect(priv->client, NULL); - while (g_main_context_iteration(priv->main_ctx, FALSE)) { + fwupd_client_disconnect(self->client, NULL); + while (g_main_context_iteration(self->main_ctx, FALSE)) { /* nothing needs to be done here */ }; - g_object_unref(priv->client); + g_object_unref(self->client); } - if (priv->current_device != NULL) - g_object_unref(priv->current_device); - g_ptr_array_unref(priv->post_requests); - g_main_loop_unref(priv->loop); - g_main_context_unref(priv->main_ctx); - g_object_unref(priv->cancellable); - g_object_unref(priv->console); - g_option_context_free(priv->context); - g_free(priv); + if (self->current_device != NULL) + g_object_unref(self->current_device); + g_ptr_array_unref(self->post_requests); + g_main_loop_unref(self->loop); + g_main_context_unref(self->main_ctx); + g_object_unref(self->cancellable); + g_object_unref(self->console); + g_option_context_free(self->context); + g_free(self); } static gboolean -fu_util_check_daemon_version(FuUtilPrivate *priv, GError **error) +fu_util_check_daemon_version(FuUtil *self, GError **error) { - const gchar *daemon = fwupd_client_get_daemon_version(priv->client); + const gchar *daemon = fwupd_client_get_daemon_version(self->client); if (daemon == NULL) { g_set_error_literal(error, @@ -4435,14 +4618,12 @@ fu_util_check_polkit_actions(GError **error) { #ifdef HAVE_POLKIT - g_autofree gchar *directory = NULL; g_autofree gchar *filename = NULL; if (g_getenv("FWUPD_POLKIT_NOCHECK") != NULL) return TRUE; - directory = fu_path_from_kind(FU_PATH_KIND_POLKIT_ACTIONS); - filename = g_build_filename(directory, "org.freedesktop.fwupd.policy", NULL); + filename = fu_path_build(FU_PATH_KIND_POLKIT_ACTIONS, "org.freedesktop.fwupd.policy", NULL); if (!g_file_test(filename, G_FILE_TEST_IS_REGULAR)) { g_set_error_literal( error, @@ -4459,24 +4640,24 @@ #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunused-function" -G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtilPrivate, fu_util_private_free) +G_DEFINE_AUTOPTR_CLEANUP_FUNC(FuUtil, fu_util_private_free) #pragma clang diagnostic pop static gchar * -fu_util_get_history_checksum(FuUtilPrivate *priv, GError **error) +fu_util_get_history_checksum(FuUtil *self, GError **error) { const gchar *csum; g_autoptr(FwupdDevice) device = NULL; g_autoptr(FwupdRelease) release = NULL; g_autoptr(GPtrArray) devices = NULL; - devices = fwupd_client_get_history(priv->client, priv->cancellable, error); + devices = fwupd_client_get_history(self->client, self->cancellable, error); if (devices == NULL) return NULL; - device = fu_util_prompt_for_device(priv, devices, error); + device = fu_util_prompt_for_device(self, devices, error); if (device == NULL) return NULL; - release = fu_util_prompt_for_release(priv, fwupd_device_get_releases(device), error); + release = fu_util_prompt_for_release(self, fwupd_device_get_releases(device), error); if (release == NULL) return NULL; csum = fwupd_checksum_get_best(fwupd_release_get_checksums(release)); @@ -4491,21 +4672,28 @@ } static gboolean -fu_util_block_firmware(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_block_firmware(FuUtil *self, gchar **values, GError **error) { guint idx = 0; g_autofree gchar *csum = NULL; g_auto(GStrv) csums_new = NULL; g_auto(GStrv) csums = NULL; + /* we dropped this in 2.1.1... */ + fu_console_print_full(self->console, + FU_CONSOLE_PRINT_FLAG_WARNING, + "%s\n", + /* TRANSLATORS: the user should not rely on this working long-term */ + _("This functionality has been removed in newer fwupd versions.")); + /* get existing checksums */ - csums = fwupd_client_get_blocked_firmware(priv->client, priv->cancellable, error); + csums = fwupd_client_get_blocked_firmware(self->client, self->cancellable, error); if (csums == NULL) return FALSE; /* get new value */ if (g_strv_length(values) == 0) { - csum = fu_util_get_history_checksum(priv, error); + csum = fu_util_get_history_checksum(self, error); if (csum == NULL) return FALSE; } else { @@ -4523,7 +4711,7 @@ } /* TRANSLATORS: we will not offer this firmware to the user */ - fu_console_print(priv->console, "%s %s", _("Blocking firmware:"), csum); + fu_console_print(self->console, "%s %s", _("Blocking firmware:"), csum); /* remove it from the new list */ csums_new = g_new0(gchar *, g_strv_length(csums) + 2); @@ -4532,19 +4720,26 @@ csums_new[idx++] = g_strdup(csums[i]); } csums_new[idx] = g_strdup(csum); - return fwupd_client_set_blocked_firmware(priv->client, csums_new, priv->cancellable, error); + return fwupd_client_set_blocked_firmware(self->client, csums_new, self->cancellable, error); } static gboolean -fu_util_unblock_firmware(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_unblock_firmware(FuUtil *self, gchar **values, GError **error) { guint idx = 0; g_auto(GStrv) csums = NULL; g_auto(GStrv) csums_new = NULL; g_autofree gchar *csum = NULL; + /* we dropped this in 2.1.1... */ + fu_console_print_full(self->console, + FU_CONSOLE_PRINT_FLAG_WARNING, + "%s\n", + /* TRANSLATORS: the user should not rely on this working long-term */ + _("This functionality has been removed in newer fwupd versions.")); + /* get existing checksums */ - csums = fwupd_client_get_blocked_firmware(priv->client, priv->cancellable, error); + csums = fwupd_client_get_blocked_firmware(self->client, self->cancellable, error); if (csums == NULL) return FALSE; @@ -4560,7 +4755,7 @@ /* get new value */ if (g_strv_length(values) == 0) { - csum = fu_util_get_history_checksum(priv, error); + csum = fu_util_get_history_checksum(self, error); if (csum == NULL) return FALSE; } else { @@ -4578,7 +4773,7 @@ } /* TRANSLATORS: we will now offer this firmware to the user */ - fu_console_print(priv->console, "%s %s", _("Unblocking firmware:"), csum); + fu_console_print(self->console, "%s %s", _("Unblocking firmware:"), csum); /* remove it from the new list */ csums_new = g_new0(gchar *, g_strv_length(csums)); @@ -4586,32 +4781,39 @@ if (g_strcmp0(csums[i], csum) != 0) csums_new[idx++] = g_strdup(csums[i]); } - return fwupd_client_set_blocked_firmware(priv->client, csums_new, priv->cancellable, error); + return fwupd_client_set_blocked_firmware(self->client, csums_new, self->cancellable, error); } static gboolean -fu_util_get_blocked_firmware(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_blocked_firmware(FuUtil *self, gchar **values, GError **error) { g_auto(GStrv) csums = NULL; + /* we dropped this in 2.1.1... */ + fu_console_print_full(self->console, + FU_CONSOLE_PRINT_FLAG_WARNING, + "%s\n", + /* TRANSLATORS: the user should not rely on this working long-term */ + _("This functionality has been removed in newer fwupd versions.")); + /* get checksums */ - csums = fwupd_client_get_blocked_firmware(priv->client, priv->cancellable, error); + csums = fwupd_client_get_blocked_firmware(self->client, self->cancellable, error); if (csums == NULL) return FALSE; - if (priv->as_json) - return fu_util_get_checksums_as_json(priv, csums, error); + if (self->as_json) + return fu_util_get_checksums_as_json(self, csums, error); /* empty list */ if (g_strv_length(csums) == 0) { /* TRANSLATORS: nothing to show */ - fu_console_print_literal(priv->console, _("There are no blocked firmware files")); + fu_console_print_literal(self->console, _("There are no blocked firmware files")); return TRUE; } /* TRANSLATORS: there follows a list of hashes */ - fu_console_print_literal(priv->console, _("Blocked firmware files:")); + fu_console_print_literal(self->console, _("Blocked firmware files:")); for (guint i = 0; csums[i] != NULL; i++) { - fu_console_print(priv->console, "%u.\t%s", i + 1, csums[i]); + fu_console_print(self->console, "%u.\t%s", i + 1, csums[i]); } /* success */ @@ -4619,16 +4821,16 @@ } static void -fu_util_show_plugin_warnings(FuUtilPrivate *priv) +fu_util_show_plugin_warnings(FuUtil *self) { FwupdPluginFlags flags = FWUPD_PLUGIN_FLAG_NONE; g_autoptr(GPtrArray) plugins = NULL; - if (priv->as_json) + if (self->as_json) return; /* get plugins from daemon, ignoring if the daemon is too old */ - plugins = fwupd_client_get_plugins(priv->client, priv->cancellable, NULL); + plugins = fwupd_client_get_plugins(self->client, self->cancellable, NULL); if (plugins == NULL) return; @@ -4665,7 +4867,7 @@ fwupd_plugin_flag_to_string(flag)); /* TRANSLATORS: %s is a link to a website */ g_string_append_printf(str, _("See %s for more information."), url); - fu_console_print_full(priv->console, + fu_console_print_full(self->console, FU_CONSOLE_PRINT_FLAG_WARNING, "%s\n", str->str); @@ -4673,20 +4875,19 @@ } static gboolean -fu_util_set_bios_setting(FuUtilPrivate *priv, gchar **input, GError **error) +fu_util_set_bios_setting(FuUtil *self, gchar **input, GError **error) { - g_autoptr(GHashTable) settings = fu_util_bios_settings_parse_argv(input, error); + g_autoptr(GHashTable) settings = NULL; + settings = fu_util_bios_settings_parse_argv(input, error); if (settings == NULL) return FALSE; - - if (!fwupd_client_modify_bios_setting(priv->client, settings, priv->cancellable, error)) { - if (!g_error_matches(*error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) - g_prefix_error(error, "failed to set BIOS setting: "); + if (!fwupd_client_modify_bios_setting(self->client, settings, self->cancellable, error)) { + g_prefix_error_literal(error, "failed to set BIOS setting: "); return FALSE; } - if (!priv->as_json) { + if (!self->as_json) { gpointer key, value; GHashTableIter iter; @@ -4697,36 +4898,36 @@ g_strdup_printf(_("Set BIOS setting '%s' using '%s'."), (const gchar *)key, (const gchar *)value); - fu_console_print_literal(priv->console, msg); + fu_console_print_literal(self->console, msg); } } - priv->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; + self->completion_flags |= FWUPD_DEVICE_FLAG_NEEDS_REBOOT; - if (priv->no_reboot_check) { + if (self->no_reboot_check) { g_debug("skipping reboot check"); return TRUE; } - return fu_util_prompt_complete(priv->console, priv->completion_flags, TRUE, error); + return fu_util_prompt_complete(self->console, self->completion_flags, TRUE, error); } static gboolean -fu_util_get_bios_setting(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_get_bios_setting(FuUtil *self, gchar **values, GError **error) { g_autoptr(GPtrArray) attrs = NULL; gboolean found = FALSE; - attrs = fwupd_client_get_bios_settings(priv->client, priv->cancellable, error); + attrs = fwupd_client_get_bios_settings(self->client, self->cancellable, error); if (attrs == NULL) return FALSE; - if (priv->as_json) - return fu_util_bios_setting_console_print(priv->console, values, attrs, error); + if (self->as_json) + return fu_util_bios_setting_console_print(self->console, values, attrs, error); for (guint i = 0; i < attrs->len; i++) { FwupdBiosSetting *attr = g_ptr_array_index(attrs, i); if (fu_util_bios_setting_matches_args(attr, values)) { g_autofree gchar *tmp = fu_util_bios_setting_to_string(attr, 0); - fu_console_print_literal(priv->console, tmp); + fu_console_print_literal(self->console, tmp); found = TRUE; } } @@ -4750,14 +4951,14 @@ } static gboolean -fu_util_security_fix(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_security_fix(FuUtil *self, gchar **values, GError **error) { #ifndef HAVE_HSI - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - /* TRANSLATORS: error message for unsupported feature */ - _("Host Security ID (HSI) is not supported")); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + /* TRANSLATORS: error message for unsupported feature */ + _("Host Security ID (HSI) is not supported")); return FALSE; #endif /* HAVE_HSI */ @@ -4770,15 +4971,68 @@ _("Invalid arguments, expected an AppStream ID")); return FALSE; } - if (!fwupd_client_fix_host_security_attr(priv->client, values[0], priv->cancellable, error)) + if (!fwupd_client_fix_host_security_attr(self->client, values[0], self->cancellable, error)) return FALSE; + + if (self->as_json) + return TRUE; + /* TRANSLATORS: we've fixed a security problem on the machine */ - fu_console_print_literal(priv->console, _("Fixed successfully")); + fu_console_print_literal(self->console, _("Fixed successfully")); return TRUE; } static gboolean -fu_util_report_devices(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_hwids_as_json(FuUtil *self, GStrv hwids_keys, GStrv hwids_values, GError **error) +{ + g_autoptr(JsonBuilder) builder = json_builder_new(); + json_builder_begin_object(builder); + for (guint i = 0; hwids_keys[i] != NULL; i++) { + json_builder_set_member_name(builder, hwids_keys[i]); + json_builder_add_string_value(builder, hwids_values[i]); + } + json_builder_end_object(builder); + return fu_util_print_builder(self->console, builder, error); +} + +static gboolean +fu_util_hwids(FuUtil *self, gchar **values, GError **error) +{ + g_auto(GStrv) hwids_keys = NULL; + g_auto(GStrv) hwids_values = NULL; + + fwupd_client_get_hwids(self->client, &hwids_keys, &hwids_values); + if (self->as_json) + return fu_util_hwids_as_json(self, hwids_keys, hwids_values, error); + + /* show debug output */ + fu_console_print_literal(self->console, "Computer Information"); + fu_console_print_literal(self->console, "--------------------"); + for (guint i = 0; hwids_keys[i] != NULL; i++) { + if (fwupd_guid_is_valid(hwids_values[i])) + continue; + fu_console_print(self->console, "%s: %s", hwids_keys[i], hwids_values[i]); + } + + /* show GUIDs */ + fu_console_print_literal(self->console, "Hardware IDs"); + fu_console_print_literal(self->console, "------------"); + for (guint i = 0; hwids_keys[i] != NULL; i++) { + g_autofree gchar *hwids_keys_real = NULL; + g_auto(GStrv) hwids_keys_strv = NULL; + if (!fwupd_guid_is_valid(hwids_values[i])) + continue; + hwids_keys_strv = g_strsplit(hwids_keys[i], "&", -1); + hwids_keys_real = g_strjoinv(" + ", hwids_keys_strv); + fu_console_print(self->console, "{%s} <- %s", hwids_values[i], hwids_keys_real); + } + + /* success */ + return TRUE; +} + +static gboolean +fu_util_report_devices(FuUtil *self, gchar **values, GError **error) { g_autofree gchar *data = NULL; g_autofree gchar *report_uri = NULL; @@ -4788,7 +5042,7 @@ g_autoptr(GPtrArray) devices = NULL; /* we only know how to upload to the LVFS */ - remote = fwupd_client_get_remote_by_id(priv->client, "lvfs", priv->cancellable, error); + remote = fwupd_client_get_remote_by_id(self->client, "lvfs", self->cancellable, error); if (remote == NULL) return FALSE; report_uri = fwupd_remote_build_report_uri(remote, error); @@ -4796,18 +5050,18 @@ return FALSE; /* include all the devices */ - devices = fwupd_client_get_devices(priv->client, priv->cancellable, error); + devices = fwupd_client_get_devices(self->client, self->cancellable, error); if (devices == NULL) return FALSE; - metadata = fwupd_client_get_report_metadata(priv->client, priv->cancellable, error); + metadata = fwupd_client_get_report_metadata(self->client, self->cancellable, error); if (metadata == NULL) return FALSE; - data = fwupd_client_build_report_devices(priv->client, devices, metadata, error); + data = fwupd_client_build_report_devices(self->client, devices, metadata, error); if (data == NULL) return FALSE; - if (priv->as_json) { - if (!priv->assume_yes) { + if (self->as_json) { + if (!self->assume_yes) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS, @@ -4816,15 +5070,16 @@ } } else { /* show the user the entire data blob */ - fu_console_print_kv(priv->console, _("Target"), report_uri); - fu_console_print_kv(priv->console, _("Payload"), data); - fu_console_print(priv->console, - /* TRANSLATORS: explain why we want to upload */ - _("Uploading a device list allows the %s team to know what hardware " - "exists, and allows us to put pressure on vendors that do not upload " - "firmware updates for their hardware."), - fwupd_remote_get_title(remote)); - if (!fu_console_input_bool(priv->console, + fu_console_print_kv(self->console, _("Target"), report_uri); + fu_console_print_kv(self->console, _("Payload"), data); + fu_console_print( + self->console, + /* TRANSLATORS: explain why we want to upload */ + _("Uploading a device list allows the %s team to know what hardware " + "exists, and allows us to put pressure on vendors that do not upload " + "firmware updates for their hardware."), + fwupd_remote_get_title(remote)); + if (!fu_console_input_bool(self->console, TRUE, "%s (%s)", /* TRANSLATORS: ask the user to upload */ @@ -4840,19 +5095,19 @@ } /* send to the LVFS */ - uri = fwupd_client_upload_report(priv->client, + uri = fwupd_client_upload_report(self->client, report_uri, data, NULL, FWUPD_CLIENT_UPLOAD_FLAG_ALWAYS_MULTIPART, - priv->cancellable, + self->cancellable, error); if (uri == NULL) return FALSE; /* success */ - if (!priv->as_json) { - fu_console_print_literal(priv->console, + if (!self->as_json) { + fu_console_print_literal(self->console, /* TRANSLATORS: success, so say thank you to the user */ _("Device list uploaded successfully, thanks!")); } @@ -4861,14 +5116,14 @@ } static gboolean -fu_util_security_undo(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_security_undo(FuUtil *self, gchar **values, GError **error) { #ifndef HAVE_HSI - g_set_error(error, - FWUPD_ERROR, - FWUPD_ERROR_NOT_SUPPORTED, - /* TRANSLATORS: error message for unsupported feature */ - _("Host Security ID (HSI) is not supported")); + g_set_error_literal(error, + FWUPD_ERROR, + FWUPD_ERROR_NOT_SUPPORTED, + /* TRANSLATORS: error message for unsupported feature */ + _("Host Security ID (HSI) is not supported")); return FALSE; #endif /* HAVE_HSI */ @@ -4881,54 +5136,58 @@ _("Invalid arguments, expected an AppStream ID")); return FALSE; } - if (!fwupd_client_undo_host_security_attr(priv->client, + if (!fwupd_client_undo_host_security_attr(self->client, values[0], - priv->cancellable, + self->cancellable, error)) return FALSE; + + if (self->as_json) + return TRUE; + /* TRANSLATORS: we've fixed a security problem on the machine */ - fu_console_print_literal(priv->console, _("Fix reverted successfully")); + fu_console_print_literal(self->console, _("Fix reverted successfully")); return TRUE; } static gboolean -fu_util_emulation_tag(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_emulation_tag(FuUtil *self, gchar **values, GError **error) { g_autoptr(FwupdDevice) dev = NULL; /* set the flag */ - priv->filter_device_include |= FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG; - dev = fu_util_get_device_or_prompt(priv, values, error); + self->filter_device_include |= FWUPD_DEVICE_FLAG_CAN_EMULATION_TAG; + dev = fu_util_get_device_or_prompt(self, values, error); if (dev == NULL) return FALSE; - return fwupd_client_modify_device(priv->client, + return fwupd_client_modify_device(self->client, fwupd_device_get_id(dev), "Flags", "emulation-tag", - priv->cancellable, + self->cancellable, error); } static gboolean -fu_util_emulation_untag(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_emulation_untag(FuUtil *self, gchar **values, GError **error) { g_autoptr(FwupdDevice) dev = NULL; /* set the flag */ - priv->filter_device_include |= FWUPD_DEVICE_FLAG_EMULATION_TAG; - dev = fu_util_get_device_or_prompt(priv, values, error); + self->filter_device_include |= FWUPD_DEVICE_FLAG_EMULATION_TAG; + dev = fu_util_get_device_or_prompt(self, values, error); if (dev == NULL) return FALSE; - return fwupd_client_modify_device(priv->client, + return fwupd_client_modify_device(self->client, fwupd_device_get_id(dev), "Flags", "~emulation-tag", - priv->cancellable, + self->cancellable, error); } static gboolean -fu_util_emulation_save(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_emulation_save(FuUtil *self, gchar **values, GError **error) { /* check args */ if (g_strv_length(values) != 1) { @@ -4940,11 +5199,11 @@ } /* save */ - return fwupd_client_emulation_save(priv->client, values[0], priv->cancellable, error); + return fwupd_client_emulation_save(self->client, values[0], self->cancellable, error); } static gboolean -fu_util_emulation_load(FuUtilPrivate *priv, gchar **values, GError **error) +fu_util_emulation_load(FuUtil *self, gchar **values, GError **error) { /* check args */ if (g_strv_length(values) != 1) { @@ -4954,57 +5213,57 @@ "Invalid arguments, expected FILENAME"); return FALSE; } - return fwupd_client_emulation_load(priv->client, values[0], priv->cancellable, error); + return fwupd_client_emulation_load(self->client, values[0], self->cancellable, error); } static gboolean -fu_util_version(FuUtilPrivate *priv, GError **error) +fu_util_version(FuUtil *self, GError **error) { g_autoptr(GHashTable) metadata = NULL; g_autofree gchar *str = NULL; /* get metadata */ - metadata = fwupd_client_get_report_metadata(priv->client, priv->cancellable, error); + metadata = fwupd_client_get_report_metadata(self->client, self->cancellable, error); if (metadata == NULL) return FALSE; /* dump to the screen in the most appropriate format */ - if (priv->as_json) - return fu_util_project_versions_as_json(priv->console, metadata, error); + if (self->as_json) + return fu_util_project_versions_as_json(self->console, metadata, error); str = fu_util_project_versions_to_string(metadata); - fu_console_print_literal(priv->console, str); + fu_console_print_literal(self->console, str); return TRUE; } static gboolean -fu_util_setup_interactive(FuUtilPrivate *priv, GError **error) +fu_util_setup_interactive(FuUtil *self, GError **error) { - if (priv->as_json) { + if (self->as_json) { g_set_error_literal(error, FWUPD_ERROR, FWUPD_ERROR_NOT_SUPPORTED, "using --json"); return FALSE; } - return fu_console_setup(priv->console, error); + return fu_console_setup(self->console, error); } static void fu_util_cancelled_cb(GCancellable *cancellable, gpointer user_data) { - FuUtilPrivate *priv = (FuUtilPrivate *)user_data; - if (!g_main_loop_is_running(priv->loop)) + FuUtil *self = (FuUtil *)user_data; + if (!g_main_loop_is_running(self->loop)) return; /* TRANSLATORS: this is from ctrl+c */ - fu_console_print_literal(priv->console, _("Cancelled")); - g_main_loop_quit(priv->loop); + fu_console_print_literal(self->console, _("Cancelled")); + g_main_loop_quit(self->loop); } static void -fu_util_print_error(FuUtilPrivate *priv, const GError *error) +fu_util_print_error(FuUtil *self, const GError *error) { - if (priv->as_json) { - fu_util_print_error_as_json(priv->console, error); + if (self->as_json) { + fu_util_print_error_as_json(self->console, error); return; } - fu_console_print_full(priv->console, FU_CONSOLE_PRINT_FLAG_STDERR, "%s\n", error->message); + fu_console_print_full(self->console, FU_CONSOLE_PRINT_FLAG_STDERR, "%s\n", error->message); } int @@ -5014,6 +5273,7 @@ gboolean allow_branch_switch = FALSE; gboolean allow_older = FALSE; gboolean allow_reinstall = FALSE; + gboolean only_emulated = FALSE; gboolean only_p2p = FALSE; gboolean is_interactive = FALSE; gboolean no_history = FALSE; @@ -5022,7 +5282,7 @@ gboolean verbose = FALSE; gboolean version = FALSE; guint download_retries = 0; - g_autoptr(FuUtilPrivate) priv = g_new0(FuUtilPrivate, 1); + g_autoptr(FuUtil) self = g_new0(FuUtil, 1); g_autoptr(GDateTime) dt_now = g_date_time_new_now_utc(); g_autoptr(GError) error = NULL; g_autoptr(GError) error_console = NULL; @@ -5082,6 +5342,14 @@ /* TRANSLATORS: command line option */ N_("Allow switching firmware branch"), NULL}, + {"only-emulated", + '\0', + 0, + G_OPTION_ARG_NONE, + &only_emulated, + /* TRANSLATORS: command line option */ + N_("Only install onto emulated devices"), + NULL}, {"force", '\0', 0, @@ -5094,7 +5362,7 @@ 'y', 0, G_OPTION_ARG_NONE, - &priv->assume_yes, + &self->assume_yes, /* TRANSLATORS: command line option */ N_("Answer yes to all questions"), NULL}, @@ -5102,7 +5370,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->sign, + &self->sign, /* TRANSLATORS: command line option */ N_("Sign the uploaded data with the client certificate"), NULL}, @@ -5110,7 +5378,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->no_unreported_check, + &self->no_unreported_check, /* TRANSLATORS: command line option */ N_("Do not check for unreported history"), NULL}, @@ -5118,7 +5386,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->no_metadata_check, + &self->no_metadata_check, /* TRANSLATORS: command line option */ N_("Do not check for old metadata"), NULL}, @@ -5126,7 +5394,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->no_remote_check, + &self->no_remote_check, /* TRANSLATORS: command line option */ N_("Do not check if download remotes should be enabled"), NULL}, @@ -5134,7 +5402,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->no_reboot_check, + &self->no_reboot_check, /* TRANSLATORS: command line option */ N_("Do not check or prompt for reboot after update"), NULL}, @@ -5142,7 +5410,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->no_safety_check, + &self->no_safety_check, /* TRANSLATORS: command line option */ N_("Do not perform device safety checks"), NULL}, @@ -5150,7 +5418,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->no_device_prompt, + &self->no_device_prompt, /* TRANSLATORS: command line option */ N_("Do not prompt for devices"), NULL}, @@ -5166,7 +5434,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->show_all, + &self->show_all, /* TRANSLATORS: command line option */ N_("Show all results"), NULL}, @@ -5174,7 +5442,7 @@ '\0', G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_NONE, - &priv->show_all, + &self->show_all, /* TRANSLATORS: command line option */ N_("Show devices that are not updatable"), NULL}, @@ -5182,7 +5450,7 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->disable_ssl_strict, + &self->disable_ssl_strict, /* TRANSLATORS: command line option */ N_("Ignore SSL strict checks when downloading files"), NULL}, @@ -5216,15 +5484,15 @@ '\0', 0, G_OPTION_ARG_NONE, - &priv->as_json, + &self->as_json, /* TRANSLATORS: command line option */ - N_("Output in JSON format"), + N_("Output in JSON format (disables all interactive prompts)"), NULL}, {"no-security-fix", '\0', 0, G_OPTION_ARG_NONE, - &priv->no_security_fix, + &self->no_security_fix, /* TRANSLATORS: command line option */ N_("Do not prompt to fix security issues"), NULL}, @@ -5258,14 +5526,20 @@ (void)fwupd_error_quark(); /* create helper object */ - priv->main_ctx = g_main_context_new(); - priv->loop = g_main_loop_new(priv->main_ctx, FALSE); - priv->console = fu_console_new(); - priv->post_requests = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); - fu_console_set_main_context(priv->console, priv->main_ctx); + self->main_ctx = g_main_context_new(); + self->loop = g_main_loop_new(self->main_ctx, FALSE); + self->console = fu_console_new(); + self->post_requests = g_ptr_array_new_with_free_func((GDestroyNotify)g_object_unref); + fu_console_set_main_context(self->console, self->main_ctx); /* add commands */ fu_util_cmd_array_add(cmd_array, + "check-reboot-needed", + _("[DEVICE-ID|GUID]"), + /* TRANSLATORS: command description */ + _("Check if any devices are pending a reboot to complete update"), + fu_util_check_reboot_needed); + fu_util_cmd_array_add(cmd_array, "get-devices,get-topology", NULL, /* TRANSLATORS: command description */ @@ -5315,7 +5589,8 @@ /* TRANSLATORS: command argument: uppercase, spaces->dashes */ _("[DEVICE-ID|GUID]"), /* TRANSLATORS: command description */ - _("Gets the list of updates for connected hardware"), + _("Gets the list of updates for all specified devices, or all " + "devices if unspecified"), fu_util_get_updates); fu_util_cmd_array_add(cmd_array, "update,upgrade", @@ -5361,6 +5636,13 @@ _("Gets the releases for a device"), fu_util_get_releases); fu_util_cmd_array_add(cmd_array, + "search", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("WORD"), + /* TRANSLATORS: command description */ + _("Finds firmware releases from the metadata"), + fu_util_search); + fu_util_cmd_array_add(cmd_array, "get-remotes", NULL, /* TRANSLATORS: command description */ @@ -5402,6 +5684,13 @@ _("Enables a given remote"), fu_util_remote_enable); fu_util_cmd_array_add(cmd_array, + "clean-remote", + /* TRANSLATORS: command argument: uppercase, spaces->dashes */ + _("REMOTE-ID"), + /* TRANSLATORS: command description */ + _("Cleans a given remote"), + fu_util_remote_clean); + fu_util_cmd_array_add(cmd_array, "disable-remote", /* TRANSLATORS: command argument: uppercase, spaces->dashes */ _("REMOTE-ID"), @@ -5605,39 +5894,45 @@ /* TRANSLATORS: command description */ _("Upload the list of updatable devices to a remote server"), fu_util_report_devices); + fu_util_cmd_array_add(cmd_array, + "hwids", + NULL, + /* TRANSLATORS: command description */ + _("Return all the hardware IDs for the machine"), + fu_util_hwids); /* do stuff on ctrl+c */ - priv->cancellable = g_cancellable_new(); - g_signal_connect(G_CANCELLABLE(priv->cancellable), + self->cancellable = g_cancellable_new(); + g_signal_connect(G_CANCELLABLE(self->cancellable), "cancelled", G_CALLBACK(fu_util_cancelled_cb), - priv); + self); /* sort by command name */ fu_util_cmd_array_sort(cmd_array); /* non-TTY consoles cannot answer questions */ - if (!fu_util_setup_interactive(priv, &error_console)) { + if (!fu_util_setup_interactive(self, &error_console)) { g_info("failed to initialize interactive console: %s", error_console->message); - priv->no_unreported_check = TRUE; - priv->no_metadata_check = TRUE; - priv->no_reboot_check = TRUE; - priv->no_safety_check = TRUE; - priv->no_remote_check = TRUE; - priv->no_device_prompt = TRUE; - priv->no_emulation_check = TRUE; - priv->no_security_fix = TRUE; + self->no_unreported_check = TRUE; + self->no_metadata_check = TRUE; + self->no_reboot_check = TRUE; + self->no_safety_check = TRUE; + self->no_remote_check = TRUE; + self->no_device_prompt = TRUE; + self->no_emulation_check = TRUE; + self->no_security_fix = TRUE; } else { is_interactive = TRUE; } - fu_console_set_interactive(priv->console, is_interactive); + fu_console_set_interactive(self->console, is_interactive); /* get a list of the commands */ - priv->context = g_option_context_new(NULL); + self->context = g_option_context_new(NULL); cmd_descriptions = fu_util_cmd_array_to_string(cmd_array); - g_option_context_set_summary(priv->context, cmd_descriptions); + g_option_context_set_summary(self->context, cmd_descriptions); g_option_context_set_description( - priv->context, + self->context, /* TRANSLATORS: CLI description */ _("This tool allows an administrator to query and control the " "fwupd daemon, allowing them to perform actions such as " @@ -5645,10 +5940,10 @@ /* TRANSLATORS: program name */ g_set_application_name(_("Firmware Utility")); - g_option_context_add_main_entries(priv->context, options, NULL); - ret = g_option_context_parse(priv->context, &argc, &argv, &error); + g_option_context_add_main_entries(self->context, options, NULL); + ret = g_option_context_parse(self->context, &argc, &argv, &error); if (!ret) { - fu_console_print(priv->console, + fu_console_print(self->console, "%s: %s", /* TRANSLATORS: the user didn't read the man page */ _("Failed to parse arguments"), @@ -5657,8 +5952,8 @@ } /* allow disabling SSL strict mode for broken corporate proxies */ - if (priv->disable_ssl_strict) { - fu_console_print_full(priv->console, + if (self->disable_ssl_strict) { + fu_console_print_full(self->console, FU_CONSOLE_PRINT_FLAG_WARNING, "%s\n", /* TRANSLATORS: try to help */ @@ -5672,7 +5967,7 @@ * want to check the clock is not set to the default of 1970-01-01... */ if (g_date_time_get_year(dt_now) < 2021) { fu_console_print_full( - priv->console, + self->console, FU_CONSOLE_PRINT_FLAG_WARNING, "%s\n", /* TRANSLATORS: try to help */ @@ -5683,28 +5978,28 @@ /* parse filter flags */ if (filter_device != NULL) { if (!fu_util_parse_filter_device_flags(filter_device, - &priv->filter_device_include, - &priv->filter_device_exclude, + &self->filter_device_include, + &self->filter_device_exclude, &error)) { g_autofree gchar *str = /* TRANSLATORS: the user didn't read the man page, %1 is '--filter' */ g_strdup_printf(_("Failed to parse flags for %s"), "--filter"); g_prefix_error(&error, "%s: ", str); - fu_util_print_error(priv, error); + fu_util_print_error(self, error); return EXIT_FAILURE; } } if (filter_release != NULL) { if (!fu_util_parse_filter_release_flags(filter_release, - &priv->filter_release_include, - &priv->filter_release_exclude, + &self->filter_release_include, + &self->filter_release_exclude, &error)) { g_autofree gchar *str = /* TRANSLATORS: the user didn't read the man page, * %1 is '--filter-release' */ g_strdup_printf(_("Failed to parse flags for %s"), "--filter-release"); g_prefix_error(&error, "%s: ", str); - fu_util_print_error(priv, error); + fu_util_print_error(self, error); return EXIT_FAILURE; } } @@ -5718,32 +6013,34 @@ } /* set up ctrl+c */ - fu_util_setup_signal_handlers(priv); + fu_util_setup_signal_handlers(self); /* set flags */ if (allow_reinstall) - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_REINSTALL; if (allow_older) - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_OLDER; if (allow_branch_switch) - priv->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + self->flags |= FWUPD_INSTALL_FLAG_ALLOW_BRANCH_SWITCH; + if (only_emulated) + self->flags |= FWUPD_INSTALL_FLAG_ONLY_EMULATED; if (force) { - priv->flags |= FWUPD_INSTALL_FLAG_FORCE; - priv->flags |= FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS; + self->flags |= FWUPD_INSTALL_FLAG_FORCE; + self->flags |= FWUPD_INSTALL_FLAG_IGNORE_REQUIREMENTS; } if (no_history) - priv->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY; + self->flags |= FWUPD_INSTALL_FLAG_NO_HISTORY; /* use peer-to-peer for metadata and firmware *only* if specified */ if (only_p2p) - priv->download_flags |= FWUPD_CLIENT_DOWNLOAD_FLAG_ONLY_P2P; + self->download_flags |= FWUPD_CLIENT_DOWNLOAD_FLAG_ONLY_P2P; #ifdef HAVE_POLKIT /* start polkit tty agent to listen for password requests */ if (is_interactive) { g_autoptr(GError) error_polkit = NULL; if (!fu_polkit_agent_open(polkit_agent, &error_polkit)) { - fu_console_print(priv->console, + fu_console_print(self->console, "Failed to open polkit agent: %s", error_polkit->message); } @@ -5751,43 +6048,43 @@ #endif /* connect to the daemon */ - priv->client = fwupd_client_new(); - fwupd_client_set_main_context(priv->client, priv->main_ctx); - fwupd_client_download_set_retries(priv->client, download_retries); - g_signal_connect(FWUPD_CLIENT(priv->client), + self->client = fwupd_client_new(); + fwupd_client_set_main_context(self->client, self->main_ctx); + fwupd_client_download_set_retries(self->client, download_retries); + g_signal_connect(FWUPD_CLIENT(self->client), "notify::percentage", G_CALLBACK(fu_util_client_notify_cb), - priv); - g_signal_connect(FWUPD_CLIENT(priv->client), + self); + g_signal_connect(FWUPD_CLIENT(self->client), "notify::status", G_CALLBACK(fu_util_client_notify_cb), - priv); - g_signal_connect(FWUPD_CLIENT(priv->client), + self); + g_signal_connect(FWUPD_CLIENT(self->client), "device-changed", G_CALLBACK(fu_util_update_device_changed_cb), - priv); - g_signal_connect(FWUPD_CLIENT(priv->client), + self); + g_signal_connect(FWUPD_CLIENT(self->client), "device-request", G_CALLBACK(fu_util_update_device_request_cb), - priv); + self); /* show a warning if the daemon is tainted */ - if (!fwupd_client_connect(priv->client, priv->cancellable, &error)) { + if (!fwupd_client_connect(self->client, self->cancellable, &error)) { #ifdef _WIN32 fu_console_print_literal( - priv->console, + self->console, /* TRANSLATORS: error message for Windows */ _("Failed to connect to Windows service, please ensure it's running.")); g_debug("%s", error->message); #else /* TRANSLATORS: could not contact the fwupd service over D-Bus */ g_prefix_error(&error, "%s: ", _("Failed to connect to daemon")); - fu_util_print_error(priv, error); + fu_util_print_error(self, error); #endif return EXIT_FAILURE; } - if (fwupd_client_get_tainted(priv->client)) { - fu_console_print_full(priv->console, + if (fwupd_client_get_tainted(self->client)) { + fu_console_print_full(self->console, FU_CONSOLE_PRINT_FLAG_WARNING, "%s\n", /* TRANSLATORS: the user is SOL for support... */ @@ -5797,34 +6094,34 @@ /* just show versions and exit */ if (version) { - if (!fu_util_version(priv, &error)) { - fu_util_print_error(priv, error); + if (!fu_util_version(self, &error)) { + fu_util_print_error(self, error); return EXIT_FAILURE; } return EXIT_SUCCESS; } - if (!priv->as_json) { + if (!self->as_json) { /* show user-visible warnings from the plugins */ - fu_util_show_plugin_warnings(priv); + fu_util_show_plugin_warnings(self); /* show any unsupported warnings */ - fu_util_show_unsupported_warning(priv->console); + fu_util_show_unsupported_warning(self->console); } /* we know the runtime daemon version now */ - fwupd_client_set_user_agent_for_package(priv->client, g_get_prgname(), PACKAGE_VERSION); + fwupd_client_set_user_agent_for_package(self->client, g_get_prgname(), PACKAGE_VERSION); /* check that we have at least this version daemon running */ - if ((priv->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && - !fu_util_check_daemon_version(priv, &error)) { - fu_util_print_error(priv, error); + if ((self->flags & FWUPD_INSTALL_FLAG_FORCE) == 0 && + !fu_util_check_daemon_version(self, &error)) { + fu_util_print_error(self, error); return EXIT_FAILURE; } /* make sure polkit actions were installed */ if (!fu_util_check_polkit_actions(&error)) { - fu_util_print_error(priv, error); + fu_util_print_error(self, error); return EXIT_FAILURE; } @@ -5836,18 +6133,18 @@ if (!no_authenticate) feature_flags |= FWUPD_FEATURE_FLAG_ALLOW_AUTHENTICATION; } - if (!fwupd_client_set_feature_flags(priv->client, + if (!fwupd_client_set_feature_flags(self->client, feature_flags, - priv->cancellable, + self->cancellable, &error)) { /* TRANSLATORS: a feature is something like "can show an image" */ g_prefix_error(&error, "%s: ", _("Failed to set front-end features")); - fu_util_print_error(priv, error); + fu_util_print_error(self, error); return EXIT_FAILURE; } /* run the specified command */ - ret = fu_util_cmd_array_run(cmd_array, priv, argv[1], (gchar **)&argv[2], &error); + ret = fu_util_cmd_array_run(cmd_array, self, argv[1], (gchar **)&argv[2], &error); if (!ret) { #ifdef SUPPORTED_BUILD /* sanity check */ @@ -5856,15 +6153,15 @@ return EXIT_FAILURE; } #endif - fu_util_print_error(priv, error); - if (!priv->as_json && + fu_util_print_error(self, error); + if (!self->as_json && g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_INVALID_ARGS)) { g_autofree gchar *cmd = g_strdup_printf("%s --help", g_get_prgname()); g_autoptr(GString) str = g_string_new("\n"); /* TRANSLATORS: explain how to get help, * where $1 is something like 'fwupdmgr --help' */ g_string_append_printf(str, _("Use %s for help"), cmd); - fu_console_print_literal(priv->console, str->str); + fu_console_print_literal(self->console, str->str); } else if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOTHING_TO_DO)) return EXIT_NOTHING_TO_DO; else if (g_error_matches(error, FWUPD_ERROR, FWUPD_ERROR_NOT_REACHABLE)) diff -Nru fwupd-2.0.8/src/fwupdmgr.md fwupd-2.0.20/src/fwupdmgr.md --- fwupd-2.0.8/src/fwupdmgr.md 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/fwupdmgr.md 2026-02-26 11:36:18.000000000 +0000 @@ -39,8 +39,182 @@ ## OPTIONS -The fwupdmgr command takes various options depending on the action. -Run **fwupdmgr \-\-help** for the full list. +The fwupdmgr command takes various options depending on the action. The actions are split into +rough behavior groups as follows: + +### Interacting With Discovered Devices + +The following actions can be used to view details about the enumerated devices on the system: + +**get-devices**: Show all devices, with optional filtering applied using `--filter`. + +**get-updates**: Show all the available updates for a specific device. + +**get-releases**: Show all the installable versions (older, the same, or newer) for a specific device. + +**get-details**: View details about a specific .cab archive, even if the target device has not been found on this system. + +**search**: Find updates present in the metadata, regardless if the device has been enumerated on the system. +Searching will match firmware releases by: + +* The full GUID of a device, e.g. `eb68dbae-3aef-5077-92ae-9016d1f0c856` + +* The AppStream ID of a device, e.g. `work.frame.Desktop.RyzenAIMax300.BIOS.firmware` + +* The name (full or subset) of a firmware update, e.g. `Desktop Ryzen AI MAX 300` + +* The name (full or subset) of a firmware vendor, e.g. `Framework` + +* The protocol used by the firmware, e.g. `org.uefi.capsule` + +* Any issue reported as solved by the update, e.g. `CVE-2022-21894` + +* Any cabinet checksum included with the update, in SHA-1 or SHA-256 format, e.g. `b34950fc65dabc0cb50e4c5f081829e40bf92e13` + +### Deploying Firmware + +The following actions can be used to install firmware onto devices: + +**update**: Update each device in turn to the newest available version. + +**install**: Install any available release to a specific device. + +**reinstall**: Re-install the same version to a specific device if available. + +**downgrade**: Install an older release to a specific release if available. + +**switch-branch**: Switch between different firmware "branches" for a specific device. +For instance, there may be a "community supported" branch and a "non-free upstream vendor" branch for +the exact same hardware, both available on the LVFS. + +**local-install**: Install a local firmware archive. The regular **install** will also fall back to this if the first argument is a local file that exists. + +**activate**: Set the installed firmware version as running, which may mean the device becomes offline or unresponsive for a few moments. + +**sync**: Install all releases with a matching BKC tag if a "Best Known Configuration" has been set for the local machine. + +### Approved and Blocked Firmware + +The following actions can be used to block or allow specific firmware releases from being installed: + +**get-blocked-firmware**: Gets the list of blocked firmware as a list of checksums. + +**block-firmware**: Blocks a specific firmware from being installed by **install** or **update**. + +**unblock-firmware**: Unblocks a specific firmware from being installed or updated. + +The following actions can be used to control the allowlist of specific firmwares: + +**set-approved-firmware**: Sets the list of approved firmware. +Once the allow-list has been set to a non-empty value only firmware matching these checksums will be installable. + +**get-approved-firmware**: Gets the list of approved firmware, returning an empty list if there is no allow-list in place. + +The checksums used for allowing or disallowing are the cabinet archive checksums in SHA-1 or SHA-256 format. + +### Emulation + +Device emulation allows recording the device behavior so that we can replay the device responses +and test writing firmware without that actual hardware plugged in. + +The following actions can be used when emulating devices: + +**emulation-tag**: Adds devices to watch for future emulation. + +**emulation-save**: Save captured device emulation data to a JSON file. +Only data from devices that have previously been tagged for emulation will be returned. + +**emulation-load**: Load device emulation data from a JSON file. + +**emulation-untag**: Removes devices to watch for future emulation. + +When tagging devices, either the device ID or GUID can be used to identify the device. + +The following actions can be used to run automated device tests: + +**device-emulate**: Emulate a device using a JSON manifest, which will operate on emulated devices only. + +**device-test**: Test a device using a JSON manifest which downloads and verifies cabinet archives and deploys them on actual physical devices. + +### Remotes + +The following actions can be used to control the remotes (the online firmware metadata store) +configured in the daemon: + +**refresh**: Download the latest online metadata from configured and enabled remotes. + +**get-remotes**: Get the list of configured remotes, which may or may not be enabled. + +**enable-remote**: Enable a pre-configured remote so that it can be refreshed and used. + +**disable-remote**: Disable a remote, but do not delete or remote stored metadata. + +**modify-remote**: Edit a remote, for instance turning on properties such as `AutomaticReports`. + +**clean-remote**: Cleans a remote, deleting metadata where required. + +### Historical Data + +The following actions can be used to upload, export or clear historical data: + +**report-export**: Export firmware history as an offline file for manual upload to a service such as the LVFS. + +**report-history**: Share firmware history with the remote owner, typically used to indicate the success ratio for a specific update. +In some cases, sharing the history will return results to webpages describing the failure in more details, some with workarounds. + +**get-history**: Show the firmware update history as stored by fwupd. + +**get-results**: Show the result of the last firmware update for a specific device. + +**clear-results**: Clears the results from the last update if possible. + +**report-devices**: Upload the list of updatable devices to a remote server so that they can put pressure on the vendor to support Linux users. + +### Platform Security + +The following actions can be used to view or fix platform security issues: + +**security**: Gets the list of host security attributes which are used to evaluate the security level of the machine. + +**security-fix**: Fix a specific host security attribute failure. + +**security-undo**: Undo the host security attribute fix, which may be required if **security-fix** caused a regression. + +The following actions can be used to list or set firmware BIOS settings: + +**get-bios-settings**: Retrieve BIOS firmware settings and the allowable values. + +**set-bios-setting**: Sets one or more BIOS firmware settings. + +**verify-update**: Update the stored checksums that are used by the **verify** action. + +### Others + +The following actions may be for from non-interactive scripts or use in CI: + +**check-reboot-needed**: Check if any devices are pending a reboot to complete update. + +**device-wait**: Wait for a device to appear in the daemon device list. + +**download**: Download a file using the same mechanisms that firmware and metadata are used. + +**modify-config**: Modifies a daemon configuration value such as `IgnorePower`. +See `man fwupd.conf` for the full list of variables. + +**reset-config**: Resets a daemon configuration section back to the default values. + +**quit**: Asks the daemon to quit after it has finished any firmware update in progress. + +**inhibit**: Inhibit the system to prevent accidental manual or automatic upgrades. + +**uninhibit**: Uninhibit the system to allow upgrades. + +**unlock**: Unlocks the device for firmware access, which may be required for some platform devices. + +**get-plugins**: Show all plugins registered with the daemon. + +**hwids**: Return all the hardware IDs for the machine. +These GUIDs are sometimes called CHIDs when using Microsoft Windows, and the values returned by fwupd should also match those from `ComputerHardwareIds.exe`. ## EXIT STATUS diff -Nru fwupd-2.0.8/src/meson.build fwupd-2.0.20/src/meson.build --- fwupd-2.0.8/src/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,5 +1,5 @@ if get_option('tests') -subdir('tests') + subdir('tests') endif client_src = [] @@ -22,14 +22,7 @@ engine_dep += passim endif -client_dep = [ - libcurl, - libjcat, - libjsonglib, - libxmlb, - sqlite, - fwupdplugin_rs_dep, -] +client_dep = [libcurl, libjcat, libjsonglib, libxmlb, sqlite, readline, fwupdplugin_rs_dep] if libsystemd.found() systemd_src += 'fu-systemd.c' endif @@ -75,12 +68,11 @@ # include event message file if host_machine.system() == 'windows' windmc = find_program('windmc') - fwupd_rc = custom_target('fwupd-rc', + fwupd_rc = custom_target( + 'fwupd-rc', input: 'fwupd-windows.mc', output: 'fwupd-windows.rc', - command: [ - windmc, '@INPUT@', '--rcdir', meson.current_build_dir(), - ], + command: [windmc, '@INPUT@', '--rcdir', meson.current_build_dir()], ) windows = import('windows') fwupd_engine_src += windows.compile_resources(fwupd_rc) @@ -88,66 +80,36 @@ fwupdutil = library( 'fwupdutil', - sources: [ - 'fu-console.c', - 'fu-util-bios-setting.c', - 'fu-util-common.c', - systemd_src, - ], + sources: ['fu-console.c', 'fu-util-bios-setting.c', 'fu-util-common.c', systemd_src], install: true, install_rpath: libdir_pkg, install_dir: libdir_pkg, - include_directories: [ - root_incdir, - fwupd_incdir, - fwupdplugin_incdir, - ], - dependencies: [ - client_dep, - ], - link_with: [ - fwupd, - fwupdplugin, - ], + include_directories: [root_incdir, fwupd_incdir, fwupdplugin_incdir], + dependencies: [client_dep], + link_with: [fwupd, fwupdplugin], ) -dbus_interface = custom_target('fwupd-generate-dbus-interface', - input : 'org.freedesktop.fwupd.xml', - output : 'org.freedesktop.fwupd.xml', - command : [ - generate_dbus_interface, - '@INPUT@', - '@OUTPUT@', - ], +dbus_interface = custom_target( + 'fwupd-generate-dbus-interface', + input: 'org.freedesktop.fwupd.xml', + output: 'org.freedesktop.fwupd.xml', + command: [generate_dbus_interface, '@INPUT@', '@OUTPUT@'], install: build_daemon, + install_tag: 'runtime', install_dir: join_paths(datadir, 'dbus-1', 'interfaces'), ) if build_daemon -fwupdmgr = executable( - 'fwupdmgr', - sources: [ - 'fu-util.c', - client_src, - ], - include_directories: [ - root_incdir, - fwupd_incdir, - fwupdplugin_incdir, - ], - dependencies: [ - libfwupd_deps, - client_dep, - ], - link_with: [ - fwupd, - fwupdplugin, - fwupdutil, - ], - install: true, - install_rpath: libdir_pkg, - install_dir: bindir -) + executable( + 'fwupdmgr', + sources: ['fu-util.c', client_src], + include_directories: [root_incdir, fwupd_incdir, fwupdplugin_incdir], + dependencies: [libfwupd_deps, client_dep], + link_with: [fwupd, fwupdplugin, fwupdutil], + install: true, + install_rpath: libdir_pkg, + install_dir: bindir, + ) endif resources_src = gnome.compile_resources( @@ -163,24 +125,27 @@ foreach lib : plugin_builtins plugin_names += lib.full_path() endforeach -plugins_hdr = custom_target('fwupd-generate-plugins-header', - output : 'fu-plugin-builtin.h', - command : [ - generate_plugins_header, - '@OUTPUT@', - '.', - ','.join(plugin_names), - ], +plugins_hdr = custom_target( + 'fwupd-generate-plugins-header', + output: 'fu-plugin-builtin.h', + command: [generate_plugins_header, '@OUTPUT@', '.', ','.join(plugin_names)], ) # build all the plugins and engine into one installed library -fwupdengine_rs = custom_target('fu-engine-rs', +fwupdengine_rs = custom_target( + 'fu-engine-rs', input: 'fu-engine.rs', output: ['fu-engine-struct.c', 'fu-engine-struct.h'], command: [ python3, join_paths(meson.project_source_root(), 'libfwupdplugin', 'rustgen.py'), - '@INPUT@', '@OUTPUT0@', '@OUTPUT1@', + '@INPUT@', + '@OUTPUT0@', + '@OUTPUT1@', + '--include', + 'fwupdplugin.h', + '--prefix', + 'Fu', ], ) fwupdengine = library( @@ -193,93 +158,97 @@ install_rpath: libdir_pkg, install_dir: libdir_pkg, include_directories: plugin_incdirs, - dependencies: [ - engine_dep, - ], - link_whole: [ - plugin_builtins, - ], - link_with: [ - fwupd, - fwupdplugin, - ], + dependencies: [engine_dep], + link_whole: [plugin_builtins], + link_with: [fwupd, fwupdplugin], ) -fwupdtool = executable( +executable( 'fwupdtool', fwupdengine_rs, plugins_hdr, export_dynamic: true, - sources: [ - 'fu-tool.c', - ], - include_directories: [ - root_incdir, - fwupd_incdir, - fwupdplugin_incdir, - ], - dependencies: [ - libfwupd_deps, - libarchive, - client_dep, - valgrind, - ], - link_with: [ - fwupdengine, - fwupdutil, - plugin_libs, - ], + sources: ['fu-tool.c'], + include_directories: [root_incdir, fwupd_incdir, fwupdplugin_incdir], + dependencies: [libfwupd_deps, libarchive, client_dep, valgrind], + link_with: [fwupdengine, fwupdutil, plugin_libs], install: true, install_rpath: libdir_pkg, - install_dir: bindir + install_dir: bindir, ) if build_daemon if get_option('man') - custom_target('fwupdmgr.1', + custom_target( + 'fwupdmgr.1', input: 'fwupdmgr.md', output: 'fwupdmgr.1', command: [ - generate_man, '@INPUT@', '-o', '@OUTPUT@', - '--replace', 'PACKAGE_VERSION', fwupd_version, + generate_man, + '@INPUT@', + '-o', + '@OUTPUT@', + '--replace', + 'PACKAGE_VERSION', + fwupd_version, ], install: true, + install_tag: 'man', install_dir: join_paths(mandir, 'man1'), ) endif if build_docs - md_targets += custom_target('fwupdmgr.md', - input: 'fwupdmgr.md', - output: 'fwupdmgr.md', - command: [ - generate_man, '@INPUT@', '-o', '@OUTPUT@', - '--replace', 'PACKAGE_VERSION', fwupd_version, - '--md', - ], - ) - man_md += ['"fwupdmgr.md"'] + md_targets += custom_target( + 'fwupdmgr.md', + input: 'fwupdmgr.md', + output: 'fwupdmgr.md', + command: [ + generate_man, + '@INPUT@', + '-o', + '@OUTPUT@', + '--replace', + 'PACKAGE_VERSION', + fwupd_version, + '--md', + ], + ) + man_md += ['"fwupdmgr.md"'] endif endif if build_standalone if get_option('man') - custom_target('fwupdtool.1', + custom_target( + 'fwupdtool.1', input: 'fwupdtool.md', output: 'fwupdtool.1', command: [ - generate_man, '@INPUT@', '-o', '@OUTPUT@', - '--replace', 'PACKAGE_VERSION', fwupd_version, + generate_man, + '@INPUT@', + '-o', + '@OUTPUT@', + '--replace', + 'PACKAGE_VERSION', + fwupd_version, ], install: true, + install_tag: 'man', install_dir: join_paths(mandir, 'man1'), ) endif if build_docs - md_targets += custom_target('fwupdtool.md', + md_targets += custom_target( + 'fwupdtool.md', input: 'fwupdtool.md', output: 'fwupdtool.md', command: [ - generate_man, '@INPUT@', '-o', '@OUTPUT@', - '--replace', 'PACKAGE_VERSION', fwupd_version, + generate_man, + '@INPUT@', + '-o', + '@OUTPUT@', + '--replace', + 'PACKAGE_VERSION', + fwupd_version, '--md', ], ) @@ -289,50 +258,25 @@ if build_daemon -# the StartServiceCtrlDispatcherA design is so different use a different source file -if host_machine.system() == 'windows' - daemon_loader_src = 'fu-main-windows.c' -else - daemon_loader_src = 'fu-main.c' -endif + # the StartServiceCtrlDispatcherA design is so different use a different source file + if host_machine.system() == 'windows' + daemon_loader_src = 'fu-main-windows.c' + else + daemon_loader_src = 'fu-main.c' + endif -fwupddaemon_rs = custom_target('fu-daemon-rs', - input: 'fu-daemon.rs', - output: ['fu-daemon-struct.c', 'fu-daemon-struct.h'], - command: [ - python3, - join_paths(meson.project_source_root(), 'libfwupdplugin', 'rustgen.py'), - '@INPUT@', '@OUTPUT0@', '@OUTPUT1@', - ], -) -executable( - 'fwupd', - fwupdengine_rs, - fwupddaemon_rs, - plugins_hdr, - sources: [ - daemon_loader_src, - 'fu-daemon.c', - 'fu-dbus-daemon.c', - ], - include_directories: [ - root_incdir, - fwupd_incdir, - fwupdplugin_incdir, - ], - dependencies: [ - valgrind, - libsystemd, - engine_dep, - ], - link_with: [ - fwupdengine, - plugin_libs, - ], - install: true, - install_rpath: libdir_pkg, - install_dir: daemon_dir -) + executable( + 'fwupd', + fwupdengine_rs, + plugins_hdr, + sources: [daemon_loader_src, 'fu-daemon.c', 'fu-dbus-daemon.c'], + include_directories: [root_incdir, fwupd_incdir, fwupdplugin_incdir], + dependencies: [valgrind, libsystemd, engine_dep], + link_with: [fwupdengine, plugin_libs], + install: true, + install_rpath: libdir_pkg, + install_dir: daemon_dir, + ) endif @@ -342,6 +286,7 @@ env.set('G_TEST_SRCDIR', meson.current_source_dir()) env.set('G_TEST_BUILDDIR', meson.current_build_dir()) env.set('FWUPD_LOCALSTATEDIR', '/tmp/fwupd-self-test/var') + env.set('LSAN_OPTIONS', 'suppressions=@0@'.format(join_paths(meson.project_source_root(), 'data', 'tests', 'lsan-suppressions.txt'))) e = executable( 'fu-self-test', fwupdengine_rs, @@ -351,46 +296,34 @@ noreqs_test_firmware, plugins_hdr, firmware_xml_gz_jcat, - sources: [ - 'fu-self-test.c', - ], - include_directories: [ - root_incdir, - fwupd_incdir, - fwupdplugin_incdir, - ], - dependencies: [ - engine_dep, - ], - link_with: [ - fwupdengine, - fwupdutil, - plugin_libs, - ], - c_args: [ - '-DSRCDIR="' + meson.current_source_dir() + '"', - ], + sources: ['fu-self-test.c'], + include_directories: [root_incdir, fwupd_incdir, fwupdplugin_incdir], + dependencies: [engine_dep], + link_with: [fwupdengine, fwupdutil, plugin_libs], + c_args: ['-DSRCDIR="' + meson.current_source_dir() + '"'], + ) + test( + 'fu-self-test', + e, + is_parallel: false, + timeout: 180, + env: env, ) - test('fu-self-test', e, is_parallel: false, timeout: 180, env: env) if polkit.found() e = executable( 'fu-polkit-test', - sources: [ - 'fu-polkit-test.c', - 'fu-polkit-agent.c', - ], - include_directories: [ - root_incdir, - fwupd_incdir, - ], - dependencies: [ - libfwupd_deps, - ], - link_with: [ - fwupd, - ], + sources: ['fu-polkit-test.c', 'fu-polkit-agent.c'], + include_directories: [root_incdir, fwupd_incdir], + dependencies: [libfwupd_deps], + link_with: [fwupd], + ) + test( + 'fu-polkit-test', + e, + env: { + 'G_DEBUG': 'fatal-criticals', + }, ) - test('fu-self-test', e, env: {'G_DEBUG': 'fatal-criticals'}) endif endif diff -Nru fwupd-2.0.8/src/org.freedesktop.fwupd.xml fwupd-2.0.20/src/org.freedesktop.fwupd.xml --- fwupd-2.0.8/src/org.freedesktop.fwupd.xml 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/org.freedesktop.fwupd.xml 2026-02-26 11:36:18.000000000 +0000 @@ -156,6 +156,17 @@ + + + + + The daemon hardware IDs, sometimes called CHIDs. + + + + + + @@ -856,6 +867,26 @@ + + + + + Deletes metadata from a remote. + + + + + + + + Remote ID, e.g. 'lvfs-testing'. + + + + + + + @@ -1073,6 +1104,33 @@ + + + + + + + + Return search data. + + + + + + + + A search term. + + + + + + + + Releases that match the search term + + + diff -Nru fwupd-2.0.8/src/tests/auth/meson.build fwupd-2.0.20/src/tests/auth/meson.build --- fwupd-2.0.8/src/tests/auth/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/auth/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,23 +1,28 @@ -jcat_tool = find_program('jcat-tool', required: false) +jcat_tool = find_program( + 'jcat-tool', + required: false, +) if jcat_tool.found() -firmware_xml_gz_jcat = custom_target('firmware-xml-gz-jcat', - input: [ - 'firmware.xml.gz', - ], - output: 'firmware.xml.gz.jcat', - command: [ - jcat_tool, '--basename', '--appstream-id', 'localhost', 'self-sign', '@OUTPUT@', '@INPUT@', - ], -) + firmware_xml_gz_jcat = custom_target( + 'firmware-xml-gz-jcat', + input: ['firmware.xml.gz'], + output: 'firmware.xml.gz.jcat', + command: [ + jcat_tool, + '--basename', + '--appstream-id', + 'localhost', + 'self-sign', + '@OUTPUT@', + '@INPUT@', + ], + ) else -firmware_xml_gz_jcat = custom_target('firmware-xml-gz-jcat', - input: [ - 'firmware.xml.gz', - ], - output: 'firmware.xml.gz.jcat', - command: [ - 'touch', '@OUTPUT@', - ], -) + firmware_xml_gz_jcat = custom_target( + 'firmware-xml-gz-jcat', + input: ['firmware.xml.gz'], + output: 'firmware.xml.gz.jcat', + command: ['touch', '@OUTPUT@'], + ) endif diff -Nru fwupd-2.0.8/src/tests/host-emulate/meson.build fwupd-2.0.20/src/tests/host-emulate/meson.build --- fwupd-2.0.8/src/tests/host-emulate/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/host-emulate/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,14 +1,14 @@ if build_standalone gzip = find_program('gzip') - foreach input_file : [ - 'thinkpad-p1-iommu.json', - ] - custom_target(input_file, + foreach input_file : ['thinkpad-p1-iommu.json'] + custom_target( + input_file, input: input_file, output: '@0@.gz'.format(input_file), capture: true, command: [gzip, '-k', '--stdout', '@INPUT@'], install: true, + install_tag: 'tests', install_dir: join_paths(datadir, 'fwupd', 'host-emulate.d'), ) endforeach diff -Nru fwupd-2.0.8/src/tests/missing-hwid/meson.build fwupd-2.0.20/src/tests/missing-hwid/meson.build --- fwupd-2.0.8/src/tests/missing-hwid/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/missing-hwid/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,20 +1,12 @@ -hwid_test_firmware = custom_target('hwid-test-firmware', - input: [ - 'firmware.bin', - 'firmware.metainfo.xml', - ], +hwid_test_firmware = custom_target( + 'hwid-test-firmware', + input: ['firmware.bin', 'firmware.metainfo.xml'], output: 'hwid-1.2.3.cab', - command: [ - gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@', - ], + command: [gcab, '--create', '@OUTPUT@', '@INPUT@'], ) -noreqs_test_firmware = custom_target('noreqs-test-firmware', - input: [ - 'firmware.bin', - 'firmware2.metainfo.xml', - ], +noreqs_test_firmware = custom_target( + 'noreqs-test-firmware', + input: ['firmware.bin', 'firmware2.metainfo.xml'], output: 'noreqs-1.2.3.cab', - command: [ - gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@', - ], + command: [gcab, '--create', '@OUTPUT@', '@INPUT@'], ) diff -Nru fwupd-2.0.8/src/tests/multiple-rels/meson.build fwupd-2.0.20/src/tests/multiple-rels/meson.build --- fwupd-2.0.8/src/tests/multiple-rels/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/multiple-rels/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -1,11 +1,6 @@ -multiple_rels_test_firmware = custom_target('multiple-rels-test-firmware', - input: [ - 'firmware-123.bin', - 'firmware-124.bin', - 'firmware.metainfo.xml', - ], +multiple_rels_test_firmware = custom_target( + 'multiple-rels-test-firmware', + input: ['firmware-123.bin', 'firmware-124.bin', 'firmware.metainfo.xml'], output: 'multiple-rels-1.2.4.cab', - command: [ - gcab, '--create', '--nopath', '@OUTPUT@', '@INPUT@', - ], + command: [gcab, '--create', '@OUTPUT@', '@INPUT@'], ) diff -Nru fwupd-2.0.8/src/tests/remotes2.d/lvfs-testing.conf fwupd-2.0.20/src/tests/remotes2.d/lvfs-testing.conf --- fwupd-2.0.8/src/tests/remotes2.d/lvfs-testing.conf 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/remotes2.d/lvfs-testing.conf 2026-02-26 11:36:18.000000000 +0000 @@ -6,6 +6,7 @@ MetadataURI=https://cdn.fwupd.org/downloads/firmware-testing.xml.@compression@ PrivacyURI=https://lvfs.readthedocs.io/en/latest/privacy.html ReportURI=https://fwupd.org/lvfs/firmware/report +FirmwareBaseURI=https://fwupd.org/downloads OrderBefore=lvfs AutomaticReports=false ApprovalRequired=false diff -Nru fwupd-2.0.8/src/tests/remotes2.d/lvfs.conf fwupd-2.0.20/src/tests/remotes2.d/lvfs.conf --- fwupd-2.0.8/src/tests/remotes2.d/lvfs.conf 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/remotes2.d/lvfs.conf 2026-02-26 11:36:18.000000000 +0000 @@ -6,6 +6,7 @@ MetadataURI=https://cdn.fwupd.org/downloads/firmware.xml.@compression@ ReportURI=https://fwupd.org/lvfs/firmware/report PrivacyURI=https://lvfs.readthedocs.io/en/latest/privacy.html +FirmwareBaseURI=https://fwupd.org/downloads AutomaticReports=false AutomaticSecurityReports=false ApprovalRequired=false diff -Nru fwupd-2.0.8/src/tests/remotes2.d/meson.build fwupd-2.0.20/src/tests/remotes2.d/meson.build --- fwupd-2.0.8/src/tests/remotes2.d/meson.build 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/remotes2.d/meson.build 2026-02-26 11:36:18.000000000 +0000 @@ -11,6 +11,7 @@ output: 'lvfs.conf', configuration: con3, install: true, + install_tag: 'runtime', install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), ) configure_file( @@ -18,6 +19,7 @@ output: 'lvfs-testing.conf', configuration: con3, install: true, + install_tag: 'runtime', install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), ) i18n.merge_file( @@ -27,7 +29,8 @@ po_dir: join_paths(meson.project_source_root(), 'po'), data_dirs: join_paths(meson.project_source_root(), 'po'), install: true, - install_dir: join_paths(get_option('datadir'), 'fwupd', 'metainfo') + install_tag: 'runtime', + install_dir: join_paths(get_option('datadir'), 'fwupd', 'metainfo'), ) i18n.merge_file( input: 'lvfs-testing.metainfo.xml', @@ -36,12 +39,15 @@ po_dir: join_paths(meson.project_source_root(), 'po'), data_dirs: join_paths(meson.project_source_root(), 'po'), install: true, - install_dir: join_paths(get_option('datadir'), 'fwupd', 'metainfo') + install_tag: 'runtime', + install_dir: join_paths(get_option('datadir'), 'fwupd', 'metainfo'), ) endif -install_data('README.md', - install_dir: join_paths(datadir, 'fwupd', 'remotes.d', 'vendor', 'firmware') +install_data( + 'README.md', + install_tag: 'doc', + install_dir: join_paths(datadir, 'fwupd', 'remotes.d', 'vendor', 'firmware'), ) # replace @datadir@ @@ -52,6 +58,7 @@ output: 'vendor.conf', configuration: con2, install: get_option('vendor_metadata'), + install_tag: 'runtime', install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), ) configure_file( @@ -59,5 +66,6 @@ output: 'vendor-directory.conf', configuration: con2, install: true, + install_tag: 'runtime', install_dir: join_paths(sysconfdir, 'fwupd', 'remotes.d'), ) diff -Nru fwupd-2.0.8/src/tests/sys/class/mei/mei0/uevent fwupd-2.0.20/src/tests/sys/class/mei/mei0/uevent --- fwupd-2.0.8/src/tests/sys/class/mei/mei0/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/class/mei/mei0/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -MEI_CL_VERSION=1 -MEI_CL_UUID=12f80028-b4b7-4b2d-aca8-46e0ff65814c -MEI_CL_NAME= -MODALIAS=mei::12f80028-b4b7-4b2d-aca8-46e0ff65814c:01: diff -Nru fwupd-2.0.8/src/tests/sys/class/mei/mei0/uuid fwupd-2.0.20/src/tests/sys/class/mei/mei0/uuid --- fwupd-2.0.8/src/tests/sys/class/mei/mei0/uuid 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/class/mei/mei0/uuid 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -12f80028-b4b7-4b2d-aca8-46e0ff65814c \ No newline at end of file diff -Nru fwupd-2.0.8/src/tests/sys/class/mei/mei0/version fwupd-2.0.20/src/tests/sys/class/mei/mei0/version --- fwupd-2.0.8/src/tests/sys/class/mei/mei0/version 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/class/mei/mei0/version 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -01 \ No newline at end of file diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -MEI_CL_VERSION=1 -MEI_CL_UUID=12f80028-b4b7-4b2d-aca8-46e0ff65814c -MEI_CL_NAME= -MODALIAS=mei::12f80028-b4b7-4b2d-aca8-46e0ff65814c:01: diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/uuid fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/uuid --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/uuid 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/uuid 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -12f80028-b4b7-4b2d-aca8-46e0ff65814c \ No newline at end of file diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/version fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/version --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/version 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/0000:00:16.0-12f80028-b4b7-4b2d-aca8-46e0ff65814c/version 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -01 \ No newline at end of file diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/class fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/class --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/class 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/class 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0x078000 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/device fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/device --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/device 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/device 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0x06e0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/dev fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/dev --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/dev 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/dev 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -235:0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/fw_status fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/fw_status --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/fw_status 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/fw_status 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -A0000245 -80218506 -00000030 -00004404 -00001F01 -44400BC9 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/fw_ver fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/fw_ver --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/fw_ver 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/fw_ver 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -0:14.1.70.2228 -0:14.1.70.2228 -0:14.0.36.1158 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/kind fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/kind --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/kind 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/kind 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -mei diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,4 +0,0 @@ -MEI_CL_VERSION=1 -MEI_CL_UUID=12f80028-b4b7-4b2d-aca8-46e0ff65814c -MEI_CL_NAME= -MODALIAS=mei::12f80028-b4b7-4b2d-aca8-46e0ff65814c:01: diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/uuid fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/uuid --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/uuid 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/uuid 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -12f80028-b4b7-4b2d-aca8-46e0ff65814c \ No newline at end of file diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/version fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/version --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/version 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/subsystem/mei0/version 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -01 \ No newline at end of file diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/mei/mei0/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -MAJOR=235 -MINOR=0 -DEVNAME=mei0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/revision fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/revision --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/revision 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/revision 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0x00 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/class fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/class --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/class 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/class 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0x0c0330 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/device fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/device --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/device 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/device 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0x06ed diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/revision fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/revision --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/revision 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/revision 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0x00 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/rom fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/rom --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/rom 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/rom 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -hello world diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/subsystem_device fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/subsystem_device --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/subsystem_device 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/subsystem_device 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0x22c2 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/subsystem_vendor fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/subsystem_vendor --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/subsystem_vendor 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/subsystem_vendor 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0x17aa diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -DRIVER=xhci_hcd -PCI_CLASS=C0330 -PCI_ID=8086:06ED -PCI_SUBSYS_ID=17AA:22C2 -PCI_SLOT_NAME=0000:00:14.0 -MODALIAS=pci:v00008086d000006EDsv000017AAsd000022C2bc0Csc03i30 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/dev fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/dev --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/dev 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/dev 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -241:1 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -MAJOR=241 -MINOR=1 -DEVNAME=hidraw1 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/modalias fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/modalias --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/modalias 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/modalias 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -hid:b0003g0001v0000093Ap00002862 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/0003:093A:2862.0076/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -DRIVER=hid-generic -HID_ID=0003:0000093A:00002862 -HID_NAME=PIXART Pixart dual-mode mouse -HID_PHYS=usb-0000:00:14.0-1/input1 -HID_UNIQ= -MODALIAS=hid:b0003g0001v0000093Ap00002862 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceClass fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceClass --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceClass 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceClass 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -03 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceNumber fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceNumber --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceNumber 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceNumber 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -01 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceProtocol fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceProtocol --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceProtocol 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceProtocol 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -02 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceSubClass fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceSubClass --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceSubClass 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/bInterfaceSubClass 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -01 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/modalias fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/modalias --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/modalias 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/modalias 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -usb:v093Ap2862d0000dc00dsc00dp00ic03isc01ip02in01 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -DEVTYPE=usb_interface -DRIVER=usbhid -PRODUCT=93a/2862/0 -TYPE=0/0/0 -INTERFACE=3/1/2 -MODALIAS=usb:v093Ap2862d0000dc00dsc00dp00ic03isc01ip02in01 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/dev fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/dev --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/dev 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/dev 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -81:0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/dev_debug fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/dev_debug --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/dev_debug 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/dev_debug 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/index fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/index --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/index 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/index 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/name fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/name --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/name 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/name 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Integrated Camera: Integrated C diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/1-1:1.1/video4linux/video0/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -MAJOR=81 -MINOR=0 -DEVNAME=video0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceClass fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceClass --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceClass 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceClass 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -00 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceProtocol fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceProtocol --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceProtocol 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceProtocol 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -00 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceSubClass fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceSubClass --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceSubClass 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bDeviceSubClass 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -00 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bcdDevice fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bcdDevice --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bcdDevice 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/bcdDevice 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0000 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/busnum fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/busnum --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/busnum 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/busnum 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -1 Binary files /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/descriptors and /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/descriptors differ diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/dev fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/dev --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/dev 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/dev 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -189:23 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/devnum fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/devnum --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/devnum 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/devnum 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -24 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/idProduct fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/idProduct --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/idProduct 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/idProduct 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -2862 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/idVendor fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/idVendor --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/idVendor 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/idVendor 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -093a diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/manufacturer fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/manufacturer --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/manufacturer 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/manufacturer 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -PIXART diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/product fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/product --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/product 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/product 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Pixart dual-mode mouse diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/removable fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/removable --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/removable 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/removable 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -removable diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -MAJOR=189 -MINOR=23 -DEVNAME=bus/usb/001/024 -DEVTYPE=usb_device -DRIVER=usb -PRODUCT=93a/2862/0 -TYPE=0/0/0 -BUSNUM=001 -DEVNUM=024 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/version fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/version --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/version 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/1-1/version 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ - 1.10 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceClass fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceClass --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceClass 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceClass 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -09 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceProtocol fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceProtocol --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceProtocol 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceProtocol 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -01 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceSubClass fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceSubClass --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceSubClass 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bDeviceSubClass 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -00 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bcdDevice fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bcdDevice --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bcdDevice 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/bcdDevice 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0608 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/busnum fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/busnum --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/busnum 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/busnum 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -1 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/dev fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/dev --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/dev 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/dev 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -189:0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/devnum fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/devnum --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/devnum 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/devnum 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -1 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/devpath fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/devpath --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/devpath 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/devpath 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/idProduct fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/idProduct --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/idProduct 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/idProduct 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0002 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/idVendor fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/idVendor --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/idVendor 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/idVendor 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -1d6b diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/manufacturer fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/manufacturer --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/manufacturer 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/manufacturer 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Linux 6.8.11-300.fc40.x86_64 xhci-hcd diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/dev fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/dev --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/dev 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/dev 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -241:1 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/hidraw/hidraw1/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -MAJOR=241 -MINOR=1 -DEVNAME=hidraw1 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/modalias fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/modalias --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/modalias 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/modalias 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -hid:b0003g0001v0000093Ap00002862 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/0003:093A:2862.0076/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -DRIVER=hid-generic -HID_ID=0003:0000093A:00002862 -HID_NAME=PIXART Pixart dual-mode mouse -HID_PHYS=usb-0000:00:14.0-1/input1 -HID_UNIQ= -MODALIAS=hid:b0003g0001v0000093Ap00002862 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceClass fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceClass --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceClass 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceClass 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -03 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceNumber fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceNumber --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceNumber 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceNumber 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -01 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceProtocol fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceProtocol --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceProtocol 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceProtocol 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -02 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceSubClass fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceSubClass --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceSubClass 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/bInterfaceSubClass 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -01 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/modalias fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/modalias --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/modalias 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/modalias 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -usb:v093Ap2862d0000dc00dsc00dp00ic03isc01ip02in01 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -DEVTYPE=usb_interface -DRIVER=usbhid -PRODUCT=93a/2862/0 -TYPE=0/0/0 -INTERFACE=3/1/2 -MODALIAS=usb:v093Ap2862d0000dc00dsc00dp00ic03isc01ip02in01 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/dev fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/dev --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/dev 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/dev 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -81:0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/dev_debug fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/dev_debug --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/dev_debug 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/dev_debug 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/index fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/index --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/index 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/index 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/name fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/name --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/name 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/name 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Integrated Camera: Integrated C diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/1-1:1.1/video4linux/video0/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,3 +0,0 @@ -MAJOR=81 -MINOR=0 -DEVNAME=video0 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceClass fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceClass --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceClass 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceClass 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -00 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceProtocol fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceProtocol --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceProtocol 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceProtocol 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -00 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceSubClass fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceSubClass --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceSubClass 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bDeviceSubClass 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -00 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bcdDevice fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bcdDevice --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bcdDevice 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/bcdDevice 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0000 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/busnum fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/busnum --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/busnum 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/busnum 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -1 Binary files /srv/release.debian.org/tmp/_HkNOXey3S/fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/descriptors and /srv/release.debian.org/tmp/qpinypKOe0/fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/descriptors differ diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/dev fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/dev --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/dev 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/dev 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -189:23 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/devnum fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/devnum --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/devnum 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/devnum 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -24 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/idProduct fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/idProduct --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/idProduct 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/idProduct 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -2862 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/idVendor fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/idVendor --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/idVendor 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/idVendor 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -093a diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/manufacturer fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/manufacturer --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/manufacturer 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/manufacturer 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -PIXART diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/product fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/product --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/product 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/product 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -Pixart dual-mode mouse diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/removable fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/removable --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/removable 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/removable 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -removable diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -MAJOR=189 -MINOR=23 -DEVNAME=bus/usb/001/024 -DEVTYPE=usb_device -DRIVER=usb -PRODUCT=93a/2862/0 -TYPE=0/0/0 -BUSNUM=001 -DEVNUM=024 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/version fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/version --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/version 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/subsystem/devices/1-1/version 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ - 1.10 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,9 +0,0 @@ -MAJOR=189 -MINOR=0 -DEVNAME=bus/usb/001/001 -DEVTYPE=usb_device -DRIVER=usb -PRODUCT=1d6b/2/608 -TYPE=9/0/1 -BUSNUM=001 -DEVNUM=001 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/version fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/version --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/version 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/usb1/version 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ - 2.00 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/vendor fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/vendor --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/vendor 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem/devices/0000:00:14.0/vendor 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0x8086 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem_device fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem_device --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem_device 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem_device 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0x22c2 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem_vendor fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem_vendor --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem_vendor 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/subsystem_vendor 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0x17aa diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/uevent fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/uevent --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/uevent 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/uevent 1970-01-01 00:00:00.000000000 +0000 @@ -1,6 +0,0 @@ -DRIVER=mei_me -PCI_CLASS=78000 -PCI_ID=8086:06E0 -PCI_SUBSYS_ID=17AA:22C2 -PCI_SLOT_NAME=0000:00:16.0 -MODALIAS=pci:v00008086d000006E0sv000017AAsd000022C2bc07sc80i00 diff -Nru fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/vendor fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/vendor --- fwupd-2.0.8/src/tests/sys/devices/pci0000:00/0000:00:16.0/vendor 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/devices/pci0000:00/0000:00:16.0/vendor 1970-01-01 00:00:00.000000000 +0000 @@ -1 +0,0 @@ -0x8086 diff -Nru fwupd-2.0.8/src/tests/sys/fs/selinux/enforce fwupd-2.0.20/src/tests/sys/fs/selinux/enforce --- fwupd-2.0.8/src/tests/sys/fs/selinux/enforce 1970-01-01 00:00:00.000000000 +0000 +++ fwupd-2.0.20/src/tests/sys/fs/selinux/enforce 2026-02-26 11:36:18.000000000 +0000 @@ -0,0 +1 @@ +0 \ No newline at end of file diff -Nru fwupd-2.0.8/subprojects/.gitignore fwupd-2.0.20/subprojects/.gitignore --- fwupd-2.0.8/subprojects/.gitignore 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/subprojects/.gitignore 2026-02-26 11:36:18.000000000 +0000 @@ -5,3 +5,4 @@ libxmlb fwupd-efi passim +.wraplock diff -Nru fwupd-2.0.8/subprojects/flashrom.wrap fwupd-2.0.20/subprojects/flashrom.wrap --- fwupd-2.0.8/subprojects/flashrom.wrap 2025-04-09 14:58:02.000000000 +0000 +++ fwupd-2.0.20/subprojects/flashrom.wrap 2026-02-26 11:36:18.000000000 +0000 @@ -1,4 +1,4 @@ [wrap-git] directory = flashrom url = https://github.com/flashrom/flashrom -revision = v1.4.0 +revision = v1.6.0